Funpack第二季第六期:Home Assistant远程读取与控制nRF7002DK开发板
Home Assistant远程读取与控制nRF7002开发板,开发板与Home Assistant服务器以MQTT协议进行wifi通信,传输开关状态与LED开关指令。
标签
Funpack活动
nRF7002-DK
HomeAssistant
氢化脱氯次氯酸
更新2023-10-10
中国科学技术大学
369

1 nRF7002-DK 简介

nRF7002-DK是用于nRF7002 Wi-Fi 6协同IC的开发套件,该开发套件采用nRF5340多协议片上系统 (SoC) 作为nRF7002的主处理器,在单一的电路板上包含了开发工作所需的一切,可让开发人员轻松开启基于nRF7002 的物联网项目。该 DK 包括 Arduino 连接器、两个可编程按钮、一个 Wi-Fi 双频段天线和一个低功耗蓝牙天线,以及电流测量引脚。

这款DK支持低功耗 Wi-Fi 应用开发,并实现了多项 Wi-Fi 6 功能,比如 OFDMA、波束成型和 TWT。nRF7002 Wi-Fi 6配套IC为另一个主机添加了低功耗Wi-Fi 6功能,提供无缝连接和基于Wi-Fi的定位(本地Wi-Fi集线器的SSID嗅探)功能。该IC设计用于搭配Nordic现有的nRF52®和nRF53®系列多协议片上系统 (SoC) 和nRF91®系列蜂窝物联网系统级封装 (SiP) 使用。nRF7002 IC 还可与非nordic主机器件搭配使用。通过SPI或QSPI与主机通信,并带有额外的共存功能,可与其他协议如蓝牙、Thread或Zigbee无缝共存。

FgO2f5FlQn_Q4vnbnlABK9yKj8sA

图1.1 外观图

2 任务及实现方案

2.1 本期任务

任务2:使用WiFi连接功能,连接网络,并实现远程控制板卡LED和读取按键信息

2.2 实现思路

结合智能家居平台Home Assistant,开发板与Home Assistant服务器以MQTT协议进行wifi通信,传输开关状态与LED开关指令。

2.3 功能展示

开发板通电后自动通过wifi与Home Assistant服务器进行通信。图2.1为Home Assistant界面,界面上显示了开发板上当前按键状态与LED控制开关。按下开发板上的key1,可以观察到key1状态变为开启。在Home Assistant控制面板中点击led开关,可以对开发板上的led进行开关操作。

图形用户界面, 文本, 应用程序  描述已自动生成

图2.1 Home Assistant界面

aAEIkFgF6ItsNltXV7d27dqoWn3qEtSI6LXZ3Ny8YsWK1tZWpQoQOrEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAzRGrAAAA1ByxCgAAQM0RqwAAANQcsQoAAEDNEasAAADUHLEKAABAjeno+P8BmzdmEXXheoQAAAAASUVORK5CYII=

图2.2 按下开关1

图形用户界面, 文本, 应用程序  描述已自动生成

图2.3 点亮LED1

FgsMyyEbkgcoEzbtP6OO0L-p06eC

图2.4 板载LED1亮起

3 实现过程

3.1 总览

图3.1为本项目的工作流程图,其中红色部分为用户操作,绿色部分为另一端的结果反馈,nRF7002和Home Assistant服务器通过MQTT协议进行通信。

图示  描述已自动生成

图3.1 工作流程图(以key1和led1为例)

3.2 MQTT通信

MQTT是基于TCP/IP协议栈构建的异步通信消息协议,是一种轻量级的发布、订阅信息传输协议,在物联网领域应用广泛。

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(topic)和负载(payload)两部分:

(1)topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);

