Building a Raspberry Pi LoRa gateway for The Things Network (TTN) or Helium requires more than just stacking a HAT on a Pi and plugging it in. The modern standard relies on the Semtech SX1302 baseband processor, which demands precise SPI timing, correct reset pin sequencing, and strict thermal management. If you are migrating from the older SX1301 chips, be aware that TTN V3 requires the fine timestamping capabilities that only the SX1302 (and SX1303) provide.
This guide targets the Raspberry Pi 4 Model B (4GB RAM) paired with the RAK2287 Pi HAT (SX1302). We will cover the physical assembly, verify the SPI bus with a custom Python script before launching the heavy C-based packet forwarder, and debug the exact error strings the Semtech daemon throws when things go wrong.
Build Spec Sheet & Difficulty Rating
- Difficulty: Intermediate (Requires Linux CLI and basic GPIO knowledge)
- Time to Complete: 2–3 hours (including OS flashing and TTN registration)
- Estimated Cost: $130–$160 USD
- Target Board Variant: Raspberry Pi 4 Model B (4GB RAM) running Raspberry Pi OS Lite (64-bit, Bookworm)
- Target Concentrator: RAKwireless RAK2287 (SX1302) Pi HAT
Hardware Selection and Pin Mapping
Do not buy the older RAK2245 (SX1301) for a new build. The SX1301 runs hot, lacks hardware fine timestamping, and is largely deprecated for new TTN V3 gateway registrations. The RAK2287 uses the SX1302, which integrates the LoRa transceivers and runs significantly cooler.
Required Parts List
- Compute: Raspberry Pi 4 Model B (4GB variant recommended for packet forwarder overhead and logging)
- Concentrator: RAK2287 Pi HAT (Choose 868 MHz for EU/AU or 915 MHz for US/Americas)
- Antenna: 3 dBi or 5 dBi LoRa tuned dipole antenna (N-male to SMA-male pigtail if required)
- GPS: Active GPS antenna with u.FL connector (required for TTN V3 time-sync if no internet fallback)
- Power: Official Raspberry Pi 27W USB-C Power Supply (5.1V / 5A) — do not use cheap phone chargers.
- Thermal: Aluminum heatsink case for the Pi 4 (e.g., Argon ONE or Geekworm armor case)
SPI and GPIO Pin Mapping
The RAK2287 communicates via the primary SPI0 bus. The most critical pin is the reset line; if the packet forwarder cannot toggle this pin, the SX1302 will remain in a locked state.
| Function | Pi 4 BCM GPIO | Physical Pin | RAK2287 HAT Pin |
|---|---|---|---|
| SPI MOSI | GPIO 10 | 19 | MOSI |
| SPI MISO | GPIO 9 | 21 | MISO |
| SPI SCLK | GPIO 11 | 23 | SCK |
| SPI CE0 | GPIO 8 | 24 | CS |
| SX1302 Reset | GPIO 17 | 11 | RST |
| GPS PPS (Optional) | GPIO 4 | 7 | PPS |
Wiring and Physical Assembly
Follow this exact sequence. Skipping step 1 is the most common way hobbyists destroy their concentrator boards.
- Attach the Antenna First: Never apply power to the RAK2287 without the LoRa antenna connected. The SX1302 power amplifiers will experience severe voltage standing wave ratio (VSWR) reflections and can burn out the RF front-end in seconds.
- Mount the HAT: Use the included M2.5 brass standoffs. Ensure no metal standoffs touch the unshielded test points on the top of the Pi 4.
- Connect the GPS: Snap the u.FL connector straight down. Do not angle it, or you will snap the ceramic base off the PCB.
- Apply Thermal Pads: If your Pi case does not have an integrated thermal block, apply a 1mm thermal pad between the SX1302 chip shield and the metal case lid.
- Enable SPI: Boot the Pi, run
sudo raspi-config, navigate to Interface Options > SPI, and enable it. Alternatively, adddtparam=spi=onto/boot/firmware/config.txt.
SPI Verification Script (Python)
Before compiling the Semtech C-based packet forwarder, verify your SPI wiring. This Python script targets the Raspberry Pi 4 Model B and reads the SX1302 silicon version register. If this script fails, the packet forwarder will definitely fail.
Prerequisites: sudo apt install python3-spidev python3-rpi.gpio
import spidev
import RPi.GPIO as GPIO
import time
import sys
# Pin Definitions for RAK2287 / SX1302 on Raspberry Pi 4
SPI_BUS = 0
SPI_DEVICE = 0
RESET_PIN = 17 # BCM GPIO 17 (Physical Pin 11)
def setup_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setup(RESET_PIN, GPIO.OUT)
def reset_sx1302():
"""Hardware reset sequence required by SX1302 datasheet."""
print("Toggling SX1302 reset pin...")
GPIO.output(RESET_PIN, GPIO.LOW)
time.sleep(0.1)
GPIO.output(RESET_PIN, GPIO.HIGH)
time.sleep(0.1) # Wait for boot sequence
def main():
setup_gpio()
reset_sx1302()
spi = spidev.SpiDev()
try:
spi.open(SPI_BUS, SPI_DEVICE)
spi.max_speed_hz = 2000000 # 2MHz safe initial speed for setup
spi.mode = 0
except Exception as e:
print(f"FATAL: Failed to open SPI device: {e}")
print("Check if SPI is enabled in raspi-config and if /dev/spidev0.0 exists.")
sys.exit(1)
# Read SX1302 Silicon Version Register (Address 0x00)
# SPI read protocol: send [Address with MSB=0, Dummy Byte]
try:
resp = spi.xfer2([0x00, 0x00])
version = resp[1]
# 0x10 is the expected Silicon ID for SX1302
if version == 0x10:
print(f"SUCCESS: SX1302 detected. Silicon version: 0x{version:02X}")
print("SPI wiring and reset pin are correctly configured.")
else:
print(f"WARNING: Unexpected version ID: 0x{version:02X}.")
print("Check MISO/MOSI swap or loose jumper wires.")
except Exception as e:
print(f"SPI transfer failed: {e}")
finally:
spi.close()
GPIO.cleanup()
if __name__ == "__main__":
main()
Troubleshooting Packet Forwarder Errors
When you launch the Semtech UDP packet forwarder (usually via sudo ./lora_pkt_fwd -c global_conf.json), it will either connect to TTN or throw a C-level error. Here is how to debug the exact error strings.
The First Three Things to Check When It Fails
- SPI Overlay and Baud Rate: Ensure
dtparam=spi=onis inconfig.txtand that yourglobal_conf.jsonspecifies a SPI speed no higher than8000000(8MHz) for the SX1302. - Reset Pin Mapping: Verify the
"reset_pin"value in the"SX1302_conf"section of your JSON config matches your physical wiring (usually17for RAK2287). - 5V Rail Voltage Drop: Measure the 5V pin with a multimeter while the gateway is booting. If it drops below 4.8V, the SX1302 will brownout during the initial RF calibration phase.
Error: lgw_connect: ERROR: failed to open SPI device
What it means: The Linux kernel is blocking the packet forwarder from accessing /dev/spidev0.0.
Ranked Causes & Fixes:
- SPI not enabled: Run
ls /dev/spi*. If nothing returns, enable it viaraspi-configand reboot. - Wrong SPI path in config: Check
global_conf.json. Ensure"spidev_path"is exactly"/dev/spidev0.0". - Permissions: The user running the daemon lacks dialout/gpio access. Run with
sudoor add the user to thespiandgpiogroups.
Error: ERROR: [main] concentrator start failed
What it means: The SPI bus is open, but the SX1302 refused the boot/calibration sequence.
Ranked Causes & Fixes:
- Incorrect Reset Pin: The daemon toggled the wrong GPIO, leaving the SX1302 in reset. Fix the
"reset_pin"integer in your JSON. - Missing Antenna VSWR Protection: The SX1302 performs an internal impedance check on boot. If the antenna is missing or the SMA pigtail is broken, it halts. Verify antenna continuity with a multimeter.
- Thermal Throttling: If the Pi 4 is at 85°C, the core clock throttles, disrupting SPI timing. Check temps with
vcgencmd measure_tempand improve case airflow.
Extending and Simplifying Your Gateway Build
Once you have the baseline UDP forwarder running, you have two paths forward depending on your maintenance tolerance.
How to Simplify the Build
If managing Linux daemons, JSON configs, and systemd services feels like overhead, abandon the manual build. Flash RAKwireless RAK OS or use BalenaOS with a pre-built TTN gateway container. These images handle the SPI overlays, reset pin toggling, and remote updates via a web GUI out of the box. You lose some low-level debugging access, but you gain 99.9% uptime without babysitting the terminal.
How to Extend the Build
For advanced makers, the Raspberry Pi 4 has plenty of leftover I/O:
- Add Environmental Telemetry: Wire an I2C BME280 sensor to GPIO 2 (SDA) and GPIO 3 (SCL). Use a Python script to push temperature and humidity data to your own MQTT broker, turning your gateway into a weather station.
- Stratum-1 Timing: If your gateway is in a remote area with poor internet, TTN requires precise time-sync for Time Difference of Arrival (TDOA) geolocation. Connect a GPS module with a Pulse-Per-Second (PPS) output to GPIO 4, and configure
chronyto discipline the Pi's system clock to the GPS atomic time.
Raspberry Pi LoRa Gateway FAQ
Can I use a Raspberry Pi Zero 2 W for a LoRa gateway?
Technically yes, but practically no. The Pi Zero 2 W shares its SPI and USB buses on the same internal controller, which causes bandwidth bottlenecks when the SX1302 is pushing high packet rates. Furthermore, the 512MB RAM is quickly consumed by the packet forwarder, logging, and OS overhead, leading to out-of-memory (OOM) kills. Stick to the Pi 4 (4GB) or Pi 5 for a reliable gateway.
Why does my Raspberry Pi LoRa gateway keep dropping offline in TTN?
TTN marks a gateway as "offline" if it misses a 30-second keep-alive heartbeat. This is almost always a network issue, not an RF issue. Check your router for aggressive firewall rules blocking outbound UDP traffic on ports 1700 and 8080. If you are on a cellular backup connection, ensure your APN allows UDP passthrough. Finally, verify that your Pi isn't dropping its Wi-Fi connection to save power by disabling Wi-Fi power management in NetworkManager.
Do I need a GPS module for my Raspberry Pi LoRa gateway?
For basic packet forwarding to TTN, no. TTN V3 can use Network Time Protocol (NTP) over the internet to synchronize your gateway's timestamps. However, if you plan to use your gateway for LoRaWAN geolocation (TDOA/RSSI mapping), a GPS module with a PPS output is strictly required to provide the microsecond-level timing accuracy that NTP cannot guarantee.






