MQTT¶
Notes pour le prof¶
Le port websocket du serveur MQTT est 3901 et non 9001. Le port mqtt est inchangé, c'est le port 1883. Ceci dans le but d'accomoder les restrictions du département.
Qu'est-ce que le MQTT?¶
Le MQTT est un protocole (Message Queuing Telemetry Transport) de messagerie simple, léger et relativement simple à implémenter. Il est conçu pour les communications entre machines dans des environnements où la bande passante et la latence sont des contraintes importantes. Nous allons l'utiliser pour communiquer entre notre Arduino et un serveur MQTT.
Concepts¶
MQTT repose sur un modèle de communication basé sur l'abonnement/souscription (publish/subscribe). Dans ce modèle, il y a deux types d'acteurs : les clients et le courtier (broker). Les clients peuvent être des capteurs, des actionneurs, des microcontrôleurs (comme l'Arduino) ou d'autres dispositifs capables de se connecter à Internet. Le courtier est un serveur central qui gère les communications entre les clients.

Le courtier (broker)¶
Le courtier est un serveur central qui gère les communications entre les clients. Il est responsable de la distribution des messages aux clients abonnés aux sujets correspondants. Il est également responsable de la gestion des connexions et de la persistance des messages. Il est possible d'utiliser un courtier tiers, comme CloudMQTT, ou d'utiliser un courtier local, comme Mosquitto.
Dans le cas de mon serveur, j'utilise mosquitto. Vous pouvez l'installer sur votre machine en utilisant la commande sudo apt install mosquitto.
Voici les étapes pour tester votre serveur MQTT :
- Lancez le serveur en utilisant la commande mosquitto.
- Connectez-vous à votre serveur en utilisant la commande mosquitto_sub -h localhost -t "test".
- Dans une autre instance de ligne de commandes, publiez un message en utilisant la commande mosquitto_pub -h localhost -t "test" -m "Hello World!".
La configuration persistente du serveur sort du cadre de ce cours. Vous pouvez trouver plus d'informations sur le site officiel de Mosquitto.
Note : En plus de l'adresse IP du serveur, il faut connaitre le port sur lequel le serveur écoute. Par défaut, le port utilisé par le serveur MQTT est le port 1883. Vous pouvez le changer dans le fichier de configuration du serveur.
Le courtier peut être protégé par un nom d'utilisateur et un mot de passe. Dans ce cas, vous devez utiliser la commande mosquitto_sub -h localhost -t "test" -u "username" -P "password". Vous pouvez aussi utiliser la commande mosquitto_pub -h localhost -t "test" -m "Hello World!" -u "username" -P "password".
Il est fortement recommandé, au minimum, d'utiliser un nom d'utilisateur et un mot de passe pour protéger votre serveur MQTT.
Dans un monde idéal, le courtier devrait être protégé par un certificat SSL. Cependant, cette notion sort du cadre de ce cours.
Les clients¶
Les clients sont des appareils qui peuvent se connecter au courtier pour envoyer et recevoir des messages. Les clients peuvent être des capteurs, des actionneurs, des microcontrôleurs (comme l'Arduino) ou d'autres dispositifs capables de se connecter à Internet. Les clients peuvent s'abonner à un ou plusieurs sujets pour recevoir les messages correspondants, et publier des messages sur des sujets spécifiques.

Les sujets (topic)¶
Le MQTT utilise des "sujets" pour organiser les messages échangés entre les clients et le courtier. Les sujets sont des chaînes de caractères hiérarchiques, semblables à des chemins de fichiers. Par exemple, un sujet peut être "maison/salon/temperature". Les clients peuvent s'abonner à un ou plusieurs sujets pour recevoir les messages correspondants, et publier des messages sur des sujets spécifiques.
Par exemple, un thermostat peut publier la température actuelle sur le sujet "maison/salon/temperature". Un autre client peut s'abonner à ce sujet pour recevoir les mises à jour de température. Un autre client peut publier la température désirée sur le sujet "maison/salon/temperature/desiree". Un autre client peut s'abonner à ce sujet pour recevoir les mises à jour de température désirée.
Les caractères spéciaux¶
Les sujets peuvent contenir des caractères alphanumériques, les tirets et les tirets bas. De plus, il y a des caractères spéciaux. Les caractères spéciaux sont les caractères suivants : + et #. Ils ne peuvent pas être utilisés dans les sujets.
Le caractère + est un caractère de remplacement. Il peut être utilisé pour remplacer un seul niveau de hiérarchie dans un sujet. Par exemple, le sujet maison/salon/+ peut être utilisé pour s'abonner à tous les messages publiés sur les sujets maison/salon/temperature, maison/salon/humidite, etc.
On peut aussi utiliser le + pour s'inscrire à un sujet qui contient plusieurs niveaux de hiérarchie. Par exemple, le sujet maison/etage_1/+/temperature peut être utilisé pour s'abonner à tous les messages publiés sur les sujets maison/etage_1/chambre/temperature, maison/etage_1/cuisine/temperature, etc.
Le caractère # est un caractère de remplacement. Il peut être utilisé pour remplacer un ou plusieurs niveaux de hiérarchie dans un sujet. Par exemple, le sujet maison/etage_1/# peut être utilisé pour s'abonner à tous les messages publiés sur les sujets maison/etage_1/temperature, maison/etage_1/humidite, maison/etage_1/chambre/temperature, maison/etage_1/cuisine/temperature, etc.
Voici des exemples de sujets valides avec des caractères spéciaux : | Sujet | Description | |-----------------------------|-----------------------------------------------------------------------| | maison/salon/temperature | Un sujet standard pour la température du salon | | maison/+/luminosite | Un sujet avec un caractère joker '+' pour la luminosité de toutes les pièces de la maison | | maison/# | Un sujet avec un caractère joker '#' pour tous les sujets sous "maison" | | capteurs/salle_01/temp | Un sujet pour la température de la salle 01 | | voiture/position/latitude | Un sujet pour la latitude de la position d'une voiture | | voiture/position/longitude | Un sujet pour la longitude de la position d'une voiture | | capteur_special/01-temperature | Un sujet pour un capteur spécial avec des caractères alphanumériques et des tirets | | printer3d/bed/temperature | Un sujet pour la température du lit d'une imprimante 3D | | printer3d/nozzle/temperature | Un sujet pour la température de la buse d'une imprimante 3D | | printer3d/+/temperature | Un sujet avec un caractère joker '+' pour la température de tous les capteurs d'une imprimante 3D |
Les messages (payload)¶
Les messages sont les données échangées entre les clients et le courtier. Les messages sont des chaînes de caractères ou données binaires. Les clients peuvent publier des messages sur des sujets spécifiques, et les clients peuvent s'abonner à un ou plusieurs sujets pour recevoir les messages correspondants.
Les messages peuvent être aussi simple que 0 ou 1 pour par exemple allumer ou éteindre une lumière, ou plus complexe comme des données de capteurs. Les messages peuvent prendre plusieurs formats, mais un format que l'on retrouve régulièrement est le JSON.
JSON crash course¶
Le JSON est une chaîne de caractères formatée selon un standard. Il est composé de paires clé-valeur. Les clés sont des chaînes de caractères entre guillemets doubles. Les valeurs peuvent être des chaînes de caractères entre guillemets doubles, des nombres, des booléens, des tableaux ou des objets.
Un fichier JSON est contenu entre accolades. Chaque paire clé-valeur est séparée par une virgule. Les clés et les valeurs sont séparées par deux points.
Voici quelques exemples de contenu JSON typique que l'on peut échanger en MQTT :
Données qui être renvoyées par un interrupteur intelligent :
Exemple pour l'état d'une ampoule intelligente :
Un autre exemple pour l'état d'une imprimante 3d :
On remarque que c'est un format de données relativement simple à comprendre. C'est un format qui est utilisé dans de nombreux domaines, et qui est facile à lire et à écrire.
C'est la raison principale pour laquelle nous allons utiliserons le JSON pour nos messages MQTT.
La qualité de service (QoS)¶
MQTT offre trois niveaux de qualité de service (QoS) pour les messages échangés entre les clients et le courtier : - QoS 0 (Au plus une fois) : Le message est envoyé sans confirmation, ce qui signifie qu'il peut être perdu. On entendra parfois que ce le mécanisme "Fire and Forget". - QoS 1 (Au moins une fois) : Le message est envoyé avec confirmation, garantissant qu'il sera reçu au moins une fois. - QoS 2 (Exactement une fois) : Le message est envoyé avec un mécanisme de contrôle qui garantit qu'il sera reçu une seule fois.
Dans notre cas, nous allons utilisé le QoS 0. Le QoS 1 et 2 sont plus compliqués à implémenter et ne sont pas nécessaire pour notre application.

Note : L'abréviation QoS a différentes significations dans le monde de l'informatique dépendant du contexte. Dans le cas du MQTT, il s'agit de la qualité de service au niveau applicatif. Dans le cas de la réseautique, il s'agit de la qualité de service au niveau du réseau.
Intégration MQTT avec Arduino¶
Dans notre situation, nous allons utiliser le MQTT pour un simple échange de données entre votre Arduino et un serveur MQTT. J'aurai préalablement configuré le serveur MQTT, car cela sort du cadre de ce cours. Vous n'aurez qu'à vous connecter à ce serveur. Vous pourrez alors envoyer des données à votre Arduino et recevoir des données de votre Arduino.
Pour utiliser MQTT, vous allez avoir besoin d'une bibliothèque MQTT telle que PubSubClient. Vous pouvez l'installer depuis le gestionnaire de bibliothèques de l'IDE Arduino. Ensuite, il faudra que l'appareil soit connecté à un réseau. Pour cela, vous avez entre vos mains le ESP8266 WiFi shield (OAS8266WF) ou un ESP01 avec un adaptateur série.
De plus vous aurez avoir besoin de la librairie WiFiEspAT pour faire fonctionner le shield. Vous pouvez l'installer depuis le gestionnaire de bibliothèques de l'IDE Arduino.
Note : Pour faire fonctionner l'exemple, il faudra avoir préconfiguré le shield pour qu'il se connecte au réseau WiFi. Allez voir le cours sur la connexion WiFi pour plus d'informations.
Exemple à utiliser¶
Nous allons utiliser l'exemple mqtt_test (ou mqtt_test_esp01) qui se retrouve dans le dossier des projets du cours. Il s'agit d'un exemple simple qui permet de publier et de souscrire à un sujet MQTT. Nous allons pouvoir l'intégrer dans notre projet pour envoyer des données à notre serveur MQTT.
Voici le code entier de l'exemple :
Cliquez pour le voir
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | |
Je vais expliquer les points importants dans les prochaines sections.
La librarie WiFiEspAT¶
Pour l'utilisation de la librairie WiFiEspAT, il faut que le shield soit configuré pour se connecter au réseau WiFi. Pour cela, référez-vous au cours sur la connexion WiFi et communication série.
Dans l'exemple, il y a la fonction wifiInit() qui initialise le shield et le connecte au réseau WiFi.
On appelera celle-ci une seule fois dans la fonction setup().
Elle dépend de la fonction printWifiStatus() qui affiche les informations de connexion au réseau WiFi.
printWifiStatus() dépend de la fonction printMacAddress() qui affiche l'adresse MAC du shield.
Afficher le code
Sauf si vous avez un Arduino Uno, vous pouvez utiliser le code tel quel. Sinon, vous devez utiliser la librairie SoftwareSerial pour simuler un port série 1 sur un Arduino Uno.
Jetez un coup d'oeil au début du code complet de l'exemple pour voir comment faire.
La librarie PubSubClient¶
La librairie PubSubClient permet de se connecter à un serveur MQTT et d'envoyer et recevoir des messages. Dans la manière que l'on l'utilise, elle est très simple.
Évidemment, il faut avoir la librairie PubSubClient installée sur votre ordinateur. Pour cela, allez dans le menu Outils > Gérer les librairies... et cherchez PubSubClient dans la liste des librairies installées.
Ensuite, il faudra avoir un serveur MQTT sur lequel se connecter. Pour cela, on va utiliser mon serveur MQTT à l'adresse indiquée dans le code. Au moment d'écrire ces lignes, l'adresse ip est 216.128.180.194.
Initialisation du client MQTT¶
La première étape sera d'initialiser le client. Le constructeur peut prendre différent type de client que l'on retrouve réguilièrement dans les librairies Arduino. Dans notre cas, on va utiliser le client WiFiClient (ou WifiEspClient).
On pourra ensuite utiliser les fonctions de l'objet client pour se connecter au serveur MQTT et envoyer/recevoir des messages.
Configuration du client au serveur MQTT¶
Pour configurer le client au serveur MQTT, on utilise la fonction setServer().
- Le premier paramètre est l'adresse ip du serveur MQTT. Le deuxième paramètre est le port sur lequel le serveur écoute. Dans notre cas, c'est le port 1883.
Pour nos besoins, on configure le client au serveur MQTT dans la fonction setup().
Connexion au serveur MQTT¶
Pour se connecter au serveur MQTT, on utilise la fonction connect(). La fonction retourne un booléeen qui indique si la connexion a réussi ou non.
Elle peut prendre plusieurs paramètres, mais il faut au moins le nom du client. Le nom du client doit être unique pour le serveur MQTT sinon la connexion échouera.
Si le serveur le requiert, on peut aussi fournir un nom d'utilisateur et un mot de passe. C'est ce qui se passe dans l'exemple.
Configuration de la fonction de rappel¶
La fonction de rappel est une fonction qui sera appelée par la librairie PubSubClient quand un message est reçu. On peut donc utiliser cette fonction pour traiter les messages reçus.
On utilise la fonction setCallback() pour configurer la fonction de rappel.
mqttEvent est le nom de la fonction de rappel.
Pour nos besoins, on configure la fonction de rappel dans la fonction setup().
Dans l'exemple la fonction de rappel est mqttEvent().
On peut voir que la fonction de rappel prend trois paramètres.
- topic est le nom du topic sur lequel le message a été reçu.
- payload est le contenu du message. Il s'agit d'un tableau de byte.
- length est la longueur du message.
On remarque dans la fonction que l'on compare le nom du topic avec la chaîne de caractère "moteur". Si les deux chaînes sont identiques, on appelle la fonction toggleMoteur().
Pour que recevoir un message, il faut que le client s'abonne au topic sur lequel il veut recevoir des messages.
Note : La fonction
strcmp()permet de comparer deux chaînes de caractères. Elle retourne 0 si les deux chaînes sont identiques. Une valeur positive si le premier caractère différent a une valeur plus grande dans la première chaîne. Une valeur négative si le premier caractère différent a une valeur plus grande dans la deuxième chaîne.
S'abonner à un sujet¶
Pour s'abonner à un sujet, on utilise la fonction subscribe(). Cette fonction prend en paramètre le nom du topic sur lequel on veut s'abonner.
Lorsque l'on s'abonne à un topic, on reçoit tous les messages qui sont publiés sur ce topic. On peut donc s'abonner à plusieurs topics. Il suffira ensuite de traiter les messages reçus dans la fonction de rappel.
Publier un message¶
Pour public un message, on utilise la fonction publish(). Cette fonction prend en paramètre le nom du topic sur lequel on veut publier le message et le message à publier.
La fonction publish() retourne un booléen qui indique si le message a été publié ou non.
Souvent la forme du message est en format JSON, mais ça peut être n'importe quoi. Un JSON est d'une chaîne de caractères formatée selon un standard. Nous allons voir les bases du JSON dans la section suivante.
Dans le cas de l'exemple, on publie un message périodiquement. On peut aussi publier un message à la demande. Par exemple, on peut publier un message quand on appuie sur un bouton.
Le JSON¶
Dans la section sur la publication de message, on a voit la construction d'un message en format JSON. Nous allons voir les bases du JSON.
Voici le message en format JSON produit.
Rappel : sprintf permet d'écrire dans un tableau de caractères. Il prend en paramètre le tableau de caractères, la chaîne de caractères formatée et les valeurs à insérer dans la chaîne de caractères. Voir les notes sur l'écriture sur un LCD
Le service MQTT qui reçoit l'information peut ensuite décoder le message et extraire les informations qui l'intéressent.
Exercice¶
- Vous pouvez voir l'interface pour le prochain laboratoire. L'adresse est "http://arduino.nb.shawinigan.info/etd/".
- Le format attendu des messages est le suivant :
- Adapter l'exemple pour qu'il publie un message sur le sujet
etd/XXà toutes les minutes avec le contenu de l'exemple du format attendu ci-haut.