Quand un ESP32 refuse de parler à un broker MQTT, le moniteur série n'affiche souvent qu'une ligne peu utile : failed, rc=-2 try again in 5 seconds. Le moyen le plus rapide d'en sortir est de tester la connexion couche par couche (Wi-Fi, DNS, port TCP, handshake MQTT, puis publication) et de s'arrêter à la première couche qui échoue.
Ce guide vous fournit un sketch de diagnostic pour le core Arduino (core ESP32 3.x avec PubSubClient 2.8) qui fait exactement cela, explique chaque résultat et montre comment confirmer côté broker que la carte est bien là.
La checklist en un coup d'œil
| Étape | Ce qui peut mal tourner | Comment cela se manifeste |
|---|---|---|
| 1. Log série | Mauvais débit en bauds, aucun log | Caractères illisibles ou silence dans le moniteur |
| 2. Wi-Fi | Mauvais SSID/mot de passe, réseau 5 GHz, signal faible | WiFi.status() n'atteint jamais WL_CONNECTED (3) |
| 3. DNS | Pas de serveur DNS via DHCP, faute de frappe dans l'hôte | hostByName échoue |
| 4. Port TCP | Pare-feu, mauvais port, port TLS avec un client non TLS | rc=-2 de PubSubClient |
| 5. MQTT CONNECT | Client ID en double, mauvais identifiants, ACL | rc=2, rc=4, rc=5 |
| 6. Publication | Paquet plus grand que le buffer | publish() renvoie false |
| 7. Rester connecté | loop() non appelée, keep alive trop court | rc=-3 au bout d'un moment |
Étape 1 : obtenir un log série lisible
Réglez le moniteur sur la même vitesse que Serial.begin() ; 115200 est la convention. Dans l'Arduino IDE, réglez aussi Tools → Core Debug Level sur Info ou Verbose : le core ESP32 affiche alors les événements Wi-Fi et les erreurs TLS que votre sketch ne voit jamais. Dans PlatformIO, utilisez monitor_speed = 115200 et build_flags = -DCORE_DEBUG_LEVEL=4.
Étapes 2 à 6 : le sketch de diagnostic
Flashez ce sketch avant de toucher à votre vrai firmware. Il vérifie chaque couche dans l'ordre et affiche exactement où il s'arrête.
#include <WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-wifi-password";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* MQTT_USER = "username"; // nullptr si le broker autorise l'accès anonyme
const char* MQTT_PASS = "password";
WiFiClient net;
PubSubClient mqtt(net);
String clientId;
bool check() {
Serial.println("\n[1] Wi-Fi");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
unsigned long t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 20000) { delay(250); Serial.print('.'); }
if (WiFi.status() != WL_CONNECTED) {
Serial.printf("\nWi-Fi failed, status=%d\n", WiFi.status());
return false;
}
Serial.printf("\nIP %s gateway %s RSSI %d dBm\n", WiFi.localIP().toString().c_str(),
WiFi.gatewayIP().toString().c_str(), WiFi.RSSI());
Serial.println("[2] DNS");
IPAddress ip;
if (!WiFi.hostByName(MQTT_HOST, ip)) { Serial.println("DNS lookup failed"); return false; }
Serial.printf("%s -> %s\n", MQTT_HOST, ip.toString().c_str());
Serial.println("[3] TCP port");
if (!net.connect(ip, MQTT_PORT)) { Serial.println("TCP connect failed"); return false; }
Serial.println("TCP OK");
net.stop();
Serial.println("[4] MQTT CONNECT");
clientId = "esp32-" + WiFi.macAddress();
clientId.replace(":", "");
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(1024);
mqtt.setKeepAlive(30);
if (!mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
Serial.printf("MQTT failed, rc=%d\n", mqtt.state());
return false;
}
Serial.printf("MQTT OK as %s\n", clientId.c_str());
Serial.println("[5] PUBLISH");
String topic = "testmqtt/esp32-check/" + clientId + "/hello";
bool ok = mqtt.publish(topic.c_str(), "ESP32 connection test passed");
Serial.printf("publish to %s: %s\n", topic.c_str(), ok ? "OK" : "FAILED");
return ok;
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println(check() ? "\nALL CHECKS PASSED" : "\nCHECK FAILED - see the last step above");
}
void loop() {
mqtt.loop();
}Lire les résultats
Codes d'état du Wi-Fi
status | Signification | Correction |
|---|---|---|
1 WL_NO_SSID_AVAIL | Réseau introuvable | Vérifiez l'orthographe du SSID ; assurez-vous qu'il émet en 2,4 GHz |
4 WL_CONNECT_FAILED | Refusé, généralement mauvais mot de passe | Ressaisissez le mot de passe ; les réseaux WPA3 seul peuvent nécessiter le mode mixte WPA2/WPA3 |
6 WL_DISCONNECTED | Toujours en tentative ou coupé | Rapprochez-vous ; un RSSI inférieur à environ -80 dBm n'est pas fiable |
Un bon RSSI se situe à peu près entre -50 et -70 dBm. Si le Wi-Fi se connecte mais décroche ensuite, consultez déconnexions Wi-Fi et MQTT sur ESP32.
DNS et TCP
DNS lookup failed signifie que la carte n'a pas reçu de serveur DNS utilisable via DHCP ou que le nom d'hôte est faux. Vérifiez le même nom depuis un portable sur le même réseau avec nslookup broker.connectmq.com. Pour un test temporaire, vous pouvez vous connecter par IP, mais ne livrez pas cela.
TCP connect failed (et rc=-2 dans votre propre firmware) signifie que rien n'a répondu sur cet hôte et ce port. Causes fréquentes : réseau invité ou pare-feu d'entreprise qui bloque le 1883, faute de frappe dans le port, ou port 8883 utilisé avec un WiFiClient classique ; les ports TLS exigent WiFiClientSecure et un certificat de CA, comme le montre MQTT sur TLS avec l'ESP32. Confirmez depuis un portable sur le même Wi-Fi avec nc -vz broker.connectmq.com 1883.
Codes de retour de PubSubClient
rc | Constante | Cause habituelle |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | Pas de CONNACK à temps, ou keep alive expiré |
| -3 | MQTT_CONNECTION_LOST | Connexion perdue ; loop() n'est pas appelée assez souvent |
| -2 | MQTT_CONNECT_FAILED | Échec de la connexion TCP (voir ci-dessus) |
| -1 | MQTT_DISCONNECTED | Déconnexion propre / jamais connecté |
| 1 | MQTT_CONNECT_BAD_PROTOCOL | Le broker n'accepte pas MQTT 3.1.1 |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Client ID refusé (vide, trop long, caractères invalides) |
| 3 | MQTT_CONNECT_UNAVAILABLE | Broker indisponible |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Nom d'utilisateur ou mot de passe incorrect |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Non autorisé : identifiants absents ou client non autorisé |
Les valeurs positives sont la réponse CONNACK du broker lui-même : le réseau fonctionne, il ne reste que la configuration. La référence des reason codes liste les codes MQTT correspondants, et PubSubClient ne se connecte pas détaille chacun d'eux.
publish() renvoie false
Le buffer par défaut de PubSubClient fait 256 octets et doit contenir le paquet entier : en-tête, topic et payload. Un payload JSON de 200 octets sur un topic long le dépasse déjà, et publish() renvoie simplement false sans que rien n'arrive au broker. Appelez setBufferSize() avant la connexion ; le calculateur de taille de paquet vous indique la taille réelle d'un paquet.
Étape 7 : rester connecté
mqtt.loop() doit s'exécuter souvent, au moins plusieurs fois par intervalle de keep alive. Un delay(60000) dans loop() ou une lecture de capteur bloquante garantit un rc=-3 au bout d'environ une période et demie de keep alive, car le broker ne reçoit jamais de PINGREQ. Remplacez les longs délais par des minuteries basées sur millis() et reconnectez-vous avec un backoff au lieu de bloquer dans une boucle while (!mqtt.connected()).
Confirmer côté broker
Une ligne série qui affiche « OK » n'est que l'avis de l'appareil. Vérifiez de façon indépendante : ouvrez le client MQTT en ligne, connectez-vous au même broker et abonnez-vous à testmqtt/esp32-check/#. Redémarrez la carte : le message hello doit arriver en quelques secondes. Depuis un terminal, la même vérification donne :
mosquitto_sub -h broker.connectmq.com -p 1883 \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t 'testmqtt/esp32-check/#' -vGuetter votre ESP32 dans le navigateur
La plupart des problèmes MQTT sur ESP32 ne sont pas des problèmes MQTT : ce sont des soucis de Wi-Fi, de DNS ou de port cachés derrière un seul code de retour. Testez une couche à la fois, lisez la valeur exacte de status ou de rc, et confirmez côté broker avant de passer à votre vrai firmware. Pour la configuration Arduino complète, poursuivez avec MQTT sur ESP32 avec Arduino.
Questions fréquentes
Que signifie "failed, rc=-2" sur un ESP32 ?
C'est MQTT_CONNECT_FAILED de PubSubClient : la connexion TCP vers le broker n'a pas pu s'ouvrir. La cause se situe sous MQTT : pas de Wi-Fi, mauvais hôte ou port, échec DNS, pare-feu, ou port TLS utilisé avec un WiFiClient classique.
Pourquoi mon ESP32 se connecte mais publish() renvoie false ?
Le plus souvent, le message dépasse le buffer de PubSubClient, de 256 octets par défaut en comptant le topic et l'en-tête. Appelez setBufferSize(1024) ou plus avant la connexion et vérifiez la valeur de retour de chaque publish.
L'ESP32 prend-il en charge le Wi-Fi 5 GHz pour MQTT ?
L'ESP32 classique, le S2, le S3 et le C3 n'ont qu'une radio 2,4 GHz et ne peuvent donc pas rejoindre un réseau uniquement 5 GHz. Des puces plus récentes comme l'ESP32-C5 sont bi-bande. Vérifiez votre module exact avant de déboguer MQTT.

