2. MQTT消息驱动的LED控制
任务目标
构建一个LVGL图形界面,包含一个开关(Switch)控件。当用户切换开关时,通过MQTT协议发布包含开关状态的JSON消息到指定主题,远程LED设备订阅该主题并执行开关动作。本案例聚焦于LVGL事件回调与MQTT消息发布的联动实现。
环境依赖
- LVGL v8.x(已初始化,显示器/触摸驱动可用)
- MQTT客户端库:
paho.mqtt.embedded-c或等效C语言MQTT库 - 网络协议栈(如lwIP)已就绪
核心实现
// mqtt_led_control.c
#include "lvgl/lvgl.h"
#include "MQTTClient.h"
// MQTT配置
#define BROKER_ADDR "192.168.1.100"
#define BROKER_PORT 1883
#define TOPIC_LED_CTRL "device/led/control"
#define CLIENT_ID "lvgl_ui_02"
static Network network;
static MQTTClient client;
static int led_state = 0; // 0: off, 1: on
// 发布LED状态到MQTT Broker
static void publish_led_state(int state) {
char payload[32];
int rc;
MQTTMessage msg;
snprintf(payload, sizeof(payload), "{\"led\":%d}", state);
msg.qos = 1;
msg.retained = 0;
msg.payload = payload;
msg.payloadlen = strlen(payload);
rc = MQTTClient_publish(&client, TOPIC_LED_CTRL, &msg);
if (rc != MQTT_SUCCESS) {
LV_LOG_ERROR("MQTT publish failed, rc=%d", rc);
} else {
LV_LOG_INFO("Published: %s", payload);
}
}
// LVGL开关事件回调
static void switch_event_cb(lv_event_t *e) {
lv_obj_t *sw = lv_event_get_target(e);
led_state = lv_obj_has_state(sw, LV_STATE_CHECKED) ? 1 : 0;
publish_led_state(led_state);
}
// MQTT连接(阻塞式,失败重试)
static void mqtt_connect(void) {
int rc;
Network_init(&network);
while (Network_connect(&network, BROKER_ADDR, BROKER_PORT) != 0) {
LV_LOG_WARN("MQTT broker unreachable, retry in 2s...");
vTaskDelay(pdMS_TO_TICKS(2000));
}
MQTTClientInit(&client, &network, 1000, (uint8_t *)malloc(128), 128,
(uint8_t *)malloc(128), 128);
MQTTString clientId = MQTTString_initializer;
clientId.cstring = CLIENT_ID;
rc = MQTTClient_connect(&client, clientId, NULL, NULL, 0, NULL, NULL, 0);
if (rc != MQTT_SUCCESS) {
LV_LOG_ERROR("MQTT connect failed, rc=%d", rc);
}
}
// UI创建:开关控件
static void ui_create(lv_obj_t *parent) {
lv_obj_t *sw = lv_switch_create(parent);
lv_obj_set_size(sw, 80, 40);
lv_obj_center(sw);
lv_obj_add_event_cb(sw, switch_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
}
// 主任务入口(由RTOS调用)
void mqtt_led_ui_task(void *arg) {
lv_obj_t *scr = lv_scr_act();
mqtt_connect();
ui_create(scr);
while (1) {
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(5));
}
}
运行与验证
- 编译:将上述代码与LVGL、MQTT库链接,确保网络接口已初始化。
- 运行:启动设备,程序会阻塞重试直至连接上Broker(
192.168.1.100:1883)。 - 验证步骤:
- 在PC上启动MQTT订阅工具(如mosquitto_sub):
mosquitto_sub -h 192.168.1.100 -t "device/led/control" - 在LVGL界面上点击开关,观察订阅终端是否收到JSON消息。
- 通过条件:每次切换开关,终端收到且仅收到一条形如
{"led":1}或{"led":0}的消息,且数值与开关状态一致。
- 在PC上启动MQTT订阅工具(如mosquitto_sub):
关键说明
- 事件回调使用
LV_EVENT_VALUE_CHANGED,仅在开关状态实际变化时触发,避免重复发布。 - MQTT QoS设为1,保证消息至少送达一次;若需严格去重可改用QoS 2。
- 远程LED端需订阅
device/led/control并解析JSON中的led字段控制GPIO。 - 本示例使用阻塞式连接,实际产品建议改为非阻塞+状态机方式。