Mon voyage dans le nuage IoT (9) : un client MQTT minimaliste
Suite logique de mon cheminement à travers l’IdO, j’implante ici un client MQTT sur une carte à microcontrôleur. La plateforme Arduino fournit tout ce qu’il faut pour cela, mais j’ai choisi une voie intellectuellement plus satisfaisante : la création de ma propre bibliothèque minimale. Car si son écriture demande plus de temps, elle oblige à mieux comprendre les rouages du protocole MQTT. Et comme chacun le sait, le chemin enrichit autant que la destination !
Notre exploration de l’Internet des Objets a débuté il y a maintenant plusieurs semaines. Où en sommes-nous ? Si vous m’avez suivi jusqu’ici, vous savez que j’ai utilisé différentes bibliothèques open source pour programmer deux clients MQTT : un pour PC, l’autre pour appareil Android. À ce stade de notre voyage, nous pouvons donc commander un dispositif par smartphone, et ce depuis n’importe quel endroit de la planète. Je rappelle que ce dispositif est commandé par une carte à microcontrôleur reliée au port USB d’un PC, et que nous envoyons les commandes au client MQTT du PC en passant par un courtier (broker) MQTT public. L’ensemble fonctionne parfaitement, mais nous aimerions nous passer de la station relais, autrement dit du PC. Et nous n’avons pas non plus abordé la question de la transmission des informations dans l’autre sens : dans une application type de l’IdO, l’information va d’un capteur local à un utilisateur situé quelque part sur l’internet.
Notre objectif est donc l’implantation d’un client MQTT sur une carte à microcontrôleur. Ce client pourra publier des sujets et permettra de s’y inscrire. Vous savez bien sûr que le protocole MQTT repose sur le protocole TCP/IP. Nous avons donc besoin pour la carte, non seulement d’une connexion physique au réseau ou d’une interface WLAN, mais aussi de bibliothèques de fonctions adaptées à notre objectif. Où les trouver ? Du côté d’Arduino : il existe en effet un shield Ethernet pour la connexion au réseau, de même qu’une bibliothèque MQTT accessible depuis l’EDI Arduino (elle y est désormais implantée de façon plus ou moins standard). La bibliothèque en question s’appelle PubSubClient (Pub = publish, Sub = subscribe). Elle a été écrite par Nick O’Leary et son code source est disponible sur GitHub.
Nous pourrions bien sûr simplement relier la carte d’extension Ethernet à une carte Uno, charger les bibliothèques nécessaires dans l’EDI Arduino et expérimenter l’ensemble avec les exemples fournis. Nous le pourrions, mais ce serait en quelque sorte trop simple. Et surtout cela ne nous permettrait pas d’en apprendre un peu plus sur les rouages de MQTT. J’ai donc choisi une voie plus longue, mais au final nous serons capables d’écrire des bibliothèques pour n’importe quelle carte à microcontrôleur. Avant toute chose, j’ai cherché quelles fonctions étaient nécessaires pour publier des messages MQTT. Cette sorte de bibliothèque minimale suffira à l’envoi de valeurs de mesures « dans le nuage ».
Voici, d’après le code source de PubSubClient, les fonctions dont nous avons besoin :
La requête de connexion et le message à publier comprennent différents champs. Pour la requête Connect, il faut au moins préciser l’ID du client (ClientID). Pour le message, il faut au moins indiquer le sujet (Topic) et bien sûr fournir les données utiles du message (il est toutefois possible d’envoyer un champ Payload d’une longueur de 0 octet). L’image ci-dessus montre le codage des champs Connect et Publish. L’agencement des octets n’a rien de compliqué, nous sommes en présence d’un protocole vraiment « léger ». J’ai également procédé à d’autres simplifications. La première concerne la longueur des deux messages, qui ne peuvent chacun dépasser 128 octets ; avec un Topic de moins de 48 octets et une charge utile de moins de 64 octets, un message a donc toujours le bon format. Une autre simplification a été de limiter à seulement quelques minutes la valeur Keep Alive. Ce paramètre, sur lequel je reviendrai dans le prochain épisode, est l’intervalle maximal autorisé entre deux transmissions de paquets de contrôle (Control Packets). La spécification MQTT se trouve sur cette page.
Une fois muni des informations nécessaires, j’ai pu écrire une bibliothèque minimale basée sur la bibliothèque PubSubClient. J’ai choisi C# car il existe déjà une bonne bibliothèque TCP pour ce langage, et aussi parce qu’il permet de visualiser des données facilement. Pour mes essais, j’ai programmé un petit client PC MQTT qui ne publie que des messages mais ne recourt pas à la vaste bibliothèque .NET M2Mqtt utilisée dans le quatrième épisode. Vous pouvez télécharger l’application et le code source depuis le lien ci-dessous. Pour tester l’envoi de données, vous pouvez par exemple utiliser le client MQTT de l’épisode 5. Dans la zone de texte Topic to subscribe, entrez un mot-clé court, « min » par exemple. Un clic sur le bouton Subscribe liera alors ce mot-clé au sujet ElektorIoTJourney/min/test.
Vous voici prêt à tester ce client minimaliste. Dès le démarrage du programme, on voit quels octets la requête de connexion Connect a envoyés au serveur MQTT public (test.mosquitto.org, le serveur que nous avions utilisé précédemment). L’identifiant client (ClientId) est par exemple elektorIoT, et la réponse du serveur la séquence d’octets 32 2 0 0. Vous pouvez maintenant entrer « min » comme sujet dans la zone de texte correspondante, et publier le message en cliquant sur Publish.
Il nous reste encore beaucoup de choses à faire et à découvrir, ce que nous remettons au prochain épisode. D’ici là, certains d’entre vous auront peut-être eu envie d’écrire un client minimaliste pour une autre plateforme. Si c’est le cas, n’hésitez pas à partager vos essais dans les commentaires ci-dessous !
Notre objectif est donc l’implantation d’un client MQTT sur une carte à microcontrôleur. Ce client pourra publier des sujets et permettra de s’y inscrire. Vous savez bien sûr que le protocole MQTT repose sur le protocole TCP/IP. Nous avons donc besoin pour la carte, non seulement d’une connexion physique au réseau ou d’une interface WLAN, mais aussi de bibliothèques de fonctions adaptées à notre objectif. Où les trouver ? Du côté d’Arduino : il existe en effet un shield Ethernet pour la connexion au réseau, de même qu’une bibliothèque MQTT accessible depuis l’EDI Arduino (elle y est désormais implantée de façon plus ou moins standard). La bibliothèque en question s’appelle PubSubClient (Pub = publish, Sub = subscribe). Elle a été écrite par Nick O’Leary et son code source est disponible sur GitHub.
Nous pourrions bien sûr simplement relier la carte d’extension Ethernet à une carte Uno, charger les bibliothèques nécessaires dans l’EDI Arduino et expérimenter l’ensemble avec les exemples fournis. Nous le pourrions, mais ce serait en quelque sorte trop simple. Et surtout cela ne nous permettrait pas d’en apprendre un peu plus sur les rouages de MQTT. J’ai donc choisi une voie plus longue, mais au final nous serons capables d’écrire des bibliothèques pour n’importe quelle carte à microcontrôleur. Avant toute chose, j’ai cherché quelles fonctions étaient nécessaires pour publier des messages MQTT. Cette sorte de bibliothèque minimale suffira à l’envoi de valeurs de mesures « dans le nuage ».
Voici, d’après le code source de PubSubClient, les fonctions dont nous avons besoin :
- une fonction de la bibliothèque TCP qui lie notre programme (client TCP) à un serveur TCP. Elle est appelée dès le début du code et peut par exemple être nommée TCPClient_Connect.
- Une fonction, appelée p. ex. MQTTClient_Connect, qui envoie ensuite une requête MQTT Connect au serveur MQTT/TCP et traite l’accusé de réception. Pour cela nous recourons à une fonction TCPClient_Send capable d’envoyer des octets par TCP et de recevoir les réponses du serveur, également sous la forme d’octets.
- La fonction MQTTClient_Publish envoie les données utiles au serveur MQTT afin que celles-ci soient publiées sous un sujet (Topic) particulier. La fonction utilise TCPClient_Send pour envoyer les octets correspondants. Dans le scénario le plus simple (Quality of Service = 0), le serveur n’envoie aucune réponse.
- Deux fonctions auxiliaires (appelées …_Write et …_WriteString dans la bibliothèque PubSubClient) facilitent l’assemblage des octets à transmettre au serveur via TCP, d’abord pour la requête Connect, ensuite pour la publication du message (Publish).
La requête de connexion et le message à publier comprennent différents champs. Pour la requête Connect, il faut au moins préciser l’ID du client (ClientID). Pour le message, il faut au moins indiquer le sujet (Topic) et bien sûr fournir les données utiles du message (il est toutefois possible d’envoyer un champ Payload d’une longueur de 0 octet). L’image ci-dessus montre le codage des champs Connect et Publish. L’agencement des octets n’a rien de compliqué, nous sommes en présence d’un protocole vraiment « léger ». J’ai également procédé à d’autres simplifications. La première concerne la longueur des deux messages, qui ne peuvent chacun dépasser 128 octets ; avec un Topic de moins de 48 octets et une charge utile de moins de 64 octets, un message a donc toujours le bon format. Une autre simplification a été de limiter à seulement quelques minutes la valeur Keep Alive. Ce paramètre, sur lequel je reviendrai dans le prochain épisode, est l’intervalle maximal autorisé entre deux transmissions de paquets de contrôle (Control Packets). La spécification MQTT se trouve sur cette page.
Une fois muni des informations nécessaires, j’ai pu écrire une bibliothèque minimale basée sur la bibliothèque PubSubClient. J’ai choisi C# car il existe déjà une bonne bibliothèque TCP pour ce langage, et aussi parce qu’il permet de visualiser des données facilement. Pour mes essais, j’ai programmé un petit client PC MQTT qui ne publie que des messages mais ne recourt pas à la vaste bibliothèque .NET M2Mqtt utilisée dans le quatrième épisode. Vous pouvez télécharger l’application et le code source depuis le lien ci-dessous. Pour tester l’envoi de données, vous pouvez par exemple utiliser le client MQTT de l’épisode 5. Dans la zone de texte Topic to subscribe, entrez un mot-clé court, « min » par exemple. Un clic sur le bouton Subscribe liera alors ce mot-clé au sujet ElektorIoTJourney/min/test.
Vous voici prêt à tester ce client minimaliste. Dès le démarrage du programme, on voit quels octets la requête de connexion Connect a envoyés au serveur MQTT public (test.mosquitto.org, le serveur que nous avions utilisé précédemment). L’identifiant client (ClientId) est par exemple elektorIoT, et la réponse du serveur la séquence d’octets 32 2 0 0. Vous pouvez maintenant entrer « min » comme sujet dans la zone de texte correspondante, et publier le message en cliquant sur Publish.
Il nous reste encore beaucoup de choses à faire et à découvrir, ce que nous remettons au prochain épisode. D’ici là, certains d’entre vous auront peut-être eu envie d’écrire un client minimaliste pour une autre plateforme. Si c’est le cas, n’hésitez pas à partager vos essais dans les commentaires ci-dessous !