ESP32 et Arduino

Guide de la bibliothèque ArduinoMqttClient : API, poll(), QoS et messages Will

Un guide complet de la bibliothèque officielle ArduinoMqttClient : l'API de connexion, l'importance de poll(), la publication et la réception en flux, les drapeaux QoS et retain, les messages Will et chaque code de connectError().

Éditeur de code affichant un sketch ArduinoMqttClient à côté d'une carte Arduino qui publie des messages MQTT
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

ArduinoMqttClient est la bibliothèque MQTT officielle maintenue par Arduino, et elle fonctionne sur tout, de l'UNO R4 WiFi et du Nano 33 IoT aux cartes MKR et à l'ESP32. Ce guide parcourt l'API ArduinoMqttClient dans l'ordre où vous l'utiliserez : configurer, se connecter, appeler poll(), publier, recevoir et définir un message Will. Chaque exemple se connecte à un vrai broker pour que vous puissiez tester au fil de la lecture.

Pourquoi choisir ArduinoMqttClient

ArduinoMqttClient enveloppe n'importe quel objet Client Arduino, comme WiFiClient, WiFiSSLClient, EthernetClient ou un client cellulaire, et y ajoute MQTT 3.1.1. Son choix de conception le plus marquant est que les messages sortants comme entrants sont traités comme des flux : vous n'avez jamais besoin d'un buffer de la taille de votre plus gros message.

Fonctionnalité ArduinoMqttClient
Protocole MQTT 3.1.1
QoS en publication 0, 1 et 2
QoS en abonnement 0, 1 et 2
Retained messages Oui
Message Will Oui, en flux avec beginWill() / endWill()
Gestion du payload En flux (print() / read())
Modèle d'événements Scrutation avec poll(), callback à la réception
Mainteneur Arduino

Si vous connaissez déjà PubSubClient, le tutoriel PubSubClient pour Arduino est un point de comparaison utile ; les deux bibliothèques résolvent le même problème avec des compromis différents.

Installer la bibliothèque

Ouvrez Outils → Gérer les bibliothèques dans l'Arduino IDE, recherchez « ArduinoMqttClient » et installez-la. Il vous faut aussi la bibliothèque réseau de votre carte :

Carte Bibliothèque réseau Client en clair Client TLS
UNO R4 WiFi WiFiS3 WiFiClient WiFiSSLClient
Nano 33 IoT, MKR WiFi 1010 WiFiNINA WiFiClient WiFiSSLClient
ESP32 WiFi (core) WiFiClient WiFiClientSecure
Cartes avec shield Ethernet Ethernet EthernetClient Non intégré

Configurer le client

Créez d'abord le client réseau, puis passez-le à MqttClient :

#include <WiFiNINA.h>          // ou WiFiS3.h, WiFi.h ...
#include <ArduinoMqttClient.h>

WiFiClient net;
MqttClient mqttClient(net);

Avant d'appeler connect(), définissez les paramètres de session :

mqttClient.setId("acm-51e09b");                       // client ID, jamais vide
mqttClient.setUsernamePassword("YOUR_USERNAME", "YOUR_PASSWORD"); // facultatif
mqttClient.setKeepAliveInterval(30 * 1000L);          // millisecondes !
mqttClient.setConnectionTimeout(10 * 1000L);          // millisecondes
mqttClient.setCleanSession(true);

Attention aux unités : contrairement à PubSubClient, les valeurs de keep alive et de timeout sont en millisecondes. Passer 30 au lieu de 30000 donne un keep alive de zéro seconde après division entière, ce qui désactive complètement le keep alive : les connexions mortes passent alors inaperçues pour le broker et votre message Will n'est jamais déclenché.

Se connecter et lire connectError()

connect(host, port) renvoie 1 en cas de succès et 0 en cas d'échec. En cas d'échec, connectError() vous en donne la raison :

connectError() Constante Signification et solution
-2 MQTT_CONNECTION_REFUSED Impossible d'ouvrir le socket : problème de Wi-Fi, de DNS, de port ou de TLS
-1 MQTT_CONNECTION_TIMEOUT Pas de CONNACK à temps : mauvais type de port ou liaison médiocre
0 MQTT_SUCCESS Connecté
1 MQTT_UNACCEPTABLE_PROTOCOL_VERSION Le broker a refusé MQTT 3.1.1 (rare)
2 MQTT_IDENTIFIER_REJECTED Client ID vide ou non autorisé
3 MQTT_SERVER_UNAVAILABLE Le broker n'accepte pas de connexions pour le moment
4 MQTT_BAD_USER_NAME_OR_PASSWORD Identifiants incorrects
5 MQTT_NOT_AUTHORIZED Client non autorisé, p. ex. client ID ne correspondant pas aux identifiants

Une fonction de connexion complète et réutilisable :

