Für meine letzten Reviews habe ich bereits sehr gerne mit dem ESP32-Controller gearbeitet, um kleine IoT-Applikationen zu entwickeln. Der Prozessor ist mit WLAN, Bluetooth und reichlich Speicher ausgestattet und per Arduino-IDE programmierbar. Darüber hinaus gibt es sehr günstige und steckbrettfreundliche Entwicklungsboards. Mit ein paar zusätzlichen Bauteilen (wie RGB-LED, Fotosensor und Steckbrücken) sowie den entsprechenden Arduino-Librarys gelangen auch Einsteiger schnell zu kleineren Projekten, bei denen etwa Sensorwerte zu Cloud-Plattformen geschickt und dort angezeigt werden.

Eine gute Ergänzung ist ein kleines Display, das Statusmeldungen wie zum Beispiel „No Network“ anzeigen kann. So zum Beispiel beim Lolin Board, das ich mir in einem früheren Review angesehen habe. Vor ein paar Wochen machte mich mein Kollege Mathias Claußen aus unserem Labor auf das M5Stack-System aufmerksam. Das Basismodul integriert ein kleines ESP32-Board in ein formschönes, kompaktes Gehäuse; fast die gesamte Oberseite wird von einem grafikfähigen Farbdisplay mit 320 x 240 Pixeln eingenommen. Darunter befinden sich drei taktile Taster; darüber hinaus gibt es diverse Erweiterungssteckverbinder auf allen Seiten des Gehäuses. Es existieren außerdem Zusatzmodule (zum Beispiel für GPS), die einfach zwischen Ober- und Unterseite des Basismoduls eingeklickt werden können, so dass man sich seinen persönlichen „Stapel“ zusammenstellen kann. Mein Kollege Clemens Valens hat bereits ein Review verfasst, in dem er das M5Stack-System vorstellt.

Wie steuere ich das Display an?

Bei meinen Reviews entwickle ich ja gerne eine kleine Applikation, aber die Zeit dafür ist immer knapp. Daher stellte sich für mich als Erstes die Frage, wie schnell ich zu einem kleinen Sketch kommen würde, der einen Text auf das Display schreibt. Die Netzwerkfunktionen konnte ich ja ganz einfach aus meinen bisherigen Projekten übernehmen – denn der ESP32 ist auch hier per Arduino-IDE programmierbar.
Ich stieß im Netz sehr schnell auf eine Anleitung, die zeigt, wie man die nötigen Bibliotheken in die Arduino-IDE einbindet. Hier war auch eine kleine Demo zu finden. Es stellte sich schnell heraus, dass die Entwickler die Funktionen zum Ansteuern des Displays (und auch zum Abfragen der Taster) in einem Objekt namens M5 gekapselt hatten. Nach Einbinden der Lib mit
 
#include <M5Stack.h>

ruft man im eigenen Sketch die Methode
 
M5.begin();

auf und kann dann über Befehle wie
 
M5.Lcd.setCursor(x, y);
M5.Lcd.setTextSize(2);
M5.Lcd.print(“Hello World!”);

das Display ansteuern.
Auf GitHub steht auch eine sogenannte API Reference zur Verfügung. Ich habe dort allerdings nicht alles gefunden, was ich gesucht habe. Zum Beispiel musste ich eigene Versuche machen, um herauszufinden, welche Werte ich als Parameter für die Schriftgröße (M5.Lcd.setTextSize) verwenden kann (die Werte 2 bis 5 finde ich sinnvoll).
Schließlich konnte ich dann aber sehr schnell mein kleines Projekt, das ich für das Lolin OLED-Board entwickelt hatte, auf den M5Stack portieren. In dieser Anwendung werden die Werte eines Fotosensors gesampelt und zur Cloud-Plattform OpenSenseMap geschickt (siehe Review). Die Spannung über dem Fotowiderstand (Schaltung) lese ich am Erweiterungssteckverbinder auf der rechten Seite des M5Stacks ein (siehe Foto).



