出生与死亡消息
出生与死亡消息
Section titled “出生与死亡消息”本节介绍 MQTT 的出生消息(Birth Message)和死亡消息(Death Message)机制。学习完成后,您将能够:
- 理解出生消息和死亡消息的概念和用途
- 在 ESP32 客户端中配置出生消息
- 在 Node-RED 中处理设备上下线事件
- 构建设备在线状态监控系统
在开始本节之前,请确保:
- 理解保留消息机制
- 理解遗嘱消息
- Mosquitto Broker 已运行
Birth Message
Section titled “Birth Message”What is a Birth Message?
Section titled “What is a Birth Message?”出生消息是客户端成功连接到 Broker 后立即发布的消息,用于宣告设备上线。
特点:
- 在 CONNECT 成功并收到 CONNACK 后发布
- 通常使用保留消息,以便新订阅者获取
- 包含设备标识、状态、能力信息
Client (ESP32) Broker │ │ │─── CONNECT ──────────────────→│ │←── CONNACK (Accepted) ────────│ │ │ │─── PUBLISH (Birth Message) ──→│ │ Topic: devices/esp32_001 │ │ Payload: {"status":"online"}│ │ Retain: true │ │ │ │ [客户端进入正常通信状态] │Death Message
Section titled “Death Message”What is a Death Message?
Section titled “What is a Death Message?”死亡消息是客户端意外断开连接时,由 Broker 代为发布的消息。
特点:
- 在 CONNECT 时注册(作为 Last Will)
- 在客户端意外断开时由 Broker 触发
- 通常清除或更新保留的在线状态
Client (ESP32) Broker │ │ │─── 正常通信 ─────────────────→│ │─── ... ─────────────────────→│ │ │ │ [突然断电/断网] │ │✗ │ │ │ │ [Broker 检测到连接超时] │ │ │ │ [Broker 代为发布死亡消息] │ │ │ │ ┌─────────────────┤ │ │ Topic: devices/ │ │ │ esp32_001 │ │ │ Payload: │ │ │ {"status": │ │ │ "offline"} │ │ │ Retain: true │ │ └─────────────────┤ │ │ │ [订阅者收到死亡消息]│Implementation
Section titled “Implementation”ESP32: Birth + Death Message
Section titled “ESP32: Birth + Death Message”#include <WiFi.h>#include <PubSubClient.h>
WiFiClient espClient;PubSubClient mqttClient(espClient);
#define DEVICE_ID "esp32_001"#define STATUS_TOPIC "devices/esp32_001/status"#define BIRTH_TOPIC "devices/esp32_001/birth"
void connectMQTT() { // 配置 MQTT 服务器 mqttClient.setServer(MQTT_BROKER, 1883); mqttClient.setCallback(mqttCallback);
// 准备遗嘱消息(死亡消息) // Last Will: 设备意外断开时 Broker 发布离线状态 const char* willTopic = STATUS_TOPIC; const char* willPayload = "{\"status\":\"offline\",\"device\":\"" DEVICE_ID "\"}"; int willQos = 1; bool willRetain = true;
// 尝试连接(注册 Last Will) if (mqttClient.connect( DEVICE_ID, // Client ID "", // Username "", // Password willTopic, // Will Topic willQos, // Will QoS willRetain, // Will Retain willPayload // Will Message )) { Serial.println("MQTT Connected");
// 发布出生消息(设备上线) String birthPayload = "{\"status\":\"online\",\"device\":\"" DEVICE_ID "\",\"ip\":\"" + WiFi.localIP().toString() + "\"}";
mqttClient.publish( STATUS_TOPIC, birthPayload.c_str(), true // retain );
// 也可以发布到单独的 birth Topic mqttClient.publish( BIRTH_TOPIC, "{\"event\":\"birth\",\"timestamp\":" + String(millis()) + "}", false ); }}
void loop() { if (!mqttClient.connected()) { connectMQTT(); } mqttClient.loop();}Node-RED: Process Birth/Death Events
Section titled “Node-RED: Process Birth/Death Events”// Node-RED Function 节点:处理设备状态变化
// 订阅所有设备的状态 Topic// Topic: devices/+/status
var topic = msg.topic;var payload = msg.payload;
// 提取设备 IDvar topicParts = topic.split('/');var deviceId = topicParts[1]; // devices/{deviceId}/status
// 解析状态var status = payload.status;var eventType = "unknown";
if (status === "online") { eventType = "device_online"; node.log("Device ONLINE: " + deviceId);} else if (status === "offline") { eventType = "device_offline"; node.warn("Device OFFLINE: " + deviceId);}
// 添加事件信息msg.event = { type: eventType, device_id: deviceId, timestamp: Date.now(), ip: payload.ip || "unknown"};
// 更新设备状态(Flow 上下文)var devices = flow.get("device_status") || {};devices[deviceId] = { status: status, last_seen: Date.now(), ip: payload.ip || "unknown"};flow.set("device_status", devices);
return msg;Use Cases
Section titled “Use Cases”1. Dashboard 设备在线状态
Section titled “1. Dashboard 设备在线状态”// 在 Node-RED 中查询所有设备状态// 通过 HTTP Endpoint 提供给前端
// HTTP GET /api/devices/statusvar devices = flow.get("device_status") || {};msg.payload = { total: Object.keys(devices).length, online: Object.values(devices).filter(d => d.status === "online").length, offline: Object.values(devices).filter(d => d.status === "offline").length, devices: devices};return msg;2. 设备离线告警
Section titled “2. 设备离线告警”// 当重要设备离线时触发告警// 接在设备状态变化节点之后
var deviceId = msg.event.device_id;var criticalDevices = ["esp32_camera_001", "esp32_sensor_main"];
if (msg.event.type === "device_offline") { if (criticalDevices.includes(deviceId)) { // 发送告警 msg.alert = { severity: "critical", message: "关键设备离线: " + deviceId, timestamp: Date.now() }; return msg; }}return null; // 非关键设备离线,不触发告警验证 Birth/Death 行为
Section titled “验证 Birth/Death 行为”# 终端 1: 订阅所有设备状态mosquitto_sub -h localhost -t "devices/+/status" -v
# 终端 2: 启动 ESP32 设备# 预期看到:# devices/esp32_001/status {"status":"online","device":"esp32_001"}
# 断开设备电源或网络# 预期看到(有延迟):# devices/esp32_001/status {"status":"offline","device":"esp32_001"}- ✅ 推荐: Birth 消息使用 Retain=true,方便新订阅者获取
- ✅ 推荐: Birth 消息包含设备 IP、固件版本等能力信息
- ✅ 推荐: Death 消息使用 Retain=true 覆盖在线状态
- ✅ 推荐: 在 Node-RED 中维护设备状态缓存
- ❌ 避免: Birth/Death Topic 与数据 Topic 混用
- ❌ 避免: 完全依赖 Death 消息(非 100% 可靠)
- ❌ 避免: Birth 消息中包含敏感信息
Summary
Section titled “Summary”本节要点总结:
- 出生消息:设备连接时主动宣告上线
- 死亡消息:通过 Last Will 在设备异常断开时宣告离线
- 实现方式:CONNECT 时注册遗嘱,连接成功后发布出生消息
- 状态管理:使用保留消息确保新订阅者获取最新状态
- 业务应用:设备在线仪表板、离线告警、设备生命周期管理