(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

本项目中,nRF7002作为客户端,需要订阅Home Assistant发来的LED控制指令,同时发布板载的按键状态信息。

nRF7002部分的代码主要参考了https://github.com/AliNordic/mqtt_over_wifi_nrf7002DK,部分重要代码分析如下:

配置mqtt的topic与服务器的IP地址与端口号(prj.conf文件):

# Application
CONFIG_MQTT_PUB_TOPIC1="homeassistant/binary_sensor/key1"
CONFIG_MQTT_PUB_TOPIC2="homeassistant/binary_sensor/key2"
CONFIG_MQTT_SUB_TOPIC="homeassistant/button/led"
#Note If you notice that the test.mosquitto.org is unresponsive,
#there are several other public MQTT brokers that you can use (Ex: broker.hivemq.com at port 1883)
CONFIG_MQTT_BROKER_HOSTNAME="192.168.1.254"

CONFIG_MQTT_CLIENT_ID="nRF7002DK"
CONFIG_MQTT_BROKER_PORT=1883

初始化mqtt客户端,这里需要注意的是原代码并没有设置mqtt的登录用户名和密码,如果与Home Assistant连接需要手动配置,代码如下(mqtt_connection.c文件client_init()函数):

struct mqtt_utf8 pass, user_name;

/**@brief Initialize the MQTT client structure
 */
int client_init(struct mqtt_client *client)
{
    int err;
    /* Initializes the client instance. */
    mqtt_client_init(client);

    /* Resolves the configured hostname and initializes the MQTT broker structure */
    err = broker_init();
    if (err) {
        LOG_ERR("Failed to initialize broker connection");
        return err;
    }

    pass.size = strlen("password");
    pass.utf8 = (uint8_t *)"password";
    user_name.size = strlen("mqtt");
    user_name.utf8 = (uint8_t *)"mqtt"; 

    /* MQTT client configuration */
    client->broker = &broker;
    client->evt_cb = mqtt_evt_handler;
    client->client_id.utf8 = client_id_get();
    client->client_id.size = strlen(client->client_id.utf8);
    client->password = &pass;
    client->user_name = &user_name;
    client->protocol_version = MQTT_VERSION_3_1_1;

    /* MQTT buffers configuration */
    client->rx_buf = rx_buffer;
    client->rx_buf_size = sizeof(rx_buffer);
    client->tx_buf = tx_buffer;
    client->tx_buf_size = sizeof(tx_buffer);

    /* We are not using TLS */
    client->transport.type = MQTT_TRANSPORT_NON_SECURE;

    return err;
}

开发板收到服务器传来的LED控制指令,触发mqtt event事件,根据topic与payload执行对应LED的开关操作(mqtt_connection.c文件mqtt_evt_handler()函数):

case MQTT_EVT_PUBLISH:
    {
        const struct mqtt_publish_param *p = &evt->param.publish;
        //Print the length of the recived message 
        LOG_INF("MQTT PUBLISH result=%d len=%d",
            evt->result, p->message.payload.len);

        //Extract the data of the recived message 
        err = get_received_payload(c, p->message.payload.len);
        
        //Send acknowledgment to the broker on receiving QoS1 publish message 
        if (p->message.topic.qos == MQTT_QOS_1_AT_LEAST_ONCE) {
            const struct mqtt_puback_param ack = {
                .message_id = p->message_id
            };

            /* Send acknowledgment. */
            mqtt_publish_qos1_ack(c, &ack);
        }

        if (err >= 0) {
            data_print("Received: ", payload_buf, p->message.payload.len);
            // Control LED1 and LED2 
            if(strncmp(payload_buf,CONFIG_TURN_LED1_ON_CMD,sizeof(CONFIG_TURN_LED1_ON_CMD)-1) == 0){
                dk_set_led_on(DK_LED1);
            }
            else if(strncmp(payload_buf,CONFIG_TURN_LED1_OFF_CMD,sizeof(CONFIG_TURN_LED1_OFF_CMD)-1) == 0){
                dk_set_led_off(DK_LED1);
            }
            else if(strncmp(payload_buf,CONFIG_TURN_LED2_ON_CMD,sizeof(CONFIG_TURN_LED2_ON_CMD)-1) == 0){
                dk_set_led_on(DK_LED2);
            }
            else if(strncmp(payload_buf,CONFIG_TURN_LED2_OFF_CMD,sizeof(CONFIG_TURN_LED2_OFF_CMD)-1) == 0){
                dk_set_led_off(DK_LED2);
            }

        // Payload buffer is smaller than the received data 
        } else if (err == -EMSGSIZE) {
            LOG_ERR("Received payload (%d bytes) is larger than the payload buffer size (%d bytes).",
                p->message.payload.len, sizeof(payload_buf));
        // Failed to extract data, disconnect 
        } else {
            LOG_ERR("get_received_payload failed: %d", err);
            LOG_INF("Disconnecting MQTT client...");

            err = mqtt_disconnect(c);
            if (err) {
                LOG_ERR("Could not disconnect: %d", err);
            }
        }
    } break;

板载按键开关状态变化时,将对应开关名称写入topic,开关状态写入payload,并发布mqtt消息(main.c文件button_handler()函数):

static void button_handler(uint32_t button_state, uint32_t has_changed)
{
    switch (has_changed) {
    case DK_BTN1_MSK:
        if (button_state & DK_BTN1_MSK){    
            int err = data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE,
                   "ON", sizeof("ON")-1, CONFIG_MQTT_PUB_TOPIC1);
            if (err) {
                LOG_ERR("Failed to send message, %d", err);
                return; 
            }
        }
        else
        {
            int err = data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE,
                   "OFF", sizeof("OFF")-1, CONFIG_MQTT_PUB_TOPIC1);
            if (err) {
                LOG_ERR("Failed to send message, %d", err);
                return; 
            }
        }
        break;
    case DK_BTN2_MSK:
        if (button_state & DK_BTN2_MSK){    
            int err = data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE,
                   "ON", sizeof("ON")-1, CONFIG_MQTT_PUB_TOPIC2);
            if (err) {
                LOG_ERR("Failed to send message, %d", err);
                return; 
            }
        }
        else
        {
            int err = data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE,
                   "OFF", sizeof("OFF")-1, CONFIG_MQTT_PUB_TOPIC2);
            if (err) {
                LOG_ERR("Failed to send message, %d", err);
                return; 
            }
        }
        break;
    }
}

3.3 Home Assistant配置

根据Home Assistant MQTT配置文档(https://www.home-assistant.io/integrations/mqtt/),分别建立key1、key2、led1和led2四个实体,其中key的类型为Binary sensor,led的类型为button。相关配置文件如下(添加在Home Assistant的configuration.yaml文件的最后):

mqtt:
  binary_sensor:
    - name: "key1"
      state_topic: "homeassistant/binary_sensor/key1"
      
    - name: "key2"
      state_topic: "homeassistant/binary_sensor/key2"
      
  switch:
    - name: "led1"
      command_topic: "homeassistant/button/led"
      payload_on: "LED1ON"
      payload_off: "LED1OFF"

    - name: "led2"
      command_topic: "homeassistant/button/led"
      payload_on: "LED2ON"
      payload_off: "LED2OFF"

4 心得体会

本次活动的开发板具有wifi、蓝牙、nfc等多种通信方式,且支持MQTT、matter等物联网通信协议,非常适合智能家居、物联网传感器等应用。同时板卡自带的例程丰富,配套软件与SDK功能齐全,比较容易上手。

附件下载
code.zip
项目代码
zephyr.zip
二进制文件
团队介绍
中国科学技术大学研究生
团队成员
氢化脱氯次氯酸
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号