Node-RED + ChirpStack: LoRaWAN-Flows über MQTT bauen

ChirpStack über MQTT in Node-RED anbinden: Uplink-Topic abonnieren, msg.payload.object in einer Function-Node lesen, an DB, Dashboard oder Downlink leiten.

LoRaWAN
Architektur

Der Datenfluss

Node-RED macht aus ChirpStack-Uplinks Low-Code-Flows: das MQTT-Event-Topic abonnieren, die dekodierten Werte aus msg.payload.object in einer Function-Node lesen und an Datenbank, Dashboard oder Downlink verteilen. Diese Anleitung zeigt den vollen Weg mit einem kleinen importierbaren Flow.

Sensor / Controller

Misst oder steuert vor Ort und sendet LoRaWAN-Uplinks.

LoRaWAN-Gateway

Empfängt die Funkpakete und reicht sie an den Server weiter.

ChirpStack

Network-Server: verwaltet Sessions und decodiert das Payload.

ThingsBoard / Grafana

Dashboards, Alarme, Regeln und Reports.

Was Node-RED auf ChirpStack draufsetzt

ChirpStack terminiert die LoRaWAN-Session bereits und liefert dir, mit einem Codec auf dem Device-Profile, dekodiertes JSON. Node-RED ist die Low-Code-Klebeschicht danach: Es verbindet einen Uplink mit einem Datenbank-Insert, einer Dashboard-Anzeige, einem Alarm oder einem Downlink, ohne dass du einen Service schreibst. Die Verbindung zwischen beiden ist schlichtes MQTT, also keine Spezialkopplung und kein Vendor-Lock-in.

Schritt 1: Events aus ChirpStack veröffentlichen

In der ChirpStack-Applikation unter Integrations die MQTT-Integration mit Marshaler auf JSON aktivieren. Jeder Uplink wird dann veröffentlicht auf:

application/{ApplicationID}/device/{DevEUI}/event/up

Der Nachrichtenkörper ist dasselbe v4-Event, das du auch anderswohin weiterleiten würdest. Mit funktionierendem Codec auf dem Device-Profile stehen die dekodierten Werte im Feld object:

{
  "deviceInfo": {
    "applicationId": "00372e31-0620-4671-8270-237632a4e227",
    "applicationName": "cold-chain",
    "deviceProfileName": "EM300-TH",
    "deviceName": "coldroom-01",
    "devEui": "24e124136b502217"
  },
  "fPort": 85,
  "fCnt": 128,
  "data": "AXVkA2etAARoSw==",
  "object": { "battery": 100, "temperature": 17.3, "humidity": 57.5 },
  "rxInfo": [{ "gatewayId": "24e124fffef54092", "rssi": -51, "snr": 13.8 }],
  "txInfo": { "frequency": 868100000 }
}

Schritt 2: Mit einer mqtt-in-Node abonnieren

Eine mqtt-in-Node anlegen, ihre Broker-Config auf den ChirpStack-MQTT-Broker zeigen lassen und abonnieren:

application/+/device/+/event/up

Die beiden +-Wildcards passen auf jede ApplicationID und jede DevEUI, eine einzige Node empfängt also alle Geräte. Stelle die Ausgabe der Node auf ein geparstes JSON-Objekt, damit die nächste Node msg.payload als Objekt statt als String erhält.

Schritt 3: Die dekodierten Werte in einer Function-Node lesen

Verdrahte die mqtt-in-Node in eine Function-Node. Die dekodierten Sensorwerte stehen in msg.payload.object, die Geräteidentität in msg.payload.deviceInfo. Forme die Nachricht für das, was als Nächstes kommt:

// mqtt in -> function: ChirpStack-v4-Uplink ist bereits zu einem Objekt geparst
var p = msg.payload;
var o = p.object || {};                 // decodierte Werte aus dem Codec

// Frueh abbrechen, wenn kein Codec lief (nur rohes Base64 in p.data)
if (!p.object) {
    node.warn("Kein decodiertes object - liegt ein Codec auf dem Device-Profile?");
    return null;
}

msg.devEui = p.deviceInfo.devEui;        // stabiler, eindeutiger Schluessel
msg.topic  = p.deviceInfo.deviceName;    // lesbares Label fuer Dashboards

msg.payload = {
    devEui: p.deviceInfo.devEui,
    name: p.deviceInfo.deviceName,
    profile: p.deviceInfo.deviceProfileName,
    ts: new Date(p.time || Date.now()).toISOString(),
    temperature: o.temperature,
    humidity: o.humidity,
    battery: o.battery,
    rssi: (p.rxInfo && p.rxInfo[0]) ? p.rxInfo[0].rssi : null
};

return msg;

Von hier aus fächerst du auf: eine influxdb out- oder postgres-Node zur Speicherung, eine ui_gauge-Node fürs Dashboard oder eine switch-Node, die einen Alarm auslöst, wenn temperature eine Schwelle überschreitet.

Ein minimaler importierbarer Flow

Das ist ein in sich geschlossener Flow mit der mqtt-in-Node, der Function-Node von oben und einer Debug-Node. Füge ihn unter Menü, Import in Node-RED ein und trage deine Broker-Zugangsdaten ein:

[
  {
    "id": "mqtt_up",
    "type": "mqtt in",
    "name": "ChirpStack up",
    "topic": "application/+/device/+/event/up",
    "qos": "0",
    "datatype": "json",
    "broker": "cs_broker",
    "wires": [["fn_shape"]]
  },
  {
    "id": "fn_shape",
    "type": "function",
    "name": "shape uplink",
    "func": "var p=msg.payload;var o=p.object||{};if(!p.object){node.warn('kein Codec-object');return null;}msg.devEui=p.deviceInfo.devEui;msg.payload={devEui:p.deviceInfo.devEui,name:p.deviceInfo.deviceName,temperature:o.temperature,humidity:o.humidity,battery:o.battery};return msg;",
    "outputs": 1,
    "wires": [["dbg_out"]]
  },
  {
    "id": "dbg_out",
    "type": "debug",
    "name": "decoded",
    "active": true,
    "complete": "payload",
    "wires": []
  },
  {
    "id": "cs_broker",
    "type": "mqtt-broker",
    "name": "ChirpStack broker",
    "broker": "localhost",
    "port": "1883"
  }
]

Einen Downlink ans Gerät zurücksenden

Um ein Gerät zu steuern (ein Relais, ein Ventil, einen Thermostat), veröffentlichst du mit einer mqtt-out-Node ein JSON-Kommando auf dem Downlink-Topic. Baue das Kommando in einer Function-Node:

// function -> mqtt out: einen ChirpStack-Downlink einreihen
var appId  = "00372e31-0620-4671-8270-237632a4e227";
var devEui = "24e124136b502217";

msg.topic = "application/" + appId + "/device/" + devEui + "/command/down";
msg.payload = {
    devEui: devEui,        // muss zur DevEUI im Topic passen
    confirmed: false,
    fPort: 85,             // muss groesser 0 sein
    data: "AQ=="           // Base64-Befehl; ChirpStack verschluesselt vor dem Senden
};
return msg;

Stelle die Ausgabe der mqtt-out-Node auf JSON (oder serialisiere den Payload selbst per JSON.stringify). Sitzt ein Codec mit encodeDownlink-Funktion auf dem Device-Profile, kannst du statt data ein Feld object mit lesbaren Werten senden. Bei Class-A-Geräten wird das Kommando im RX-Fenster nach dem nächsten Uplink zugestellt, bei Class-C-Geräten nahezu sofort.

Stolperfallen aus der Praxis

  • Eigene Topic-Vorlage: Die application/...-Event- und -Command-Topics stammen aus dem Block integration.mqtt in chirpstack.toml. Die Standardvorlage hat kein Region-Präfix, aber eine angepasste Installation kann die Vorlage ändern. Sieht dein Abo keinen Traffic, vergleiche also das konfigurierte event_topic mit dem, was du abonniert hast. (Das eu868/...-Präfix, das dir anderswo begegnet, gehört zu den separaten Gateway-Bridge-Topics gateway/..., nicht zu diesen Applikations-Topics.)
  • Kein Codec, kein object: Ist msg.payload.object leer, hast du nur msg.payload.data (Base64). Lege einen Codec auf das Device-Profile, siehe den ChirpStack Payload Decoder.
  • Datatype an der mqtt-in-Node: Lass es auf ein geparstes JSON-Objekt, sonst kommt msg.payload als String an und .object ist undefined.
  • fPort größer als 0: Downlinks mit fPort 0 werden abgelehnt.
  • DevEUI als Schlüssel: Schlüssele Speicher und Dashboards auf devEui, nicht auf den Anzeigenamen, damit das Umbenennen eines Geräts seine Historie nicht aufspaltet.

Wann Node-RED die richtige Schicht ist und wann nicht

Node-RED eignet sich ideal für Prototyping, leichtes Routing und das Überbrücken eigenwilliger Protokolle. Für langfristige Speicherung und richtige Dashboards leitest du die geformten Daten an einen Time-Series-Store mit Grafana weiter oder an ThingsBoard, siehe ChirpStack zu ThingsBoard. Wir betreiben Broker, Flows und Backups für dich als Node-RED Managed Hosting neben dem ChirpStack-Hosting, sodass die von dir gebauten Flows dir gehören.

Häufige Fragen

Eine einfache mqtt-in-Node reicht. ChirpStack veröffentlicht das Event als JSON, du parst es und liest msg.payload.object direkt. Die Nodes node-red-contrib-chirpstack bieten Komfort (Event-Filterung, Downlink-Helfer), sind aber optional.
In msg.payload.object, sofern ein Payload-Codec auf dem Device-Profile läuft. Ohne Codec bekommst du nur msg.payload.data als Base64-String und musst in der Function-Node selbst dekodieren.
application/+/device/+/event/up für jeden Uplink über alle Applikationen hinweg. Die beiden Plus-Zeichen sind Wildcards für ApplicationID und DevEUI, eine mqtt-in-Node fängt also alle Geräte.
Ja. Veröffentliche ein JSON-Kommando auf application/{ApplicationID}/device/{DevEUI}/command/down mit devEui, einem fPort größer als 0 und entweder einem Base64-Feld data oder einem Feld object, wenn ein Codec konfiguriert ist.
Ja, beides läuft als Managed Hosting auf europäischer Infrastruktur, inklusive Broker, Flows und Backups. Die Flows und die Daten bleiben in deinem Eigentum.

Sprechen wir über Ihre Infrastruktur. Digital und vor Ort.

Ob IoT-Plattformentwicklung, Hardwareauswahl, Managed Hosting für ChirpStack, ThingsBoard, Grafana oder NetBird VPN, oder Migration von einem Self-Hosted-Setup - wir finden die passende Lösung für Ihren Anwendungsfall. Buchen Sie ein kostenloses 30-Minuten-Gespräch, unverbindlich.

Timo Wevelsiep

Ihr Ansprechpartner

Timo Wevelsiep

Gründer, merkaio

15 Minuten, unverbindlich, direkt mit Timo.

Mit dem Absenden stimmen Sie unserer Datenschutzerklärung zu.

merkaio ist unabhängiger Integrator und nicht mit Milesight affiliiert.