Wenn ein ESP32 nicht mit einem MQTT-Broker reden will, zeigt der serielle Monitor meist nur eine wenig hilfreiche Zeile: failed, rc=-2 try again in 5 seconds. Am schnellsten kommst du weiter, wenn du die Verbindung Schicht für Schicht testest – WLAN, DNS, TCP-Port, MQTT-Handshake, dann Publish – und bei der ersten Schicht stoppst, die fehlschlägt.
Dieser Guide liefert dir einen Diagnose-Sketch für den Arduino-Core (ESP32-Core 3.x mit PubSubClient 2.8), der genau das tut, erklärt jedes Ergebnis und zeigt, wie du von der Broker-Seite aus bestätigst, dass das Board wirklich da ist.
Die Checkliste im Überblick
| Schritt | Was schiefgehen kann | Woran du es erkennst |
|---|---|---|
| 1. Serielles Log | Falsche Baudrate, gar kein Log | Zeichensalat oder Stille im Monitor |
| 2. WLAN | Falsche SSID/Passwort, 5-GHz-Netz, schwaches Signal | WiFi.status() erreicht nie WL_CONNECTED (3) |
| 3. DNS | Kein DNS-Server per DHCP, Tippfehler im Host | hostByName schlägt fehl |
| 4. TCP-Port | Firewall, falscher Port, TLS-Port mit normalem Client | rc=-2 von PubSubClient |
| 5. MQTT CONNECT | Doppelte Client-ID, falsche Zugangsdaten, ACL | rc=2, rc=4, rc=5 |
| 6. Publish | Paket größer als der Puffer | publish() liefert false |
| 7. Verbunden bleiben | loop() wird nicht aufgerufen, Keep-Alive zu kurz | Nach einer Weile rc=-3 |
Schritt 1: ein lesbares serielles Log
Stelle den Monitor auf dieselbe Geschwindigkeit wie Serial.begin() – 115200 ist üblich. Setze in der Arduino IDE außerdem Tools → Core Debug Level auf Info oder Verbose: Dann gibt der ESP32-Core WLAN-Events und TLS-Fehler aus, die dein eigener Sketch nie sieht. In PlatformIO nutzt du monitor_speed = 115200 und build_flags = -DCORE_DEBUG_LEVEL=4.
Schritte 2–6: der Diagnose-Sketch
Flashe diesen Sketch, bevor du deine eigentliche Firmware anfasst. Er prüft jede Schicht der Reihe nach und gibt genau aus, wo er hängen bleibt.
#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, wenn der Broker anonymen Zugriff erlaubt
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();
}Die Ergebnisse lesen
WLAN-Statuscodes
status | Bedeutung | Lösung |
|---|---|---|
1 WL_NO_SSID_AVAIL | Netz nicht gefunden | SSID-Schreibweise prüfen; sicherstellen, dass auf 2,4 GHz gesendet wird |
4 WL_CONNECT_FAILED | Abgelehnt, meist falsches Passwort | Passwort neu eingeben; reine WPA3-Netze brauchen ggf. den WPA2/WPA3-Mischmodus |
6 WL_DISCONNECTED | Versucht es noch oder abgebrochen | Näher heran; ein RSSI unter etwa -80 dBm ist unzuverlässig |
Ein guter RSSI liegt ungefähr zwischen -50 und -70 dBm. Wenn das WLAN verbindet, später aber abbricht, lies ESP32-WLAN- und MQTT-Verbindungsabbrüche.
DNS und TCP
DNS lookup failed bedeutet, dass das Board per DHCP keinen brauchbaren DNS-Server bekommen hat oder der Hostname falsch ist. Prüfe denselben Namen von einem Laptop im gleichen Netz mit nslookup broker.connectmq.com. Als vorübergehender Test kannst du per IP verbinden, aber so solltest du nicht ausliefern.
TCP connect failed (und rc=-2 in deiner eigenen Firmware) bedeutet, dass auf diesem Host und Port nichts geantwortet hat. Häufige Ursachen: ein Gastnetz oder eine Firmen-Firewall, die 1883 blockiert, ein Tippfehler im Port oder Port 8883 mit einem normalen WiFiClient – TLS-Ports brauchen WiFiClientSecure und ein CA-Zertifikat, wie in ESP32 MQTT über TLS gezeigt. Bestätige das von einem Laptop im selben WLAN mit nc -vz broker.connectmq.com 1883.
Rückgabecodes von PubSubClient
rc | Konstante | Übliche Ursache |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | Kein CONNACK rechtzeitig oder Keep-Alive abgelaufen |
| -3 | MQTT_CONNECTION_LOST | Verbindung abgebrochen; loop() wird zu selten aufgerufen |
| -2 | MQTT_CONNECT_FAILED | TCP-Verbindung fehlgeschlagen (siehe oben) |
| -1 | MQTT_DISCONNECTED | Sauber getrennt / nie verbunden |
| 1 | MQTT_CONNECT_BAD_PROTOCOL | Broker akzeptiert MQTT 3.1.1 nicht |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Client-ID abgelehnt (leer, zu lang, ungültige Zeichen) |
| 3 | MQTT_CONNECT_UNAVAILABLE | Broker nicht verfügbar |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Falscher Benutzername oder falsches Passwort |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Nicht autorisiert – Zugangsdaten fehlen oder Client nicht erlaubt |
Positive Werte sind die CONNACK-Antwort des Brokers selbst – das Netzwerk funktioniert also, es bleibt nur die Konfiguration. Die Reason-Code-Referenz listet die passenden MQTT-Codes, und PubSubClient verbindet nicht geht auf jeden einzelnen tiefer ein.
publish() liefert false
Der Standardpuffer von PubSubClient hat 256 Bytes und muss das ganze Paket fassen: Header, Topic und Payload. Ein JSON-Payload von 200 Bytes auf einem langen Topic überschreitet das bereits, und publish() gibt einfach false zurück, ohne dass beim Broker etwas ankommt. Rufe setBufferSize() vor dem Verbinden auf; der Paketgrößen-Rechner zeigt dir, wie groß ein Paket wirklich ist.
Schritt 7: verbunden bleiben
mqtt.loop() muss häufig laufen – mindestens mehrmals pro Keep-Alive-Intervall. Ein delay(60000) in loop() oder ein blockierendes Auslesen eines Sensors führt garantiert nach etwa anderthalb Keep-Alive-Perioden zu rc=-3, weil der Broker nie ein PINGREQ erhält. Ersetze lange Delays durch millis()-Timer und verbinde dich mit Backoff neu, statt in einer while (!mqtt.connected())-Schleife zu blockieren.
Von der Broker-Seite bestätigen
Eine serielle Zeile mit „OK“ ist die Meinung des Geräts. Prüfe es unabhängig: Öffne den Online-MQTT-Client, verbinde dich mit demselben Broker und abonniere testmqtt/esp32-check/#. Setze das Board zurück, und die Hello-Nachricht sollte innerhalb weniger Sekunden ankommen. Im Terminal sieht derselbe Check so aus:
mosquitto_sub -h broker.connectmq.com -p 1883 \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t 'testmqtt/esp32-check/#' -vIm Browser auf deinen ESP32 warten
Die meisten MQTT-Probleme mit dem ESP32 sind gar keine MQTT-Probleme – es sind WLAN-, DNS- oder Port-Probleme hinter einem einzigen Rückgabecode. Teste eine Schicht nach der anderen, lies den genauen Status- oder rc-Wert und bestätige auf der Broker-Seite, bevor du zu deiner echten Firmware übergehst. Das vollständige Arduino-Setup findest du unter ESP32 MQTT mit Arduino.
Häufig gestellte Fragen
Was bedeutet "failed, rc=-2" auf einem ESP32?
Das ist MQTT_CONNECT_FAILED von PubSubClient: Die TCP-Verbindung zum Broker ließ sich nicht öffnen. Die Ursache liegt unterhalb von MQTT – kein WLAN, falscher Host oder Port, DNS-Fehler, eine Firewall oder ein TLS-Port mit einem normalen WiFiClient.
Warum verbindet sich mein ESP32, aber publish() liefert false?
Meist ist die Nachricht größer als der Puffer von PubSubClient, der standardmäßig 256 Bytes inklusive Topic und Header umfasst. Rufe vor dem Verbinden setBufferSize(1024) oder mehr auf und prüfe den Rückgabewert jedes Publish.
Unterstützt der ESP32 5-GHz-WLAN für MQTT?
Der klassische ESP32, S2, S3 und C3 haben nur 2,4-GHz-Funk und können keinem reinen 5-GHz-Netz beitreten. Neuere Chips wie der ESP32-C5 unterstützen Dualband. Prüfe dein genaues Modul, bevor du MQTT debuggst.

