IIn het vorige deel hebben we een klein sensorknooppunt ontwikkeld, dat meetwaarden via MQTT naar het internet stuurt. Het was opgebouwd met een Pretzel-board (bestukt met een ATmega en de WLAN-chip ESP8266). Een RGB-LED dient daarbij voor het weergeven van de status: geel geeft aan dat een poging wordt gedaan om een verbinding te maken, groen staat voor succes en rood voor een fout. Zoals we weten, moet de ESP8266 eerst inloggen in een WLAN-netwerk (SSID en wachtwoord staan hard gecodeerd in de Arduino-sketch voor de ATmega). Na een druk op de knop maakt de chip via TCP/IP verbinding met de HiveMQ-MQTT-testbroker op Internet. Als ook dat gelukt is, kunnen we MQTT-commando’s versturen. Nog even het geheugen opfrissen over MQTT: Voordat we berichten kunnen publiceren (Publish) of ons kunnen abonneren op berichten uit een bepaald topic (Subscribe), moeten we eerst een MQTT-Connect-aanvraag naar de broker zenden, die daarop een vastgelegd antwoord stuurt. In de laatste delen hebben we, nogal bruut, voor het publiceren van een bericht (met een meetwaarde) telkens opnieuw de TCP/IP-verbinding afgekapt, een nieuwe verbinding gemaakt en dan de Connect-aanvraag opnieuw verstuurd. In dit deel, waarin we eigenlijk geen data zenden, maar ontvangen, houden we de verbinding open.

In het vorige deel had ik al geprobeerd me te abonneren op een berichtenstroom en de data te ontvangen. Daarvoor had ik mijn kleine MQTT-bibliotheek uitgebreid met de functies:

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


Met de eerste functie kunnen we ons abonneren op één topic, maar we kunnen ons ook abonneren op een heleboel topics tegelijk. We geven dat aan door wildcards te gebruiken in de parameter topicfilter. De tweede functie moeten we als MQTT-client periodiek aanroepen om te kijken, of er berichten zijn aangekomen in de topics waarop we zijn geabonneerd. De berichten bereiken ons vanaf de MQTT-broker via TCP/IP. Hij geeft de bytes, die hij van een of andere MQTT-client heeft ontvangen op deze topics gewoon door. Hij geeft niet alleen het topic en de nuttige data door, maar werkelijk alle bytes 1:1, inclusief de commandobytes van het Publish-commando en alle lengtebytes. Die simpele aanpak bespaart de broker een hoop tijdrovend werk: hij hoeft de data niet opnieuw te coderen. En voor ons is het heel gemakkelijk om de nuttige data eruit te halen. In de functie MQTTClient_Get wordt de nuttige data naar het byte-array MQTTClient[] gekopieerd (dit array is beschikbaar gesteld door de MQTT-library en wordt ook gebruikt als zendbuffer).

Nu is het tijd voor een kleine demo-toepassing: het schakelen van een digitale actuator via het Internet. De hardware uit de eerdere delen (zie voor het schema deel 13), die bestaat uit een Pretzel-board op een breadboard, een druktoets en de RGB-LED, wordt hier opnieuw gebruikt. Lui als ik ben, zeker met de paasdagen, gebruikte ik gewoon D13 als schakeluitgang, zodat de status van de actuator (aan/uit) overeenkomt met die van de blauwe LED D3 op het Pretzel-board. De code van de Arduino-sketch Actor_ESP8266_Ref kunt u onderaan deze pagina downloaden. De setup-functie blijft bijna helemaal onveranderd: na het aansluiten van de voedingsspanning logt de ESP8266 in op een WLAN-netwerk en als dat gelukt is laat hij de RGB-LED groen oplichten. Nu moeten we weer op de druktoets op het breadboard drukken om de kaart op scherp te zetten (de RGB-LED moet nu geel licht geven). In de code wordt nu de functie ConnectAndSubscribeToTopic() aangeroepen. Die laat de ESP8266 eerst een Connect-aanvraag naar de HiveMQ-testserver sturen, gevolgd door een Subscribe-aanvraag met het gewenste topic „/ElektorMyJourneyIoT/TestTopic/test“. De broker moet nu antwoorden met de bytes 144 en 3. Als die bytes worden ontvangen, licht de RGB-LED weer groen op en de kaart luistert naar binnenkomende data. Daartoe wordt ongeveer eens per seconde de functie MQTTClient_Get aangeroepen. De teruggegeven waarde geeft het aantal ontvangen bytes, die dan in het array MQTTClient_PayloadBuffer[] te vinden zijn.

