Von Dr. Claus Kühnel

Im neuen Elektor-Buch „LoRaWAN-Knoten im IoT” werden kommerziell angebotene Sensorknoten sowie mit einfachen Mitteln und zu niedrigen Kosten entwickelte LoRaWAN-Sensorknoten eingesetzt. Diese LoRaWAN-Knoten senden ihre Daten an einen LoRaWAN-Server, von dem sie abgerufen und in eine beliebige Anwendung integrierbar sind. Das hier gezeigte, dem Buch entnommene Praxisbeispiel zeigt die Registrierung eines mit einem M5Stack COM.LoRaWAN Moduls erweiterten M5Stack Basic Core im The Things Network (TTN) und die Visualisierung der erfassten Daten auf einem Raspberry Pi mit Node-Red.

Wenn Sie bislang mit einem Arduino erste Erfahrungen sammeln konnten, dann sind Sie bestens auf diese Aufgabenstellung vorbereitet. Die Arduino IDE deckt alle im Buch betrachteten Mikrocontroller unterschiedlicher Architektur ab, so dass Sie nicht mit unterschiedlichen Entwicklungsumgebungen arbeiten müssen.
Mit den im Buch vermittelten Erkenntnissen werden Sie in die Lage versetzt, Geräte und Gateways in der The Things Stack Community Edition (TTS (CE)) anzumelden oder Ihre bereits im TTN V2 befindlichen Geräte auf TTS (CE) zu migrieren.

M5Stack COM.LoRaWAN

Ich arbeite schon länger mit verschiedenen M5Stack-Komponenten, wie ich unter anderem in meinem Blog https://ckblog2016.net/2019/05/06/rapid-prototyping-mit-m5stack/ berichtet hatte.
Für mich war deshalb von Interesse, neben den zahlreichen anderen auch einen LoRaWAN-Knoten auf der Basis von M5Stack-Komponenten aufzubauen. Die M5Stack-Komponenten unterstützen ein effektives Prototyping und bieten gleichzeitig ansprechende Gehäuselösungen in diesem Bereich.
COM.LoRaWAN ist ein LoRaWAN-Kommunikationsmodul der stapelbaren M5Stack-Modulreihe (Bild 1).

 
M5Stack COM.LoRaWAN
Bild 1: M5Stack COM.LoRaWAN

Das auf einem ASR6501 basierende LoRaWAN-Modul kapselt die PSoC 4000- und SX1262-Chips, unterstützt das 868-MHz-Frequenzband, basiert auf einem Design mit extrem geringem Stromverbrauch und verbraucht im Tiefschlafmodus sehr geringen Strom (3,5 μA).
Zusammen mit dem M5Stack Basic Core (Bild 2), welcher den steuernden ESP32-Mikrocontroller sowie TFT-Display und Tasten zur Bedienung sowie einen Grove-Anschluss für externe Sensoren aufweist, sind alle Komponenten für einen komfortablen IoT-Knoten vereint. Ist man auf den Batteriebetrieb nicht festgelegt, dann kann zusätzlich die BLE- oder WiFi-Konnektivität genutzt werden.

 
M5Stack Basic Core
Bild 2: M5Stack Basic Core

