In de vorige afleveringen hebben we het MQTT-protocol gebruikt om berichten met meetwaarden en commando’s uit te wisselen tussen verschillende clients. We hebben een actuator- en een sensorknooppunt gerealiseerd met een ESP32 DevKitC Board (verkrijgbaar in de Elektor-Shop). Dit board bevat de ESP32, een krachtige microcontroller met WLAN-functionaliteit, die dankzij slimme bibliotheken gemakkelijk is te programmeren met de Arduino-IDE.

In de laatste delen hebben we geleerd hoe het eenvoudige MQTT-protocol werkt: via een TCP/IP-verbinding worden telkens maar een paar bytes heen en weer gestuurd tussen de client (onze ESP32-kaart) en de server (de MQTT-testbroker ergens in de cloud). In dit deel gaan we met een ander protocol werken, namelijk HTTP (Hypertext Transfer Protocol), dat ook op TCP/IP gebaseerd is. En nu keren we de rollen om: Onze ESP32-hardware wordt de TCP/IP-server en een PC in hetzelfde lokale WLAN-netwerk werkt als TCP/IP-client. Voor de PC hoeven we geen extra software te ontwikkelen, we kunnen een heel gewone webbrowser gebruiken. Als u deze regels leest, hebt u namelijk zojuist het HTTP-protocol gebruikt: U hebt in de adresregel van uw browser www.elektormagazine.nl ingevoerd en de Elektor-server heeft u de overeenkomstige website toegestuurd als nuttige lading van het HTTP-protocol. De website is gecodeerd in HTML: Naast teksten worden schakelvlakjes, weblinks, afbeeldingen en allerlei andere elementen overgedragen.

Ook de ESP32 kan websites genereren die we in een browser op de PC kunnen bekijken. Zo’n website kan bijvoorbeeld een formulier bevatten waarin we configuratiegegevens kunnen invoeren. Met een druk op een schakelvlakje sturen we die gegevens dan weer terug naar de ESP32. Misschien begrijpt u al waar we naartoe willen: tot nu toe moesten we alle instelmogelijkheden voor onze kaart hard coderen in een Arduino-sketch. Het zou veel mooier zijn om onze hardware te kunnen configureren via het WLAN. De monitor, de muis en het toetsenbord van de PC vormen dan een comfortabele gebruikersinterface voor onze kaart. Om te leren hoe we de ESP32 kunnen inzetten als een eenvoudige webserver, heb ik om te beginnen een eenvoudigere toepassing bedacht: De ESP32 moet op aanvraag van de webbrowser een website met een klein formulier genereren (zie screenshot). In het eerste tekstveld naast „LED1“ kunnen we dan „00“ of „FF“ invoeren om de rode LED te schakelen, die we al in deel 17 op de ESP32-kaart hebben aangesloten. Ook de RGB-LED gaan we weer gebruiken: die bevestigt dat het inloggen in het draadloze netwerk is gelukt.

Webserver-code voor de ESP32

Bij het programmeren van de webserver heb ik natuurlijk niet het wiel opnieuw uitgevonden. Even googelen naar „esp32 webserver arduino“ leverde al deze mooie Arduino-sketch op. Wat hier in de setup-functie staat, kennen we al: de ESP32 logt in op het WLAN. Daarna geeft hij zijn eigen adres in het lokale netwerk weer op de seriële monitor. Dat adres hebben we straks nog nodig. Eén regel is nieuw voor ons: Met
 
server.begin();

wordt een TCP/IP-server opgestart, die gaat staan wachten op een aanvraag van een client. Dat kan een browser zijn op de PC, maar ook op een smartphone of een tablet. Dat kan een heel prettige mobiele afstandsbediening zijn.

Het interessante werk gebeurt in de functie loop. Als er een aanvraag van de client beschikbaar is, analyseert de microcontroller de tekens, die hij via TCP/IP binnenkrijgt. De aanvraag wordt gedaan via het HTTP-protocol. Omdat alle tekens ook worden uitgevoerd op de seriële monitor, kunnen we goed zien hoe praatgraag HTTP is in vergelijking met MQTT. Het is wel een voordeel dat er alleen bytes in ASCII-code worden gebruikt, daarom kunnen we voor de analyse de comfortabele string-functies van de Arduino gebruiken. Het Line-Feed-scheidingsteken verdeelt de datastroom van de aanvraag in regels (zie screenshot).



Voor de evaluatie is vooral de eerste regel, die met „GET“ begint, van belang. In het eenvoudigste geval heeft de gebruiker alleen het lokale adres van de ESP32 (bijvoorbeeld „192.168.0.23“) ingevoerd in de adresregel van zijn webbrowser en op Enter gedrukt om de aanvraag te starten. Maar het is ook mogelijk om achter een schuine streep nog meer ASCII-tekens naar de server te sturen. Bij het surfen op het web wordt dat bijvoorbeeld gebruikt om specifieke pagina’s op de server te kiezen, maar we kunnen er ook besturingscommando’s mee naar een webserver sturen. In de toepassing die ik heb gebruikt als voorbeeld, wordt een URL als „192.168.0.23/H“ gebruikt om een LED in te schakelen. In de datastroom van de aanvraag verschijnt de „/H“ dan meteen achter „GET“ (gescheiden door een spatie). Omdat erachter ook weer een spatie volgt, is het besturingscommando gemakkelijk uit te pakken voor de Arduino-code. Daarna kan de software het schakelen van de LED verzorgen.