bool connectMqtt(const char* host, int port) {
  Serial.print("Connecting to ");
  Serial.print(host);
  Serial.print(':');
  Serial.println(port);

  if (!mqttClient.connect(host, port)) {
    Serial.print("MQTT connect failed, connectError = ");
    Serial.println(mqttClient.connectError());
    return false;
  }
  Serial.println("MQTT connected");
  return true;
}

Pour les tests, utilisez broker.connectmq.com sur le port 1883, le serveur MQTT public gratuit, réservé aux tests et aux prototypes. Il accepte les connexions anonymes avec n'importe quel client ID unique et non vide. Gardez vos topics sous un préfixe unique comme connectmq/demo/<your-id>/ et ne publiez rien de sensible.

Pourquoi poll() est essentiel

poll() est le cœur battant d'ArduinoMqttClient. Chaque appel :

  1. Lit les octets entrants depuis le client réseau et analyse les paquets complets.
  2. Appelle votre callback onMessage (ou rend le message disponible pour parseMessage()).
  3. Répond aux échanges QoS 1 et QoS 2 des messages entrants.
  4. Envoie un PINGREQ lorsque l'intervalle de keep alive est sur le point d'expirer.

Si poll() n'est pas appelé pendant plus longtemps que l'intervalle de keep alive, le broker ferme la connexion. Évitez les appels à delay() dépassant une fraction de votre keep alive et remplacez-les par une temporisation basée sur millis() :

unsigned long lastSend = 0;

void loop() {
  mqttClient.poll();
  if (millis() - lastSend >= 10000) {
    lastSend = millis();
    // publier ici
  }
}

Publier des messages

La publication est un flux en trois étapes :

// Message QoS 0 simple
mqttClient.beginMessage("connectmq/demo/acm-51e09b/temperature");
mqttClient.print(22.7);
mqttClient.endMessage();

// Retenu, QoS 1
mqttClient.beginMessage("connectmq/demo/acm-51e09b/status", true, 1);
mqttClient.print("online");
mqttClient.endMessage();

La signature complète est beginMessage(topic, retain = false, qos = 0, dup = false). Il existe aussi une surcharge qui prend la taille du payload à l'avance, beginMessage(topic, size, retain, qos, dup). Quand vous connaissez la taille exacte, utilisez-la : la bibliothèque peut alors écrire les données directement sur le réseau au lieu de mettre d'abord tout le message en mémoire tampon, ce qui compte pour les gros payloads sur les cartes disposant de peu de RAM.

endMessage() renvoie 1 en cas de succès. Pour les QoS 1 et 2, la bibliothèque gère les paquets d'acquittement lors des appels suivants à poll().

Comme print() accepte les nombres, les chaînes et les flottants, vous pouvez construire du JSON sans buffer temporaire :

mqttClient.beginMessage(topicTelemetry);
mqttClient.print("{\"t\":");
mqttClient.print(temperature, 2);
mqttClient.print(",\"h\":");
mqttClient.print(humidity, 1);
mqttClient.print("}");
mqttClient.endMessage();

Recevoir des messages

Abonnez-vous après une connexion réussie, puis à nouveau après chaque reconnexion si vous utilisez une session propre :

mqttClient.onMessage(onMqttMessage);
mqttClient.subscribe("connectmq/demo/acm-51e09b/cmd/#", 1);

Le callback ne reçoit que la taille du message. Vous lisez le topic et le payload en flux :

void onMqttMessage(int messageSize) {
  String topic = mqttClient.messageTopic();
  Serial.print("Topic: ");
  Serial.print(topic);
  Serial.print(", QoS ");
  Serial.print(mqttClient.messageQoS());
  Serial.print(", retained ");
  Serial.println(mqttClient.messageRetain());

  char payload[128];
  int n = 0;
  while (mqttClient.available() && n < (int)sizeof(payload) - 1) {
    payload[n++] = (char)mqttClient.read();
  }
  payload[n] = '\0';
  Serial.println(payload);
}

Les octets que vous ne lisez pas sont abandonnés à l'arrivée du message suivant. Les messages trop volumineux sont donc sans danger : il suffit d'arrêter la lecture à la limite de votre buffer.

Au lieu du callback, vous pouvez appeler int size = mqttClient.parseMessage(); dans loop() et traiter les messages directement lorsque size est supérieur à zéro. Utilisez l'un ou l'autre style, pas les deux.

Messages Will

Un message Will (Last Will and Testament) est publié par le broker si votre appareil se déconnecte sans dire au revoir, par exemple après une coupure de courant ou une perte du Wi-Fi. Configurez-le avant connect() :

const char willTopic[] = "connectmq/demo/acm-51e09b/status";

mqttClient.beginWill(willTopic, true, 1);   // retenu, QoS 1
mqttClient.print("offline");
mqttClient.endWill();

if (connectMqtt("broker.connectmq.com", 1883)) {
  mqttClient.beginMessage(willTopic, true, 1);
  mqttClient.print("online");
  mqttClient.endMessage();
}