Am Boden abgeschlossen werden kann der Stapel durch eine  M5GO/FIRE Battery Bottom Charging Base, die einen zusätzlichen LiPo-Akku sowie 2 x 5 seitlich angeordnete Neopixels aufweist (https://bit.ly/3e79Xrs).

Bei meiner Recherche bin ich auf das Programmbeispiel m5stack_core_ttn_sensornode_v1_03_ino.ino von Achim Kern gestossen, das alles für einen solchen LoRaWAN-Knoten beinhaltet. Als Sensor wird eine ENV.II Sensor Unit von M5Stack eingesetzt. Ausserdem kann ein PIR-Sensor als Anwesenheitsdetektor eingesetzt werden. Darauf habe ich hier aber verzichtet.
Die Registrierung des M5Stack COM.LoRaWAN muss manuell erfolgen, da M5Stack-Komponenten bei TTS (CE) noch nicht aufgenommen wurden. Im Buch ist die manuelle Registrierung Schritt-für-Schritt erläutert.

Den Quelltext des Programms möchte ich an dieser Stelle nur bezüglich der Payload erläutern. Zur Decodierung der Payload wird die Kenntnis deren Aufbaus benötigt.
Alle Programmbeispiele des Buchs sind auf Github unter https://github.com/ckuehnel/LoRaWAN-Node/tree/master/---%20Elektor%20--- abgelegt.
Die vom Sensor ermittelten Messwerte für Temperatur, Feuchtigkeit und Druck werden mit 100 multipliziert in der Payload abgespeichert. Für die Batteriespannung wird ein Prozentwert ermittelt und gleichermaßen verfahren. Daraus ergeben sich dann zwölf Byte Payload. Nach der Umwandlung des Bytearrays in einen String kann dieser dann versendet werden.

 
/*------------------------------------------------------------------*/
/* Function void send_to_TTN(void)                                  */
/*                                                                  */
/* TASK    : send sensor data to TTN                                */
/* UPDATE  : 25.01.2021                                             */
/*------------------------------------------------------------------*/
void send_to_TTN(void)

  // show picture
  iot_picture="/ttn.jpg";
  M5.Lcd.drawJpgFile(SD, iot_picture.c_str());
 
  // neopixels red
  rgb_neopixel(255,0,0);
 
  // activate communication
  Serial.println("LoraSet=?"); 
  ATCommand("LoraSet", "?");
  delay(500);
 
  Serial.println(F(" "));
  Serial.print(F(application)); Serial.print(F(" Version "));
  Serial.println(F(aktu_version));
  Serial.println(F(" "));
 
  // check if we can access battery functions
  if(!M5.Power.canControl())
  {
    Serial.println(F("[!] No communication with IP5306 chip"));
  }
 
  // actual battery level
  uint8_t bat = M5.Power.getBatteryLevel();
  Serial.print(F("[?] M5STACK BATTERY LEVEL --> "));
  Serial.print(bat);
  Serial.println(F(" %"));
  m5stack_bat=bat;
  int32_t battery_int = bat * 100;
   
  #ifdef ENABLE_SENSOR_ENVII    
    sensor_env2();
    m5stack_temp=env2_tmp;
    m5stack_humi=env2_hum;
    m5stack_press=env2_pressure;
    Serial.print(F("[?] M5STACK Unit ENVII --> "));
    Serial.print("ENVII-P:"); Serial.print(env2_pressure);
    Serial.print("  ENVII-T:"); Serial.print(env2_tmp); 
    Serial.print("  ENVII-H:"); Serial.println(env2_hum);                
  #endif
 
  // now we create the payload and send it to the TTN
  int32_t temp_int      = env2_tmp * 100;
  int32_t pressure_int  = env2_pressure * 100;
  int32_t hum_int       = env2_hum * 100;
 
  byte payload[12];
  payload[0] = temp_int;
  payload[1] = temp_int >> 8;
  payload[2] = temp_int >> 16;
 
  payload[3] = hum_int;
  payload[4] = hum_int >> 8;
  payload[5] = hum_int >> 16;
 
  payload[6] = pressure_int;
  payload[7] = pressure_int >> 8;
  payload[8] = pressure_int >> 16;
   
  payload[9]  = battery_int;
  payload[10] = battery_int >> 8;
  payload[11] = battery_int >> 16;
 
  Serial.print(F("[x] actual TTN payload --> "));
  char str[32] = "";
  array_to_string(payload, 12, str);
  Serial.println(str);
 
  // now send all to TTN
  ATCommand("SendHex", str);
  // neopixels now off
  rgb_neopixel_off();
  // display sensor data
  tft_display_m5stack();      
}
 
