Les Carnets de Byfeel domotique , Objets connectés , DIY , Programmation, Nouvelles Technologies ….

Client MQTT dans votre module ESP

MQTT est un service de messagerie TCP/IP simple et extrêmement léger. Les messages sont envoyés par des publieurs sur un Topic ( canal d’information ). Ces messages peuvent être lus par les abonnés (souscrire à un canal ). Les Topics ont une hiérarchie qui permet de sélectionner finement les informations que l’on désire.

Dans cet article, je vais expliquer comment mettre en place un client publieur et un client souscripteur MQTT au sein de votre objet connecté , et comment structurer ses données.

Exemple arborescence PlatformIO
Exemple arborescence PlatformIO

Les exemples sont réalisés sous PlatformIO, facilement adaptable à ArduinoIDE.

Retrouvez les tuto , sur mon github.

Installation du serveur broker

Pour illustrer cet article, j’utilise le broker mosquito, très simple à installer et à utiliser.

Pour l’installer sur une distribution Linux, il suffit d’exécuter les commandes suivantes

sudo apt-get update
sudo apt-get install mosquitto
sudo apt-get install mosquitto-clients

Il est possible d’installer, aussi, le broker sur un NAS synology ( par exemple ) via les paquets. Il faut pour cela installer la source de paquet fournis par la communauté synology.

Dans source paquet, ajouter l’enplacement : http://packages.synocommunity.com/

ou encore , sous jeedom via le composant JMQTT de Domotruc.

Une fois votre serveur MQTT en place , passons à la programmation de l’ESP.

Envoie des données via Mqtt depuis votre esp8266

Dans ce premier exemple, je vais expliquer les éléments à mettre en place, afin de publier nos premières données. Pour cela je vais avoir besoin des bibliothèques suivantes 

  • ESP8266WiFi.h
  • WiFiUdp.h
  • PubSubClient.h

Ci dessous le code du projet :

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>  

//WiFi Connection configuration
char ssid[] = "nom du reseau WIFI";     //  le nom du reseau WIFI
char password[] = "mot de passe";  // le mot de passe WIFI
//mqtt server
char mqtt_server[] = "127.0.0.1";  //adresse IP serveur 
#define MQTT_USER ""
#define MQTT_PASS ""

WiFiClient espClient;
PubSubClient MQTTclient(espClient);

//Fonctions mqtt
void MQTTsend() {
  static int cpt=0;
  cpt++;
 String reponse="test Mqtt n°"+(String)cpt;
 MQTTclient.publish("byfeel/tutomqtt/msg",reponse.c_str());
}

void MQTTconnect() {

  while (!MQTTclient.connected()) {
      Serial.print("Attente  MQTT connection...");
      String clientId = "TestClient-";
      clientId += String(random(0xffff), HEX);

    // test connexion
    if (MQTTclient.connect(clientId.c_str(),"","")) {
      Serial.println("connected");

    } else {  // si echec affichage erreur
      Serial.print("ECHEC, rc=");
      Serial.print(MQTTclient.state());
      Serial.println(" nouvelle tentative dans 5 secondes");
      delay(5000);
    }
  }
}

void setup() {
Serial.begin(115200);
  // Conexion WIFI
   WiFi.begin(ssid, password);
   Serial.println("");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
   Serial.println("Connected");
   MQTTclient.setServer(mqtt_server, 1883);
  
}

void loop() {
  static uint32_t  lastTimeMqtt= 0;
  // connect serveur MQTT
  if (!MQTTclient.connected()) {
    MQTTconnect();
  }

  if (millis() - lastTimeMqtt >= 10000)  // toutes les 20 secondes
   {
     lastTimeMqtt = millis();
     MQTTsend();
   }
}

Description du programme :

En début de script, les bibliothèques nécessaires, ESP8266 et WiFiUdp pour gérer la connexion Wi-Fi et le protocole UDP. Puis la bibliothèque PubSubClient qui permet de publier ou Souscrire sur un broker MQTT.

