ENPLDEPT
Flash
Alle Docs
LoRaWAN-Gateway

Deinen LoRaWAN-Gateway zu Sensmos hinzufügen — passiver Hörer für Semtech Packet Forwarder

Ein Helium-, TTN- oder privater Gateway sieht jeden LoRa-Frame um sich herum. Ein passives Skript meldet die Funk-Metadaten an Sensmos — Emergency-Aussendungen und Sensor-Frames von Nodes in der Nähe werden zugestellt, und dein Gateway ändert nie, was er für sein eigenes Netz tut.

radio · 868 MHz · emergency frame
Heard by a neighbour · 2.1 kmgarage · temp 3.4°C · door OPEN · 21:44

Für wen das ist

Jede Linux-Kiste mit einem einfachen Semtech Packet Forwarder (UDP, Port-1700-Stil), der auf einen entfernten Server zeigt: Helium-Hotspots und ihre Multi-Mining-Stacks, RAK, Dragino, Mikrotik, ein Raspberry Pi mit Concentrator-HAT. Läuft dein Gateway stattdessen mit ChirpStack Gateway Bridge, gibt es einen lokalen MQTT-Broker und ein anderer Agent kommt zum Einsatz.

Du brauchst außerdem einen Sensmos-Node im selben LAN wie der Gateway (jeder ESP32 mit der Firmware, kein Funkmodul nötig) und seine PIN. Der Node bürgt für den Gateway; ohne ihn stellt das Backend kein Token aus.

Warum passiv und kein Relay

Der naheliegende Weg, einen Forwarder anzuzapfen, ist, etwas dazwischenzuschalten — den Forwarder auf uns zeigen lassen und weiterleiten. Das tun wir absichtlich nicht. Auf Hardware, die verdient, würde unser Ausfall zu deinem Ausfall. Der Forwarder schickt ohnehin jeden Frame separat an jeden konfigurierten Server über Loopback; das Skript liest auf lo einfach mit. Es ist niemandes Ziel, leitet nichts weiter und verzögert nichts.

Schnellstart

Führe all das auf dem Gateway aus, nicht auf deinem Laptop. Nur Python 3 — keine pip-Pakete, nichts zu bauen. Root ist nötig, weil das Lesen roher Pakete CAP_NET_RAW erfordert.

# 1 — install the script
sudo curl -fsSL -o /usr/local/bin/sensmos-gw-udp.py https://raw.githubusercontent.com/Galusz/sensmos-gateway/main/sensmos-gw-udp.py
sudo chmod +x /usr/local/bin/sensmos-gw-udp.py

# 2 — see what would leave your box (sends nothing, needs no pairing)
sudo sensmos-gw-udp.py --dry-run

# 3 — find a Sensmos node on this network (prints the exact pair command)
sudo sensmos-gw-udp.py --find

# 4 — pair with it (address from step 3, PIN = the node's PIN)
sudo sensmos-gw-udp.py --pair NODE_IP --pin YOUR_PIN --name "rooftop RAK"

# 5 — install & start it as a boot-persistent service
sudo sensmos-gw-udp.py --install-service

# 6 — confirm
systemctl status sensmos-gw-udp --no-pager
journalctl -u sensmos-gw-udp -f

Schritt 6 sollte active (running) zeigen, und das Log gibt connection works — first batch accepted aus, sobald Frames fließen, danach jede Minute eine Statuszeile. In der App erscheint der Gateway unter den Attachments des Nodes; auf der Live-Karte bekommt der Node die RF-Ebene.

Schon gekoppelt, kein Dienst? Das Token bleibt erhalten; wiederhole Schritt 1 und 5. Nur wenn --install-service pair first meldet, wiederhole Schritt 4.

Schau nach, bevor du uns vertraust

--dry-run gibt als JSON genau die Frames aus, die es weiterleiten würde, und hört dort auf. Füge --log-tx hinzu, um auch zu sehen, wann dein Gateway sendet. Das Skript ist eine einzelne lesbare Datei; der Quellcode liegt auf GitHub.

Pairing — wie das Token funktioniert