Das Programm von Achim Kern ist sehr übersichtlich und lesbar geschrieben, so dass ich Sie auf den Quelltext im Repository verweisen möchte. Außerdem gibt es auf Hackster einen sehr guten Beitrag, der alle erforderlichen Informationen liefert (https://www.hackster.io/kehosoftware/m5stack-com-lorawan-using-arduino-ide-38ab24).
Bitte beachten Sie aber, dass im zitierten Beitrag die Integration des LoRaWAN-Knotens noch ins TTN V2 vorgenommen wurde.

Die Ausgaben über die Console nach dem Start des Programms zeigt Bild 3.
 
Ausgabe über die Console nach Programmstart
Bild 3: Ausgabe über die Console nach Programmstart


Die folgenden Abbildungen zeigen die gut gestalteten Ausgaben auf dem TFT-Display des M5Stack Basic Core, die nacheinander im Programmablauf auf dem Display ausgegeben werden. In der sechsten Abbildung der Folge ist die gesamte Messanordnung einschliesslich ENV.II Sensor Unit zu sehen.
Die Bilder selbst sind als JPG-Datei auf einer SD-Card im M5Stack Basic Core gespeichert und sind ebenfalls im Repository zu finden. In einigen Abbildungen kann man auch deutlich den Einsatz der beidseitig angeordneten Neopixel sehen.





Registrierung des LoRaWAN-Knotens im TTS (CE)

Ich habe hier den M5Stack COM.LoRaWAN Knoten als Anwendung m5-stack-lorawan in der TTS (CE) registriert (Bild 4).

 
Anwendung m5-stack-lorawan im TTN V3
Bild 4: Anwendung m5-stack-lorawan im TTN V3
Gleichzeitig habe ich dieser Anwendung ein End Device mit den Namen M5Stack-COM.LoRaWAN und der End Device ID mym5stack zugeordnet (Bild 5).
 
End Device M5Stack-COM.LoRaWAN im TTN V3
Bild 5: End Device M5Stack-COM.LoRaWAN im TTN V3.

Die DevEUI wurde aus der ChipID des ESP32 abgeleitet. Die AppEUI kann in der TTS (CE) auf Null gesetzt werden und für den AppKey kann eine entsprechende Zahl erzeugt werden.
Wie in Bild 5 zu sehen ist, meldet sich der neue LoRaWAN-Knoten dann im Feld Live data.

Um aus der übermittelten Payload lesbare Daten zu erhalten, bedarf es einiger Zeilen JavaScript im Payload-Formatter, der aus den zwölf übermittelten Bytes die entsprechenden Parameter isoliert. In Bild 5 habe ich eine Zeile mit der decodierten Payload hineinkopiert.

 
function decodeUplink(input) {
  var data = {};
 
  data.temp  = ((input.bytes[2] << 16) + (input.bytes[1] << 8) + input.bytes[0])/100;
  data.humi  = ((input.bytes[5] << 16) + (input.bytes[4] << 8) + input.bytes[3])/100;
  data.press = ((input.bytes[8] << 16) + (input.bytes[7] << 8) + input.bytes[6])/100;
  data.bat   = ((input.bytes[11] << 16) + (input.bytes[10] << 8) + input.bytes[9])/100;
 
  var warnings = [];
  if (data.temp < 0) {
    warnings.push("under zero");
  }
  return {
    data: data,
    warnings: warnings
  };
}


Integrations

Als Integrations bezeichnet TTS (CE) die Möglichkeit, an den Netzwerkserver gesendete Daten mit einer Anwendung zu verknüpfen.
So stellt der Anwendungsserver u.a. einen MQTT-Server bereit, um mit Streaming-Ereignissen zu arbeiten. Die Storage Integration (Speicherintegration) ermöglicht es, empfangene Upstream-Nachrichten in einer persistenten Datenbank zu speichern und zu einem späteren Zeitpunkt wieder abzurufen.
Außerdem werden sogenannte Webhook-Templates bereitgestellt, die die Verknüpfung mit zahlreichen Plattformen unterstützen. Die Verknüpfung mit Cayenne, Thingspeak und Datacake wird im Buch beschrieben.

Node-RED Installation


Hier möchte ich Ihnen die Verknüpfung mit Node-RED vorstellen. Node-RED ist ein kostenloser, JavaScript-basierter Server und ein Web-GUI zur Vernetzung von Hardwaregeräten, APIs und Onlinediensten.
Mit dem in TTN V2 zur Verfügung stehenden Things Network Nodes for Node-RED war es sehr einfach möglich, Gerätenachrichten und Aktivierungen praktisch ohne Code zu verarbeiten.
In TTS (CE) wird dieses Package (https://www.npmjs.com/package/node-red-contrib-ttn) nicht mehr unterstützt. Durch die Verwendung von MQTT ist aber auch hier der Datenzugriff in vergleichbarer Weise möglich.
Sie können Node-Red auf einem Windows-PC oder einem Linux-Device, wie dem Raspberry Pi, installieren und recht einfach über das Web-GUI zu TTS (CE) gesendete Daten visualisieren. So bleiben Sie vollkommen unabhängig von einer der Cloud-Lösungen.

Zur Installation von Node-Red unter Windows starten Sie am Einfachsten auf der Website https://nodered.org/docs/getting-started/windows. Ich habe da die Version 14.17.3 LTS (long time stable) heruntergeladen und die betreffende MSI-Datei aufgerufen.
Nach erfolgter Installation kann beispielsweise über die PowerShell die korrekte Installation von NodeJs und npm überprüft werden. Sehen Sie die in Bild 6 ausgegebenen Versionsnummern, dann ist die Installation in Ordnung.
Durch die Installation von Node-Red als globales Modul wird der Befehl node-red zum Systempfad hinzugefügt. In Bild 6 sehen Sie das hierfür erforderliche Kommando.

 
Installation von Node-Red
Bild 6: Installation von Node-RED.

Durch Aufruf von node-red in der Powershell wird nun der Node-Red Server gestartet und über das Web-GUI kann mit dem Erstellen eines sogenannten Flows, der Node-Red Anwendung begonnen werden.
Das Web-GUI ist über den Browser durch Aufruf von localhost:1880 oder der betreffenden IP-Adresse und Port 1880 zu starten und zeigt sich in einer Form gemäß Bild 7.
 
Node-Red Web-GUI
Bild 7: Node-Red Web-GUI
Eine gute Hilfe bei der Programmierung in Node-RED bietet das Buch „Programming with Node-RED“ von Dogan Ibrahim aus dem Elektor-Verlag, was ich an einigen Stelle zu Rate gezogen habe.

Nun wird nicht jeder gerne seinen PC dauerhaft eingeschaltet lassen, um den Node-RED Server am Laufen zu halten. Das führt uns zu einem abgesetzten Server auf einem Raspberry Pi, den ich Ihnen im Folgenden noch vorstellen möchte.

Ich verwende hier ein Raspberry Pi Bundle RPI4 BDL 2GB 7TD von Reichelt, um einen abgesetzten Server aufzubauen, der dann im Dauerbetrieb laufen soll.
Das eingesetzte Bundle besteht aus:
  • einem Raspberry Pi 4 B mit 2 GB RAM
  • einem 7", kapazitiven Touch-Display mit 800 x 480 Pixel
  • einem passenden Gehäuse für das Touch-Display und den Raspberry Pi
  • einem leistungsstarken Netzteil mit 5.1 V DC, 3 A am USB Typ-C Stecker
  • einer 16 GB microSD-Karte (Class 10) mit vorinstalliertem Betriebssystem
Hinzu kommen noch der Einfachheit halber kabelgebundene Tastatur und Maus, die eigentlich nur für das Einrichten des Raspberry Pi benötigt werden und später abgezogen werden können.

Die hier verwendete Geräteausstattung soll nur das von mir eingesetzte Equipment zeigen. Haben Sie einen anderen Raspberry Pi bereits vorhanden, dann tut er das in der Regel genauso.
Zur Installation von Node-RED starten Sie am Einfachsten auf der Website https://nodered.org/docs/getting-started/raspberrypi.
Wie vor jedem neuen Projekt sollte die Software auf dem Raspberry Pi vorgängig einem Update/Upgrade unterzogen werden. Ich verwende hier das Raspberry Pi OS (Raspbian).
Über die Eingabe der folgenden Kommandos in einem Terminal starten Sie ein Update, bei dem die Listen der installierten Programmpakete neu eingelesen und aktualisiert werden, und anschließend ein Upgrade, bei dem alle neuen Versionen eines Paketes installiert werden.

$ sudo apt update && sudo apt upgrade

Um sicher zu gehen, dass alle notwendigen Vorkehrungen für die Installation von Node-Red getroffen sind, sollten Sie noch das folgende Kommando ausführen.

$ sudo apt install build-essential git curl

Ist das alles erfolgt, dann kann die eigentliche Installation durch Eingabe des folgenden Kommandos gestartet werden.
 
$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)

Stellen Sie sich auf einen längeren Installationsvorgang ein. Bei meiner hier vorgestellten Hardware mit neu aufgesetzter Firmware dauerte dieser Prozess allerdings nur wenige Minuten.
Um den Node-Red Server lokal zu starten, genügt die Eingabe des Kommandos node-red in einem Terminal. Durch Drücken von Ctrl-C oder durch Schließen des Terminalfensters wird Node-Red gestoppt.

Aufgrund des begrenzten Speichers des Raspberry Pi müssen Sie Node-RED mit einem zusätzlichen Argument starten, um den zugrunde liegenden Node.js-Prozess anzuweisen, ungenutzten Speicher früher als sonst freizugeben.

$ node-red-pi --max-old-space-size=256

Der Installationsscript für Node-Red auf dem Raspberry Pi erlaubt es auch Node-RED als Service im Hintergrund laufen zu lassen und während des Bootprozesses automatisch zu starten.

Der Start von Node-RED unterscheidet sich nicht von der PC-Version. Das Web-GUI wird wieder durch die Eingabe der URL http://localhost:1880 aufgerufen. Ist das Editieren bei einem 7“ Monitor etwas wenig komfortabel, dann können Sie das auch von einem PC aus erledigen.


Node-RED Anwendung


Für den oben vorgestellten Sensorknoten M5Stack COM-LoRaWAN möchte ich hier eine Node-RED Anwendung zeigen.
Der erwähnte Sensorknoten ermittelt Temperatur, relative Luftfeuchtigkeit und barometrischen Druck und sendet diese Daten sowie den Ladezustand der Batterie in Prozent zum TTS (CE)-Server. Ich möchte mit der Node-RED Anwendung diese Daten vom Server zurücklesen und visualisieren. Zusätzlich möchte ich die aktuellen Wetterdaten von OpenWeatherMap abfragen und zum Vergleich anzeigen.

Wenn Sie das hier vorgestellte Anwendungsbeispiel nachvollziehen möchten, dann ist eine Erweiterung der Node-RED Palette um node-red-dashboard und node-red-node-openweathermap erforderlich. Bild 8 zeigt die Beschreibung der Anwendung im sogenannten Node-RED Flow.

Die Abfrage des MQTT-Servers von TTS (CE) wird über den Knoten mqtt in mit der Bezeichnung TTS (CE)  M5Stack Data vorgenommen. Im Payload Decoder erfolgt eine Konvertierung der empfangenen Daten in ein JSON-Objekt, welches bei eingeschaltetem Debug-Knoten angezeigt werden kann. Der Debug-Knoten ist gemäss Bild 8 hier aber ausgeschaltet, da diese Einstellung nach Inbetriebnahme nicht mehr gebraucht wird.

 
Node-Red Flow
Bild 8: Node-Red Flow

In den folgenden Codezeilen ist ein Ausschnitt aus dem empfangenen JSON-Objekt gezeigt. Fett markiert habe ich die Nutzdaten, die visualisiert werden sollen.
 
{"end_device_ids":
  {"device_id":"mym5stack",
   "application_ids":
     {"application_id":"m5stack-com-lorawan"},
      "dev_eui":"0000AC33A5C40A24",
      "join_eui":"0000000000000000",
      "dev_addr":"260BD16E"},
      "correlation_ids":[...],
      "received_at":"2021-07-28T00:26:41.836179970Z",
      "uplink_message":
        {"session_key_id":"AXrJUnM2U0ar5bZMJkpi3Q==",
         "f_port":2,
         "f_cnt":931,
         "frm_payload":"lAcAaCAABnoBECcA",
         "decoded_payload":
           {"bat":100,"humi":82.96,"press":967.74,"temp":19.4},
 
         "rx_metadata":
           [{"gateway_ids":
             {"gateway_id":"ck-lps8",
              "eui":"A840411F4B104150"},
              "time":"2021-07-28T00:26:41.606956Z",
              "timestamp":4229742507,
              "rssi":-57,
              "channel_rssi":-57,
              "snr":7.5,
              "location":
           {"latitude":…,"longitude":…,"altitude":420,"source":"…"},
              "uplink_token":"…",
              "channel_index":2},
              {"gateway_ids":
                 {"gateway_id":"packetbroker"},
                 "packet_broker":
                   {"message_id":"…",
                    …
                   "home_network_cluster_id":"ttn-eu1"},
              "time":"2021-07-28T00:26:41.579083919Z",
              "rssi":-67,
              "channel_rssi":-67,
              "snr":8.25,
              "uplink_token":"..."}],
              "settings":
               {"data_rate":
                {"lora":{"bandwidth":125000,"spreading_factor":7}},
                 "data_rate_index":5,
                 "coding_rate":"4/5",
                 "frequency":"868500000",
                 "timestamp":4229742507,
                 "time":"2021-07-28T00:26:41.606956Z"},
                 "received_at":"2021-07-28T00:26:41.625142501Z",
                 "confirmed":true,
                 "consumed_airtime":"0.061696s",
                 "locations":{"user":{"…"}},
                 "network_ids":{…}}}
 

Der Zugriff auf die Temperatur erfolgt beispielsweise durch die folgenden JavaScript-Zeilen, die dem Funktionsknoten, der den Anzeigeknoten vorgelagert ist, zugeordnet wird. Ich habe die betreffenden Schlüsselwörter (Keys) wieder fett markiert.
 
var temp = { payload: msg.payload.length };
temp.payload = msg.payload.uplink_message.decoded_payload.temp;
return temp;
 

Dem Funktionsknoten folgen zwei Anzeigeknoten – einer für ein Zeigerinstrument (Gauge) und einer für ein Liniendiagramm (Chart), das hier die Werte der letzten zwölf Stunden zur Anzeige bringt. In vergleichbarer Weise wird mit den Zweigen für Feuchtigkeit, Druck und den Batteriezustand vorgegangen.
Die Werte für Datum & Zeit sowie RSSI und Spreading Factor sind in einem anderen Zweig zu finden. Die Decodierung ist aber vergleichbar.
 
var rssi = { payload: msg.payload.length };
rssi.payload = msg.payload.uplink_message.rx_metadata[0].rssi;
return rssi;
 

Damit stehen die Daten für die Anzeigeelemente zur Verfügung und das Node-RED Dashboard kann eingerichtet werden.

Für den mit meinem Wohnort bezeichneten OpenWeatherMap Knoten bedarf es eines Accounts bei OpenWeatherMap und der dort erzeugte API-Key ist in den Knoten zur Verbindungsausnahme einzutragen. Die einzelnen Parameter werden wieder aus dem JSON-Objekt decodiert und dann aber in einen Objekt zur Ausgabe zusammengesetzt.
Das Node-RED Dashboard für einen PC-Bildschirm zeigt Bild 9. Temperatur und relative Luftfeuchtigkeit unterscheiden sich auch im Tagesverlauf zwischen den Messstellen kaum. Beim Druck sieht das etwas anders aus. Die Messstellen liegen im gleichen Ort ca. 1.5 km auseinander.

 
Node-Red Dashboard auf dem PC
Bild 9: Node-Red Dashboard auf dem PC

Für das beim Raspberry Pi eingesetzte 7“-Display muss der darzustellende Inhalt etwas reduziert werden. Ich habe als erstes die Liniendiagramme entfernt, da diese keine besondere Lesbarkeit gezeigt haben. Damit die Textausgaben neben den Zeigerinstrumenten sichtbar sind, habe ich diese unterhalb angeordnet. Bei einer Vollbild-Darstellung und der Skalierung des Browsers auf 90% ist das Anzeigeergebnis akzeptabel (Bild 10).

 
Node-Red Dashboard auf dem Raspberry Pi 7“ Display – Vollbild
Bild 10: Node-Red Dashboard auf dem Raspberry Pi 7“ Display – Vollbild

Der auf dem Raspberry Pi installierte Node-Red Server kann nun im Dauerbetrieb laufen, ohne dass ein schlechtes Gewissen aufkommen muss.


Das neue Elektor-Buch „LoRaWAN-Knoten im IoT” ist im Elektor-Shop erhältlich.