Ik gebruikte mijn heel simpele toepassingsprotocol uit deel 8, waarbij een tekststring „0000“ de actuator uitschakelt en „00FF“ hem inschakelt (daarbij worden niet de bytes 0 en 255 verzonden, maar de ASCII-code van de vier tekens). Ik testte het met de al bekende MQTT-testclient voor de PC (zie screenshot). Na het invoeren van „TestTopic“ (het middelste deel van de bovengenoemde topic-naam) in het veld „Topic to publish“ en van „0000“ of „00FF“ in het veld „Text to publish“ kon ik met de button „Publish“ schakelacties triggeren.

Helaas werkte dat maar een paar minuten. Als we de MQTT-specificatie nog eens goed bekijken, wordt duidelijk, waarom dat zo is. Om onderbroken verbindingen snel te kunnen detecteren, moeten MQTT-clients zich namelijk van tijd tot tijd bij de broker melden via TCP/IP, door een aanvraag te doen of door iets publiceren; anders kapt de broker de verbinding af. De zogenaamde Keep Alive Time bepaalt, hoe lang dit tijdvenster is en wordt door de client vastgelegd in de Connect-aanvraag. In mijn rudimentaire MQTT-library is deze tijd altijd 180 seconden.

Maar hoe moet het dan in ons geval met een MQTT-client die alleen maar luistert en niet periodiek berichten naar de broker zendt? In dit geval moet de client zich met een zogenaamd Ping Request melden bij de server. Een Ping Request bestaat uit de bytes 192 en 0; de broker antwoordt als het gelukt is met een Ping Response (bytes 208 en 0). Zo kan de client controleren, of de verbinding er nog is.

Ik breidde mijn MQTT-library uit met de functie int MQTTClient_Ping(), die bij succes een 1 teruggeeft. Ik roep die functie ongeveer eens in de 15 seconden aan in de applicatiecode. Het ping-verzoek wordt weergegeven door de RGB-LED (kort) geel te laten oplichten, daarna wordt de LED groen of rood. In tegenstelling tot de sensor-toepassing uit de laatste twee delen, hield dit programma de verbinding (zowel TCP/IP als MQTT) continu in stand.

Het schakelen bleef nu ook voor langere tijd werken. Maar er was voor degene die het MQTT-bericht verstuurde nog geen enkele terugmelding, of het schakelen ook succesvol was uitgevoerd. Het zou immers kunnen dat de verbinding verbroken was. En ook als de actuator net bezig was om zich met een ping bij de broker te melden, kwamen berichten niet aan. Mijn simpele oplossing was om op een extra-topic een korte bevestiging van de actuator-client naar de schakel-client te sturen. De tekst „R1“ betekende succesvol inschakelen van de blauwe LED en „R0“ betekende dat de LED succesvol was uitgeschakeld.

Probeer het zelf maar eens uit: met mijn MQTT-testclient (beschikbaar in de download) kunt u schakelcommando’s versturen zoals hierboven beschreven en ook de terugmeldingen ontvangen. Eerst moeten we ons abonneren op de topics voor de berichten in beide richtingen (één keer „TestTopic“ en één keer „TestTopicBack“ invoeren in het veld „Topic to subscribe“ en telkens daarna de Subscribe-button indrukken). Als in het veld „Received text“ kort na de tekst „00FF“ geen „R1“ verschijnt en na een „0000“ geen „R0“, weten we dat het schakelcommando niet is verwerkt. In dat geval kunnen we het bericht met de Publish-button meteen opnieuw verzenden.

Soms kan het zijn, dat er helemaal niets meer gebeurt; de verbinding is dan afgebroken. In dat geval zien we de RGB-LED op de actuatorkaart rood oplichten. Door opnieuw op de druktoets te drukken, kunnen we de kaart opdracht geven om weer verbinding met de HiveMQ-testserver te maken (RGB-LED moet dan eerst geel en daarna groen worden). Dit kunnen we natuurlijk ook automatiseren, dat lijkt me een leuk thema voor de volgende aflevering. Blijft u aan de lijn!