Das Skript hat keine eigenen Zugangsdaten. Es bittet den Sensmos-Node in deinem LAN (mit der PIN des Nodes), für es zu bürgen; der Node leitet die Anfrage über seine eigene Verbindung ans Backend weiter, und das Backend stellt ein Token aus, das auf radio.frames beschränkt ist — empfangene Frames melden, sonst nichts. Das Token wird in /etc/sensmos-gw-udp.json (Modus 0600) gespeichert. Von da an spricht das Skript direkt mit dem Backend; der Node ist raus aus der Schleife.

  • Ein Node kann für bis zu vier Attachments bürgen.
  • Der Besitzer kann jedes davon aus der App widerrufen (Node → Attachments); das Skript wird sofort wirkungslos.
  • Ein Token lebt 30 Tage ab erster Nutzung, und das Skript verlängert es von selbst.

Der vollständige Vertrag (Endpunkte, Scopes, Formate) steht unter Attachments & die EXT-API.

Was es liest

Semtech GWMP über UDP, erkannt am Paket-Header, nicht am Port, damit es funktioniert, egal welche Ports dein Stack nutzt:

Byte
0Protokollversion (1 oder 2)
1–2Token
3Typ — 0 PUSH_DATA (empfangene Frames), 3 PULL_RESP (Gateway soll senden)
4–11Gateway-EUI
RestJSON

Aus PUSH_DATA nimmt es die rxpk-Einträge: Frequenz, Spreading Factor, RSSI, SNR, Größe, CRC-Ergebnis und die rohe PHY-Payload. Anwendungs-Payloads sind mit Schlüsseln verschlüsselt, die wir nicht haben; was das Netzwerk nutzt, sind die Funk-Metadaten und das Erkennen von Sensmos-Frames im Verkehr.

PULL_RESP — eine Aussendung, die dein Gateway gleich macht — wird nur gemeldet, wenn sie für andere hörbar ist (ipol = false, z. B. Coverage-Beacons, die manche Netze senden). Ein normaler LoRaWAN-Downlink nutzt invertiertes IQ, das niemand sonst hören kann, beweist nichts und wird übersprungen. Gemeldete sind der Beweis, dass der Gateway echt ist: Ihre Payload ist unvorhersehbar, sodass ein Sensmos-Node, der dieselben Bytes unabhängig hört, bestätigt, dass der Gateway tatsächlich gesendet hat.

Ein Multi-Server-Forwarder schickt den identischen Frame an jeden konfigurierten Server, sodass dieselbe Aussendung mehrfach auf Loopback auftaucht (RF packets received: 1 / PUSH_DATA datagrams sent: 2 in deinem Forwarder-Log). Das Skript dedupliziert; eine Aussendung wird einmal gemeldet. Frames werden gebündelt (einmal pro Sekunde oder alle 50 Frames) und an https://sensmos.com/v1/ext/radio/frames geschickt.

Flags

FlagBedeutung
--ifacezu beobachtende Schnittstelle, Standard lo
--dry-runausgeben, was gesendet würde, nichts senden
--log-txzusätzlich loggen, was dein Gateway sendet
--finddas LAN nach Sensmos-Nodes durchsuchen und den Pair-Befehl ausgeben
--pair IP --pin PIN --name "…"einmaliges Pairing über einen Node
--install-servicenach /usr/local/bin kopieren, systemd-Unit schreiben und aktivieren (wird dir vorher gezeigt)
--be URLalternatives Backend, zum Testen

Die Unit, die es schreibt, falls du es lieber von Hand machst:

# /etc/systemd/system/sensmos-gw-udp.service
[Unit]
Description=Sensmos passive gateway listener
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/sensmos-gw-udp.py
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target

Docker und Container

Das Skript sniffit AF_PACKET auf dem Loopback des Hosts. Loopback ist pro Netzwerk-Namespace privat, ein isolierter Container sieht also ein leeres lo und hört nichts. In einem Container braucht es --network host und --cap-add NET_RAW. Der unterstützte Weg ist der systemd-Dienst auf dem Host; ein Container-Build ist deine Sache.

Updates

Das Skript aktualisiert sich nie selbst — mit Absicht. Es läuft als Root auf deinem Gateway, in deinem LAN; Code automatisch von einem Server zu ziehen würde diesen Server zu einem Weg in dein Netzwerk machen. Zum Aktualisieren wiederhole Schritt 1 und starte den Dienst neu:

sudo curl -fsSL -o /usr/local/bin/sensmos-gw-udp.py https://raw.githubusercontent.com/Galusz/sensmos-gateway/main/sensmos-gw-udp.py
sudo systemctl restart sensmos-gw-udp

Fehlerbehebung

  • root required: run with sudo — Raw-Capture braucht CAP_NET_RAW. Nutze sudo (oder setcap cap_net_raw+ep auf der Python-Binary, was es jedem Python-Programm gewährt — sudo ist sauberer).
  • Verbunden, aber keine Frames — prüfe, ob der Verkehr wirklich auf Loopback läuft: sudo tcpdump -i lo -n -c 10 udp. Spricht dein Forwarder über das Netzwerk mit einem entfernten Server statt mit einer lokalen Bridge, zeig das Skript auf diese Schnittstelle: --iface eth0.
  • AF_PACKET is Linux only — ist es. Das läuft auf dem Gateway, nicht auf einem Mac.
  • --find findet nichts — der Node muss im selben LAN-Segment und online sein (sein /info muss mit ws_connected: true antworten). Gib stattdessen die IP des Nodes aus der App an.
  • pair first bei --install-service — das Pairing ist nicht durchgelaufen; prüfe die PIN und dass der Node online ist.

Deinstallieren

sudo systemctl disable --now sensmos-gw-udp
sudo rm /etc/systemd/system/sensmos-gw-udp.service /usr/local/bin/sensmos-gw-udp.py /etc/sensmos-gw-udp.json

Widerrufe das Attachment auch in der App. Sonst wurde nie etwas geändert, also gibt es nichts weiter rückgängig zu machen.

Im Netzwerk heute

Der erste fremde Gateway kam im August 2026 dazu (Deutschland), verbürgt durch einen Node ohne eigenes Funkmodul. Frames, die er hört — Emergency-Aussendungen und Sensor-Frames von Nodes in Reichweite — werden zugestellt wie Frames, die irgendein Node hört. Details dazu, was LoRa für einen Node-Besitzer tut: LoRa.

Fragen, klare Antworten

Ändert das die Konfiguration meines Gateways?

Nein. Nichts wird bearbeitet, kein Dienst neu gestartet, kein Server zum Forwarder hinzugefügt. Das Skript liest nur Pakete, die ohnehin über Loopback fließen. Beende es, und der Gateway merkt nichts.

Kann es meinen Gateway zum Senden bringen?

Nein — dafür gibt es keinen Codepfad. Es meldet Aussendungen, die der Gateway von sich aus macht (hörbare Coverage-Beacons), löst aber nie eine aus.

Brauche ich ein Konto?

Nein. Ein Sensmos-Node im selben LAN bürgt für den Gateway, und das Backend stellt ihm ein enges Token aus, das auf das Melden von Frames beschränkt ist. Der Besitzer des Nodes kann es jederzeit aus der App widerrufen.

Was bekommt das Netzwerk?

Jeden LoRa-Frame, den der Gateway hört, als Funk-Metadaten plus die rohen PHY-Bytes. Sensmos-Frames (Emergency-Aussendungen, Sensor-Frames) werden erkannt und an ihre Besitzer zugestellt; fremder LoRaWAN-Verkehr zählt nur als Spektrumstatistik — die Payloads sind verschlüsselt und wir haben keine Schlüssel.

Verdient ein Gateway GALU?

Das Gateway selbst hat kein Wallet. Der Node, der für es bürgt, erhält die RF-Datenkategorie, was sein Reward-Gewicht erhöht (+10 % pro Datenkategorie). Zusätzlich: Wird ein bezahlter Sensor-Frame-Empfang dem ersten fremden Hörer des Tages gutgeschrieben und ist das dein Gateway, gehen 70 % der Gebühr an den Besitzer des bürgenden Nodes.

Mein Gateway läuft mit ChirpStack Gateway Bridge, nicht mit einem nackten Forwarder

Dann gibt es einen lokalen MQTT-Broker, und ein anderer Agent (sensmos-gw-mqtt.py, im selben Repository) passt besser — er ist aber ungetestet: Wir haben keinen solchen Gateway, um ihn zu prüfen. Starte ihn mit --dry-run und sag uns, was du siehst. Diese Seite behandelt den Fall des nackten Packet Forwarders, den die meisten Gateways der Helium-Klasse ausführen.

Aktualisiert: 2026-09-03