وضعیت آنلاین دستگاه با Last Will در MQTT
چطور بفهمیم دستگاه آفلاین شده؟ Keep-Alive، پیام وصیت (Last Will) و پیام ماندگار را کنار هم بگذارید تا وضعیت آنلاین و آفلاین همیشه درست نمایش داده شود.
مشکل: دستگاهی که بیصدا قطع میشود#
وقتی برق دستگاه میرود یا اینترنت قطع میشود، دستگاه فرصت ندارد به کسی خبر بدهد. داشبورد یا برنامه شما هنوز آخرین دما را نشان میدهد و نمیداند آن عدد مال یک ساعت پیش است. MQTT برای این مشکل دو ابزار دارد که با هم کار میکنند: Keep-Alive برای اینکه بروکر قطعی را تشخیص دهد، و پیام وصیت (Last Will) برای اینکه بروکر آن را به بقیه اعلام کند.
Keep-Alive: بروکر از کجا میفهمد؟#
هر کلاینت هنگام اتصال یک عدد Keep-Alive بر حسب ثانیه اعلام میکند و قول میدهد در این فاصله حداقل یک بسته بفرستد؛ اگر پیامی برای ارسال نداشت، کتابخانه خودش یک PINGREQ میفرستد. اگر بروکر در ۱.۵ برابر این مدت هیچ بستهای از کلاینت نگیرد، اتصال را مرده فرض میکند و میبندد.
- PubSubClient روی ESP32 و ESP8266: پیشفرض ۱۵ ثانیه، با
client.setKeepAlive(30)قابل تغییر - paho-mqtt در پایتون: آرگومان
keepaliveدرconnect، معمولاً ۶۰ - mqtt.js: گزینه
keepalive، پیشفرض ۶۰
عدد کوچکتر یعنی تشخیص سریعتر، ولی پینگ بیشتر و مصرف باتری بیشتر. برای بیشتر پروژهها ۳۰ تا ۶۰ ثانیه مناسب است. یادتان باشد اگر حلقه برنامه (مثلاً client.loop()) مدت طولانی اجرا نشود، پینگ به موقع نمیرود و بروکر اتصال سالم را هم قطع میکند.
Last Will: پیامی که بروکر به جای دستگاه میفرستد#
پیام وصیت را دستگاه هنگام اتصال به بروکر میدهد: یک تاپیک، یک متن، QoS و پرچم retain. بروکر آن را نگه میدارد و فقط وقتی منتشرش میکند که اتصال بدون بسته DISCONNECT تمام شود: قطع برق، قطع شبکه، تمام شدن Keep-Alive، یا بسته شدن اتصال از سمت بروکر.
دو نکته مهم در mqttcloud.ir:
- تاپیک پیام وصیت هم مثل بقیه تاپیکها باید با
نامکاربری/شروع شود. بروکر هنگام انتشار وصیت همان قاعده دسترسی را بررسی میکند و وصیتی که تاپیکش خارج از فضای نام شما باشد، بیصدا دور ریخته میشود. - اگر دستگاه خودش درست قطع شود (مثلاً
client.disconnect())، وصیت فرستاده نمیشود؛ چون بروکر میداند قطع عمدی بوده.
الگوی پیشنهادی: online و offline ماندگار#
رایجترین الگو این است که هر دستگاه یک تاپیک وضعیت داشته باشد، مثلاً alice/esp32-01/status:
- هنگام اتصال، وصیت را
offlineبا retain تنظیم کنید. - بلافاصله بعد از اتصال، خود دستگاه
onlineرا با retain منتشر کند. - قبل از قطع عمدی، خود دستگاه
offlineرا منتشر کند و بعد قطع شود.
چون هر دو پیام ماندگارند، هر برنامهای که بعداً روی alice/+/status مشترک شود، فوراً وضعیت فعلی همه دستگاهها را میگیرد. پیامهای ماندگار در حافظه بروکر نگه داشته میشوند و با راهاندازی مجدد بروکر پاک میشوند؛ دستگاهی که دوباره وصل شود، online را دوباره منتشر میکند، پس برنامهتان را طوری بنویسید که نبودن پیام وضعیت را «نامشخص» در نظر بگیرد. برای آشنایی بیشتر با retain مقاله تاپیکها، QoS و پیام ماندگار را ببینید.
ESP32 و ESP8266 با PubSubClient#
در PubSubClient وصیت در همان فراخوانی connect تنظیم میشود. بقیه کد مثل راهنمای ESP32 است:
const char* STATUS_TOPIC = "alice/esp32-01/status";
void connectMqtt() {
client.setKeepAlive(30);
// connect(id, user, pass, willTopic, willQos, willRetain, willMessage)
if (client.connect(CLIENT_ID, MQTT_USER, MQTT_PASS,
STATUS_TOPIC, 1, true, "offline")) {
client.publish(STATUS_TOPIC, "online", true); // retained
client.subscribe("alice/esp32-01/cmd");
}
}Node.js با mqtt.js#
import mqtt from "mqtt";
const USER = "alice";
const STATUS = `${USER}/node-01/status`;
const client = mqtt.connect("mqtts://mqtt.mqttcloud.ir:8883", {
clientId: "node-01",
username: USER,
password: process.env.MQTT_PASSWORD,
keepalive: 30,
will: { topic: STATUS, payload: "offline", qos: 1, retain: true },
});
client.on("connect", () => {
client.publish(STATUS, "online", { qos: 1, retain: true });
});
// Clean shutdown: no will is sent, so publish offline ourselves first.
process.on("SIGINT", () => {
client.publish(STATUS, "offline", { qos: 1, retain: true }, () => client.end());
});نمونه پایتون همین الگو با will_set در راهنمای رزبریپای آمده است.
سمت گیرنده و داشبورد#
برنامهای که وضعیت را نمایش میدهد کافی است روی alice/+/status مشترک شود؛ بخش دوم تاپیک شناسه دستگاه است. علاوه بر این، داشبورد mqttcloud.ir خودش وضعیت آنلاین هر دستگاه و زمان آخرین اتصال را نشان میدهد و صفحه «فعالیت» تاریخچه اتصال و قطعها را تا ۳۰ روز نگه میدارد؛ برای بررسی اینکه دستگاه چند بار در روز قطع شده، همانجا را ببینید.
اگر دستگاه مدام قطع و وصل میشود، مقاله عیبیابی اتصال MQTT را بخوانید.
