Grâce à une carte Pretzel (équipée d'un ATmega et d'une puce Wi-Fi ESP8266), nous avons, dans l'épisode précédent, mis au point une petite chaîne de caractères capable de transmettre des mesures sur Internet via un serveur MQTT. Une LED RVB nous sert de témoin d'état : le jaune symbolise la tentative de connexion, le vert la réussite et le rouge l'échec. Nous nous rappelons que l'ESP8266 commence par se connecter à un réseau Wi-Fi (le SSID et le mot de passe sont codés directement dans le croquis Arduino de l'ATmega). Quand on appuie sur le bouton, la puce se connecte sur Internet via TCP/IP avec le courtier (ou serveur) MQTT HiveMQ. Si la connexion est établie, les commandes MQTT peuvent être envoyées.

Petite révision sur le MQTT : avant de pouvoir publier (Publish) des messages ou s'abonner (Subscribe) à un topic défini, il faut d'abord envoyer une demande de connexion (Connect) MQTT, à laquelle le courtier répond avec des octets fixes. Dans le dernier épisode, avant la publication d'un message (contenant une valeur mesurée) nous avons sans finesse systématiquement mis fin à la communication TCP/IP, puis l'avons rétablie et réémis une requête MQTT-Connect. Dans cet épisode, nous devons recevoir et non pas transmettre des données et nous allons au contraire maintenir la communication ouverte.

Dans l'épisode précédent j'avais déjà exploré l'abonnement à un flux de messages et la réception de données et, à cet effet, complété ma petite bibliothèque MQTT avec les fonctions

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


La première fonction nous permet de nous abonner à un topic donné, mais aussi à plusieurs dans une même opération, en spécifiant des jokers dans le paramètre topicfilter. Comme client MQTT, nous devons invoquer la seconde fonction périodiquement pour examiner si nous avons reçu des messages relatifs aux topics auxquels nous sommes abonnés. Ces messages nous parviennent via TCP/IP depuis le courtier MQTT. Ce dernier se contente de retransmettre les octets des messages qu'il a reçus de clients MQTT quelconques et qui sont envoyés au titre des topics auxquels nous sommes abonnés. Il retransmet non seulement le topic et les données utiles, mais l'ensemble des octets 1:1, y compris les octets de la commande Publish et tous les octets de longueur. Cette méthode simple évite au courtier toute tâche fastidieuse de transcodage et nous facilite en même temps l'extraction des données utiles. La fonction MQTTClient_Get recopie les données utiles dans le tableau d'octets MQTTBytesPayloadBuffer[] (mis à disposition par la bibliothèque MQTT et qui sert également de tampon d'émission).
 
Il est grand temps d'aborder une petite application de démonstration : la commande par Internet d'un actionneur logique. Je n'ai rien changé au matériel des derniers épisodes (voir épisode 13) constitué de la carte Pretzel sur une plaque d'essai, d'un bouton et d'une LED RVB. Flemmard comme une marmotte émergeant de l'hibernation, je me suis contenté d’utiliser D13 comme sortie logique de sorte que seule la LED bleue D3 de la carte Pretzel témoigne de l'état de l'actionneur (M/A). Ci-dessous, vous pouvez télécharger le code du croquis Arduino Actor_ESP8266_Ref. La fonction Setup est pratiquement inchangée : à la mise sous tension, l'ESP8266 se connecte au réseau Wi-Fi et en cas de réussite, allume la LED RVB en vert. Il faut alors actionner le bouton de la plaque d'essai pour établir la communication formelle (la LED RVB doit d'abord virer au jaune). Le code appelle ensuite la fonction ConnectAndSubscribeToTopic().

Elle ordonne à l'ESP8266 d'envoyer une demande de connexion au serveur du courtier HiveMQ, suivi par une commande SUBSCRIBE assortie du topic souhaité « /ElektorMyJourneyIoT/TestTopic/test ».
Le courtier devrait répondre en envoyant les octets 144 et 3. Si ces octets ont été reçus, la LED RVB repasse au vert et la carte attend des données. Lancée toutes les secondes environ, la fonction MQTTClient_Get est chargée de cette tâche. La valeur retournée indique le nombre d'octets utiles reçus et stockés dans le tableau MQTTClient_PayloadBuffer[].

J'utilise le protocole applicatif de l'épisode 8 qui permet de désactiver l'actionneur (chaîne 0000) et de l'activer (chaîne 00FF). Les valeurs envoyées ne sont pas les octets 0 et 255, mais les codes ASCII des quatre caractères). J'ai déjà testé l'ensemble avec le client test MQTT bien connu pour PC (voir la copie d'écran). Après saisie du TestTopic (la partie centrale du nom du topic désigné ci-dessus) dans le champ Topic to Publish et de 0000 ou 00FF dans le champ Text to publish, le bouton Publish a bien déclenché l'action voulue.

Mais cela n'a fonctionné que quelques minutes. En examinant la spécification MQTT de plus près, la raison apparaît clairement. Pour détecter rapidement les interruptions de communication, les clients MQTT doivent se manifester auprès du courtier par TCP/IP, soit par une commande quelconque, soit en publiant quelque chose ; dans la négative, le courtier ferme la communication unilatéralement. Le temps dit Keep Alive Time détermine la durée de cette fenêtre. C'est le client qui la fixe comme paramètre de la commande CONNECT. Dans ma bibliothèque MQTT rudimentaire, cette durée est fixée à 180 s.

Mais que se passe-t-il si le client MQTT ne fait qu'écouter et n'envoie aucun message au courtier ? Dans ce cas, le client doit se manifester auprès du serveur en envoyant une requête dite Ping Request (constituée des octets 192 et 0). En cas de réussite, le courtier répond avec une Ping Response (octets 208 et 0). Cela permet au client de vérifier que la communication n'est pas interrompue.

J'ajoute une fonction int MQTTClient_Ping() à ma bibliothèque MQTT. Elle renvoie un 1 en cas de réussite. Le code applicatif appelle cette fonction toutes les 15 s. L'envoi du Ping est signalé par un bref allumage de la LED RVB en jaune ; ensuite elle repasse au vert ou au rouge. Contrairement à l'application capteur des deux derniers épisodes, la communication (que ce soit par TCP/IP ou MQTT) reste en permanence ouverte.

La commande de l'actionneur fonctionne désormais durablement. Cependant, pour celui qui envoie le message MQTT de commande de l'actionneur, il n'y a encore aucun retour pour témoigner du succès ou de l'échec de celle-ci. Une interruption de communication peut par ex. passer inaperçue. Dans ce cas, les commandes ne parviennent pas non plus si, à cet instant précis, l'actionneur se manifeste au courtier par un Ping. J'ai trouvé une solution simple, en envoyant en plus sur le topic une courte confirmation au client de commande depuis le client actionneur. Le texte R1 signifie l'allumage réussi de la LED bleue et R0 l'extinction réussie de cette même LED.

Essayez vous-même : comme indiqué ci-dessus, mon client test MQTT (en téléchargement) peut déposer des messages de commande de commutation et recevoir les messages en retour. Avant, il fallait s'abonner aux topics correspondant aux deux sens de transmission (en saisissant un fois TestTopic et une fois TestTopicBack dans le champ Topic to subscribe et à chaque fois actionner le bouton Subscribe). Si dans le champ Received Text peu après le texte 00FF le texte R1 (respectivement après 0000 le texte R0) n'apparaît pas, nous savons que la commande de commutation n'a pas été traitée. Dans ce cas, nous pouvons renvoyer immédiatement le message à l'aide du bouton Publish..

De temps en temps, il arrive que plus rien ne se passe. La communication reste interrompue durablement. Dans ce cas, la LED RVB de la carte actionneur reste rouge. En actionnant à nouveau le bouton de la plaque d'essai, la carte doit se connecter à nouveau au serveur du courtier HiveMQ (la LED RVB brille en jaune puis passe au vert). Tout cela peut bien sûr être effectué automatiquement, c'est ce que nous verrons dans le prochain épisode. Restez à l'écoute !