This document describes how to use Dire Wolf as an iGate, digipeater, and decoder for LoRa APRS using a Raspberry Pi with a supported LoRa module.
Native SPI driver (LCHANNEL) |
Python bridge (lora_kiss_bridge.py) |
|
|---|---|---|
| How it works | loraspi.c compiled into Dire Wolf; talks to SX1276/SX1262 directly via Linux SPI |
Python script using LoRaRF library connects to Dire Wolf over TCP |
| RX | yes | yes |
| TX | yes | yes |
| Python required | no | yes (pip3 install LoRaRF pyyaml) |
| Extra processes | none | lora_kiss_bridge.py must be running |
| Supported chips | SX1276, SX1278, SX1262 | any chip supported by LoRaRF |
| Recommended for | supported hats wired to Pi SPI bus | unsupported hardware, non-Linux hosts |
For most users with a supported LoRa hat directly wired to the Pi, LCHANNEL is the recommended approach — no Python packages or separate bridge process required.
LoRa hat (SX1276 / SX1262 wired to SPI bus)
│ Linux spidev + sysfs GPIO
▼
Dire Wolf (loraspi.c) ← direwolf.conf (LCHANNEL, LORAHW, LORAFREQ …)
│
iGate / digipeater / decoder
LoRa radio hardware (SX1276, SX1262, etc.)
│ SPI/GPIO (via LoRaRF Python library)
▼
lora_kiss_bridge.py ← lora.conf (RF parameters, hardware profile)
│ TNC2 text lines over TCP
▼
Dire Wolf (loratnc.c) ← direwolf.conf (LORAPORT)
│
iGate / digipeater / decoder
Hardware (both approaches):
- Raspberry Pi (any model with SPI)
- Supported LoRa module (see Hardware Profiles below)
SPI must be enabled before Dire Wolf or the bridge can communicate with the LoRa module:
sudo raspi-config nonint do_spi 0 sudo rebootVerify with:
ls /dev/spidev*— you should see/dev/spidev0.0and/dev/spidev0.1.
Additional requirements for the Python bridge only:
pip3 install LoRaRF pyyamlNote for Raspberry Pi OS Bookworm / Debian Trixie (2023+): pip3 will refuse to install system-wide packages by default. Add
--break-system-packagesor use a virtual environment:pip3 install --break-system-packages LoRaRF pyyaml
The LCHANNEL block must appear before any PBEACON lines that reference
that channel number.
# No physical audio device (LoRa-only setup)
ADEVICE null null
# LoRa SPI hat — native driver
# Must appear before any PBEACON lines referencing channel 10
LCHANNEL 10
MYCALL W1ABC-10
LORAHW lorapi_rfm95w # hardware profile (see table below)
LORAFREQ 433.775 # MHz
LORASF 12 # spreading factor
LORABW 125 # kHz bandwidth
LORACR 5 # coding rate 4/5
LORASW 0x12 # LoRa APRS sync word
LORATXPOWER 17 # dBm
# Position beacon over LoRa RF
PBEACON delay=1 every=30 sendto=10 overlay=L symbol="igate" lat=0.0000 long=0.0000 comment="LoRa APRS iGate"
# Also beacon to APRS-IS
PBEACON delay=1 every=30 sendto=IG overlay=L symbol="igate" lat=0.0000 long=0.0000 comment="LoRa APRS iGate"
IGSERVER noam.aprs2.net
IGLOGIN W1ABC-10 12345
Replace 0.0000 with your actual latitude and longitude in decimal degrees.
Replace lorapi_rfm95w with the profile matching your hardware (see table below).
Generate your APRS passcode at https://apps.magicbug.co.uk/passcode if needed.
direwolf -c ~/direwolf.confNo bridge script needed. Dire Wolf opens the SPI device at startup and logs:
loraspi ch10: SX1276 init OK, freq=433.775 MHz SF=12 BW=125 CR=5 SW=0x12 TXpow=17 dBm
# In direwolf.conf — use the channel number from LCHANNEL
DIGIPEAT 10 10 ^TEST$ ^WIDE[12]-[12]$
| File | Location on Pi | Purpose |
|---|---|---|
lora.conf |
~/lora.conf |
LoRa RF parameters and hardware profile |
direwolf.conf |
~/direwolf.conf |
Dire Wolf station configuration |
lora_kiss_bridge.py |
~/direwolf/scripts/ |
LoRa hardware driver script |
hardware_profiles.yaml |
~/direwolf/scripts/ |
Hardware pin definitions |
Note:
lora_kiss_bridge.pyandhardware_profiles.yamlare not installed to/usr/local/binbymake install. Run them directly from the source tree, or copy them manually:sudo cp ~/direwolf/scripts/lora_kiss_bridge.py /usr/local/bin/ sudo cp ~/direwolf/scripts/hardware_profiles.yaml /usr/local/bin/
Copy the template from the source tree to your home directory and edit it:
cp ~/direwolf/conf/lora.conf ~/lora.confKey settings:
# Select your hardware (see hardware_profiles.yaml for options)
HARDWARE meshadv
# RF parameters — must match other LoRa APRS stations
LORAFREQ 433.775 # MHz (Region 1/3) or 915.000 (Region 2)
LORABW 125 # kHz bandwidth
LORASF 12 # Spreading factor (SF12 = max range)
LORACR 5 # Coding rate 4/5
LORASW 0x12 # LoRa APRS sync word
LORATXPOWER 17 # dBm
# TCP connection to Dire Wolf
KISSHOST 127.0.0.1
KISSPORT 8002
MYCALL W1ABC-10
# No physical audio device (LoRa-only setup)
ADEVICE null null
# LoRa APRS bridge connection — Dire Wolf listens, the bridge connects
LORAPORT 8002
# Position beacon directly to APRS-IS (no RF TX required for iGate-only use)
PBEACON delay=1 every=30 sendto=IG overlay=L symbol="igate" lat=0.0000 long=0.0000 comment="LoRa APRS iGate 433.775 MHz SF12"
IGSERVER noam.aprs2.net
IGLOGIN W1ABC-10 12345
Start Dire Wolf before lora_kiss_bridge.py. Dire Wolf listens on
LORAPORT; the bridge connects to it and retries automatically if not yet ready.
# Terminal 1 — Dire Wolf starts the LORAPORT listener
direwolf -c ~/direwolf.conf
# Terminal 2 — bridge connects to Dire Wolf
python3 /usr/local/bin/lora_kiss_bridge.py -c ~/lora.conf
# Debug logging
python3 /usr/local/bin/lora_kiss_bridge.py -c ~/lora.conf --log-level DEBUG# In direwolf.conf — replace 2 with the LoRa channel number printed at startup
DIGIPEATER 2 WIDE1-1
Profiles are defined in hardware_profiles.yaml (Python bridge) or selected
with LORAHW in direwolf.conf (native SPI driver). Both use the same
profile names.
| Profile name | Module | Chip | Frequency |
|---|---|---|---|
lorapi_rfm95w |
Digital Concepts LoRa-Pi (RFM95W) | SX1276 | 433/868/915 MHz |
generic_sx1276 |
Generic SX1276/SX1278 breakout | SX1276 | varies |
meshadv |
MeshAdv-Pi Hat | SX1262 | 900 MHz |
e22_900m30s |
Ebyte E22-900M30S | SX1262 | 868/915 MHz |
e22_400m30s |
Ebyte E22-400M30S | SX1268 | 433/470 MHz |
ebyte_e22 |
Ebyte E22 generic | SX1262 | varies |
ttgo_uart |
TTGO/Heltec over USB serial | external | varies |
To add a new profile for the Python bridge, copy an existing entry in
hardware_profiles.yaml and adjust the SPI bus, GPIO pins, and chip type.
To add a new profile for the native SPI driver, add a row to the
hw_profiles[] array in src/loraspi.c and rebuild.
Service files are provided in systemd/.
Native SPI driver — only Dire Wolf itself needs to run as a service:
sudo cp systemd/direwolf.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable direwolf
sudo systemctl start direwolfPython bridge — additionally install the bridge service (it depends on Dire Wolf and starts after it):
sudo cp systemd/lora-kiss-bridge.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable lora-kiss-bridge
sudo systemctl start lora-kiss-bridge| Region | Frequency | Notes |
|---|---|---|
| Region 1 (Europe, Africa, Middle East) | 433.775 MHz | Standard |
| Region 2 (Americas) | 433.775 MHz | Standard |
| Region 3 (Asia-Pacific) | 433.775 MHz | Standard |
Standard parameters: SF12, BW125, CR4/5, sync word 0x12.
| Color | Meaning |
|---|---|
| Green | Packet received from LoRa radio |
| Magenta | Packet transmitted over LoRa radio |
| Red | Error or invalid/dropped packet |
| Dark green | Debug messages |
Colors are suppressed automatically when output is redirected to a file or systemd journal (when stderr is not a TTY).
WARNING: Dropping packet with invalid TNC2 header (Python bridge) — the
bridge decoded bytes from the LoRa radio that do not form a valid APRS address
header. Usually a weak-signal packet that the hardware partially corrupted.
The packet is discarded before Dire Wolf sees it.
Native SPI driver: no init message at startup
- Check SPI is enabled:
ls /dev/spidev* - Verify
LORAHWindirewolf.confmatches a known profile name (case-sensitive) - Check SPI device permissions:
ls -l /dev/spidev* - Ensure the
LCHANNELblock appears before anyPBEACONlines indirewolf.conf
Native SPI driver: "Config file: Send to channel N is not valid"
- The
LCHANNELblock must appear before anyPBEACON sendto=Nlines indirewolf.conf. Move theLCHANNELblock to the top of the file.
Python bridge fails to start
- Check
HARDWAREinlora.confmatches a profile inhardware_profiles.yaml - Verify SPI is enabled:
ls /dev/spi* - Check GPIO permissions:
ls -l /dev/gpiomem
Dire Wolf shows "LoRa bridge: waiting for connection" (LORAPORT approach)
- Make sure Dire Wolf is running before the bridge
- Check
KISSPORTinlora.confmatchesLORAPORTindirewolf.conf
No packets received
- Confirm frequency and sync word match other stations in your area (0x12 is standard)
- Verify spreading factor (SF12 is standard; some networks use SF9 or SF11)
- Try
--log-level DEBUG(Python bridge) or check Dire Wolf console for SPI errors