In der letzten Folge haben wir auf Basis des Pretzel-Boards (bestückt mit einem ATmega und dem WLAN-Chip ESP8266) einen kleinen Sensor-Knoten entwickelt, der Messwerte über MQTT ins Internet übermittelt. Eine RGB-LED dient dabei als Status-Anzeige: Gelb steht für einen Verbindungsversuch, Grün für einen Erfolg und Rot für einen Fehler. Wir erinnern uns, dass sich der ESP8266 zuerst in ein WLAN-Netzwerk einloggen muss (SSID und Passwort stehen hartcodiert im Arduino-Sketch für den ATmega). Nach einem Tastendruck verbindet sich der Chip über TCP/IP mit dem HiveMQ-MQTT-Testbroker im Internet. Wenn auch das geklappt hat, können MQTT-Kommandos gesendet werden. Kleine Wiederholung MQTT: Bevor man Nachrichten veröffentlichen (Publish) oder Nachrichten unter einem bestimmten Topic abonnieren kann (Subscribe), muss man zuerst eine MQTT-Connect-Anfrage senden, die der Broker mit festgelegten Bytes beantwortet. Etwas brutal haben wir in den letzten Folgen vor dem Veröffentlichen einer Nachricht (mit einem Messwert) jedes Mal aufs Neue die TCP/IP-Verbindung gekappt, neu hergestellt und dann die Connect-Anfrage erneut gesendet. In dieser Folge – in der wir im Wesentlichen keine Daten senden, sondern empfangen – werden wir die Verbindung dagegen offen halten.

Schon in der letzten Folge hatte ich das Abonnieren eines Nachrichtenstroms und das Empfangen von Daten ausprobiert und dafür meine kleine MQTT-Bibliothek um die Funktionen

int MQTTClient_Subscribe(String topicfilter)
int MQTTClient_Get(byte MQTTBytesPayloadBuffer[])


erweitert. Mit der ersten Funktion können wir uns auf ein einzelnes Topic, aber auch in einem Rutsch auf eine Vielzahl von Topics abonnieren, was mit Wildcards im Parameter topicfilter angegeben wird. Die zweite Funktion müssen wir als MQTT-Client periodisch aufrufen, um nachzuschauen, ob unter dem abonnierten Topic Nachrichten angekommen sind. Diese Nachrichten erreichen uns über TCP/IP vom MQTT-Broker. Hierzu leitet dieser die Nachrichten-Bytes, die er von irgendwelchen MQTT-Clients unter den von uns abonnierten Topics zugeschickt bekommen hat, einfach weiter. Und zwar nicht nur das Topic und die Nutzdaten, sondern sämtliche Bytes 1:1, inklusive des Publish-Kommando-Bytes und aller Längenbytes. Diese simple Methode entlastet den Broker von zeitaufwendigen Umcodierungsarbeiten, und macht es uns gleichzeitig sehr einfach, die Nutzdaten zu extrahieren. In der Funktion MQTTClient_Get werden die Nutzdaten in das Byte-Array MQTTBytesPayloadBuffer[] hineinkopiert (welches von der MQTT-Lib zur Verfügung gestellt wird und gleichzeitig auch als Sende-Puffer dient).

Nun wurde es Zeit für eine kleine Demo-Anwendung: Das Schalten eines digitalen Aktors über das Internet. Die Hardware aus den letzten Folgen (Schaltung siehe Folge 13), bestehend aus dem Pretzel-Board auf einem Steckbrett, einem Taster und der RGB-LED, ließ ich unangetastet. Faul, wie ich an den Ostertagen nun einmal bin, benutzte ich einfach D13 als Schaltausgang, so dass der Status des Aktors (an/aus) von der blauen LED D3 auf dem Pretzel-Board angezeigt werden konnte. Den Code des Arduino-Sketches Actor_ESP8266_Ref können Sie unten herunterladen. In der Setup-Funktion bleibt nahezu alles wie gehabt: Nach Anlegen der Betriebsspannung loggt sich der ESP8266 in ein WLAN-Netzwerk ein und lässt die RGB-LED im Erfolgsfall grün leuchten. Nun muss man wieder den Taster auf dem Steckbrett betätigen, um das Board scharf zu schalten (RGB-LED muss zuerst gelb leuchten). Im Code wird jetzt die Funktion ConnectAndSubscribeToTopic() aufgerufen. Sie lässt den ESP8266 zuerst eine Connect-Anfrage an den HiveMQ-Testserver schicken, gefolgt von der Subscribe-Anfrage, mit dem gewünschten Topic „/ElektorMyJourneyIoT/TestTopic/test“. Der Broker sollte jetzt mit den Bytes 144 und 3 antworten. Wurden diese Bytes empfangen, leuchtet die RGB-LED wieder grün und das Board lauscht auf Daten. Hierzu wird etwa jede Sekunde die Funktion MQTTClient_Get aufgerufen. Der zurückgegebene Wert zeigt die Anzahl der empfangenen Nutzbytes, die nun im Array MQTTClient_PayloadBuffer[] zu finden sind.