Github de la bibliothèque PubSubClient , écrit par Nick O’Leary ( API de la bibliothèque ici )

//WiFi Connection configuration
char ssid[] = "nom du reseau WIFI";     //  le nom du reseau WIFI
char password[] = "mot de passe";  // le mot de passe WIFI

//mqtt server
char mqtt_server[] = "127.0.0.1";  //adresse IP serveur 
#define MQTT_USER ""   //user MQTT si utilisé
#define MQTT_PASS ""

WiFiClient espClient;
PubSubClient MQTTclient(espClient);

Ici, on va définir les paramètres SSID et password du réseauWI-FI, et aussi adresse Ip du serveur broker. Et enfin, l’instance MQTTclient est créée.

Dans la fonction setup() , on affecte l’adresse du serveur et le port , au client MQTT créé.

MQTTclient.setServer(mqtt_server, 1883);

Puis dans la boucle loop(), on vérifie la connexion au Broker, afin de se reconnecter si besoin par la fonction MQTTconnect().

void MQTTconnect() {

  while (!MQTTclient.connected()) {
      Serial.print("Attente  MQTT connection...");
      String clientId = "TestClient-";   // création d'un idclient unique
      clientId += String(random(0xffff), HEX);

    // test connexion  
    if (MQTTclient.connect(clientId.c_str(),MQTT_USER,MQTT_PASS)) {
      Serial.println("connected");

    } else {  // si echec affichage erreur
      Serial.print("ECHEC, rc=");
      Serial.print(MQTTclient.state());
      Serial.println(" nouvelle tentative dans 5 secondes");
      delay(5000);
    }
  }
}

Les clients se connectant au serveur Broker, doivent disposer d’un identifiant unique. Celui-ci est généré de façon aléatoire sous la forme : TestClientxxxx ( où xxxx sont 4 chiffres Hexadécimal aléatoire ).

la commande MQTTclient.connect(clientId.c_str(),MQTT_USER,MQTT_PASS) , permet de se connecter au broker.
Si erreur, on affiche le résultat, puis nouvelle tentative dans les 5 secondes.

Dans la fonction loop() , j’ai réalisé une boucle qui publie toutes les 10 secondes , le texte « test Mqtt n° x » , via la fonction MQTTsend().

  if (millis() - lastTimeMqtt >= 10000)  // toutes les 10 secondes
   {
     lastTimeMqtt = millis();
     MQTTsend();
   }

La fonction MQTTsend() , se charge de formater la donnée à envoyer en utilisant la commande MQTTclient.publish.

void MQTTsend() {
  static int cpt=0;
  cpt++;
 String reponse="test Mqtt n°"+(String)cpt;
 MQTTclient.publish("byfeel/tutomqtt/msg",reponse.c_str());
}

Le premier paramètre est le Topic : dans cet exemple byfeel/tutomqtt/msg et la valeur , ici « test Mqtt n° x ».

Récupérer les infos envoyés , via un client MQTT

Si vous avez un client mosquitto , installé sous linux , vous pouvez taper la commande suivante :

mosquitto_sub -h 192.168.10.12 -t « byfeel/tutomqtt/msg »

Ou bien utiliser votre solution domotique favorite JEEDOM .Pour cela , je vais utiliser le plugin JMQTT de DOMOTRUC , que je récupère dans le market :

une fois installé et activé ( ne pas installer le broker , si vous en avez déjà un sur votre reseau ). Renseignez votre broker et aller sur le mode inclusion. Au bout de quelques secondes un nouvel équipement sera créé avec les infos correspondant à votre publication.

équipement créé automatiquement
Le topic et sa valeur renseigné , actualisé toutes les 10 secondes

Réception donnée sur esp8266

Dans un premier temps , je vais souscrire au topic que je souhaite écouter , afin de récupérer cette information. Pour cela j’utilise la fonction : MQTTclient.subscribe(« byfeel/mqtt/RX »);

Le topic qui sera écouté est : byfeel/mqtt/RX

Pour traiter les messages reçu sur ce topic , je créé la fonction MQTTcallback()

