跳转到内容

MQTT 智能家居通信

MQTT 智能家居通信

本节介绍智能家居面板的 MQTT 通信设计和实现。学习完成后,您将能够:

  • 设计智能家居系统的 MQTT Topic 结构
  • 实现面板状态和传感器数据的 MQTT 发布
  • 处理 MQTT 远程控制指令并更新 UI
  • 实现 WiFi 断线重连和状态同步

在开始本节之前,请确保:

  • 已掌握 MQTT 协议基础
  • 已完成 LVGL 基础配置
  • 已完成传感器数据采集配置
  • Mosquitto/EMQX Broker 已运行
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 更新通知
原则说明示例
层级清晰按功能/房间/设备分类home/light/livingroom/set
命令分离控制命令和状态上报分开setstatus 不同主题
房间隔离每个房间有独立的主题层级home/light/livingroom/ vs home/light/bedroom/
设备标识使用 device_id 区分设备home/panel/panel_01/status
#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 Broker
void 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 更新
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);
}
}
// 灯光按钮点击 → 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);
}
// 连接状态指示器
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);
}
}
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);
}

原因: QoS 等级过低或网络不稳定

解决方案:

// 对关键控制命令使用 QoS 1
mqttClient.publish("home/light/livingroom/set", payload, true); // retained = true
// 订阅时指定 QoS 1
mqttClient.subscribe("home/light/livingroom/status", 1);

原因: 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();
}

本节介绍了智能家居面板的 MQTT 通信:

  1. Topic 设计:分层的智能家居 Topic 结构(房间/设备/功能)
  2. MQTT 集成:面板状态发布、远程命令订阅和 UI 更新
  3. 触摸到 MQTT:触摸操作通过 MQTT 控制智能家居设备
  4. 状态同步:WiFi/MQTT 连接状态实时显示
  5. UI 数据绑定:MQTT 消息驱动 LVGL UI 更新