Voor mijn eigen toepassing gebruik ik een ander, in elke browser ingebouwd, mechanisme. Een webformulier is opgebouwd uit HTML-besturingselementen. Een erg nuttig besturingselement ziet er als volgt uit:
 
<input type="submit" value="Submit" />

De browser geeft dit element weer als een drukknop, in dit geval met het opschrift „Submit“ (verzenden). Als de gebruiker erop klikt, stuurt de browser een nieuwe aanvraag naar de webserver. Alle data in het formulier (bijvoorbeeld de tekst in de tekstvelden) worden als zogenaamde parameters achter het adres gehangen. Als er bijvoorbeeld een tekstveld is met de naam „Country“ waarin „Germany“ is ingevuld en in een tweede tekstveld, „City“, staat bijvoorbeeld „Aachen“, dan zou de browser de volgende aanvraag naar onze webserver in het lokale netwerk sturen:
 
192.168.0.23/?Country=Germany&City=Aachen

Alles achter de „/“ verschijnt weer in de TCP/IP-datastroom van de aanvraag; de scheidingstekens „=“ en „&“ maken de verwerking gemakkelijk.

Een kleine webserver-library

Nu had ik alle ingrediënten bij elkaar om een dergelijke webserver-toepassing te programmeren; het resultaat kunt u downloaden onderaan deze pagina. Ik heb er weer met opzet van afgezien om alles tot in het laatste detail te abstraheren en te optimaliseren, ook de foutafhandeling is nog rudimentair. Mijn kleine webserver-library bestaat uit de functies
 
void Webserver_Start()
String Webserver_GetRequestGETParameter()
void Webserver_SendHTMLPage(String HTMLPage)

De eerste functie bevat alleen het bovengenoemde commando server.begin(), dat moet worden aangeroepen in de setup-functie. Interessant is de tweede functie die snel herhaald cyclisch moet worden doorlopen. Als er geen aanvraag aanwezig is, geeft hij gewoon een lege string terug. Als een client een aanvraag heeft gestuurd, dan worden de HTTP-regels geëvalueerd. Als de gebruiker alleen maar een adres zoals „192.168.0.23“ heeft ingevoerd, dan geeft de functie de string „-“ terug. Als het gaat om een aanvraag in de vorm „192.168.0.23/?Country=Germany&City=Aachen“, dan wordt de uitdrukking achter het vraagteken teruggegeven. Na een aanvraag van de client wordt de verbinding open gehouden. We kunnen dan een HTML-pagina naar de client sturen met de derde bovengenoemde functie.

Om het geheel voor te bereiden voor de configuratie-toepassing die ik wil gaan bouwen, heb ik een paar arrays gedefinieerd, met elk 8 elementen (voor elke instelmogelijkheid één). ConfigName[x] is daarbij de naam van de instelmogelijkheid, ConfigValue[x] is de betreffende waarde en ConfigStatus[x] geeft aan, of de waarde nog onbepaald (0), geldig (1) of ongeldig (-1) is. In de demo-toepassing zijn steeds de waarden „00“ en „FF“ geldig.
In de hoofdlus wordt nu cyclisch het volgende gedaan:
  1. We roepen de functie Webserver_GetRequestGETParameter aan en controleren, of er een aanvraag (HTTP-Request) van de webbrowser binnenkomt. Als dat het geval is, wordt de verbinding met de webbrowser (client) open gehouden en worden de tekens geëvalueerd. De functie geeft dan ofwel een „-“ ofwel de GET-parameter achter het „?“ terug.
  2. Als het gaat om meer dan één teken, dan nemen we aan dat de gebruiker op de „Submit“-button gedrukt heeft en de data uit het formulier binnenkomen (in theorie kunnen we het systeem te slim af zijn door handmatig tekens in te voeren in de adresregel).
  3. De GET-parameter wordt opgesplitst en de betreffende waarden van ConfigValue[x] worden ingevuld (de volgorde van de waarden in de parameters is bepalend).
  4. In de functie ProcessAndValidateConfigValues(…) worden de waarden van ConfigValue[x] gecontroleerd. Op basis van het resultaat van die controle wordt ConfigStatus[x] ingevuld. Verder wordt de rode LED aangestuurd op basis van de waarde in het eerste tekstveld. Deze functie is heel specifiek voor de toepassing en staat daarom in de sketch direct voor de loop-functie.
  5. Tenslotte wordt de (nieuwe) webpagina samengesteld uit HTML-elementen. Het formulier wordt daarbij opnieuw opgebouwd met de actuele configuratiewaarden in de tekstvelden. De achtergrondkleuren groen en rood signaleren geldige en ongeldige waarden. De webpagina wordt dan voorzien van een HTTP-header (HTTP-Response) en verzonden. Dan wordt de verbinding met de client afgesloten.
  6. Enzovoort.
Probeer de kleine webserver meteen maar eens uit! U moet in de Arduino-sketch, zoals altijd, eerst de SSID en het wachtwoord van uw draadloze netwerk invoeren, voordat u het programma compileert en naar de kaart stuurt. U krijgt vast en zeker meteen ideeën voor mogelijke veranderingen. We zouden bijvoorbeeld ongeldige invoer niet kunnen accepteren of de LED kunnen schakelen met een radio-button in plaats van met een tekstinvoer (op Internet zijn veel goede gratis HTML-tutorials te vinden).

In de volgende delen gaan we weer verder!