Die ChirpStack-MQTT-Integration veröffentlicht jedes LoRaWAN-Ereignis Ihres eigenen Netzwerkservers an einen MQTT-Broker und ist damit der natürliche Ausgangspunkt, um Sensordaten aus dem Feld in Dashboards und Automatisierungen zu bringen. In diesem Tutorial konfigurieren Sie die Integration in ChirpStack v4, dekodieren Payloads mit einem Codec im Geräteprofil und leiten dekodierte Uplinks auf zwei Arten an einen Cloud-Broker weiter: über eine Mosquitto-Bridge ganz ohne Code und über einen Node.js-Forwarder, der die Daten in Ihren eigenen Topic-Baum umformt.
Wie ChirpStack MQTT nutzt
ChirpStack ist ein Open-Source-LoRaWAN-Netzwerkserver. In einer typischen Installation (zum Beispiel dem offiziellen Docker-Compose-Setup) taucht MQTT zweimal auf:
- Gateway-Backend. Gateway-Bridges veröffentlichen rohe Funkframes an einen lokalen Mosquitto-Broker, und ChirpStack abonniert sie.
- MQTT-Integration. Nach Deduplizierung, Entschlüsselung und Dekodierung veröffentlicht ChirpStack Anwendungsereignisse (Uplinks, Joins, Bestätigungen, Fehler) auf MQTT-Topics desselben lokalen Brokers.
Die Ereignisse der Integration sind das, was Sie brauchen. Sie liegen auf einem Broker im Netzwerk des LoRaWAN-Servers, der von Dashboards, Smartphones oder anderen Standorten aus meist nicht erreichbar ist. Die Weiterleitung an einen Cloud-Broker löst das, ohne den Netzwerkserver selbst offenzulegen.
Sensors ~~LoRa~~> Gateway --> local Mosquitto <--> ChirpStack
|
application/.../event/up
|
Mosquitto bridge or Node.js forwarder
| (TLS)
v
Cloud MQTT broker --> Node-RED, dashboards, alerts
Schritt 1: Die ChirpStack-MQTT-Integration aktivieren
In chirpstack.toml sieht der Integrationsabschnitt bei einer Standardinstallation so aus:
[integration]
enabled = ["mqtt"]
[integration.mqtt]
server = "tcp://mosquitto:1883/"
json = true
Mit json = true sind die Ereignisse JSON statt Protobuf, womit sich weit einfacher arbeiten lässt. Das Standard-Topic für Ereignisse lautet:
application/<application_id>/device/<dev_eui>/event/<event>
wobei <event> einer der Werte up, join, ack, txack, log, status oder location ist. Starten Sie ChirpStack nach der Änderung der Datei neu und beobachten Sie dann den lokalen Broker auf dem Server:
mosquitto_sub -h localhost -p 1883 -t 'application/+/device/+/event/up' -v
Schritt 2: Payloads mit einem Codec dekodieren
Rohe LoRaWAN-Payloads sind Bytes und werden base64-kodiert im Feld data geliefert. Fügen Sie dem Geräteprofil einen JavaScript-Codec hinzu (Device profiles > your profile > Codec, Typ JavaScript), damit ChirpStack jedem Uplink ein dekodiertes Feld object hinzufügt:
function decodeUplink(input) {
var b = input.bytes;
if (input.fPort !== 1 || b.length < 6) {
return { errors: ["unexpected fPort or length"] };
}
var temp = (b[2] << 8) | b[3];
if (temp & 0x8000) temp -= 0x10000;
return {
data: {
moisture_pct: ((b[0] << 8) | b[1]) / 10,
soil_temp_c: temp / 10,
battery_v: ((b[4] << 8) | b[5]) / 1000
}
};
}
Ein Uplink-Ereignis enthält dann neben anderen Feldern deviceInfo.deviceName, deviceInfo.devEui, fCnt, fPort, time, object (Ihre dekodierten Werte) und rxInfo (ein Eintrag pro Gateway mit rssi und snr).
Option A: Weiterleitung mit einer Mosquitto-Bridge
Läuft ChirpStack bereits mit Mosquitto, ist eine Bridge der schnellste Weg: ein paar Zeilen Konfiguration, und Mosquitto leitet passende Topics an den Cloud-Broker weiter und verbindet sich automatisch neu. Legen Sie /etc/mosquitto/conf.d/cloud-bridge.conf an (oder das Äquivalent in Ihrem Docker-Volume):
connection connectmq
address broker.connectmq.com:1883
remote_clientid cs-bridge-c3h7k5
cleansession true
try_private false
bridge_protocol_version mqttv311
notifications true
notification_topic connectmq/demo/c3h7k5/bridge/state
# local: application/<app>/device/<eui>/event/up
# remote: connectmq/demo/c3h7k5/<app>/device/<eui>/event/up
topic +/device/+/event/up out 1 application/ connectmq/demo/c3h7k5/
Die Zeile topic nimmt ein Muster, eine Richtung, eine QoS und zwei Präfixe entgegen. Mosquitto entfernt das lokale Präfix application/ und fügt das entfernte Präfix hinzu; so bleibt das entfernte Topic bei acht Ebenen, dem Maximum auf dem öffentlichen MQTT-Broker. Ersetzen Sie c3h7k5 durch Ihre eigene Zufallszeichenfolge; der öffentliche MQTT-Broker wird geteilt, ist für jeden lesbar und nur für Tests und Prototypen gedacht, nicht für Produktivdaten. try_private false wird empfohlen, wenn der entfernte Broker kein Mosquitto ist.
Starten Sie Mosquitto neu und abonnieren Sie von einem beliebigen Ort aus:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-c3h7k5 \
-t 'connectmq/demo/c3h7k5/#' -v
Für den Produktivbetrieb wechseln Sie auf TLS und die Zugangsdaten Ihres privaten Brokers:
connection connectmq
address broker.connectmq.com:8883
bridge_cafile /etc/ssl/certs/ca-certificates.crt
remote_clientid YOUR_CLIENT_ID
remote_username YOUR_USERNAME
remote_password YOUR_PASSWORD
cleansession true
try_private false
notifications true
notification_topic farm1/lorawan/bridge/state
topic +/device/+/event/up out 1 application/ farm1/lorawan/
Die Einschränkung einer Bridge besteht darin, dass Payloads unverändert weitergeleitet werden. ChirpStack-Ereignisse sind umfangreich, mit base64-Daten, Sendeinformationen und vollständigen Gateway-Metadaten, und die Topics verwenden Application-UUIDs und DevEUIs statt lesbarer Namen.
Option B: Ein ChirpStack-MQTT-Forwarder in Node.js
Wenn Verbraucher kompakte, benannte Daten sehen sollen, ist ein kleiner Forwarder besser. Dieser nutzt MQTT.js v5, abonniert Uplinks auf dem lokalen Broker und veröffentlicht pro Gerät einen sauberen Datensatz. Installation mit npm install mqtt.
// forwarder.mjs
import mqtt from "mqtt";
const LOCAL_URL = process.env.LOCAL_URL ?? "mqtt://localhost:1883";
const OUT_URL = process.env.OUT_URL ?? "mqtt://broker.connectmq.com:1883";
const OUT_PREFIX = process.env.OUT_PREFIX ?? "connectmq/demo/c3h7k5/farm";
const out = mqtt.connect(OUT_URL, {
clientId: process.env.OUT_CLIENT_ID ?? "cs-forwarder-c3h7k5",
username: process.env.OUT_USER,
password: process.env.OUT_PASS,
protocolVersion: 5,
will: { topic: `${OUT_PREFIX}/forwarder/status`, payload: "offline", qos: 1, retain: true },
});
out.on("connect", () => {
console.log("cloud broker connected");
out.publish(`${OUT_PREFIX}/forwarder/status`, "online", { qos: 1, retain: true });
});
out.on("error", (err) => console.error("cloud:", err.message));
const local = mqtt.connect(LOCAL_URL, { clientId: "cs-forwarder-local-c3h7k5" });
local.on("connect", () => {
local.subscribe("application/+/device/+/event/up", { qos: 1 });
console.log("local broker connected");
});
local.on("message", (topic, payload) => {
let ev;
try {
ev = JSON.parse(payload.toString());
} catch {
return console.warn("non-JSON event on", topic);
}
if (!ev.object) {
return console.warn(`${ev.deviceInfo?.deviceName}: no decoded object, check the codec`);
}
const rx = ev.rxInfo ?? [];
const best = rx.reduce((a, g) => ((g.rssi ?? -999) > (a.rssi ?? -999) ? g : a), {});
const name = ev.deviceInfo.deviceName.replace(/[^A-Za-z0-9_-]/g, "_");
const record = {
ts: ev.time,
dev_eui: ev.deviceInfo.devEui,
...ev.object,
f_cnt: ev.fCnt,
rssi: best.rssi ?? null,
snr: best.snr ?? null,
gateways: rx.length,
};
out.publish(`${OUT_PREFIX}/${name}/telemetry`, JSON.stringify(record), { qos: 1, retain: true });
console.log(name, record);
});
Führen Sie ihn neben ChirpStack aus:
node forwarder.mjs
# Produktivbetrieb
OUT_URL=mqtts://broker.connectmq.com:8883 OUT_USER=YOUR_USERNAME \
OUT_PASS=YOUR_PASSWORD OUT_CLIENT_ID=YOUR_CLIENT_ID OUT_PREFIX=farm1/north \
node forwarder.mjs
Gerätenamen werden bereinigt, bevor sie ins Topic gelangen, da Zeichen wie +, # und / die Hierarchie zerstören würden. Die Datensätze sind retained, sodass Dashboards sofort den neuesten Messwert jedes Sensors sehen, auch wenn LoRaWAN-Geräte vielleicht nur alle 15 Minuten melden. In Industriebetrieben, die LoRaWAN etwa für Energiezähler, Tankfüllstände oder Raumklima auf dem Werksgelände nutzen, lohnt es sich, OUT_PREFIX gleich an der ISA-95-Hierarchie auszurichten, zum Beispiel acme/werk-augsburg/tanklager. So landen die Funksensoren an der richtigen Stelle im Unified Namespace (UNS), neben den Daten aus SPS und MES.
Die Wahl zwischen beiden
| Kriterium | Mosquitto-Bridge | Node.js-Forwarder |
|---|---|---|
| Einrichtungsaufwand | Eine Konfigurationsdatei | Ein kleines Skript und ein Dienst |
| Payload | Vollständiges ChirpStack-Ereignis | Kompakter, selbst definierter Datensatz |
| Topic-Namen | Application ID und DevEUI | Lesbare Gerätenamen, Ihre Hierarchie |
| Bewegliche Teile | Keine außer Mosquitto | Ein zusätzlicher Prozess zu überwachen |
| Downlinks | Eine in-Topic-Zeile ergänzen |
Ein Abonnement und ein Publish ergänzen |
Viele Installationen beginnen mit der Bridge, um Daten zum Fließen zu bringen, und wechseln zu einem Forwarder, sobald die Zahl der Verbraucher wächst. Das Tutorial zu The Things Network MQTT nutzt dieselbe Forwarder-Idee in Python; das ist hilfreich, wenn Ihr Betrieb Community- und private LoRaWAN-Netze mischt.
Downlinks über ChirpStack senden
Um einen Downlink einzureihen, veröffentlichen Sie auf das Befehls-Topic des Geräts auf dem lokalen Broker:
mosquitto_pub -h localhost -p 1883 \
-t 'application/YOUR_APP_ID/device/0004a30b001c1234/command/down' \
-m '{"devEui":"0004a30b001c1234","confirmed":false,"fPort":2,"data":"Hg=="}'
data ist base64 (Hg== ist das einzelne Byte 30, zum Beispiel ein Meldeintervall in Minuten). Geräte der Klasse A empfangen ihn nach ihrem nächsten Uplink. Leiten Sie Downlink-Befehle aus der Cloud nur weiter, wenn Sie jedem Client vertrauen, der darauf veröffentlichen kann, und validieren Sie sie im Forwarder.
Fehlersuche
- Nichts auf
event/up. Prüfen Sie, ob das Gerät beigetreten ist (achten Sie aufevent/join), ob die Integration aktiviert ist und objson = truegesetzt ist, wenn Sie JSON erwarten. objectfehlt. Der Codec fehlt, ist fehlgeschlagen oder hat Fehler zurückgegeben;event/log-Nachrichten zeigen Codec-Fehler.- Die Bridge verbindet sich und bricht dann ab. Doppelte
remote_clientid, falsche Zugangsdaten oder eine fehlende CA-Datei für Port 8883. Das Mosquitto-Log zeigt den Grund. - Nachrichten werden vom öffentlichen Broker abgelehnt. Das Topic überschreitet acht Ebenen oder der Client mehr als zehn Nachrichten pro Sekunde; verwenden Sie kürzere Präfixe und leiten Sie
log-Ereignisse nicht dorthin weiter.
Den Weiterleitungspfad überwachen
Eine LoRaWAN-Pipeline hat mehrere Stellen, an denen sie still ausfallen kann: Ein Gateway verliert den Strom, ChirpStack dekodiert nach einer Codec-Änderung nicht mehr, oder der Forwarder stirbt. Überwachen Sie jede Schicht einzeln. Die Weboberfläche von ChirpStack zeigt, wann Gateways zuletzt gesehen wurden, und die Frames pro Gerät. Das retained status-Topic des Forwarders, abgesichert durch seinen Last Will, zeigt, ob der Forwarder selbst lebt, und das Benachrichtigungs-Topic der Bridge leistet dasselbe für Option A. Schließlich verrät das Alter der retained telemetry-Nachricht jedes Geräts, ob die Daten noch durchgängig fließen.
Auf der Cloud-Seite ist der Live-Topic-Explorer von ConnectMQ der schnellste Weg, nach einer Codec-Änderung zu bestätigen, dass die Datensätze in der erwarteten Form ankommen, und eine Schwellwert-Alarmregel auf ein Feld wie battery_v warnt frühzeitig, bevor ein Sensor verstummt.
Hinweise zum Produktivbetrieb
Halten Sie Netzwerkserver und Gateway-Broker privat und machen Sie den Forwarder oder die Bridge zum einzigen Weg nach außen. Ein verwalteter MQTT-Broker wird dann zum gemeinsamen Knotenpunkt für LoRaWAN-Daten, WLAN-Geräte und Steuerungen gleichermaßen, und der Artikel zum MQTT-Broker in der Cloud vergleicht die Hosting-Optionen. Was Sie mit den Daten anfangen können, zeigen der Leitfaden zu Smart Farming IoT und der Bau einer automatischen Bewässerungsanlage, und der Leitfaden zur IoT-Automatisierung behandelt das Gesamtbild.
ChirpStack-Daten an ConnectMQ weiterleiten
ConnectMQ gibt Ihren LoRaWAN-Daten einen privaten, per TLS gesicherten MQTT-Broker mit Live-Topic-Explorer, Dashboard und Schwellwert-Alarmregeln für Werte wie niedrigen Batteriestand oder Frost. Erstellen Sie unter app.connectmq.com einen kostenlosen Serverless-Broker, ohne Kreditkarte, und richten Sie Ihre Bridge oder Ihren Forwarder darauf aus.
Häufig gestellte Fragen
Welches MQTT-Topic verwendet ChirpStack v4 für Uplinks?
Standardmäßig application/<application_id>/device/<dev_eui>/event/up, mit JSON-Payloads, wenn in der MQTT-Integration json = true gesetzt ist.
Kann ChirpStack direkt an einen Cloud-Broker veröffentlichen?
Sie können den server der Integration auf einen anderen Broker zeigen lassen, aber wenn Sie ihn lokal halten und ausgewählte Topics weiterleiten, behalten Sie die Kontrolle darüber, was das Netzwerk verlässt.
Warum sind ChirpStack-Payloads base64-kodiert?
Das Feld data enthält die rohen LoRaWAN-Bytes in base64-Kodierung. Fügen Sie dem Geräteprofil einen Codec hinzu, um dekodierte Werte im Feld object zu erhalten.
Puffert die Mosquitto-Bridge Nachrichten, wenn das Internet ausfällt?
Mit cleansession false und QoS 1 kann Mosquitto Nachrichten für die Bridge zwischenspeichern, solange die Verbindung unterbrochen ist, innerhalb der konfigurierten Warteschlangenlimits. Mit cleansession true werden während des Ausfalls veröffentlichte Nachrichten nicht weitergeleitet.



