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.
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.
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 :
#includeCe 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 }}'
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”
Salut, je reviens vers toi car j’ai 2 petites question concernant le mqtt avec le notif’heure.
Je viens de le mettre en place dans HA j’arrive a récupérer la température des sondes dht22 recu et installé ce jour.
Par contre je n’arrive pas a valider la config pour avoir temp ET hum je ne comprends pas pourquoi 0o
Sinon j’essai de trouver un autre moyen que de passer par script et automation pour envoyer un message car sur un notif’heure je voudrais n’afficher qu’un seul message sur une action donné mais vu que le message ne s’envoi que sur changement de texte, ca ne va pas, as-tu une idée pour faire ca au mieux?
Et dernière question, penses-tu intégrer les fonctions pour agir sur les différents équipements du notif’heure via mqtt?
car pour le moment il ne me semble pas possible de gérer l’affichage de l’heure, l’activation de la led ou d’un buzzer?
En espérant ne pas trop t’embêter a chaque fois avec toutes mes questions ^^
Merci d’avance a toi pour les réponses
bonjour pour recuperer les infos ils faut renseigner dans le script sensor.yaml les commandes suivantes
– platform: mqtt
state_topic: ‘byfeel/NotifheureXL/46fxxx/bureau/state’
name: ‘humidtite notif bureau’
device_class: ‘humidity’
unit_of_measurement: ‘%’
value_template: ‘{{ value_json.humidity }}’
– platform: mqtt
state_topic: ‘byfeel/NotifheureXL/46fxxx/bureau/state’
name: ‘Temperature notif bureau’
device_class: ‘temperature’
unit_of_measurement: ‘°C’
value_template: ‘{{ value_json.temperature }}’
46fxx etant l’id du notifheure , l’info est renseigné dans la page html du notifheure dans onglet MQTT
Les fonctions sont activés dabs le MQTT , avec le paramètre OPT dans message
Merci je vais regarder j avais pas pensé a mettre 2 fois – platform: mqtt
Peu etre pour ca.
Sinon je pensais a un truc qui pourrais etre sympa, ce serait d ecrire un billet sur ta realisation des notif’heure:
Boitier, integration, soudire etc.
Au moins pour nous donner aussin une « image » du final.
Perso pour le moment c est un pcb avec des fils et des composant qui traine a l air libre et je sais pas comment tout faire propre ^^
Superbe article, merci pour la rapidité entre la mise en place de mqtt et de HA et aussi pour la redaction de l article.
C’est vraiment un super projet et je suis bien content de pouvoir l exporter sur HA.
J’ai deja prévenu tout ceux que je connais que le notif’heure aller y etre intégré ils on tous été aussi heureux que moi ^^