IoT personnalisé – MQTT Pub and Sub

IoT personnalisé - MQTT Pub and Sub

La dernière fois, nous avons vu les débuts de notre serveur MakerMQTT qui a pu maintenir une connexion active entre un serveur et un client. Cette fois, nous ajouterons les commandes de publication et d’abonnement pour nous permettre d’envoyer et de recevoir des données!

Solutions IoT personnalisées – Introduction à l’Internet des objets

Solutions IoT personnalisées – ESP32 vs ESP8266

Solutions IoT personnalisées – HTTP vs MQTT

Solutions IoT personnalisées – Créer un système de publication HTTP

Solutions IoT personnalisées – Comment faire des requêtes HTTP GET

Solutions IoT personnalisées – Présentation de MQTT

Solutions IoT personnalisées – Framework MQTT

Publiez et abonnez-vous

Dans le dernier projet MakerMQTT, nous avons appris comment garder un client et un serveur connectés en utilisant des minuteries Keep Alive. Bien que cela soit utile pour déterminer si les périphériques ont expiré et libérer par la suite les ressources du serveur, il n’y avait aucun moyen pour les périphériques d’envoyer et de recevoir des données vers et depuis le serveur. Avant de voir comment cela se fait, commençons par récapituler sur ce que sont la publication et l’abonnement.

Dans MQTT, la publication se produit lorsqu’un appareil enregistre une valeur dans une variable spécifique telle que «température» et «humidité». L’abonnement se produit lorsqu’un périphérique demande au serveur de recevoir toutes les mises à jour d’une variable dès qu’elles sont effectuées. Il est important de comprendre que dans MQTT, tous les appareils abonnés à la même variable recevront tous des mises à jour de toute modification apportée à cette variable et que tout appareil qui publie sur cette variable enverra effectivement cette valeur à tous les abonnés via le serveur.

La mise en œuvre de la publication est simple car un périphérique client enverra simplement la commande de publication au serveur. À partir de là, le serveur ajoutera la nouvelle valeur à un fichier de variables existant ou en créera un nouveau. L’abonnement, cependant, est un peu plus complexe et nécessite à la fois que notre serveur et notre client soient capables de gérer les changements dynamiques. Alors, voyons comment nous le faisons!

Publier – Client et serveur

Le code pour publier des données sur le client est très simple et fait en utilisant la fonction qui prend le nom de la variable et les données sous forme de chaînes, les ajoute à une chaîne plus grande qui contient la commande PUB, puis l’ajoute au tampon de sortie. Étant donné que toutes les données vers et depuis le serveur sont au format chaîne, il n’y a pas besoin de conversions de type, et l’utilisation de la fonction transmetQueue signifie que nous devons seulement ajouter la commande au tampon de sortie de message.

création d'une application iot personnalisée - mqtt pub and sub publish client et serveur

Si le code serveur pour gérer une commande de publication peut sembler complexe, il est en fait très simple (personnellement, je préfère ajouter plus de lignes de code pour décomposer chaque opération étape par étape). L’instruction if vérifie d’abord si la commande de publication a été émise et, dans l’affirmative, extrait les paramètres pertinents du message (nom de la variable et valeur). Le bloc de code suivant est une boucle while qui tente continuellement d’ouvrir le fichier de variables qui stocke toutes les valeurs passées associées à cette variable. Si le fichier est en cours d’utilisation, cela lèvera une exception qui est interceptée par le bloc try-except. Finalement, le code pourra ouvrir le fichier pour édition et une fois terminé, il quittera la boucle while.

N’oubliez pas que chaque variable de notre serveur MakerMQTT a un fichier unique qui stocke les données au format texte!

Avec le fichier ouvert et prêt pour l’édition, l’étape suivante consiste à obtenir l’heure actuelle. Ceci est obtenu afin que nous puissions horodater nos variables, ce qui nous donne la possibilité de mettre à jour tous les abonnés sur un nouvel horodatage OU une nouvelle valeur. La date et l’heure actuelles sont ensuite ajoutées à la valeur qui a été envoyée, puis enregistrées dans le fichier texte correspondant. Une fois enregistré, le code renvoie alors la commande OK afin que le client sache que la variable publiée a été correctement enregistrée.

pub et sous mqtt - serveur makermqttpub et sous-marin mqtt - makermqtt

Abonnement au serveur

L’abonnement est un peu plus complexe à implémenter et, sur le serveur, nous utilisons un type de données utile en Python pour faciliter l’implémentation; dictionnaires. Les dictionnaires peuvent être considérés comme des tableaux qui, au lieu d’utiliser des nombres pour accéder aux éléments, utilisent plutôt des noms. Par exemple, un dictionnaire peut contenir des éléments nommés «Type de voiture», «Couleur» et «Modèle», tandis que leurs valeurs seraient «Volvo», «Noir» et «V40». Ceci est très utile dans notre situation car nous pouvons facilement faire référence aux variables souscrites dans notre dictionnaire par leur nom au lieu d’avoir à parcourir tout le tableau correspondant au paramètre de nom à la variable que nous recherchons.

