在嵌入式物联网项目中,将设备数据可视化并实现远程控制是一个常见需求。使用Home Assistant(HA)作为智能家居中枢,配合MQTT协议实现设备接入,是目前最流行的解决方案之一。本文将详细介绍如何基于NetX Duo协议栈,在STM32平台上实现MQTT通信,并利用HA的自动发现功能快速集成设备。
这个方案的核心价值在于:
我们选用STM32F746G-DISCO开发板作为硬件平台,主要考虑以下因素:
提示:如果使用其他STM32系列开发板,需要确认以太网外设是否可用,并相应调整PHY驱动。
在STM32CubeMX中需要启用以下组件:
配置步骤:
MQTT主题设计遵循HA的自动发现规范:
c复制/* 状态上报主题 */
#define FAN_STATE_TOPIC "device/ap_fan/status"
/* 自动发现配置主题 */
#define HA_DISC_BASE "homeassistant/sensor/fan_node"
#define HA_DISC_TEMP1_TOPIC HA_DISC_BASE "/temp_1/config"
#define HA_DISC_TEMP2_TOPIC HA_DISC_BASE "/temp_2/config"
#define HA_DISC_RPM_TOPIC HA_DISC_BASE "/fan_rpm/config"
主题设计原则:
创建MQTT客户端的主要步骤:
c复制status = nxd_mqtt_client_create(&mqtt_client, "F746_Gateway",
MQTT_CLIENT_ID_STRING, strlen(MQTT_CLIENT_ID_STRING),
&client_ip, &client_pool,
(VOID*)mqtt_client_stack, sizeof(mqtt_client_stack),
MQTT_THREAD_PRIORITY, NULL, 0);
/* 设置认证信息 */
status = nxd_mqtt_client_login_set(&mqtt_client,
MQTT_USERNAME, strlen(MQTT_USERNAME),
MQTT_PASSPORT, strlen(MQTT_PASSPORT));
关键参数说明:
HA自动发现机制允许设备通过MQTT主动注册自己。设备需要向特定的配置主题发送JSON格式的设备描述信息,包含:
c复制void HA_Discovery_Init(void) {
char config_payload[1024];
const char* device_info = ",\"dev\":{\"ids\":[\"ap_fan_01\"],\"name\":\"AP风扇控制器\",\"mdl\":\"STM32F746-G0\",\"mf\":\"DIY\"}";
/* 温度传感器1 */
snprintf(config_payload, sizeof(config_payload),
"{\"name\":\"温度1\",\"stat_t\":\"" FAN_STATE_TOPIC "\",\"val_tpl\":\"{{value_json.t_fan}}\",\"unit_of_meas\":\"°C\",\"dev_cla\":\"temperature\",\"unique_id\":\"fan_t1\"%s}", device_info);
nxd_mqtt_client_publish(&mqtt_client, HA_DISC_TEMP1_TOPIC, strlen(HA_DISC_TEMP1_TOPIC),
config_payload, strlen(config_payload), NX_TRUE, 1, NX_WAIT_FOREVER);
/* 温度传感器2 */
snprintf(config_payload, sizeof(config_payload),
"{\"name\":\"温度2\",\"stat_t\":\"" FAN_STATE_TOPIC "\",\"val_tpl\":\"{{value_json.t_env}}\",\"unit_of_meas\":\"°C\",\"dev_cla\":\"temperature\",\"unique_id\":\"fan_t2\"%s}", device_info);
nxd_mqtt_client_publish(&mqtt_client, HA_DISC_TEMP2_TOPIC, strlen(HA_DISC_TEMP2_TOPIC),
config_payload, strlen(config_payload), NX_TRUE, 1, NX_WAIT_FOREVER);
/* 风扇转速 */
snprintf(config_payload, sizeof(config_payload),
"{\"name\":\"转速\",\"stat_t\":\"" FAN_STATE_TOPIC "\",\"val_tpl\":\"{{value_json.rpm}}\",\"unit_of_meas\":\"RPM\",\"unique_id\":\"fan_rpm\"%s}", device_info);
nxd_mqtt_client_publish(&mqtt_client, HA_DISC_RPM_TOPIC, strlen(HA_DISC_RPM_TOPIC),
config_payload, strlen(config_payload), NX_TRUE, 1, NX_WAIT_FOREVER);
}
关键字段说明:
stat_t:状态主题,HA从这个主题读取实时数据val_tpl:值提取模板,使用Jinja2语法dev_cla:设备类别,影响HA的UI展示unique_id:必须全局唯一数据上报采用JSON格式,包含三个关键指标:
c复制snprintf(mqtt_payload, sizeof(mqtt_payload),
"{\"t_fan\":%ld.%01u,\"t_env\":%ld.%01u,\"rpm\":%u}",
t1 / 10, (unsigned int)(t1 < 0 ? -t1 % 10 : t1 % 10),
t2 / 10, (unsigned int)(t2 < 0 ? -t2 % 10 : t2 % 10),
(unsigned int)g_fan_data.rpm);
设计考虑:
采用事件驱动方式上报数据:
c复制while(1) {
/* 等待新数据信号 */
if (tx_semaphore_get(&sem_new_fan_data, TX_WAIT_FOREVER) == TX_SUCCESS) {
/* 构造并发布JSON负载 */
status = nxd_mqtt_client_publish(&mqtt_client, FAN_STATE_TOPIC,
strlen(FAN_STATE_TOPIC), mqtt_payload, strlen(mqtt_payload),
NX_FALSE, 1, 100);
if (status != NX_SUCCESS) break; // 断线重连
}
}
MQTT连接需要处理网络波动:
c复制while(1) {
/* 尝试连接Broker */
status = nxd_mqtt_client_connect(&mqtt_client, &server_ip, MQTT_PORT, 60, NX_TRUE, NX_WAIT_FOREVER);
if (status == NX_SUCCESS) {
HA_Discovery_Init(); // 重连后重新注册
/* 数据上报循环 */
while(1) {
// ...数据上报逻辑...
if (status != NX_SUCCESS) break; // 内层循环退出触发重连
}
}
tx_thread_sleep(500); // 重连间隔
}
HA无法发现设备
数据更新不及时
内存不足
除了数据上报,可以实现HA到设备的控制:
通过MQTT实现固件更新:
对于多设备场景:
在实际部署中发现,保持MQTT连接稳定性的关键在于合理设置心跳间隔和重连策略。对于工业环境,建议将心跳间隔设置为60-120秒,并采用指数退避算法进行重连。此外,JSON模板中的值提取表达式需要特别注意数据类型匹配,否则可能导致HA无法正确解析数据。