Mein Weg in das IoT (19): Einfacher Webserver mit dem ESP32
11. Oktober 2017
über
über
In den letzten Folgen haben wir das MQTT-Protokoll verwendet, um Nachrichten mit Messwerten und Kommandos zwischen verschiedenen Clients auszutauschen. Mit einem ESP32 DevKitC Board (gibt es im Elektor-Shop) haben wir einen Aktor- und einen Sensorknoten realisiert. Das Board enthält mit dem ESP32 einen leistungsstarken, WLAN-fähigen Mikrocontroller, der sich dank cleverer Bibliotheken einfach über die Arduino-IDE programmieren lässt.
In den letzten Folgen haben wir gelernt, wie das leichtgewichtige MQTT-Protokoll funktioniert: Über eine TCP/IP-Verbindung huschen nur jeweils ein paar Bytes vom Client (unserem ESP32-Board) zum Server (dem MQTT-Testbroker draußen in der Cloud) und zurück. In dieser Folge wenden wir uns einem anderen Protokoll zu, das ebenfalls auf TCP/IP aufsetzt, nämlich HTTP (Hypertext Transfer Protocol). Dabei kehren wir die Verhältnisse um: Unsere ESP32-Hardware wird zum TCP/IP-Server, ein PC, der sich im gleichen lokalen WLAN-Netzwerk befindet, übernimmt die Rolle des TCP/IP-Clients. Für den PC brauchen wir keine extra Software zu entwickeln, wir können einen ganz normalen Webbrowser benutzen. Wenn Sie diese Zeilen lesen, haben Sie nämlich gerade das HTTP-Protokoll verwendet: Sie haben in der Browserzeile www.elektormagazine.de eingegeben und vom Elektor-Server die entsprechende Webseite ausgeliefert bekommen, als Nutzlast des HTTP-Protokolls. Diese ist HTML-codiert: Neben Texten werden Schaltflächen, Weblinks, Bilder und viele weitere Elemente übertragen.
Auch der ESP32 kann Webseiten ausliefern, die sich in einem Browser auf dem PC darstellen lassen. So eine Webseite kann zum Beispiel ein Formular enthalten, auf dem wir Konfigurationswerte eintragen und mit einem Druck auf eine Schaltfläche wieder zurück zum ESP32 schicken können. Vielleicht ahnen Sie auch schon, wo die Reise hingehen soll: Bisher mussten wir alle Einstellungen für unser Board hardcodiert in einen Arduino-Sketch eintragen. Schöner wäre es, wenn wir unsere Hardware über das WLAN konfigurieren könnten; PC-Monitor, Maus und Tastatur bilden dabei ein komfortables User-Interface für unser Board. Um zu lernen, wie man den ESP32 als einfachen Webserver einsetzt, habe ich mir aber erst einmal eine einfachere Anwendung ausgedacht: Der ESP32 soll auf Anfrage des Webbrowsers eine Webseite mit einem kleinen Formular ausliefern (siehe Screenshot). In das erste Textfeld neben „LED1“ können wir dann „00“ oder „FF“ eintragen, um die rote LED zu schalten, die wir schon in Folge 17 an das ESP32-Board angeschlossen haben. Auch die RGB-LED setzen wir wieder ein: Sie bestätigt das erfolgreiche Einloggen in das heimische WLAN.
wird ein TCP/IP-Server angeworfen, auf Anfragen eines Clients zu lauschen. Das kann ein Browser auf dem PC, aber auch auf dem Smartphone oder Tablet sein, womit man eine schöne mobile Fernsteuerung bekommt.
Das eigentlich Spannende passiert in der loop-Funktion. Falls eine Anfrage vom Client anliegt, analysiert der Mikrocontroller die Zeichen, die nun über TCP/IP zu ihm kommen. Als Protokoll für die Anfrage dient HTTP. Da alle Zeichen auch auf dem Seriellen Monitor ausgegeben werden, kann man gut beobachten, wie geschwätzig HTTP im Vergleich zu MQTT ist. Ein Vorteil ist jedoch, dass nur Bytes im ASCII-Wertebereich genutzt werden; zur Analyse kann man deshalb die komfortablen Arduino-String-Funktionen nutzen. Das Line-Feed-Trennzeichen unterteilt den Datenstrom der Anfrage in Zeilen (siehe Screenshot).
Interessant für die Auswertung ist die erste Zeile, die mit „GET“ beginnt. Im einfachsten Fall hat der User in die Adresszeile seines Webbrowsers nur die lokale Adresse des ESP32 (zum Beispiel „192.168.0.23“) eingegeben und auf Enter gedrückt, um die Anfrage zu starten. Es ist aber möglich, nach einem Schrägstrich noch weitere ASCII-Zeichen zum Server zu schicken. Beim Surfen im Web wird dies zum Beispiel genutzt, um Unterseiten anzugeben, die man besuchen will, doch man kann damit auch Steuerkommandos zu einem Webserver senden. In der Anwendung, die ich als Vorlage genutzt habe, wird eine URL wie „192.168.0.23/H“ verwendet, um eine LED einzuschalten. Im Datenstrom der Anfrage erscheint das „/H“ dann direkt nach „GET“ und einem Leerzeichen. Da danach noch ein weiteres Leerzeichen folgt, kann der Steuerbefehl vom Arduino-Code leicht extrahiert und das Schalten der LED veranlasst werden.
Für meine eigene Anwendung nutze ich einen weiteren, in jedem Browser eingebauten Mechanismus. Ein Web-Formular ist aus HTML-Steuerelementen aufgebaut, ein sehr nützliches besitzt folgende Form:
Der Browser stellt das Element als Schaltfläche dar, in diesem Fall mit der Beschriftung „Submit“ (Absenden). Wenn der User darauf klickt, dann schickt der Browser eine neue Anfrage zum Webserver; alle Daten im Formular (zum Beispiel der Text in den Textfeldern) werden als sogenannter Parameter an die Adresse angehängt. Steht in einem Textfeld namens „Country“ zum Beispiel „Germany“ und in einem zweiten Textfeld „City“ zum Beispiel „Aachen“, dann würde der Browser folgende Anfrage zu unserem Webserver im lokalen Netzwerk schicken:
Alles ab dem „/“ erscheint wieder im TCP/IP-Datenstrom der Anfrage; die Trennzeichen „=“ und „&“ machen die Auswertung einfach.
Die erste Funktion kapselt lediglich das obengenannte Kommando server.begin(), das in der setup-Funktion aufzurufen ist. Interessant ist die zweite Funktion, die in schneller Folge zyklisch durchlaufen werden muss. Wenn keine Anfrage vorliegt, gibt sie einfach einen Leerstring zurück. Übermittelt ein Client eine Anfrage, dann werden die HTTP-Zeilen ausgewertet. Hat der Nutzer einfach eine Adresse wie „192.168.0.23“ eingegeben, dann gibt die Funktion den String „-“ zurück. Handelt es sich um eine Anfrage nach dem Muster „192.168.0.23/?Country=Germany&City=Aachen“, dann wird der Ausdruck nach dem Fragezeichen zurückgegeben. Nach einer Anfrage des Clients wird die Verbindung offen gehalten. Nun können wir diesem eine HTML-Seite zukommen lassen, mit der dritten oben genannten Funktion.
Um das Ganze für die kommende Konfigurations-Anwendung vorzubereiten, habe ich ein paar Arrays definiert, mit jeweils 8 Elementen (für jede Einstellmöglichkeit eines). ConfigName[x] ist dabei der Name des Einstellmöglichkeit, ConfigValue[x] der jeweilige Wert und ConfigStatus[x] zeigt an, ob der Wert noch unbestimmt (0), gültig (1) oder ungültig (-1) ist. In der Demo-Anwendung sind jeweils die Werte „00“ und „FF“ gültig.
In der Hauptschleife wird nun zyklisch Folgendes gemacht:
Weiter geht es in der nächsten Folge!
In den letzten Folgen haben wir gelernt, wie das leichtgewichtige MQTT-Protokoll funktioniert: Über eine TCP/IP-Verbindung huschen nur jeweils ein paar Bytes vom Client (unserem ESP32-Board) zum Server (dem MQTT-Testbroker draußen in der Cloud) und zurück. In dieser Folge wenden wir uns einem anderen Protokoll zu, das ebenfalls auf TCP/IP aufsetzt, nämlich HTTP (Hypertext Transfer Protocol). Dabei kehren wir die Verhältnisse um: Unsere ESP32-Hardware wird zum TCP/IP-Server, ein PC, der sich im gleichen lokalen WLAN-Netzwerk befindet, übernimmt die Rolle des TCP/IP-Clients. Für den PC brauchen wir keine extra Software zu entwickeln, wir können einen ganz normalen Webbrowser benutzen. Wenn Sie diese Zeilen lesen, haben Sie nämlich gerade das HTTP-Protokoll verwendet: Sie haben in der Browserzeile www.elektormagazine.de eingegeben und vom Elektor-Server die entsprechende Webseite ausgeliefert bekommen, als Nutzlast des HTTP-Protokolls. Diese ist HTML-codiert: Neben Texten werden Schaltflächen, Weblinks, Bilder und viele weitere Elemente übertragen.
Auch der ESP32 kann Webseiten ausliefern, die sich in einem Browser auf dem PC darstellen lassen. So eine Webseite kann zum Beispiel ein Formular enthalten, auf dem wir Konfigurationswerte eintragen und mit einem Druck auf eine Schaltfläche wieder zurück zum ESP32 schicken können. Vielleicht ahnen Sie auch schon, wo die Reise hingehen soll: Bisher mussten wir alle Einstellungen für unser Board hardcodiert in einen Arduino-Sketch eintragen. Schöner wäre es, wenn wir unsere Hardware über das WLAN konfigurieren könnten; PC-Monitor, Maus und Tastatur bilden dabei ein komfortables User-Interface für unser Board. Um zu lernen, wie man den ESP32 als einfachen Webserver einsetzt, habe ich mir aber erst einmal eine einfachere Anwendung ausgedacht: Der ESP32 soll auf Anfrage des Webbrowsers eine Webseite mit einem kleinen Formular ausliefern (siehe Screenshot). In das erste Textfeld neben „LED1“ können wir dann „00“ oder „FF“ eintragen, um die rote LED zu schalten, die wir schon in Folge 17 an das ESP32-Board angeschlossen haben. Auch die RGB-LED setzen wir wieder ein: Sie bestätigt das erfolgreiche Einloggen in das heimische WLAN.
Webserver-Code für den ESP32
Beim Programmieren des Webservers habe ich das Rad natürlich nicht neu erfunden. Ein einfaches Googeln nach „esp32 webserver arduino“ liefert diesen schönen Arduino-Sketch. Was hier in der setup-Funktion steht, kennen wir schon: Der ESP32 loggt sich ins WLAN ein. Danach gibt er die Adresse, die er im lokalen Netzwerk besitzt, auf dem Seriellen Monitor aus; denn diese benötigen wir noch. Eine Zeile ist neu für uns: Mitserver.begin();
wird ein TCP/IP-Server angeworfen, auf Anfragen eines Clients zu lauschen. Das kann ein Browser auf dem PC, aber auch auf dem Smartphone oder Tablet sein, womit man eine schöne mobile Fernsteuerung bekommt.
Das eigentlich Spannende passiert in der loop-Funktion. Falls eine Anfrage vom Client anliegt, analysiert der Mikrocontroller die Zeichen, die nun über TCP/IP zu ihm kommen. Als Protokoll für die Anfrage dient HTTP. Da alle Zeichen auch auf dem Seriellen Monitor ausgegeben werden, kann man gut beobachten, wie geschwätzig HTTP im Vergleich zu MQTT ist. Ein Vorteil ist jedoch, dass nur Bytes im ASCII-Wertebereich genutzt werden; zur Analyse kann man deshalb die komfortablen Arduino-String-Funktionen nutzen. Das Line-Feed-Trennzeichen unterteilt den Datenstrom der Anfrage in Zeilen (siehe Screenshot).
Interessant für die Auswertung ist die erste Zeile, die mit „GET“ beginnt. Im einfachsten Fall hat der User in die Adresszeile seines Webbrowsers nur die lokale Adresse des ESP32 (zum Beispiel „192.168.0.23“) eingegeben und auf Enter gedrückt, um die Anfrage zu starten. Es ist aber möglich, nach einem Schrägstrich noch weitere ASCII-Zeichen zum Server zu schicken. Beim Surfen im Web wird dies zum Beispiel genutzt, um Unterseiten anzugeben, die man besuchen will, doch man kann damit auch Steuerkommandos zu einem Webserver senden. In der Anwendung, die ich als Vorlage genutzt habe, wird eine URL wie „192.168.0.23/H“ verwendet, um eine LED einzuschalten. Im Datenstrom der Anfrage erscheint das „/H“ dann direkt nach „GET“ und einem Leerzeichen. Da danach noch ein weiteres Leerzeichen folgt, kann der Steuerbefehl vom Arduino-Code leicht extrahiert und das Schalten der LED veranlasst werden.
Für meine eigene Anwendung nutze ich einen weiteren, in jedem Browser eingebauten Mechanismus. Ein Web-Formular ist aus HTML-Steuerelementen aufgebaut, ein sehr nützliches besitzt folgende Form:
<input type="submit" value="Submit" />
Der Browser stellt das Element als Schaltfläche dar, in diesem Fall mit der Beschriftung „Submit“ (Absenden). Wenn der User darauf klickt, dann schickt der Browser eine neue Anfrage zum Webserver; alle Daten im Formular (zum Beispiel der Text in den Textfeldern) werden als sogenannter Parameter an die Adresse angehängt. Steht in einem Textfeld namens „Country“ zum Beispiel „Germany“ und in einem zweiten Textfeld „City“ zum Beispiel „Aachen“, dann würde der Browser folgende Anfrage zu unserem Webserver im lokalen Netzwerk schicken:
192.168.0.23/?Country=Germany&City=Aachen
Alles ab dem „/“ erscheint wieder im TCP/IP-Datenstrom der Anfrage; die Trennzeichen „=“ und „&“ machen die Auswertung einfach.
Kleine Webserver-Library
Jetzt hatte ich alles Zutaten beisammen, um eine entsprechende Webserver-Anwendung zu programmieren; das Ergebnis können Sie unten downloaden. Ich habe mit Absicht wieder darauf verzichtet, alles bis ins letzte Detail zu abstrahieren und zu optimieren, auch die Fehlerbehandlung ist noch rudimentär. Meine kleine Webserver-Library besteht aus den Funktionenvoid Webserver_Start()
String Webserver_GetRequestGETParameter()
void Webserver_SendHTMLPage(String HTMLPage)
String Webserver_GetRequestGETParameter()
void Webserver_SendHTMLPage(String HTMLPage)
Die erste Funktion kapselt lediglich das obengenannte Kommando server.begin(), das in der setup-Funktion aufzurufen ist. Interessant ist die zweite Funktion, die in schneller Folge zyklisch durchlaufen werden muss. Wenn keine Anfrage vorliegt, gibt sie einfach einen Leerstring zurück. Übermittelt ein Client eine Anfrage, dann werden die HTTP-Zeilen ausgewertet. Hat der Nutzer einfach eine Adresse wie „192.168.0.23“ eingegeben, dann gibt die Funktion den String „-“ zurück. Handelt es sich um eine Anfrage nach dem Muster „192.168.0.23/?Country=Germany&City=Aachen“, dann wird der Ausdruck nach dem Fragezeichen zurückgegeben. Nach einer Anfrage des Clients wird die Verbindung offen gehalten. Nun können wir diesem eine HTML-Seite zukommen lassen, mit der dritten oben genannten Funktion.
Um das Ganze für die kommende Konfigurations-Anwendung vorzubereiten, habe ich ein paar Arrays definiert, mit jeweils 8 Elementen (für jede Einstellmöglichkeit eines). ConfigName[x] ist dabei der Name des Einstellmöglichkeit, ConfigValue[x] der jeweilige Wert und ConfigStatus[x] zeigt an, ob der Wert noch unbestimmt (0), gültig (1) oder ungültig (-1) ist. In der Demo-Anwendung sind jeweils die Werte „00“ und „FF“ gültig.
In der Hauptschleife wird nun zyklisch Folgendes gemacht:
- Wir rufen die Funktion Webserver_GetRequestGETParameter auf und schauen nach, ob eine Anfrage (HTTP-Request) vom Webbrowser hereinkommt. Wenn ja, dann wird die Verbindung zum Webbrowser (Client) offengehalten und die Zeichen werden ausgewertet. Die Funktion gibt nun eine „-“ oder den GET-Parameter nach dem „?“ zurück.
- Handelt es sich um mehr als ein Zeichen, dann nehmen wir an, dass der User auf den „Submit“-Button gedrückt hat und die Daten aus dem Formular hereinkommen (theoretisch kann man das System durch manuelle Eingabe von Zeichen in die Adresszeile überlisten).
- Der GET-Parameter wird zerlegt und die jeweiligen Werte von ConfigValue[x] werden neu gesetzt (die Reihenfolge der Werte im Parameter ist maßgeblich).
- In der Funktion ProcessAndValidateConfigValues(…) werden die Werte von ConfigValue[x] überprüft. ConfigStatus[x] wird entsprechend gesetzt; darüber hinaus wird je nach dem Wert im ersten Textfeld die rote LED geschaltet. Diese Funktion ist hoch anwendungsspezifisch und steht daher im Sketch direkt vor der loop-Funktion.
- Zum Schluss wird die (neue) Webseite aus HTML-Elementen zusammengesetzt. Das Formular wird dabei neu aufgebaut, mit den aktuellen Konfigurations-Werten innerhalb der Textfelder. Die Hintergrundfarben grün und rot symbolisieren gültige und ungültige Werte. Die Webseite wird dann mit einem HTTP-Header versehen (HTTP-Response) und verschickt. Dann wird die Verbindung zum Client geschlossen.
- Und so fort.
Weiter geht es in der nächsten Folge!
Mehr anzeigen
Weniger anzeigen
Diskussion (3 Kommentare)