Lors de l’initialisation de la fonction d’exécution dans la classe client, le code ci-dessous montre le dictionnaire déclaré pour utilisation.

abonnement mqtt pub et sous-serveur

Lorsque la commande d’abonnement est envoyée au serveur, il vérifie d’abord si l’appareil y est déjà abonné. Si ce n’est pas le cas, la variable est ajoutée au dictionnaire avec une valeur de chaîne vide. Après, le client reçoit le message OK du serveur indiquant l’ajout réussi de la variable à la liste d’abonnement.

mqtt pub et sous - abonnement serveur

Maintenant qu’un périphérique client peut s’abonner à un message, nous devons maintenant gérer ce qui doit se passer lorsque cette variable change de valeur. Après le bloc de décodage de message dans le serveur, le prochain bloc de code à exécuter est le moteur d’abonnement. Le premier code à être exécuté est une boucle for qui parcourt chaque variable à laquelle le client s’est abonné. Pour chaque variable, le nom de la variable et la dernière valeur enregistrée sont copiés dans les variables varName et varValue, respectivement. Ensuite, le nom de fichier qui pointe vers cette variable est généré, puis le fichier est ouvert. Une fois ouvertes, toutes les lignes de ce fichier sont lues dans un tableau, fileContents et le fichier se ferme. La dernière entrée est ensuite extraite (car il s’agit de la dernière valeur à être stockée) et la valeur et l’horodatage sont alors obtenus.

Dans cet exemple, nous utilisons une instruction if pour comparer la dernière valeur stockée dans le fichier avec celle stockée dans notre dictionnaire, et s’il y a un écart, nous envoyons une mise à jour à notre client avec le nom de la variable et sa dernière valeur. Nous mettons également à jour la valeur contenue dans le dictionnaire afin qu’elle ait la version la plus récente et ainsi toute modification d’une variable entraînera la détection par le moteur d’abonnement de cette modification et l’envoi des messages appropriés aux clients connectés.

mqtt pub et sous-serveur abonnements clients

Abonnement client

Un client qui doit s’abonner à une variable doit stocker toutes les variables par nom et être en mesure de les récupérer. Bien que C ++ ne nous autorise pas à utiliser des types de données tels que des dictionnaires, il autorise l’utilisation d’objets que nous utiliserons.

La classe MakerMQTT, qui contient des fonctions liées à la communication serveur et au décodage des messages, contient également un tableau d’objets appelés vars. Chaque objet est de type Variable (dont la définition peut être trouvée dans le fichier MakerMQTT.h), et ces objets contiennent un nom de variable, une valeur et un indicateur activé. Il convient de préciser qu’un périphérique client utilisant notre bibliothèque est limité à l’abonnement à 32 variables, mais cela peut être modifié en ajustant la ligne #define MAX_VAR 32 trouvée dans MakerMQTT.h.

Lors de l’abonnement à une variable, le client parcourt d’abord les 32 entrées d’objet variable pour savoir si nous sommes déjà abonnés à cette variable. S’il est déjà abonné, le client définira simplement l’indicateur d’activation sur true pour indiquer que l’entrée est en cours d’utilisation. Si elle n’est pas trouvée, la première entrée disponible dans la liste des entrées de variable est trouvée en regardant l’indicateur activé. Une fois qu’une entrée vide est trouvée, la valeur activée de l’entrée est définie sur true et les détails de la variable (comme le nom) lui sont transmis. Ensuite, le client envoie la commande subscribe au serveur et donc le client est maintenant abonné à cette variable. Cette section de code devrait être améliorée pour vérifier une réponse OK AVANT de définir l’entrée, mais par souci de simplicité, nous ne prendrons pas la peine de vérifier les réponses.

mqtt pub and sub - abonnement clientLa réception des mises à jour d’abonnement (lorsqu’une valeur est modifiée) est facilement gérée dans la fonction principale updateSystem. Si une commande SUB est reçue par le client, chaque entrée de variable est analysée pour trouver l’entrée qui correspond aux données reçues du serveur, et une fois trouvée, l’entrée est mise à jour.

pub et sous mqtt - sous-client

Alors que les variables sont accessibles depuis le programme principal en utilisant l’accesseur dot (par exemple clientEngine.vars[0].varName), ce n’est pas une solution idéale. Au lieu de cela, une fonction simple est utilisée appelée getValue () qui prend le nom de la variable sous forme de chaîne et renvoie la valeur sous forme de chaîne.

pub et sous mqtt - sous-client

Projet suivant – Fonctions appelables

Cet exemple de projet présente un problème, à savoir qu’il est difficile de réagir à un message entrant soudain. Par exemple, comment pouvons-nous faire en sorte que notre appareil client réponde dès qu’un message est reçu? Dans le prochain projet, le dernier projet de cette série, nous implémenterons des fonctions appelables qui nous permettent de créer une fonction C ++, et l’appelons dès qu’une variable est reçue. Ainsi, nous pouvons implémenter des interruptions logicielles et répondre immédiatement aux nouvelles données.