Meine erste M5Stack-App können Sie unten downloaden. Übrigens ist im M5Stack ein kleiner Akku eingebaut (ein Extra-Akku ist ebenfalls erhältlich). Mit dem integrierten Akku läuft das Ganze leider nur etwa ein Stündchen ohne externe Stromversorgung.

Nachrichten über Webserver

Bei dieser Quick-and-Dirty-Portierung eines alten Projekts war das Display und die Kompakt- und Geschlossenheit des Gehäuses aber noch nicht richtig zur Geltung gekommen. Da fügte es sich gut, dass mir eine weitere Anwendungsidee geradewegs zufiel. Ich habe zuhause ja eine kleine DJ-Bühne; von Zeit zu Zeit stehen Freunde und Bekannte dort an den Decks, und wir nehmen das Ganze mit verschiedenen Kameras auf. Mitunter hatte ich mir vom Regieplatz aus eine Möglichkeit gewünscht, dem DJ dort oben etwas zu signalisieren, ohne mit meinen Händen vor ihm herumwedeln zu müssen.
Das M5Stack-Display hatte genau die richtige Größe, um auf der Bühne kleine Textnachrichten anzuzeigen. Schnell verworfen habe ich die Idee, eine kleine App für den PC zu schreiben, die mit dem M5Stack über UDP oder rudimentäres TCP/IP kommuniziert. Einfacher und flexibler ist es, Textnachrichten über einen simplen Webbrowser einzugeben und zum Display zu schicken – das geht ja dann vom PC aus genauso wie von einem Mobilgerät.
Einen ESP32-Webserver hatte ich ja schon für meinen IoT-Blog programmiert; damit wurde ein kleines Formular ausgeliefert, über das man die SSID und das Passwort für das eigene WLAN-Netzwerk einstellen konnte. Um die Werte in einem Browser eingeben zu können, musste man sich zuerst in ein vom ESP32 selbst aufgespanntes „Hilfs“-Netzwerk einloggen, und dann über die Adresse 192.168.4.1 an den ESP32 wenden (ESP32 im Modus Access Point). Nachdem die Login-Daten für das Routernetzwerk beim ESP32 angekommen waren, konnte sich dieser einloggen, und bekam dann eine andere Adresse (bei mir war das zum Beispiel 192.168.0.38).
Um meine Anwendung zu realisieren, nahm ich einen meiner bisherigen Sketche als Grundlage. Ich erweiterte den Webserver so, dass er nach einem Aufruf unter dieser zweiten Adresse eine neue kleine Webseite auslieferte, mit einem einzigen Textfeld (für die Nachricht an den DJ) sowie einen Submit-Button zum Abschicken. Nachdem der ESP32-Webserver den Text vom Client-Webbrowser empfangen hatte, wurde die Nachricht auf dem M5Stack-Display dargestellt.

WiFiWebserver- und DisplayBuffer-Library

Allerdings störten mich bei meinem ersten Prototyp noch drei Dinge. Erstens flackerte die Anzeige des Displays. Es gibt in der M5Stack-API übrigens keinen dezidierten Befehl zum Löschen des Displays, man muss, um Text zu löschen, eine Kette von Leerzeichen ausgeben, und dann den neuen Text darüber schreiben.
Zweitens fand ich meinen Code nicht schön, es waren zu viele eigene Funktionen damit beschäftigt, die beiden verschiedenen Webseiten zusammenzustellen und die Requests auszuwerten. Hier sollte eine Library her, die für beide Webseiten brauchbar war – egal ob bis zu acht Konfigurationswerte oder nur ein kleiner Nachrichtentext einzugeben waren.
Drittens wollte ich davon weg, den ESP32 die ganze Zeit sowohl im Access-Point- als auch im Station-Modus zu betreiben. Das war nicht nur potentiell unsicher, sondern erhöhte auch den Stromverbrauch.

