LoRaWAN UDP Packet Forwarder 1
17.12.2024
Elektronik | Funk | Software
Der Technik-Blog
In der Regel gibt es für LoRaWAN-Sensoren direkt vom Hersteller einen fertigen Javascript-Decoder für die Einbindung bei TTN (The Things Network, The Things Stack), Helium oder einem anderen LoRaWAN-Netzwerk. In seltenen Fällen oder insbesondere bei Sensoren für den Business-Bereich (Wasserzähler, Gaszähler, Energiezähler usw.) gibt der Hersteller meistens keinen fertigen Decoder heraus, sondern eine Dokumentation über die Byteaufteilung der Payload. In diesem Artikel geht es um einen Wasserzähler (Qualcosonic W1 von AXIOMA), wo anhand der Byte-Tabelle ein Javascript Decoder für TTN erstellt wird.
Grundlagen LoRaWAN Payload
Starthilfe für LoRaWAN-Einsteiger
LoRaWAN ist eine sehr sparsame Funktechnologie, wo nicht nur auf den Energieverbrauch geachtet wird, sondern auch auf eine möglichst effiziente und kurze Datenübertragungszeit. Übliche Übertragungsformate wie JSON oder Klartext sind deshalb keine Option. Die von einem Sensor erfassten Messdaten werden lokal in eine sehr sparsame Payload encodiert, wo nur so viele Bit und Bytes per Funk übertragen werden, wie unbedingt notwendig. Der serverseitige Payload Decoder wandelt diese Daten zurück und vergibt einen entsprechenden Parametername für die einzelnen Werte, welche wiederum meistens im JSON-Format an eine Applikation weitergegeben werden.
Als Beispiel für dieses Projekt kommt ein Wasserzähler (Qualcosonic W1 von AXIOMA) zum Einsatz. Dieser wurde zuvor bereits bei TTN registriert und sendet nun alle 24 Stunden den aktuellen Zählerstand mit Zeitstempel sowie einen Statuscode bzw. Fehlercode mit. Vom Hersteller wird anstelle eines fertigen Decoders eine Tabelle geliefert, wo ersichtlich ist, welche Bytes zu einem bestimmten Messwert gehören. Anhand dieser Tabelle wird nun ein entsprechender Decoder erstellt. Die Wertetabelle des Herstellers aus dem Datenblatt sieht wie folgt aus:
Order | Number of bytes | Description |
---|---|---|
1 | 4 | Current date and time |
2 | 1 | Status code |
3 | 4 | Current volume |
4 | 4 | Log date and time |
5 | 4 | Volume at log date and time |
Zuerst werden die einzelnen Bytes in die jeweiligen Bereiche eingeteilt. Als Beispiel wird eine Payload vom 20.10.2022 um 7:02:19 (UTC) mit einem aktuellen Zählerstand von 83,902 m3 und den Fehlercode "0" (Kein Fehler) verwendet. Die Hexadezimale Payload für diese Werte sieht wie folgt aus:
Eine weitere wichtige Information vom Hersteller ist die Bitwertigkeit. Dabei gibt es MSB (Most Significant Bit) und LSB (Least Significant Bit). Eine bevorzugte Bitwertigkeit gibt es nicht, jedoch muss der Decoder entsprechend auf die Herstellervorgabe programmiert werden, um daraus plausible Werte zu erhalten. In der folgenden Grafik wird der aktuelle Zählerstand (Byte 5-8) aus der Payload genommen und als LSB/MSB dargestellt.
Wie in der oberen Grafik erkennbar ist, werden zwischen LSB und MSB einfach die Bytes gespiegelt. Das erste Byte (BE) bei LSB ist nun das letzte Byte bei MSB, während das letzte Byte von LSB (00) nun an erster Stelle bei MSB steht. Der Hersteller hat sich bei diesem Sensor auf eine Codierung in MSB festgelegt. Hexadezimal ist übersichtlicher als Binär, jedoch wird für das bessere Verständnis dieser Vorgehensweise die Hexadezimale MSB Payload in eine Binärzahl umgerechnet:
Werden die vier Bytes vom aktuellen Zählerstand von hexadezimal in binär umgerechnet und aneinandergereiht, so entsteht eine 32-Bit Binärzahl. Wird diese nun in eine Dezimalzahl umgerechnet, so erhält man den aktuellen Zählerstand in Liter. Wird dieser durch 1000 dividiert, so erhält man den aktuellen Zählerstand in Kubikmeter.
Der Payload-Decoder wird in Javascript programmiert. Dabei handelt es sich um eine Funktion, die standardmäßig nach dem Datenempfang auf dem LoRaWAN-Server ausgeführt wird. Übergeben werden dieser Funktion die Bytes der Payload und ein Port.
Die Variable "vol" besteht nun aus Byte 8-5. In diesem Fall handelt es sich um ein Bit-Shifting nach MSB, weshalb mit Byte[8] (Blau) begonnen wird und dieses um 24 Stellen nach links verschoben wird. Anschließend kommt Byte[7] (Grün) dazu und wird um 16 Stellen nach links geschoben. Byte[6] (Gelb) wird um 8 Stellen nach links geschoben und Byte[5] (Orange) füllt nun die letzten 8 Stellen. Daraus ergibt sich ein 32-Bit Zahlenwert (Integer bzw. Long) der über "return" als "VolumeTotal" zurückgeben wird. Eine Division durch 1000 wandelt Liter in Kubikmeter um. Der Decoder für den aktuellen Zählerstand sieht nun wie folgt aus:
//More information at https://www.aeq-web function Decoder(bytes, port) { var vol = bytes[8] << 24 | bytes[7] << 16 | bytes[6] << 8 | bytes[5]; return { VolumeTotal: vol / 1000 //Volume in m³ } }
Der Wasserzähler sendet neben den Zählerstand auch seinen aktuellen Zeitstempel (Byte 0-3) mit. Es handelt sich dabei um einen Unix-Timestamp, der ebenfalls aus einem 32-Bit Zahlenwert besteht. Das Bit-Shifting funktioniert daher exakt gleich wie beim Zählerstand, nur das dafür die ersten vier Bytes der Payload anstelle Byte 5-8 verwendet werden müssen. Unixzeit zählt die vergangenen Sekunden seit 01.01.1970 00:00 in UTC. Anhand der vergangenen Sekunden seit diesem Zeitstempel kann nun eine sekundengenaue Uhrzeit berechnet werden. Eine Funktion zur Umrechnung gibt es praktisch bei allen Programmiersprachen, so auch bei Javascript. Folgende Funktion gibt aus Unixzeit "1666249339" den Datum-Zeitstempel "20.10.2022 07:02:19" zurück:
//More information at https://www.aeq-web.com function Decoder(bytes, port) { var systime = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; var systemtime = unixToDateTime(systime); return { DateTime: systemtime } function unixToDateTime(unixtime) { var date = new Date(unixtime * 1000); var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var hours = date.getHours(); var minutes = "0" + date.getMinutes(); var seconds = "0" + date.getSeconds(); var year = date.getFullYear(); var month = months[date.getMonth()]; var day = date.getDate(); var DateTime = day + '-' + month + '-' + year + ' ' + hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2); return DateTime; } }
Neben Verbrauchswerten und Zeitstempeln wird auch ein Fehlercode übertragen. Dieser besteht lediglich aus einem Byte und ist daher sehr einfach zu Decodieren:
//More information at https://www.aeq-web function Decoder(bytes, port) { var sta = bytes[4]; return { Status: sta } }
Der vollständige Code für den Qualcosonic W1 Payload-Decoder sieht nun wie folgt aus:
//More information at https://www.aeq-web.com function Decoder(bytes, port) { if (port == 100) { var systime = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; var sta = bytes[4]; var vol = bytes[8] << 24 | bytes[7] << 16 | bytes[6] << 8 | bytes[5]; var systemtime = unixToDateTime(systime); return { DateTime: systemtime, VolumeTotal: vol / 1000, //Volume in m³ Status: sta } } function unixToDateTime(unixtime) { var date = new Date(unixtime * 1000); var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var hours = date.getHours(); var minutes = "0" + date.getMinutes(); var seconds = "0" + date.getSeconds(); var year = date.getFullYear(); var month = months[date.getMonth()]; var day = date.getDate(); var DateTime = day + '-' + month + '-' + year + ' ' + hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2); return DateTime; } }
Einstieg in das LoRaWAN (TTN) mit dem Heltec LoRa32 V3 und Einrichtung vom Board in der Arduino IDE
WeiterlesenStarthilfe LoRaWAN - Diese Seite richtet sich an alle Einsteiger, die mit LoRaWAN starten wollen und ihre Sensoren in das IoT-Netzwerkt TTN integrieren wollen
WeiterlesenAEQ-WEB © 2015-2024 All Right Reserved