Publier un online retenu sur le même topic juste après la connexion fournit à chaque tableau de bord un indicateur de présence fiable : online tant que l'appareil est connecté, offline dès que le broker détecte sa disparition, ce qui se produit après environ une fois et demie l'intervalle de keep alive.

Un schéma complet résistant aux reconnexions

Assemblez les éléments avec cette liste :

  1. Connectez le Wi-Fi et attendez une adresse IP valide.
  2. Définissez l'ID, les identifiants, le keep alive et le Will.
  3. Connectez-vous ; en cas d'échec, affichez connectError() et réessayez après un délai qui augmente à chaque échec.
  4. En cas de succès, enregistrez onMessage, abonnez-vous et publiez online en retenu.
  5. Dans loop(), vérifiez le Wi-Fi, vérifiez mqttClient.connected(), appelez poll() et publiez selon un timer basé sur millis().

Des versions de ce schéma propres à chaque carte figurent dans le tutoriel Arduino UNO R4 WiFi MQTT et le guide Nano 33 IoT MQTT avec WiFiNINA. Pour un projet plus large, le tutoriel IoT ESP32 montre comment le côté appareil s'articule avec les tableaux de bord et les alertes.

Erreurs fréquentes avec ArduinoMqttClient

  • Appeler beginWill() après connect(). Le Will fait partie du paquet CONNECT : il doit donc être configuré avant.
  • S'abonner uniquement dans setup(). Après une reconnexion avec une session propre, le broker a oublié vos abonnements. Abonnez-vous plutôt dans votre fonction de connexion.
  • Réutiliser un même client ID sur plusieurs cartes. Le broker déconnecte l'ancienne session chaque fois qu'une autre carte se connecte, ce qui produit une boucle de reconnexion sans fin.
  • Publier depuis onMessage. Cela fonctionne, mais terminez d'abord la lecture du payload entrant ; sinon les octets non lus sont perdus.
  • Oublier endMessage(). Sans lui, rien n'est envoyé et l'appel suivant à beginMessage() échoue.

Production : TLS et identifiants

Pour de vrais déploiements, passez à un client TLS sur le port 8883 et authentifiez-vous avec vos propres identifiants. Un broker MQTT managé comme ConnectMQ émet un nom d'utilisateur, un mot de passe et un client ID pour chaque broker créé dans le portail ; le client ID est lié à ces identifiants, définissez-le donc exactement avec setId(). Le certificat de ConnectMQ provient de Let's Encrypt, que les modules Wi-Fi des cartes Arduino actuelles reconnaissent d'emblée. Passez en revue les bonnes pratiques de sécurité MQTT avant la mise en production.

Démarrer avec ConnectMQ

ConnectMQ est un broker MQTT cloud compatible MQTT 3.1.1 et 5.0, avec TLS et WebSockets, ainsi qu'un portail comprenant un client MQTT dans le navigateur, un explorateur de topics en direct et des alertes de seuil. C'est le moyen le plus simple de voir ce que votre sketch ArduinoMqttClient envoie réellement. Créez un broker Serverless gratuit sur app.connectmq.com, sans carte bancaire.

Questions fréquentes

ArduinoMqttClient est-il meilleur que PubSubClient ?

Aucun n'est meilleur dans l'absolu. ArduinoMqttClient prend en charge la publication en QoS 1 et 2 et traite les payloads en flux, tandis que PubSubClient est plus léger et dispose d'une immense base d'exemples. Choisissez ArduinoMqttClient pour les cartes Arduino officielles et les besoins en QoS.

ArduinoMqttClient prend-il en charge MQTT 5 ?

Non, il implémente MQTT 3.1.1. Les brokers modernes servent côte à côte des clients 3.1.1 et 5.0 : votre appareil peut donc partager des topics avec des applications MQTT 5.

Pourquoi ma connexion ArduinoMqttClient tombe-t-elle toutes les minutes ?

Généralement, poll() n'est pas appelé assez souvent, le keep alive a été défini en secondes au lieu de millisecondes, ou un autre client utilise le même client ID.

Quelle est la taille maximale d'un message ?

La bibliothèque traite les payloads en flux : la limite est donc surtout celle de votre broker. Le serveur MQTT public gratuit broker.connectmq.com accepte jusqu'à 64 Ko par message ; l'offre Serverless (Free) autorise 256 Ko.

Articles liés

Exemple MQTT ESP-IDF : construire un client esp-mqtt à partir de zéro

Construisez un projet ESP-IDF à partir d'un dossier vide avec le composant esp-mqtt : configuration du Wi-Fi en mode station, gestionnaire d'événements MQTT, publication et abonnement, propriétés MQTT 5 et TLS avec le bundle de certificats.

9 min de lecture

Horodatage NTP ESP32 MQTT : ajouter une heure précise à chaque message

Synchronisez l'horloge de l'ESP32 avec NTP, formatez des horodatages ISO 8601 et epoch en millisecondes, et joignez-les aux payloads MQTT pour que les mesures conservent leur véritable heure de capture même lorsqu'elles sont publiées en retard.

8 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.