void MQTTcallback(char* topic, byte* payload, unsigned int length) {
 Serial.print("Message MQTT [");
  Serial.print(topic);
  Serial.print("] ");

  payload[length] = '\0';
  String s = String((char*)payload);

 Serial.println("message reçu : "+s);
}

cette fonction de callback est défini dans le client MQTT par la commande ci dessous , dans la fonction setup()

MQTTclient.setCallback(MQTTcallback);

Afin d’intercepter ces messages lors de l’exécution du programme, je n’oublie pas de rajouter dans la boucle loop , la fonction : MQTTclient.loop(); Cette dernière a pour but de surveiller les données entrantes et de rediriger vers la fonction définie par setcallback ( ici MQTTcallback).

Pour tester , il suffit depuis mon client mosquitto d’envoyer la commande

mosquitto_pub -h 192.168.10.12 -t byfeel/mqtt/RX -m « message provenant de broker »

Structurer ses données avec Json.

Envoie depuis ESP

Dès que les informations sont plus nombreuses, il convient de structurer les envoies et les receptions , afin de s’y retrouver . Pour cela , j’utilise la bibliothèque ArduinoJson.

Vous avez pu découvrir cette bibliothèque dans les différents articles sur le Notifheure, j’utilise régulièrement cette dernière afin de structurer les sauvegardes où les envoient des données.

j’inclus la bibliothèque :

#include

Ce qui nous donne le code suivant pour la fonction MQTTsend()

void MQTTsend() {
char buffer[512];  // on reserve une taille de buffer pour stocker le Json
  DynamicJsonDocument docMqtt(512);
  docMqtt["temperature"] = "23";    // on affecte les valeurs
  docMqtt["humidity"]= "40";
  docMqtt["notification"]= "il fait chaud !!!";
  size_t n = serializeJson(docMqtt, buffer);  // on serialize le json pour l'envoie
  MQTTclient.publish("byfeel/sensor/state", buffer, n);  // et on publie le tout
}

résultat de la requete mosquitto

phil@debianDock:~$ mosquitto_sub -h 192.168.10.12 -t byfeel/sensor/state
{"temperature":"23","humidity":"40","notification":"il fait chaud !!!"}
{"temperature":"23","humidity":"40","notification":"il fait chaud !!!"}

Exemple sous jeedom

Sous JMQTT JEEDOM , l’equipement à été mis à jour avec les infos suivantes

exemple sous home assistant

Sous home assistant , il suffit d’ajouter dans le fichiers de config sensors.yaml , une déclaration pour chaque valeur que l’on désire afficher ( par exemple la température ).

  ### sensor.yaml
### TUTO test MQTT
        - platform: mqtt
          state_topic: 'byfeel/sensor/state'
          name: 'temperature'
          device_class: 'temperature'
          unit_of_measurement: '°C'
          value_template: '{{ value_json.temperature }}'
exemple de rendu avec Notifheure bureau

Réception depuis ESP

La modification sera réalisée dans la fonction MQTTcallback

void MQTTcallback(char* topic, byte* payload, unsigned int length) {

  Serial.print("Message MQTT [");
  Serial.print(topic);
  Serial.print("] ");

DynamicJsonDocument docMqtt(512);
deserializeJson(docMqtt, payload, length);   // on deserialize le payload reçu
String msg , title;
msg=docMqtt["msg"]|"";                       // j'affecte chaque champ a des variables
title=docMqtt["title"]|"";                   
Serial.println("message reçu : "+msg +" et titre "+title);

}

La ligne msg=docMqtt[« msg »]| » »; permet d’affecter la valeur de l’attribut msg à la variable String msg . Si cet attribut n’existe pas ou n’est pas définis , j’attribue la valeur par défaut ( || «  » ).

Exemple sous jeedom

Sous JEEDOM , avec le plugin JQMTT , il est trés facile de mettre en place une commande pour envoyer le message .

Dans l’equipement créé précedemant ajout d’une commande action ( type message )

