ESP32 ve Arduino

ESP32 MQTT Last Will: Her Cihaz İçin Güvenilir Online/Offline Durumu

ESP32'de retained bir Last Will and Testament ile bir birth mesajı yapılandırın; böylece her dashboard ve betik, çökmeler, elektrik kesintileri ve Wi-Fi kayıplarından sonra bile cihaz erişilebilirliğini anında görsün.

ESP32 MQTT Last Will mesajlarını kullanarak cihazları online ve offline olarak listeleyen dashboard
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

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:

  1. Normal başlangıç. Kartı çalıştırın. Birkaç saniye içinde .../status online görürsünüz.
  2. Çö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 offline yayınlar.
  3. Düzgün kapanış. Yeniden açın, online mesajını bekleyin, ardından BOOT'a basın. Cihazın kendisi tarafından yayınlanan offline hemen 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.

İlgili yazılar

ESP32 NTP MQTT Zaman Damgası: Her Mesaja Doğru Zaman Ekleyin

ESP32 saatini NTP ile eşitleyin, ISO 8601 ve epoch milisaniye zaman damgalarını biçimlendirin ve bunları MQTT payload'larına ekleyin; böylece ölçümler geç yayınlansa bile gerçek alınma zamanlarını korur.

6 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.