Damit ChirpStack aus den rohen LoRaWAN-Bytes eines Milesight-Sensors lesbare Werte wie Temperatur und Feuchte macht, hinterlegen Sie einen Payload-Decoder: Holen Sie die offizielle JavaScript-Decoder-Datei aus dem GitHub-Repo Milesight-IoT/SensorDecoders, öffnen Sie in ChirpStack das passende Device-Profile, setzen Sie unter dem Reiter Codec den Payload-Codec auf "JavaScript functions" und fügen Sie den Decoder ein. Beim nächsten Uplink erscheinen die dekodierten Felder im Gerät. Diese Anleitung zeigt das Schritt für Schritt am Beispiel der EM300-TH (Temperatur und Feuchte) - vollständig self-hosted auf Ihrer eigenen ChirpStack-Instanz, ohne Cloud-Lock-in und ohne Per-Device-Lizenzen.
Diese Anleitung setzt voraus, dass Ihr Gateway und ChirpStack bereits laufen. Falls nicht, richten Sie zuerst ChirpStack und das LoRaWAN-Gateway ein. Bezugsversion ist ChirpStack v4 (aktuell 4.19.2, September 2026).
Warum LoRaWAN-Payloads dekodiert werden müssen
LoRaWAN ist ein Funkprotokoll mit knappem Zeitbudget (Duty Cycle) und sehr kleinen Paketen. Damit ein batteriebetriebener Sensor jahrelang läuft und in das schmale Funkbudget passt, sendet er keine Klartext-JSON-Daten, sondern eine möglichst kurze Bytefolge. Eine typische EM300-TH-Nutzlast sieht so aus:
01 75 5A 03 67 EA 00 04 68 8F
ChirpStack entschlüsselt mit dem AppKey zwar die LoRaWAN-Sicherheitsschicht, kennt aber das herstellerspezifische Format dieser Anwendungsdaten nicht. Ohne Decoder bleibt im Uplink-Event das Feld object leer und Sie sehen nur den Base64- oder Hex-Rohwert. Der Payload-Codec ist die Übersetzungsregel: Er liest die Bytes nach dem Milesight-Kanalschema (Kanal-ID, Kanal-Typ, Daten) und gibt strukturierte Felder zurück. Mehr zu den Schichten erklärt unser Grundlagenartikel Was ist LoRaWAN.
Der entscheidende Vorteil im self-hosted Betrieb: Sie hinterlegen den Decoder einmal auf Ihrer eigenen ChirpStack-Instanz und behalten die volle Kontrolle über die Logik. Anders als bei proprietären Cloud-Plattformen (AWS IoT, Azure IoT, Cumulocity) gibt es keine Per-Device-Gebühr für das Dekodieren und keinen Lock-in - der Codec ist frei einsehbarer Open-Source-Code, den Sie versionieren, anpassen und in der EU betreiben.
Schritt 1: Den offiziellen Milesight-Decoder holen
Milesight pflegt alle Codecs zentral im offiziellen GitHub-Repository Milesight-IoT/SensorDecoders (Lizenz GPL-3.0). Das Repo ist nach Produktserie geordnet:
| Serie | Beispielgeräte | Typische Werte |
|---|---|---|
| em-series | EM300-TH, EM300-MCS, EM500-CO2 | Temperatur, Feuchte, CO2, Füllstand |
| am-series | AM103, AM307, AM319 | Luftqualität (CO2, PM2.5, TVOC, Lux) |
| vs-series | VS121, VS133, VS373 | Personen- und Belegungszählung |
| ws-series | WS101, WS301, WS523 | Taster, Leckage, Smart Plug |
Für die EM300-TH liegt die Decoder-Datei unter em-series/em300-th/em300-th-decoder.js. Jeder Geräteordner enthält zusätzlich einen Encoder (für Downlinks) und eine *-codec.json im LoRaWAN-Device-Repository-Format. Kopieren Sie den Inhalt von em300-th-decoder.js in die Zwischenablage. Wichtig: Nehmen Sie immer die Datei zu Ihrem genauen Modell - die Kanalbelegung unterscheidet sich zwischen den Serien.
Schritt 2: Decoder in ChirpStack als Payload-Codec hinterlegen
In ChirpStack v4 hängt der Codec am Device-Profile, nicht am einzelnen Gerät. So gilt er für alle Sensoren desselben Typs:
- Öffnen Sie in der Weboberfläche Device profiles und wählen Sie das Profil Ihres Sensors (oder legen Sie es neu an, siehe Gateway-Anleitung).
- Wechseln Sie zum Reiter Codec.
- Setzen Sie Payload codec auf JavaScript functions.
- Fügen Sie den gesamten Inhalt von
em300-th-decoder.jsin das Feld Codec functions ein und speichern Sie.
Die Milesight-Datei bringt die von ChirpStack v4 erwartete Einstiegsfunktion bereits mit. Entscheidend ist dieser Wrapper am Anfang der Datei:
// ChirpStack v4
function decodeUplink(input) {
var decoded = milesightDeviceDecode(input.bytes);
return { data: decoded };
}
ChirpStack übergibt der Funktion ein Objekt input mit bytes (Byte-Array), fPort und variables. Die eigentliche Logik steckt in milesightDeviceDecode(), das die Kanal-/Typ-Bytes durchläuft. Die JavaScript-Umgebung von ChirpStack basiert auf QuickJS (ES2020); die Node.js-Buffer-Klasse ist verfügbar. Dieselbe Datei enthält außerdem Decode(fPort, bytes) für ChirpStack v3 und Decoder(bytes, port) für The Things Network - für v4 ist allein decodeUplink relevant.
Schritt 3: Uplink prüfen und Werte sehen
Speichern Sie das Profil und warten Sie auf den nächsten Uplink (EM300 sendet standardmäßig alle 10 Minuten). Prüfen Sie das Ergebnis an zwei Stellen:
- Web-UI: Öffnen Sie das Gerät, Reiter Events, und sehen Sie sich das
up-Event an. Im Feldobjectstehen jetzt die dekodierten Werte statt nur der Rohbytes. - MQTT: Abonnieren Sie das Uplink-Topic und sehen Sie das
objectlive:
mosquitto_sub -h SERVER-IP -t "application/+/device/+/event/up" -v
Für die Beispielnutzlast liefert der Decoder dieses Objekt:
{ "battery": 90, "temperature": 23.4, "humidity": 71.5 }
Wollen Sie testen, ohne auf einen echten Uplink zu warten, gibt es im Reiter Codec keine Eingabe - nutzen Sie stattdessen einen lokalen Node.js-Aufruf der Datei oder das Test-Setup aus dem Repo. Sobald object gefüllt ist, können Sie die Werte über MQTT in eine Zeitreihen-Datenbank und ein Grafana-IoT-Dashboard leiten.
Beispiel: EM300-TH Payload Byte für Byte
Milesight kodiert jeden Messwert als Block aus Kanal-ID + Kanal-Typ + Daten (Daten little-endian). Die EM300-TH belegt drei Kanäle:
| Bytes | Kanal-ID | Kanal-Typ | Rohwert | Dekodiert |
|---|---|---|---|---|
01 75 5A |
0x01 Battery | 0x75 | 0x5A = 90 | 90 % |
03 67 EA 00 |
0x03 Temperatur | 0x67 | INT16 LE 0x00EA = 234 | 23,4 °C |
04 68 8F |
0x04 Feuchte | 0x68 | 0x8F = 143 | 71,5 %RH |
So entsteht aus 01 75 5A 03 67 EA 00 04 68 8F das JSON-Objekt oben. Die Temperatur ist ein vorzeichenbehafteter 16-Bit-Wert geteilt durch 10, die Feuchte ein Byte geteilt durch 2. Andere Modelle nutzen weitere Kanäle - eine AM319 etwa liefert zusätzlich CO2, PM2.5, TVOC und Beleuchtungsstärke. Der jeweilige Decoder aus dem Repo kennt diese Kanäle bereits, Sie müssen nichts selbst rechnen.
Häufige Stolpersteine
objectbleibt leer: Codec hängt am falschen Device-Profile, oder das Gerät nutzt ein anderes Profil. Prüfen Sie die Zuordnung.- Unsinnige Werte: Falsche Decoder-Datei (falsches Modell) oder fehlgeschlagener Join. Bei falschem AppKey bleiben die Anwendungsdaten verschlüsselt.
- Decoder aktualisiert nicht: Nach dem Einfügen das Profil speichern und auf den nächsten Uplink warten - bereits gespeicherte Events werden nicht rückwirkend neu dekodiert.
- Falsches Modell aus einer Serie: EM300-TH, EM300-MCS und EM300-DI teilen Kanäle, unterscheiden sich aber - immer die exakte Modell-Datei verwenden.
Nächste Schritte
Mit einem hinterlegten Payload-Codec liefert ChirpStack saubere, strukturierte Messwerte - die Grundlage für Dashboards, Alarme und Auswertungen, vollständig souverän auf Ihrer eigenen Infrastruktur. Einrichtung, Decoder-Pflege für gemischte Sensorflotten und den Betrieb übernehmen wir auf Wunsch komplett. Mehr dazu auf unseren Seiten zu Milesight-Sensorintegration und ChirpStack & LoRaWAN Network Server sowie im Überblick zu LoRaWAN-Lösungen und im IoT-Bereich von WZ-IT. Ein unverbindliches Kennenlern-Gespräch buchen Sie direkt online.