dans le champ valeur renseigner les infos suivantes : {msg:’#message#’,title:’#title#’}

Exemple sous Home assistant :

Pour l’envoie de donnée sou Home assistant , il faudra dans un premier temps , créer un champ text Input dans le menu configuration.yaml

#configuration
input_text:
  textnotif:
    name: notification
    initial: msg a envoyer

Puis mettre en place un script pour l’envoie de requete via MQTT , dans le fichier script.yaml

## mqtt notify
notify_mqtt:
    sequence:
      - service: mqtt.publish
        data_template:
          payload: '{msg:"{{ message }}"}'
          topic: "{{ target }}"
          retain: true

et enfin , configuré une automatisation , pour chaque changement d’état sur le champ texte. Modification du fichier automation.yaml

- alias: Tuto notification
  trigger:
    platform: state
    entity_id: input_text.textnotif
  action:
    service: script.notify_mqtt
    data_template:
      target: "byfeel/mqtt/RX"
      message: "{{ states.input_text.textnotif.state}}"
     

Si vous avez une autre plateforme , voir dans la documentation , comment intégrer MQTT.

Script final

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <PubSubClient.h> 
#include <ArduinoJson.h>

//WiFi Connection configuration
char ssid[] = "nomWIFI";     //  your network SSID (name)
char password[] = "passWIFI";  // your network password
//mqtt server
char mqtt_server[] = "127.0.0.1";
WiFiClient espClient;
PubSubClient MQTTclient(espClient);

#define MQTT_USER ""    // si user existe
#define MQTT_PASS ""    // si pass MQTT

//mqtt
void MQTTsend() {
  char buffer[512];
  DynamicJsonDocument docMqtt(512);
  docMqtt["temperature"] = "33";
  docMqtt["humidity"]= "40";
  docMqtt["notification"]= "il fait chaud !!!";
  size_t n = serializeJson(docMqtt, buffer);
  MQTTclient.publish("byfeel/sensor/state", buffer, n);
}

void MQTTconnect() {
  while (!MQTTclient.connected()) {
      Serial.print("Attente  MQTT connection...");
      String clientId = "NotifheureClient-";
      clientId += String(random(0xffff), HEX);

    // Attempt to connect
    if (MQTTclient.connect(clientId.c_str(),MQTT_USER,MQTT_PASS)) {
      Serial.println("connected");
      MQTTclient.subscribe("byfeel/mqtt/RX");
    } else {
      Serial.print("ECHEC, rc=");
      Serial.print(MQTTclient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void MQTTcallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message MQTT [");
  Serial.print(topic);
  Serial.print("] ");

DynamicJsonDocument docMqtt(512);
deserializeJson(docMqtt, payload, length);  
String msg , title;
msg=docMqtt["msg"]|"";
title=docMqtt["title"]|"";
Serial.println("message reçu : "+msg +" et titre reçu "+title);
}

void setup() {
Serial.begin(115200);
  // Conexion WIFI
   WiFi.begin(ssid, password);
   Serial.println("");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
   Serial.println("Connected");
   MQTTclient.setServer(mqtt_server, 1883);
   MQTTclient.setCallback(MQTTcallback);
}

void loop() {
  static uint32_t  lastTimeMqtt= 0;
  // connecte serveur MQTT
  if (!MQTTclient.connected()) {
    MQTTconnect();
  }

  MQTTclient.loop();
  if (millis() - lastTimeMqtt >= 10000)  // toutes les 20 secondes
   {
     lastTimeMqtt = millis();
     MQTTsend();
   }
}

Conclusion

Il est assez facile , d’inclure le protocole MQTT au sein d’objet connectés. A la demande de plusieurs lecteurs , je vais donc inclure , dans la prochaine version du NotifheureXL, ce protocole d’échange simple et rapide.

Pour terminer une petite astuce , pour purger tous vos essais sur le serveur mosquitto . Il suffit de redémarrer le service.

4 commentaires sur “Client MQTT dans votre module ESP”

Les commentaires sont fermés.