Mit der Last-Will-Funktion von ESP32 MQTT verkündet der Broker im Namen Ihres Geräts, dass es offline gegangen ist, selbst wenn das Gerät abgestürzt ist oder den Strom verloren hat und sich nicht selbst verabschieden konnte. In diesem Tutorial konfigurieren Sie einen retained Last Will, eine passende Birth Message und eine Nachricht für das saubere Herunterfahren, sodass jedes Dashboard und jedes Skript jederzeit einen korrekten Online-/Offline-Status sieht.
Was Last Will and Testament leistet
Wenn sich ein MQTT-Client verbindet, kann er dem Broker eine Nachricht zur späteren Veröffentlichung übergeben: ein Topic, einen Payload, eine QoS-Stufe und ein Retain-Flag. Das ist der Last Will and Testament (LWT). Der Broker speichert ihn und veröffentlicht ihn nur, wenn der Client ohne ordentliches DISCONNECT-Paket verschwindet. Das deckt genau die Fälle ab, die Sie interessieren:
- Das Gerät hat den Strom verloren oder einen Brownout erlitten.
- Die Firmware ist abgestürzt, hängt oder wurde von einem Watchdog zurückgesetzt.
- Das WLAN ist ausgefallen und der Keepalive-Timer ist abgelaufen.
- Das Netzwerk zwischen Gerät und Broker ist ausgefallen.
Trennt sich der Client sauber, verwirft der Broker den Will. Deshalb braucht ein sauberes Herunterfahren einen eigenen „offline"-Publish, der weiter unten beschrieben wird.
Für sich allein verkündet ein Will nur das Ende. Kombinieren Sie ihn mit einer Birth Message, einem retained „online"-Publish direkt nach jedem erfolgreichen Connect, dann enthält das Status-Topic stets die aktuelle Wahrheit: online, solange das Gerät verbunden ist, und offline ab dem Moment, in dem der Broker bemerkt, dass es weg ist.
Das Muster für das Status-Topic
| Ereignis | Wer veröffentlicht | Topic | Payload | Retained |
|---|---|---|---|---|
| Gerät verbindet sich | ESP32 (Birth) | connectmq/demo/f5c820/<device>/status |
online |
Ja |
| Gerät fällt unerwartet aus | Broker (Will) | dasselbe | offline |
Ja |
| Gerät fährt sauber herunter | ESP32 | dasselbe | offline |
Ja |
Beide Nachrichten müssen retained sein. Ohne Retain-Flag würde ein Dashboard, das nach dem Online-Gehen des Geräts startet, dessen Zustand nie erfahren, weil die Birth Message weg ist. Mit Flag erhält jeder neue Subscriber sofort den aktuellen Status.
Verwenden Sie für Birth und Will dasselbe Topic und halten Sie die Payloads einfach. Schlichtes online und offline funktioniert ohne Parsing mit Verfügbarkeits-Topics in Home Assistant, Node-RED-Schaltern und den meisten Dashboards. Wenn Sie mehr Details möchten, verwenden Sie für beide JSON, zum Beispiel {"state":"online","fw":"1.2.0"}.
In Industrieanlagen ist dasselbe Muster die Grundlage für Verfügbarkeitsanzeigen und OEE-Auswertungen: Liegt das Status-Topic im Zweig der jeweiligen Anlage, etwa acme/werk-stuttgart/montage/linie-1/zelle-3/status, sieht die Leitstelle im Unified Namespace (UNS) sofort, welche Zelle ihr Gateway verloren hat. Sparkplug B setzt mit NDEATH- und NBIRTH-Nachrichten auf denselben Mechanismus.
ESP32 MQTT Last Will mit PubSubClient
PubSubClient setzt den Will über eine Überladung von connect():
boolean connect(const char* id,
const char* user, const char* pass,
const char* willTopic, uint8_t willQos,
boolean willRetain, const char* willMessage);
Für anonyme Verbindungen, etwa zum öffentlichen MQTT-Broker broker.connectmq.com, gibt es eine Variante ohne user und pass. Hier ist ein vollständiger Sketch, der die Birth Message veröffentlicht, den Status korrekt hält und sauber herunterfährt, wenn Sie den BOOT-Taster drücken:
#include <WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const int SHUTDOWN_BUTTON = 0; // BOOT-Taster
WiFiClient net;
PubSubClient mqtt(net);
String deviceId, statusTopic, telemetryTopic;
unsigned long lastPublish = 0;
void connectMqtt() {
while (!mqtt.connected()) {
bool ok = mqtt.connect(deviceId.c_str(),
statusTopic.c_str(), // Will-Topic
1, // Will-QoS
true, // Will retained
"offline"); // Will-Payload
if (ok) {
mqtt.publish(statusTopic.c_str(), "online", true); // Birth Message
Serial.println("Connected, status=online");
} else {
Serial.printf("Connect failed, state=%d\n", mqtt.state());
delay(3000);
}
}
}
void cleanShutdown() {
mqtt.publish(statusTopic.c_str(), "offline", true);
mqtt.loop();
delay(100);
mqtt.disconnect(); // der Broker verwirft den Will
WiFi.disconnect(true);
Serial.println("Clean shutdown done");
esp_deep_sleep_start(); // schlafen bis zum Reset
}
void setup() {
Serial.begin(115200);
pinMode(SHUTDOWN_BUTTON, INPUT_PULLUP);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(250);
deviceId = "esp32-lwt-" + String((uint32_t)ESP.getEfuseMac(), HEX);
statusTopic = "connectmq/demo/f5c820/" + deviceId + "/status";
telemetryTopic = "connectmq/demo/f5c820/" + deviceId + "/telemetry";
mqtt.setServer("broker.connectmq.com", 1883);
mqtt.setKeepAlive(15); // Sekunden; der Broker erkennt den Verlust nach etwa dem 1,5-Fachen
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
if (digitalRead(SHUTDOWN_BUTTON) == LOW) cleanShutdown();
if (millis() - lastPublish > 10000) {
lastPublish = millis();
String payload = "{\"uptime\":" + String(millis() / 1000) + "}";
mqtt.publish(telemetryTopic.c_str(), payload.c_str());
}
}
Alle drei Fälle testen
Abonnieren Sie von einem Rechner aus jedes Status-Topic unter Ihrem Präfix:
mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-f5c820 -t 'connectmq/demo/f5c820/+/status' -v
Probieren Sie dann jedes Szenario aus:
- Normaler Start. Versorgen Sie das Board mit Strom. Innerhalb weniger Sekunden sehen Sie
.../status online. - Absturz oder Stromausfall. Ziehen Sie das USB-Kabel. Nach etwa anderthalb Keepalive-Intervallen (rund 20–25 Sekunden bei 15 Sekunden Keepalive) veröffentlicht der Broker
.../status offline. - Sauberes Herunterfahren. Schalten Sie das Board wieder ein, warten Sie auf
onlineund drücken Sie dann BOOT.offlineerscheint sofort, veröffentlicht vom Gerät selbst.
Beenden Sie mosquitto_sub und starten Sie es erneut, egal in welchem Zustand sich das Gerät befindet. Da beide Nachrichten retained sind, erhalten Sie sofort den aktuellen Status. Genau auf dieses Verhalten verlassen sich Ihre Dashboards.
Das Keepalive abstimmen
Das Keepalive-Intervall bestimmt, wie schnell der Will nach einem stillen Ausfall ausgelöst wird. Der Broker wartet etwa das 1,5-Fache des Keepalive ohne Pakete vom Client, bevor er die Verbindung aufgibt.
| Keepalive | Ungefähre Zeit bis „offline" | Abwägung |
|---|---|---|
| 5 s | unter 10 s | Schnelle Erkennung, mehr Ping-Verkehr, falsches Offline bei schlechtem WLAN |
| 15 s (Standard von PubSubClient) | etwa 20–25 s | Guter Standard für netzbetriebene Geräte |
| 60 s | etwa 90 s | Weniger Verkehr, langsame Erkennung |
Ein kurzes Keepalive funktioniert nur, wenn mqtt.loop() häufig läuft. Blockiert Ihre Schleife länger als das Keepalive, trennt der Broker ein gesundes Gerät und löst den Will aus, und der Status springt zwischen online und offline hin und her. Lange blockierende Aufrufe führen außerdem zu Resets, wie in ESP32-Task-Watchdog-Resets beheben erklärt.
Häufige Fehler
Die Birth Message vergessen. Ohne sie bleibt der Status nach einem Reconnect offline, weil der Will des vorherigen Ausfalls noch retained auf dem Topic liegt.
Den Will nicht als retained setzen. Ein nicht retained Will erreicht nur Subscriber, die in diesem Moment verbunden sind. Alles, was später startet, sieht den letzten retained Wert, vermutlich online, und hält ein totes Gerät für lebendig.
Ein Status-Topic für mehrere Geräte nutzen. Jedes Gerät braucht ein eigenes Status-Topic, meist mit Client ID oder Gerätenamen im Pfad. Ein gemeinsames Topic zeigt nur das Gerät, das zuletzt gesprochen hat.
Den Will bei sauberem Trennen erwarten. mqtt.disconnect() teilt dem Broker mit, dass der Client absichtlich geht, also wird der Will verworfen. Veröffentlichen Sie offline vor dem Trennen selbst, wie es cleanShutdown() tut.
Doppelte Client IDs. Verwenden zwei Geräte dieselbe Client ID, trennt der Broker die ältere Verbindung, sobald sich die neuere verbindet. Das löst den Will der alten aus und erzeugt verwirrende Statuswechsel. Leiten Sie die ID aus der MAC-Adresse ab.
Geräte mit Deep Sleep
Batterieknoten, die aufwachen, veröffentlichen und schlafen, sind ein Sonderfall. Trennen sie sich vor dem Schlafen sauber, wird der Will nie ausgelöst, und ein Status-Topic würde mit jedem Zyklus zwischen online und offline wechseln. Besser ist es bei schlafenden Geräten, einen retained last_seen-Zeitstempel oder einen Uptime-Zähler zu veröffentlichen und den Empfänger entscheiden zu lassen, dass ein Gerät offline ist, wenn dieser Wert älter als einige Intervalle ist. Der Leitfaden zu batteriebetriebenen ESP32-Sensoren diskutiert diese Abwägung.
Will Delay in MQTT 5 und Status auf Broker-Seite
PubSubClient spricht MQTT 3.1.1. Mit einem MQTT-5-Client wie esp-mqtt aus ESP-IDF können Sie zusätzlich ein Will Delay Interval setzen, sodass der Broker einige Sekunden wartet, bevor er den Will veröffentlicht. Verbindet sich das Gerät in diesem Zeitfenster neu, etwa nach einem kurzen WLAN-Aussetzer, wird der Will verworfen und der Status springt nicht hin und her. Das ESP-IDF-MQTT-Beispiel zeigt, wo der Will konfiguriert wird, und MQTT-5-Funktionen erklärt die Property.
Auf Broker-Seite veröffentlicht ConnectMQ außerdem den Verbindungsstatus jedes Clients unter $SYS/broker/connection/{clientId}/state, was sich als Gegenprobe eignet, wenn Sie ein unerwartetes offline untersuchen.
Geräteverfügbarkeit mit ConnectMQ anzeigen
Alles oben Beschriebene läuft auf dem öffentlichen MQTT-Broker, sodass Sie Ausfallszenarien ohne Konto testen können; für produktive Daten ist er nicht gedacht. Für eine echte Flotte hält ein privater verwalteter MQTT-Broker Status-Topics von der Öffentlichkeit fern. Der Live-Topic-Explorer von ConnectMQ zeigt den retained Status jedes Geräts auf einen Blick. Erstellen Sie einen Broker im kostenlosen Serverless-Tarif unter app.connectmq.com, ohne Kreditkarte, und verbinden Sie sich mit Ihren generierten YOUR_CLIENT_ID, YOUR_USERNAME und YOUR_PASSWORD über die connect()-Überladung mit Zugangsdaten. Das vollständige Geräteprojekt zeigt das ESP32-IoT-Tutorial.
Häufig gestellte Fragen
Wann veröffentlicht der Broker den Last Will?
Wenn der Client verschwindet, ohne DISCONNECT zu senden: bei Stromausfall, Absturz, Netzwerkausfall oder Keepalive-Timeout. Ein sauberes disconnect() hebt den Will auf.
Sollte die Last-Will-Nachricht retained sein?
Ja, bei Status-Topics. Ist sie retained, sieht auch jeder später verbundene Subscriber, dass das Gerät offline ist.
Welche QoS sollte ich für den Will verwenden?
QoS 1 ist ein guter Standard, weil es die Zustellung an verbundene Subscriber sicherstellt. Der kostenlose ConnectMQ-Serverless-Tarif unterstützt QoS 0 und 1, was dieses Muster abdeckt.
Warum flackert der Status meines Geräts zwischen online und offline?
Meist ist mqtt.loop() länger als das Keepalive blockiert, oder zwei Geräte teilen sich eine Client ID. Entfernen Sie lange blockierende Aufrufe und geben Sie jedem Gerät eine eindeutige ID.



