NetX Duo MQTT 接入 Home Assistant:设备数据上报与自动发现

在嵌入式项目中,将设备接入 Home Assistant(HA)实现可视化与自动化控制,是非常常见的需求。
本文基于 NetX Duo + MQTT 协议,实现一个风扇控制器的数据上报,并通过 HA 自动发现(Discovery)机制,自动生成传感器实体。

设备通过 MQTT 向 HA 上报:

  • 环境温度1
  • 环境温度2
  • 风扇转速(rpm)

实验硬件:

  • STM32F746G-DISCO 开发板

1. 核心原理: MQTT自动发现

HA 自动发现的核心在于配置主题 (Config Topic)。设备启动时,需向特定格式的主题发送 JSON 配置信息,告知 HA:

  • 设备的名称和唯一 ID
  • 数据上报的 状态主题 (State Topic)
  • 如何从状态 JSON 中提取具体数值(JSON Template)

Topic 格式规范:

homeassistant/<component>/<node_id>/<object_id>/config

2. 宏定义与主题设计

首先定义 MQTT 相关的 Topic。我们将传感器归类在 fan_node 节点下。

/* --- MQTT Topic 宏定义 --- */
/* 状态上报主题:HA通过这个主题获取实时数据 */
#define         FAN_STATE_TOPIC         "device/ap_fan/status"

/* HA 自动发现配置主题:HA通过这些主题自动创建实体 */
#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"

3. 实现自动发现:HA_Discovery_Init

此函数在 MQTT 连接成功后调用一次。它定义了设备的基本信息(型号、厂家),并将多个传感器绑定到同一个“设备”下。

void HA_Discovery_Init(void) {
    char config_payload[1024]; // 增加缓冲区大小以容纳设备信息

    // 定义共同的设备信息 JSON 字符串
    // identifiers 是唯一的 ID(建议用 MAC 地址或固定字符串)
    const char* device_info = ",\"dev\":{\"ids\":[\"ap_fan_01\"],\"name\":\"AP风扇控制器\",\"mdl\":\"STM32F746-G0\",\"mf\":\"DIY\"}";

    // 1. 注册温度计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. 注册温度计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);
`
    // 3. 注册转速表
    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:指定如何从状态 JSON 中提取数值。这里我们使用了 value_json 变量来解析 JSON 字符串。
  • device_info:将多个传感器绑定到同一个设备,方便 HA 进行管理和展示。

3. 添加NetxDuo与MQTT组件

  • 添加 NetX Duo 协议栈

可通过 STM32CubeMX 直接集成 NetX Duo Firmware。具体操作步骤可参考如下文档:

添加 NetXDuo支持 - STM32F779I-EVAL

  • 启用 MQTT 功能组件

在 Software Packs → Components 中,勾选 MQTT Add-on,即可启用 MQTT 协议支持。

4. MQTT 线程入口:连接并上报数据

在主循环中,我们构造紧凑的 JSON 负载进行上报。通过信号量驱动,确保数据更新时才触发发布。

/* --- MQTT 线程入口:连接并上报数据 --- */
void mqtt_app_thread_entry(ULONG thread_input) {
    UINT status;
    NXD_ADDRESS server_ip;
    char mqtt_payload[128];

    server_ip.nxd_ip_version = NX_IP_VERSION_V4;
    server_ip.nxd_ip_address.v4 = MQTT_SERVER_ADDRESS;

    /* 1. 创建 MQTT 客户端 */
    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));

    int32_t t1, t2;

    while(1) {
        /* 2. 连接 MQTT 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) {
                /* 3. 等待新数据通知 */
                if (tx_semaphore_get(&sem_new_fan_data, TX_WAIT_FOREVER) == TX_SUCCESS) {
                    // 构造 JSON 负载
                	t1 = g_fan_data.temp_fan;
                	t2 = g_fan_data.temp_env;

                	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);

                    // 发布实时数据到状态主题
                    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; // 发布失败(断线),退出内循环重连
                }
            }
        }
        tx_thread_sleep(500); // 断线重连等待
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值