ESP32 MQTT Last Will özelliği, cihaz çöktüğünde veya gücü kesildiğinde ve kendisi veda edemediğinde bile, broker'ın cihazınız adına çevrimdışı olduğunu duyurmasını sağlar. Bu eğitimde retained bir Last Will, buna uygun bir birth mesajı ve düzgün kapanış mesajı yapılandıracaksınız; böylece her dashboard ve betik her zaman doğru online/offline durumunu görür.
Last Will and Testament Ne Yapar?
Bir MQTT istemcisi bağlandığında broker'a daha sonra yayınlaması için bir mesaj verebilir: bir topic, bir payload, bir QoS seviyesi ve bir retain bayrağı. Buna Last Will and Testament (LWT) denir. Broker bunu saklar ve yalnızca istemci düzgün bir DISCONNECT paketi göndermeden kaybolursa yayınlar. Bu da tam olarak önemsediğiniz durumları kapsar:
- Cihazın gücü kesildi veya brownout yaşadı.
- Firmware çöktü, takıldı veya bir watchdog tarafından sıfırlandı.
- Wi-Fi koptu ve keepalive zamanlayıcısı doldu.
- Cihaz ile broker arasındaki ağ çöktü.
İstemci bağlantıyı düzgünce keserse broker will mesajını atar. Bu yüzden düzgün bir kapanış, aşağıda anlatılan kendi "offline" yayınına ihtiyaç duyar.
Will tek başına yalnızca ölümü duyurur. Onu bir birth mesajıyla, yani her başarılı bağlantıdan hemen sonra gönderilen retained bir "online" yayınıyla eşleştirin; durum topic'i her zaman güncel gerçeği tutar: cihaz bağlıyken online, broker cihazın gittiğini fark ettiği andan itibaren offline.
Durum Topic'i Kalıbı
| Olay | Kim yayınlar | Topic | Payload | Retained |
|---|---|---|---|---|
| Cihaz bağlanır | ESP32 (birth) | connectmq/demo/f5c820/<device>/status |
online |
Evet |
| Cihaz beklenmedik şekilde ölür | Broker (will) | aynı | offline |
Evet |
| Cihaz düzgünce kapanır | ESP32 | aynı | offline |
Evet |
Her iki mesaj da retained olmalıdır. Retain bayrağı olmadan, cihaz çevrimiçi olduktan sonra başlayan bir dashboard durumunu asla öğrenemez, çünkü birth mesajı gitmiştir. Bayrakla birlikte her yeni abone en son durumu anında alır.
Birth ve will için aynı topic'i kullanın ve payload'ları basit tutun. Düz online ve offline, Home Assistant availability topic'leri, Node-RED anahtarları ve çoğu dashboard ile hiçbir ayrıştırma gerektirmeden çalışır. Daha fazla ayrıntı istiyorsanız her ikisi için de JSON kullanın, örneğin {"state":"online","fw":"1.2.0"}.
PubSubClient ile ESP32 MQTT Last Will
PubSubClient, will'i connect()'in bir overload'u aracılığıyla ayarlar:
boolean connect(const char* id,
const char* user, const char* pass,
const char* willTopic, uint8_t willQos,
boolean willRetain, const char* willMessage);
broker.connectmq.com'un public alanı gibi anonim bağlantılar için user ve pass olmayan bir varyant vardır. İşte birth mesajını yayınlayan, durumu doğru tutan ve BOOT butonuna bastığınızda düzgünce kapanan eksiksiz bir sketch:
#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 button
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 retain
"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(); // the broker discards the will
WiFi.disconnect(true);
Serial.println("Clean shutdown done");
esp_deep_sleep_start(); // sleep until 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); // seconds; the broker detects loss after about 1.5x this
}
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());
}
}
Üç Durumu da Test Etmek
Bir bilgisayardan önekiniz altındaki her durum topic'ine abone olun:
mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-f5c820 -t 'connectmq/demo/f5c820/+/status' -v
Ardından her senaryoyu deneyin:
- Normal başlangıç. Kartı çalıştırın. Birkaç saniye içinde
.../status onlinegörürsünüz. - Çökme veya elektrik kesintisi. USB kablosunu çekin. Yaklaşık bir buçuk keepalive aralığından sonra (15 saniyelik keepalive ile yaklaşık 20–25 saniye) broker
.../status offlineyayınlar. - Düzgün kapanış. Yeniden açın,
onlinemesajını bekleyin, ardından BOOT'a basın. Cihazın kendisi tarafından yayınlananofflinehemen görünür.
Cihaz herhangi bir durumdayken mosquitto_sub'ı durdurup yeniden başlatın. Her iki mesaj da retained olduğu için güncel durumu anında alırsınız. Dashboard'larınızın dayandığı davranış budur.
Keepalive'ı Ayarlamak
Keepalive aralığı, sessiz bir arızadan sonra will'in ne kadar hızlı tetikleneceğini belirler. Broker, bağlantıdan vazgeçmeden önce istemciden hiçbir paket gelmeden keepalive süresinin yaklaşık 1,5 katı kadar bekler.
| Keepalive | "offline" olana kadar yaklaşık süre | Ödünleşim |
|---|---|---|
| 5 sn | 10 sn'nin altında | Hızlı algılama, daha fazla ping trafiği, zayıf Wi-Fi'da yanlış offline |
| 15 sn (PubSubClient varsayılanı) | yaklaşık 20–25 sn | Şebekeden beslenen cihazlar için iyi bir varsayılan |
| 60 sn | yaklaşık 90 sn | Daha az trafik, yavaş algılama |
Kısa bir keepalive yalnızca mqtt.loop() sık çalışıyorsa işe yarar. Döngünüz keepalive süresinden uzun engellenirse broker sağlıklı bir cihazı düşürür ve will'i tetikler; durumun online ile offline arasında gidip geldiğini görürsünüz. Uzun engelleyen çağrılar, ESP32 task watchdog sıfırlamalarını düzeltmek yazısında açıklandığı gibi sıfırlamalara da yol açar.
Yaygın Hatalar
Birth mesajını unutmak. Bu olmadan durum yeniden bağlandıktan sonra offline kalır, çünkü önceki arızadan kalan will topic'te hâlâ retained durumdadır.
Will'i retained yapmamak. Retained olmayan bir will yalnızca o anda bağlı olan abonelere ulaşır. Daha sonra başlayan her şey son retained değeri görür; bu da muhtemelen online'dır ve ölü bir cihazın canlı olduğuna inanır.
Tek bir durum topic'ini cihazlar arasında paylaşmak. Her cihazın, genellikle yolunda client ID veya cihaz adı bulunan kendi durum topic'ine ihtiyacı vardır. Paylaşılan bir topic yalnızca en son hangi cihaz konuştuysa onu gösterir.
Düzgün bir bağlantı kesmede will beklemek. mqtt.disconnect() broker'a istemcinin bilerek ayrıldığını söyler; bu yüzden will atılır. cleanShutdown()'ın yaptığı gibi bağlantıyı kesmeden önce offline'ı kendiniz yayınlayın.
Yinelenen client ID'ler. İki cihaz aynı client ID'yi kullanırsa broker, yenisi bağlandığında eski bağlantıyı keser; bu da eskisinin will'ini tetikler ve kafa karıştırıcı durum değişikliklerine yol açar. ID'yi MAC adresinden türetin.
Deep Sleep Cihazları
Uyanan, yayınlayan ve uyuyan pilli düğümler özel bir durumdur. Uyumadan önce bağlantıyı düzgünce keserlerse will hiç tetiklenmez ve durum topic'i her döngüde online/offline arasında değişir. Uykulu cihazlar için daha iyi bir yaklaşım, retained bir last_seen zaman damgası veya uptime sayacı yayınlamak ve bu değer birkaç aralıktan daha eski olduğunda cihazın çevrimdışı olduğuna tüketicinin karar vermesine izin vermektir. ESP32 pilli sensör rehberi bu ödünleşimi tartışır.
MQTT 5 Will Gecikmesi ve Broker Tarafı Durum
PubSubClient MQTT 3.1.1 konuşur. ESP-IDF'in esp-mqtt'si gibi bir MQTT 5 istemcisiyle bir will gecikme aralığı da ayarlayabilirsiniz; böylece broker will'i yayınlamadan önce birkaç saniye bekler. Cihaz bu pencerede yeniden bağlanırsa (örneğin kısa bir Wi-Fi aksaklığından sonra) will iptal edilir ve durum gidip gelmez. ESP-IDF MQTT örneği will'in nerede yapılandırıldığını gösterir; MQTT 5 özellikleri de bu özelliği açıklar.
Broker tarafında ConnectMQ ayrıca her istemcinin bağlantı durumunu $SYS/broker/connection/{clientId}/state altında yayınlar; bu da beklenmedik bir offline durumunda hata ayıklarken çapraz kontrol olarak faydalıdır.
Cihaz Erişilebilirliğini ConnectMQ'da Gösterin
Yukarıdaki her şey herkese açık MQTT test broker'ı üzerinde çalışır; böylece hesap olmadan arıza senaryolarını test edebilirsiniz. Gerçek bir filo için özel bir barındırılan MQTT broker, durum topic'lerini herkesin görebileceği yerden uzak tutar. ConnectMQ'nun canlı topic gezgini her cihazın retained durumunu tek bakışta gösterir. app.connectmq.com adresinde kredi kartı gerekmeden ücretsiz Serverless planında bir broker oluşturun ve kimlik bilgili connect() overload'unu kullanarak oluşturulan YOUR_CLIENT_ID, YOUR_USERNAME ve YOUR_PASSWORD ile bağlanın. Eksiksiz cihaz projesi için ESP32 IoT eğitimine bakın.
Sık Sorulan Sorular
Broker Last Will'i ne zaman yayınlar?
İstemci DISCONNECT göndermeden kaybolduğunda: elektrik kesintisi, çökme, ağ arızası veya keepalive zaman aşımı. Düzgün bir disconnect() will'i iptal eder.
Last Will mesajı retained olmalı mı?
Durum topic'leri için evet. Retained olması, daha sonra bağlanan herhangi bir abonenin cihazın çevrimdışı olduğunu yine de görmesi anlamına gelir.
Will için hangi QoS'u kullanmalıyım?
QoS 1, bağlı abonelere teslimatı sağladığı için iyi bir varsayılandır. Ücretsiz ConnectMQ Serverless planı QoS 0 ve 1'i destekler; bu da bu kalıbı karşılar.
Cihaz durumum neden online ile offline arasında gidip geliyor?
Genellikle mqtt.loop() keepalive süresinden uzun engelleniyordur ya da iki cihaz aynı client ID'yi paylaşıyordur. Uzun engelleyen çağrıları kaldırın ve her cihaza benzersiz bir ID verin.



