MQTT 智能家居通信
MQTT 智能家居通信
本节介绍智能家居面板的 MQTT 通信设计和实现。学习完成后,您将能够:
- 设计智能家居系统的 MQTT Topic 结构
- 实现面板状态和传感器数据的 MQTT 发布
- 处理 MQTT 远程控制指令并更新 UI
- 实现 WiFi 断线重连和状态同步
在开始本节之前,请确保:
- 已掌握 MQTT 协议基础
- 已完成 LVGL 基础配置
- 已完成传感器数据采集配置
- Mosquitto/EMQX Broker 已运行
MQTT Topic Design
Section titled “MQTT Topic Design”Smart Home Topic Tree
Section titled “Smart Home Topic Tree”home/ # 根主题├── panel/{device_id}/ # 面板相关│ ├── status # 面板状态│ ├── sensors # 传感器数据│ ├── screen # 屏幕控制│ ├── command # 面板命令│ └── display # 显示内容控制├── sensor/{room}/ # 传感器数据│ ├── temperature # 温度│ ├── humidity # 湿度│ ├── light # 光照│ └── motion # 运动检测├── light/{room}/ # 灯光控制│ ├── set # 设置灯光状态│ ├── brightness # 设置亮度│ ├── color_temp # 设置色温│ └── status # 灯光状态报告├── climate/{room}/ # 环境控制│ ├── temperature_set # 温度设定│ ├── mode # 模式│ └── status # 状态报告├── scene/{room}/ # 场景控制│ └── trigger # 触发场景├── alarm/{zone}/ # 安防告警│ └── sensor # 传感器告警└── system/ # 系统消息 ├── status # 系统状态 └── ota # OTA 更新通知Topic Design Best Practices
Section titled “Topic Design Best Practices”| 原则 | 说明 | 示例 |
|---|---|---|
| 层级清晰 | 按功能/房间/设备分类 | home/light/livingroom/set |
| 命令分离 | 控制命令和状态上报分开 | set 和 status 不同主题 |
| 房间隔离 | 每个房间有独立的主题层级 | home/light/livingroom/ vs home/light/bedroom/ |
| 设备标识 | 使用 device_id 区分设备 | home/panel/panel_01/status |
MQTT Communication Implementation
Section titled “MQTT Communication Implementation”Complete MQTT Integration
Section titled “Complete MQTT Integration”#include <WiFi.h>#include <PubSubClient.h>
// MQTT 配置const char* mqttServer = "192.168.1.100";const int mqttPort = 1883;const char* mqttUser = "node_red";const char* mqttPassword = "node_red";const char* deviceID = "panel_livingroom_01";
WiFiClient wifiClient;PubSubClient mqttClient(wifiClient);
// Topic 定义const char* topicStatus = "home/panel/livingroom_01/status";const char* topicSensors = "home/panel/livingroom_01/sensors";const char* topicScreen = "home/panel/livingroom_01/screen";const char* topicCommand = "home/panel/livingroom_01/command";
// 订阅主题列表const char* subscribeTopics[] = { "home/light/livingroom/status", "home/climate/livingroom/status", "home/scene/livingroom/trigger", topicScreen, topicCommand};
// MQTT 回调处理void mqttCallback(char* topic, byte* payload, unsigned int length) { String message; for (unsigned int i = 0; i < length; i++) { message += (char)payload[i]; }
Serial.printf("MQTT 接收: [%s] %s\n", topic, message.c_str());
// 灯光状态更新 if (strcmp(topic, "home/light/livingroom/status") == 0) { StaticJsonDocument<128> doc; deserializeJson(doc, message);
bool lightOn = doc["state"] == "ON"; int brightness = doc["brightness"] | 100;
// 更新 LVGL UI update_light_ui(lightOn, brightness); } // 场景触发 else if (strcmp(topic, "home/scene/livingroom/trigger") == 0) { update_scene_ui(message.c_str()); } // 屏幕控制 else if (strcmp(topic, topicScreen) == 0) { if (message == "sleep") { lv_disp_set_brightness(NULL, 0); // 熄屏 } else if (message == "wake") { lv_disp_set_brightness(NULL, 255); // 点亮 } } // 面板命令 else if (strcmp(topic, topicCommand) == 0) { handle_panel_command(message.c_str()); }}
// 连接 MQTT Brokervoid connectMQTT() { while (!mqttClient.connected()) { Serial.print("MQTT 连接中...");
if (mqttClient.connect(deviceID, mqttUser, mqttPassword)) { Serial.println("已连接");
// 订阅所有主题 for (const char* subTopic : subscribeTopics) { mqttClient.subscribe(subTopic); Serial.printf("已订阅: %s\n", subTopic); }
// 发布上线消息 mqttClient.publish(topicStatus, "{\"state\":\"online\",\"device\":\"panel_livingroom_01\"}"); } else { Serial.printf("连接失败 (rc=%d),5秒后重试\n", mqttClient.state()); delay(5000); } }}UI Update via MQTT
Section titled “UI Update via MQTT”MQTT-Driven UI Updates
Section titled “MQTT-Driven UI Updates”// 灯光 UI 更新void update_light_ui(bool isOn, int brightness) { // 更新灯光图标 lv_obj_t* lightIcon = lv_obj_get_child(lightCard, 0); if (isOn) { lv_label_set_text(lightIcon, "💡"); lv_obj_set_style_bg_color(lightCard, lv_color_hex(0xFFD600), 0); } else { lv_label_set_text(lightIcon, "🔅"); lv_obj_set_style_bg_color(lightCard, lv_color_hex(0x333333), 0); }
// 更新亮度标签 char buf[16]; sprintf(buf, "亮度 %d%%", brightness); lv_obj_t* brightnessLabel = lv_obj_get_child(lightCard, 1); lv_label_set_text(brightnessLabel, buf);
// 更新亮度滑块(但不触发值变化事件) lv_obj_t* slider = lv_obj_get_child(lightCard, 2); lv_slider_set_value(slider, brightness, LV_ANIM_ON);}
// 场景 UI 更新void update_scene_ui(const char* sceneName) { if (strcmp(sceneName, "离家") == 0) { lv_label_set_text(sceneIndicator, "🏃 离家模式"); lv_obj_set_style_bg_color(sceneIndicator, lv_palette_main(LV_PALETTE_GREY), 0); } else if (strcmp(sceneName, "回家") == 0) { lv_label_set_text(sceneIndicator, "🏠 回家模式"); lv_obj_set_style_bg_color(sceneIndicator, lv_palette_main(LV_PALETTE_GREEN), 0); } else if (strcmp(sceneName, "睡眠") == 0) { lv_label_set_text(sceneIndicator, "🌙 睡眠模式"); lv_obj_set_style_bg_color(sceneIndicator, lv_palette_main(LV_PALETTE_INDIGO), 0); }}Touch Commands to MQTT
Section titled “Touch Commands to MQTT”Button to MQTT
Section titled “Button to MQTT”// 灯光按钮点击 → MQTT 命令void light_btn_cb(lv_event_t* e) { static bool lightState = false; lightState = !lightState;
// 构建 MQTT 消息 StaticJsonDocument<64> doc; doc["state"] = lightState ? "ON" : "OFF";
char buffer[64]; serializeJson(doc, buffer);
mqttClient.publish("home/light/livingroom/set", buffer);}
// 亮度滑块 → MQTT 命令void brightness_slider_cb(lv_event_t* e) { lv_obj_t* slider = lv_event_get_target(e); int value = lv_slider_get_value(slider);
StaticJsonDocument<64> doc; doc["brightness"] = value;
char buffer[64]; serializeJson(doc, buffer); mqttClient.publish("home/light/livingroom/brightness/set", buffer);}Connection Monitoring
Section titled “Connection Monitoring”WiFi and MQTT Status Display
Section titled “WiFi and MQTT Status Display”// 连接状态指示器lv_obj_t* wifiIndicator;lv_obj_t* mqttIndicator;
void create_connection_indicators() { // WiFi 状态文字 wifiIndicator = lv_label_create(screen); lv_obj_align(wifiIndicator, LV_ALIGN_TOP_RIGHT, -10, 10);
// MQTT 状态文字 mqttIndicator = lv_label_create(screen); lv_obj_align(mqttIndicator, LV_ALIGN_TOP_RIGHT, -10, 30);}
void update_connection_indicators() { // 更新 WiFi 状态 if (WiFi.status() == WL_CONNECTED) { lv_label_set_text(wifiIndicator, "📶 WiFi 已连接"); lv_obj_set_style_text_color(wifiIndicator, lv_palette_main(LV_PALETTE_GREEN), 0); } else { lv_label_set_text(wifiIndicator, "📶 WiFi 断开"); lv_obj_set_style_text_color(wifiIndicator, lv_palette_main(LV_PALETTE_RED), 0); }
// 更新 MQTT 状态 if (mqttClient.connected()) { lv_label_set_text(mqttIndicator, "🔗 MQTT 已连接"); lv_obj_set_style_text_color(mqttIndicator, lv_palette_main(LV_PALETTE_GREEN), 0); } else { lv_label_set_text(mqttIndicator, "🔗 MQTT 断开"); lv_obj_set_style_text_color(mqttIndicator, lv_palette_main(LV_PALETTE_RED), 0); }}Main Loop Integration
Section titled “Main Loop Integration”Complete Main Loop
Section titled “Complete Main Loop”void setup() { Serial.begin(115200);
// 连接 WiFi WiFi.begin("SSID", "PASSWORD"); while (WiFi.status() != WL_CONNECTED) { delay(1000); }
// 初始化 LVGL 和屏幕 lv_init(); init_display(); init_touch(); create_ui();
// MQTT 配置 mqttClient.setServer(mqttServer, mqttPort); mqttClient.setCallback(mqttCallback); connectMQTT();}
void loop() { // MQTT 保活 if (!mqttClient.connected()) { connectMQTT(); } mqttClient.loop();
// LVGL 任务处理 lv_timer_handler();
// 状态更新 static unsigned long lastStatusUpdate = 0; if (millis() - lastStatusUpdate > 1000) { lastStatusUpdate = millis(); update_connection_indicators(); }
delay(5);}问题 1: MQTT 消息丢失
Section titled “问题 1: MQTT 消息丢失”原因: QoS 等级过低或网络不稳定
解决方案:
// 对关键控制命令使用 QoS 1mqttClient.publish("home/light/livingroom/set", payload, true); // retained = true
// 订阅时指定 QoS 1mqttClient.subscribe("home/light/livingroom/status", 1);问题 2: UI 更新不及时
Section titled “问题 2: UI 更新不及时”原因: LVGL 定时器处理间隔过长
解决方案: 使用定时器实现周期性 UI 刷新
// LVGL 定时器lv_timer_t* uiRefreshTimer = lv_timer_create(ui_refresh_cb, 100, NULL);
void ui_refresh_cb(lv_timer_t* timer) { // 更新传感器数据 update_sensor_display();}Summary
Section titled “Summary”本节介绍了智能家居面板的 MQTT 通信:
- Topic 设计:分层的智能家居 Topic 结构(房间/设备/功能)
- MQTT 集成:面板状态发布、远程命令订阅和 UI 更新
- 触摸到 MQTT:触摸操作通过 MQTT 控制智能家居设备
- 状态同步:WiFi/MQTT 连接状态实时显示
- UI 数据绑定:MQTT 消息驱动 LVGL UI 更新