Quand votre ESP32 ne se connecte pas à un broker MQTT, la cause est presque toujours l'un d'une douzaine de problèmes prévisibles, de la mauvaise bande Wi-Fi à un client TLS sur un port en clair. Ce guide vous donne un ordre de vérification méthodique, explique la signification de chaque code d'erreur PubSubClient et montre comment valider chaque correction avec broker.connectmq.com.
Commencez par le code d'erreur
Avant de modifier quoi que ce soit, déterminez où la connexion échoue. PubSubClient expose la raison via client.state(). Affichez-la chaque fois que connect() renvoie false :
if (!mqtt.connect(clientId.c_str())) {
Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
}
state() |
Constante | Ce que cela signifie généralement |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT |
Le serveur n'a pas répondu dans le délai de keepalive |
| -3 | MQTT_CONNECTION_LOST |
La connexion est tombée après avoir été établie |
| -2 | MQTT_CONNECT_FAILED |
La connexion TCP ou TLS n'a pas pu être ouverte du tout |
| -1 | MQTT_DISCONNECTED |
Le client est proprement déconnecté |
| 1 | MQTT_CONNECT_BAD_PROTOCOL |
Le broker n'accepte pas la version du protocole |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID |
Client ID refusé |
| 3 | MQTT_CONNECT_UNAVAILABLE |
Broker indisponible |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS |
Nom d'utilisateur ou mot de passe incorrect |
| 5 | MQTT_CONNECT_UNAUTHORIZED |
Client non autorisé |
Cette répartition est utile : les codes négatifs signifient que le problème se situe sous MQTT (réseau, DNS, TCP, TLS), les codes positifs que le broker a répondu et vous a refusé. Un -2 vous renvoie aux solutions 1 à 6, un 4 ou un 5 aux solutions 7 à 9.
Prouvez d'abord que le côté broker fonctionne
Écartez le broker avant de toucher au firmware. Depuis un ordinateur du même réseau, lancez :
mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-test-4821 -t 'connectmq/demo/4821/#' -v
Dans un second terminal :
mosquitto_pub -h broker.connectmq.com -p 1883 -i pc-pub-4821 -t connectmq/demo/4821/hello -m test
Si le message arrive, le broker et le chemin Internet fonctionnent, et le problème vient de l'ESP32 ou de son segment réseau. S'il n'arrive pas non plus sur votre ordinateur, vérifiez d'abord le pare-feu de votre réseau. La page du serveur MQTT public gratuit liste les ports et les limites sur lesquels vous pouvez tester.
Solutions au niveau réseau (codes d'état négatifs)
1. Vérifiez que le Wi-Fi est réellement connecté en 2,4 GHz
L'ESP32 (hormis les variantes récentes compatibles 5 GHz) ne dialogue qu'avec les réseaux 2,4 GHz. Un routeur bi-bande avec un seul SSID pour les deux bandes fonctionne généralement, mais un SSID uniquement en 5 GHz ne fonctionnera jamais. Vérifiez aussi l'ordre des opérations : appeler mqtt.connect() avant que WiFi.status() == WL_CONNECTED échoue toujours avec -2. Affichez WiFi.localIP() et WiFi.RSSI() ; un signal plus faible qu'environ -80 dBm provoque des échecs intermittents qui ressemblent à des problèmes de broker.
2. Vérifiez la résolution DNS
Si le nom d'hôte ne se résout pas, la connexion TCP ne démarre jamais. Testez-la directement :
IPAddress ip;
if (WiFi.hostByName("broker.connectmq.com", ip)) {
Serial.println(ip);
} else {
Serial.println("DNS lookup failed");
}
Un échec ici désigne le serveur DNS fourni par le DHCP ou une configuration d'IP statique sans entrée DNS. Lorsque vous utilisez WiFi.config() pour une adresse statique, passez aussi la passerelle et un serveur DNS.
3. Associez le port à la bonne classe de client
C'est de loin l'erreur la plus fréquente. WiFiClient parle TCP en clair et doit utiliser le port 1883. WiFiClientSecure parle TLS et doit utiliser le port 8883. Les mélanger produit un -2 ou un -4 sans autre indice.
| Classe de client | Port | Transport |
|---|---|---|
WiFiClient |
1883 | MQTT en clair sur TCP |
WiFiClientSecure |
8883 | MQTT sur TLS |
| Bibliothèques WebSocket | 8083 / 8084 (chemin /mqtt) |
MQTT sur WebSocket / WebSocket sécurisé |
PubSubClient ne parle pas WebSocket : les ports 8083 et 8084 sont donc réservés aux clients navigateur et aux bibliothèques compatibles WebSocket.
4. Corrigez la validation du certificat TLS
Avec WiFiClientSecure, un certificat de CA absent ou erroné fait échouer le handshake sans bruit. ConnectMQ utilise un certificat Let's Encrypt : chargez donc la racine ISRG Root X1 avec setCACert(). Pour confirmer que le TLS est en cause, appelez temporairement secureClient.setInsecure() : si la connexion fonctionne soudain, votre CA est mauvaise. Ne livrez pas setInsecure() en production. Lisez l'erreur TLS avec secureClient.lastError(buf, sizeof(buf)) pour obtenir des détails, et consultez ESP32 MQTT sur SSL/TLS pour une configuration sécurisée complète.
5. Donnez au TLS assez de mémoire et de temps
Les handshakes TLS nécessitent des dizaines de kilo-octets de heap. Si votre sketch alloue de gros tampons, des données d'image ou plusieurs clients sécurisés, le handshake échoue avec -2. Affichez ESP.getFreeHeap() juste avant la connexion. Vérifiez aussi l'horloge : la validation des certificats compare des dates, et certaines bibliothèques exigent une heure valide. Synchroniser NTP avec configTime() avant la connexion évite toute une catégorie d'échecs déroutants.
6. Vérifiez que le chemin réseau ne bloque pas les ports
Les Wi-Fi invités, les réseaux d'entreprise et certains points d'accès mobiles bloquent le 1883 ou le 8883 en sortie. Si le test sur ordinateur ci-dessus n'a fonctionné que depuis un autre réseau, essayez l'ESP32 sur un partage de connexion de téléphone. S'il s'y connecte, le réseau d'origine filtre les ports MQTT et vous devez demander à l'administrateur réseau de les ouvrir.
Solutions au niveau broker (codes d'état positifs)
7. Utilisez un client ID non vide et unique
Chaque client a besoin de son propre client ID. Deux appareils flashés avec le même ID codé en dur s'éjecteront mutuellement du broker à répétition, ce qui ressemble à des déconnexions aléatoires avec l'état -3. Un client ID vide est aussi refusé par de nombreux brokers, dont la zone publique de broker.connectmq.com. Dérivez l'ID de l'adresse MAC de la puce :
String clientId = "esp32-" + String((uint32_t)ESP.getEfuseMac(), HEX);
8. Utilisez exactement les identifiants et le client ID du portail
Sur un broker ConnectMQ privé, le client ID est lié aux identifiants émis. Si vous inventez votre propre client ID tout en utilisant le nom d'utilisateur et le mot de passe générés, le broker rejette la connexion avec le code 5, même si le mot de passe est correct. Copiez les trois valeurs depuis le portail : YOUR_CLIENT_ID, YOUR_USERNAME, YOUR_PASSWORD. Méfiez-vous des espaces invisibles en fin de chaîne lors du collage dans le code.
9. Stoppez les tempêtes de tentatives avant d'être bloqué
Une boucle de reconnexion sans délai peut bombarder le broker de nombreuses tentatives de connexion par seconde. Avec de mauvais identifiants, c'est particulièrement nuisible, car ConnectMQ bloque les adresses IP après des échecs d'authentification répétés dans le cadre de sa protection contre le flood. Espacez les tentatives et augmentez le délai après chaque échec :
unsigned long nextAttempt = 0;
unsigned long backoff = 2000;
void ensureMqtt() {
if (mqtt.connected() || millis() < nextAttempt) return;
if (mqtt.connect(clientId.c_str())) {
backoff = 2000;
mqtt.subscribe("connectmq/demo/4821/cmd");
} else {
Serial.printf("connect failed, state=%d, retry in %lu ms\n", mqtt.state(), backoff);
nextAttempt = millis() + backoff;
backoff = min(backoff * 2, 60000UL);
}
}
Cette structure non bloquante garde aussi loop() réactive. Le guide de reconnexion MQTT ESP32 approfondit la logique de reconnexion robuste.
Solutions pour les connexions qui tombent après s'être établies
10. Appelez mqtt.loop() assez souvent
PubSubClient n'envoie les pings de keepalive et ne lit les paquets entrants qu'à l'intérieur de loop(). Un long delay() ou une lecture de capteur bloquante plus longue que l'intervalle de keepalive (15 secondes par défaut) fait croire au broker que le client est mort, et vous voyez -3 ou -4. Remplacez les longs délais par des minuteries millis(), ou augmentez le keepalive avec mqtt.setKeepAlive(30). Si la carte redémarre aussi, lisez l'article sur les resets du task watchdog ESP32 dans les projets MQTT.
11. Agrandissez le tampon pour les gros messages
Le tampon de paquets par défaut de PubSubClient fait 256 octets, topic et en-tête compris. Une publication plus grande échoue sans bruit : publish() renvoie false, et un message entrant trop grand est ignoré. Appelez mqtt.setBufferSize(1024) avant la connexion, en le dimensionnant pour votre plus gros message.
12. Respectez les limites du serveur MQTT public gratuit
La zone publique de broker.connectmq.com autorise 64 Ko par message, 10 messages par seconde et par client, 10 abonnements par client et jusqu'à 8 niveaux de topic. Un sketch qui publie dans une boucle serrée ou s'abonne à de nombreux topics peut être déconnecté pour les avoir dépassées. Limitez le rythme de publication, utilisez un seul abonnement avec wildcard plutôt que de nombreux abonnements individuels, et gardez des topics courts comme connectmq/demo/4821/room/temp.
Un sketch de diagnostic
Flashez-le quand vous êtes bloqué. Il rend compte de chaque étape séparément, pour que vous voyiez exactement où la chaîne casse :
#include <WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* HOST = "broker.connectmq.com";
WiFiClient net;
PubSubClient mqtt(net);
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) { delay(250); Serial.print('.'); }
Serial.printf("\nWi-Fi OK ip=%s rssi=%d\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());
IPAddress ip;
if (!WiFi.hostByName(HOST, ip)) { Serial.println("DNS FAILED"); return; }
Serial.printf("DNS OK %s\n", ip.toString().c_str());
if (!net.connect(HOST, 1883)) { Serial.println("TCP 1883 FAILED"); return; }
Serial.println("TCP OK");
net.stop();
String id = "esp32-diag-" + String((uint32_t)ESP.getEfuseMac(), HEX);
mqtt.setServer(HOST, 1883);
if (mqtt.connect(id.c_str())) {
Serial.println("MQTT OK");
mqtt.publish("connectmq/demo/4821/diag", "hello from diag");
} else {
Serial.printf("MQTT FAILED state=%d\n", mqtt.state());
}
}
void loop() { mqtt.loop(); }
Laissez mosquitto_sub tourner sur connectmq/demo/4821/# et vous verrez le message de diagnostic dès que tout fonctionne.
Quand ça marche sur le broker de test mais pas en production
La plupart des échecs en production se résument aux solutions 3, 4 et 8 : passer au port 8883 sans passer à WiFiClientSecure, oublier le certificat de CA, ou utiliser un client ID maison avec des identifiants du portail. Changez une seule chose à la fois et continuez d'afficher le code d'erreur. Le tutoriel IoT ESP32 présente un projet complet qui se connecte proprement dans les deux modes, et ce que fait un broker MQTT explique pourquoi le broker refuse certaines connexions.
Mettez votre ESP32 en ligne avec ConnectMQ
Une fois que l'appareil se connecte au broker de test, migrez-le vers un broker privé avec l'offre gratuite Serverless sur app.connectmq.com, sans carte bancaire. Le client MQTT intégré au navigateur et l'explorateur de topics en direct du portail permettent de confirmer facilement le trafic côté appareil comme côté serveur. ConnectMQ est un broker MQTT managé avec le TLS sur 8883 prêt à l'emploi : le seul certificat dont vous avez besoin sur l'ESP32 est la racine publique Let's Encrypt.
Questions fréquentes
Que signifie PubSubClient rc=-2 ?
Cela signifie que la connexion TCP ou TLS n'a pas pu être ouverte : le broker n'a donc jamais vu de paquet MQTT. Vérifiez le Wi-Fi, le DNS, la combinaison port et classe de client, le certificat de CA et le heap libre.
Pourquoi mon ESP32 se connecte-t-il puis se déconnecte-t-il toutes les quelques secondes ?
Généralement, un autre appareil utilise le même client ID, ou mqtt.loop() n'est pas appelée assez souvent pour envoyer les pings de keepalive. Donnez à chaque appareil un ID unique et supprimez les longs délais bloquants.
Puis-je utiliser le port 8083 ou 8084 avec PubSubClient ?
Non. Ce sont des ports WebSocket, et PubSubClient ne parle MQTT que sur TCP brut ou TLS. Utilisez 1883 avec WiFiClient ou 8883 avec WiFiClientSecure.
Pourquoi mes identifiants sont-ils refusés alors que le mot de passe est correct ?
Sur un broker privé avec des identifiants émis, le client ID leur est lié. Utilisez exactement le client ID affiché dans le portail avec le nom d'utilisateur et le mot de passe générés.



