Forward LoRaWAN gateway data bidirectionally between ChirpStack and The Things Network (TTN)
A lightweight bridge that enables bidirectional communication for LoRaWAN gateways:
- Uplink (↑): Gateway data from ChirpStack → TTN & TTN → ChirpStack
- Downlink (↓): Server responses TTN → ChirpStack & ChirpStack → TTN
This allows:
- ✅ Devices from TTN to register on ChirpStack network
- ✅ Devices from ChirpStack to register on TTN network
- ✅ Joint network operation with automatic failover
- ✅ Zero gateway configuration changes
- 🔄 Bidirectional: Uplink + Downlink forwarding
- 🌐 Multi-Gateway: Automatically detects and tracks all connected gateways
- 🔧 Zero Gateway Config: All configuration server-side
- 📡 JOIN Support: Devices can successfully join either network
- 🚀 Lightweight: Minimal dependencies (paho-mqtt)
- 🐳 Containerized: Docker/Compose ready
- 📊 Logging: Full message tracking for debugging
TTN Gateway Network ChirpStack Network
↓ (uplink) ↓ (uplink)
└─────────LoRaBridge─────────┘
↕ (bidirectional)
MQTT & UDP Conversion
┌─────────LoRaBridge─────────┐
↑ (downlink) ↑ (downlink)
TTN application server ChirpStack application
- Uplink: MQTT JSON (ChirpStack) ↔ UDP Semtech (TTN)
- Downlink: UDP Semtech (TTN) ↔ MQTT JSON (ChirpStack)
git clone https://github.com/jdanielcmedina/LoRaBridge.git
cd LoRaBridge
docker-compose up -dgit clone https://github.com/jdanielcmedina/LoRaBridge.git
cd LoRaBridge
sudo ./install.sh- ChirpStack 4.x with Gateway Bridge (Basic Station)
- Gateway Bridge must use JSON marshaling (protobuf not supported)
- Gateways can be registered in either or both ChirpStack and TTN
Edit your Gateway Bridge config and add:
[integration]
marshaler="json" # Must be "json", not "protobuf"Then restart Gateway Bridge container.
Copy env.example to .env and adjust if needed:
TTN_SERVER=eu1.cloud.thethings.network # Change region if needed
TTN_PORT=1700
CHIRPSTACK_MQTT_HOST=mosquitto
CHIRPSTACK_MQTT_PORT=1883
MQTT_TOPIC=eu868/gateway/+/event/up # Adjust region (eu868, us915, etc)Docker:
docker-compose up -d
docker-compose logs -fStandalone:
sudo systemctl start chirpstack-ttn-bridge
sudo journalctl -u chirpstack-ttn-bridge -f- Device sends JOIN request (heard by any gateway)
- Gateway in ChirpStack network receives uplink
- Bridge forwards to TTN over UDP
- TTN's Network Server processes JOIN
- TTN sends JOIN Accept downlink
- Bridge forwards back to gateway via ChirpStack MQTT
- Gateway transmits JOIN Accept to device ✓
- Device sends JOIN request (heard by any gateway)
- If gateway in TTN network, TTN receives uplink
- Bridge forwards to ChirpStack via MQTT
- ChirpStack's Network Server processes JOIN
- ChirpStack sends JOIN Accept
- Bridge forwards to gateway via UDP (Semtech)
- Gateway transmits JOIN Accept to device ✓
The bridge automatically detects all gateways. Just:
- Add gateway to ChirpStack
- Add gateway to TTN (same EUI)
- Configure gateway to point to your server
- Done! Bridge auto-forwards all data
Expected output:
✓ Connected to ChirpStack MQTT
📡 New gateway detected: fcc23dfffe20f0a7
✓ [fcc23dfffe20f0a7] Uplink to TTN - Freq: 868.1 MHz, RSSI: -91 dBm
✓ [fcc23dfffe20f0a7] Received downlink from TTN - forwarding to ChirpStack
- Check Gateway Bridge config has
marshaler="json" - Verify gateway is registered in TTN with same EUI
- Check bridge is running:
docker-compose psorsystemctl status chirpstack-ttn-bridge - View logs:
docker-compose logs -forjournalctl -u chirpstack-ttn-bridge -f
- Verify gateway is in both ChirpStack and TTN configs (same EUI)
- Check MQTT connectivity: TTN should receive uplink first
- Verify
MQTT_TOPICmatches your region (EU868, US915, etc) - Check device is programmed with correct AppEUI/DevEUI for target network
- Gateway Bridge config issue
- Ensure
marshaler="json"is in[integration]section - Restart Gateway Bridge after changing config
- Check MQTT broker is running
- Verify
CHIRPSTACK_MQTT_HOSTandCHIRPSTACK_MQTT_PORT - TTN UDP port must be open (1700/udp outbound)
Device → Gateway → ChirpStack(MQTT/JSON) → Bridge → TTN(UDP/Semtech)
TTN(UDP/Semtech) → Bridge → ChirpStack(MQTT/JSON) → Gateway → Device
- Bridge tracks gateway EUI from MQTT messages
- Dynamic gateway detection (no manual registration)
- Supports unlimited gateways per network
- Bidirectional routing based on packet type
.
├── README.md # This file
├── Dockerfile # Docker image
├── docker-compose.yml # Docker deployment
├── chirpstack-ttn-bridge-docker.py # Docker version (env vars)
├── chirpstack-ttn-bridge.py # Standalone version (config)
├── chirpstack-ttn-bridge.service # Systemd service
├── install.sh # Installation script
├── gateway-bridge-config.toml # Example Gateway Bridge config
├── env.example # Environment variables template
└── LICENSE # MIT License
- ✨ Downlink support (TTN → ChirpStack)
- ✨ Downlink support (ChirpStack → TTN)
- ✨ Device JOIN support in both directions
- 🔧 Improved gateway tracking
- 📊 Enhanced logging
- Uplink only (ChirpStack → TTN)
MIT License - see LICENSE
Found an issue? Have a suggestion?
- Open an issue on GitHub
- Check existing documentation above
- ChirpStack - Open-source LoRaWAN Network Server
- The Things Network - Global LoRaWAN network
- Semtech LoRaWAN Gateway API - Gateway protocol specification