Ich verwendete mein sehr simples Anwendungsprotokoll aus Folge 8, bei dem ein Textstring „0000“ den Aktor aus und „00FF“ den Aktor einschaltet (transportiert werden wohlgemerkt nicht die Bytes 0 und 255, sondern die ASCII-Codes der vier Zeichen). Das Ganze testete ich mit dem schon wohlbekannten MQTT-Testclient für den PC (siehe Screenshot). Nach Eingabe von „TestTopic“ (der Mittelteil des obengenannten Topic-Namens) ins Feld „Topic to Publish“ und von „0000“ oder „00FF“ in das Feld „Text to publish“ konnte ich mit dem Button „Publish“ Schaltvorgänge auslösen.

Allerdings funktionierte das nur ein paar Minuten. Wenn man sich die MQTT-Spezifikation ansieht, wird klar, warum das so ist. Um unterbrochene Verbindungen schnell detektieren zu können, müssen sich MQTT-Clients nämlich von Zeit zu Zeit über TCP/IP beim Broker melden, indem sie irgendeine Anfrage stellen oder etwas veröffentlichen; ansonsten kappt dieser seinerseits die Verbindung. Die sogenannte Keep Alive Time legt fest, wie lang dieses Zeitfenster ist, sie wird vom Client innerhalb der Connect-Anfrage festgelegt. In meiner rudimentären MQTT-Library ist diese Zeit immer 180 Sekunden.

Was aber, wenn in unserem Fall der MQTT-Client nur lauscht und keine Nachrichten periodisch zum Broker sendet? In diesem Fall muss sich der Client mit einer sogenannten Ping Request beim Server melden (bestehend aus den Bytes 192 und 0), der Broker antwortet im Erfolgsfall mit einer Ping Response (Bytes 208 und 0). Der Client kann somit überprüfen, ob die Verbindung noch steht.

Ich erweiterte meine MQTT-Lib um die Funktion int MQTTClient_Ping(), die im Erfolgsfall eine 1 zurückgibt; diese rief ich im Applikationscode etwa alle 15 Sekunden auf. Der Ping-Versuch wird durch eine (kurz) gelb leuchtende RGB-LED angezeigt, danach leuchtet die LED grün oder rot. Im Gegensatz zur Sensor-Anwendung aus den letzten zwei Folgen blieb die Verbindung (sowohl TCP/IP als auch MQTT) nun die ganze Zeit offen.

Jetzt funktionierte das Schalten auch über längere Zeit; allerdings gab es für denjenigen, der die MQTT-Nachricht verschickte, noch keinerlei Rückmeldung, ob der Schaltvorgang auch erfolgreich ausgeführt worden war. Es konnte ja beispielsweise ein Verbindungsabbruch vorliegen; auch kamen Nachrichten dann nicht an, wenn der Aktor sich gerade mit einem Ping beim Broker meldete. Meine simple Lösung bestand darin, auf einem Extra-Topic eine kurze Bestätigung vom Aktor-Client zum Schalt-Client zu schicken. Der Text „R1“ bedeutete ein erfolgreiches Anschalten der blauen LED und „R0“, dass die LED erfolgreich ausgeschaltet wurde.

Probieren Sie es selbst aus: Mit meinem MQTT-Testclient (im Download) kann man wie oben gezeigt Schaltnachrichten absetzen und auch die Rückmeldungen empfangen. Vorher sollte man sich auf die Topics für beide Nachrichtenrichtungen abonnieren (einmal „TestTopic“ und einmal „TestTopicBack“ in das Feld „Topic to subscribe“ eingeben und jeweils danach den Subscribe-Button betätigen). Falls man im Feld „Received Text“ kurz nach dem Text „00FF“ kein „R1“ angezeigt bekommt beziehungsweise nach einem „0000“ kein „R0“, weiß man, dass das Schaltkommando nicht verarbeitet wurde. In diesem Fall kann man die Nachricht mit dem Publish-Button gleich nochmals verschicken.

Von Zeit zu Zeit kann es passieren, dass gar nichts mehr geht – die Verbindung ist dauerhaft abgebrochen. In diesem Fall wird man auf dem Aktor-Board die RGB-LED rot leuchten sehen. Mit einem erneuten Druck auf den Taster kann man das Board dazu bringen, sich wieder mit dem HiveMQ-Testserver zu verbinden (RGB-LED muss gelb leuchten, dann grün). Dies ließe sich natürlich auch automatisieren – das könnte Thema der nächsten Folge sein. Bleiben Sie dran!