Nach etwas Entwicklungszeit kam ich auf die Lösung, die Sie unten downloaden können.
Gegen das Flackern des Displays verwende ich einen kleinen Zeichen-Puffer ein – genau genommen sind es bis zu vier, die man wahlfrei an verschiedenen Cursor-Positionen einsetzen kann. In meiner Anwendung ist der erste Buffer nur für die Heartbeat-Anzeige (ein Zeichen, das zwischen 0 und 1 wechselt) zuständig. Ein weiterer Puffer nimmt den Statustext auf (eingeloggt oder nicht), darüber hinaus wird hier die Adresse angezeigt, unter der der ESP32 zu erreichen ist. Der dritte Buffer ist für die Anzeige des Nachrichtentexts verantwortlich. Im Quellcode sind die Funktionen zum Setzen der DisplayBuffer-Parameter (Position, Schriftgröße, Länge des Textes) und zum Schreiben von Text natürlich dokumentiert.

Um den Webserver zu abstrahieren, habe ich zuerst eine kleine Klasse Param geschrieben – sie verwaltet einen Satz von bis zu acht Parametern, die einen Namen, einen Wert und einen Typ haben. Mit den Zeilen
 
Param Messages = Param();
Param NetworkConfig = Param();

„ziehe“ ich mir im Anwendungssketch zwei Objekte dieser Klasse. Das erste Objekt nimmt den empfangenen Nachrichtentext auf (Messages.PValue[0]). Das zweite Objekt dient zur Verwaltung der Einstellungen (NetworkConfig.PValue[0] ist die SSID, NetworkConfig.PValue[1] ist das Passwort). Über ein solches Objekt können wir an andere Librarys gleichzeitig einen Satz von Strings übergeben. Darüber hinaus können wir Funktionen schreiben, die ein Param-Objekt (also mehrere Strings gleichzeitig) zurückgeben.
Meine neue Webserver-Library macht davon Gebrauch. Die Zeilen
 
#include <WiFiWebserver.h>
WiFiWebserver myWebserver = WiFiWebserver();

machen die Webserver-Funktionen über das Objekt myWebserver zugänglich. Die Zeile
 
NetworkConfig = myWebserver.DeSerializeFromGETParameter(GETParameter, NetworkConfig);

nimmt den GET-Parameter auseinander, der bei einem übermittelten Webformular die vom Nutzer eingetragenen Werte codiert (in der Browseraddresszeile hinter dem „?“). Die erhaltenen Werte werden dann in das Objekt NetworkConfig geschrieben und sind mit NetworkConfig.PValue[x] zugänglich. Umgekehrt kann man mit myWebserver.SerializeToHTMLInputTable(NetworkConfig) eine HTML-Tabelle mit den nötigen Eingabefeldern erhalten, in die der User die Werte von NetworkConfig (SSID, Passwort) eingeben kann.

Tastendrücke

Blieb noch das dritte Problem – der ESP32 sollte möglichst nicht ständig als Access-Point sein eigenes Netz aufspannen. Bei meiner neuen Anwendung geht der ESP32 zu Beginn des Programms in den Station-Mode und versucht sich beim Router-Netzwerk anzumelden. Bei Erfolg erscheint die Adresse in diesem Netzwerk und der Text „Network“; der Webserver wartet dann auf den Request eines Webbrowsers. Will man die Einstellungen ändern, dann muss man den linken und den mittleren Taster des M5Stacks gleichzeitig drücken (schauen Sie einmal im Quellcode, wie die Taster abgefragt werden). Die Adresse 192.168.4.1 erscheint, die der ESP32 im selbst aufgespannten Netzwerk besitzt. Wenn Sie sich (zum Beispiel mit einem Smartphone) in dieses Netzwerk einloggen, können Sie über diese Adresse die Konfigurations-Webseite aufrufen. Bei diesem Projekt sind nur die SSID und das Passwort einzugeben. Nach dem Abschicken versucht sich der ESP32 in das Routernetzwerk einzuloggen (dauert einen Moment). Wenn Sie die beiden Taster abermals zusammen betätigen, dann erscheint nach wenigen Sekunden die Adresse in diesem Netzwerk. Die Taster sollten Sie immer dann betätigen, wenn die Hearbeat-Anzeige läuft – falls diese stillsteht, dann ist der ESP32 mit anderen Aufgaben blockiert. Sowas ist für den Nutzer natürlich nicht optimal – wir werden in einem der kommenden Reviews sehen, wie wir das lösen können!