nRF7002-DK联网功能一级棒,在SDK中,还提供了mqtt连接的模块,这篇分享,就基于mqtt,将nRF7002-DK接入到node-red系统,实现在nod-red中控制板载的两个LED。
一、nRF7002-DK接入到mqtt服务
1. 从mqtt样例新建工程:
在VSCode的nRF Connect插件界面中,可以直接创建一个新的程序,模版使用样例:
有好几个mqtt样例,这里直接选择第一个即可:
添加完成,就会自动打开新建的工程:
2. 配置修改:
要让这个工程,能够为nRF7002-DK所用,需要做一些配置处理。
1) 首先,先打开dts文件,看一下led的定义:
文件:/opt/nordic/ncs//v2.5.0/nrf/boards/arm/nrf7002dk_nrf7001_nrf5340/nrf7002dk_nrf7001_nrf5340_cpuapp.dts
led定义:
aliases {
led0 = &led0;
led1 = &led1;
sw0 = &button0;
sw1 = &button1;
bootloader-led0 = &led0;
};
从上述dts中可以看到,nRF7002-DK定义了两个led,命名为led0、led1
2) 在prj.conf中开启GPIO,并设置WIFI连接信息
文件:prj.conf
# WiFi
CONFIG_WIFI_CREDENTIALS_STATIC_SSID="OpenBSD"
CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="********"
CONFIG_WIFI_CREDENTIALS_STATIC_TYPE_PSK=y
# GPIO
CONFIG_GPIO=y
3) 配置Kconfig:
文件:src/modules/transport/Kconfig.transport
config MQTT_SAMPLE_TRANSPORT_BROKER_HOSTNAME
string "MQTT broker hostname"
default "192.168.1.15"
config MQTT_SAMPLE_TRANSPORT_BROKER_USERNAME
string "MQTT broker username"
default "test"
config MQTT_SAMPLE_TRANSPORT_BROKER_PASSWORD
string "MQTT broker password"
default "********"
config MQTT_SAMPLE_TRANSPORT_PUBLISH_TOPIC
string "MQTT publish topic"
default "device/report"
config MQTT_SAMPLE_TRANSPORT_SUBSCRIBE_TOPIC
string "MQTT subscribe topic"
default "device/cmd"
在上述配置中,定义了mqtt服务的地址、用户名、密码,以及用于发布消息对应的topic:device/report,和接受命令对应的topic:device/cmd
在开发板上,通过mqtt发送消息出去,那么就发送到了 设备MAC/device/report
在开发板上,通过mqtt祸首命令消息,那么就要订阅 设备MAC/device/cmd
4) 修改sdk源码
sdk中,mqtt连接部分,之列把password留空了,不知道咋想的。好在提供了源码,我们可以自己修改。
文件:/opt/nordic/ncs/v2.5.0/nrf/include/net/mqtt_helper.h
struct mqtt_helper_conn_params {
/* The hostname must be null-terminated. */
struct mqtt_helper_buf hostname;
struct mqtt_helper_buf device_id;
struct mqtt_helper_buf user_name;
struct mqtt_helper_buf password; // 新增的部分
};
文件:/opt/nordic/ncs/v2.5.0/nrf/subsys/net/lib/mqtt_helper/mqtt_helper.c
static int client_connect(struct mqtt_helper_conn_params *conn_params)
{
int err;
struct mqtt_utf8 user_name = {
.utf8 = conn_params->user_name.ptr,
.size = conn_params->user_name.size,
};
// 新增的部分-开始
struct mqtt_utf8 password = {
.utf8 = conn_params->password.ptr,
.size = conn_params->password.size,
};
// 新增的部分-结束
...
mqtt_client.user_name = conn_params->user_name.size > 0 ? &user_name : NULL;
// 新增的部分-开始
mqtt_client.password = conn_params->password.size > 0 ? &password : NULL;
// 新增的部分-结束
...
}
在上述代码中,添加password的定义,并在实际发送参数给mqtt服务的时候,带上传入参数的password
5) 修改工程源码
首先修改文件: src/modules/sampler/sampler.c
#define FORMAT_STRING "Board is ready, uptime is: %d"
然后修改核心处理文件:src/modules/transport/transport.c
// GPIO头文件
#include <zephyr/drivers/gpio.h>
// LED定义
#define LED1_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
#define LED2_NODE DT_ALIAS(led1)
static const struct gpio_dt_spec led2 = GPIO_DT_SPEC_GET(LED2_NODE, gpios);
// LED状态
static uint8_t status1 = 0;
static uint8_t status2 = 0;
static void on_mqtt_publish(struct mqtt_helper_buf topic, struct mqtt_helper_buf payload)
{
LOG_INF("Received payload: %.*s on topic: %.*s", payload.size,
payload.ptr,
topic.size,
topic.ptr);
// 检查收到的指令
payload.ptr[payload.size] = '\0';
if(strcmp(payload.ptr, "LED1ON") == 0 ) {
LOG_INF("led1: ON");
status1 = 1;
}
else if(strcmp(payload.ptr, "LED1OFF") == 0 ) {
LOG_INF("led1: OFF");
status1 = 0;
}
else if(strcmp(payload.ptr, "LED2ON") == 0 ) {
LOG_INF("led2: ON");
status2 = 1;
}
else if(strcmp(payload.ptr, "LED2OFF") == 0 ) {
LOG_INF("led2: OFF");
status2 = 0;
}
else if(strcmp(payload.ptr, "LEDON") == 0 ) {
LOG_INF("led1~2: ON");
status1 = 1;
status2 = 1;
}
else if(strcmp(payload.ptr, "LEDOFF") == 0 ) {
LOG_INF("led1~2: OFF");
status1 = 0;
status2 = 0;
}
gpio_pin_set_dt(&led1, status1);
gpio_pin_set_dt(&led2, status2);
}
static void transport_task(void)
{
// 此处为添加的代码-开始
// LED设备初始化
int ret;
if (!gpio_is_ready_dt(&led1)) {
LOG_ERR("led1 is not ready");
return;
}
if (!gpio_is_ready_dt(&led2)) {
LOG_ERR("led2 is not ready");
return;
}
ret = gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
LOG_ERR("led1 configure, error: %d", ret);
return;
}
ret = gpio_pin_configure_dt(&led2, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
LOG_ERR("led2 configure, error: %d", ret);
return;
}
// 默认关闭
status1 = 0;
status2 = 0;
gpio_pin_set_dt(&led1, status1);
gpio_pin_set_dt(&led2, status2);
// 此处为添加的代码-结束
}
以上代码,实在原有演示代码的基础上进行添加的。
主要添加的内容包括:
- 添加gpio头文件
- 定义LED1、LED2,分别对应dts中定义的led0、led1
- 在 on_mqtt_publish() 中,收到消息后,检查是否对应的控制指令
- 在 transport_task() 中,进行GPIO LED设备的初始化
控制指令具体对应关系如下:
- LED1ON:打开LED1(对应dts-led0)
- LED1OFF:关闭LED1(对应dts-led0)
- LED2ON:打开LED2(对应dts-led1)
- LED2OFF:关闭LED2(对应dts-led1)
- LEDON:打开两个LED
- LEDOFF:关闭两个LED
3. 编译测试
编译上述代码并下载到nRF7002-DK开发板:
下载完成后,使用串口监听工具监听,可以看到下面的输出信息:
从上述输出可以看到,实际订阅的topic为:F4CE360026BC/device/cmd,那么上报消息对应的topic为:F4CE360026BC/device/report
根据不同的开发板,这里的 设备MAC地址前缀,是不同的。
4) 使用mqttf客户端接受消息和下发控制指令
首先订阅 F4CE360026BC/device/report 主题,以便接受消息。
然后给 F4CE360026BC/device/cmd 发送控制指令:
在串口监听工具中,也可以看到输出:
同时,开发板上的LED,会跟随控制指令点亮或者熄灭。
二、nRF7002-DK接入node-red平台
nRF7002-DK要接入node-red平台,本质是在node-red平台上,与对应的控制nRF7002-DK的mqtt的topic关联起来,从而实现在node-red上,通过mqtt控制nRF7002-DK。
1. 安装node-red
我是在鲁班猫上安装node-red的,具体步骤如下:
sudo apt update
sudo apt install npm
node -v
npm -v
# 配置国内镜像
npm config set registry http://mirrors.cloud.tencent.com/npm/
npm config get registry
# 建立工作目录
mkdir mkdir ~/Projects/node-red/node_modules
cd ~/Projects/node-red
# 安装node-red
npm install --unsafe-perm node-red
export PATH=/home/cat/Projects/node-red/node_modules/.bin:$PATH
# 启动出错,版本太低,至少要node 14
node-red
但是,运行的时候,居然出错了:
经过查找资料,确定是nodejs版本太低,至少需要nodejs 14才可以运行。
鲁班猫使用的是Ubuntu 22.04系统,使用nvm可以很方便的管理nodejs版本,下面就使用nvm来安装nodejs 14.
# 安装nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
vim ~/.bashrc_nvm
# 使用nvm安装stable版本node
. ~/.bashrc_nvm
nvm install 14
nvm use 14
node -v
npm -v
# 重新安装:务必删除了,重新安装
rm -rf node_modules
npm install --unsafe-perm node-red
export PATH=/home/cat/Projects/node-red/node_modules/.bin:$PATH
# 启动
node-red
需要注意的是,一定要把原有的node_modules目录删除了,重新进行,分则可能运行node-red的时候出现段错误。
安装完成,启动node-red,输出信息如下:
2. node-red中文设置
设置完成后,要刷新一下界面,才会变为中文界面。
3. 安装dashboard
要在界面上显示按钮,点击按钮时产生操作,需要安装dashboard界面ui模块,具体如下:
4. 添加按钮
在左边搜索button,然后依次添加6个按钮,对应如下:
并依次设置每个按钮:
5. 设置mqtt
在左边搜索mqtt,添加mqtt out,用于发送mqtt指令。
首次添加mqtt节点的时候,需要添加mqtt服务器的配置。
6. 关联按钮到mqtt
从按钮尾部拉线到mqtt节点即可,将所有的按钮都连接到刚才添加的mqtt out节点:
7. 添加UI界面:
在按钮配置界面的Group中,添加新的Tab和Group:
然后从右上角的小箭头,选择dashboard,进入界面排版设置:
在该界面,可以拖动按钮节点的位置,从而实现最终的按钮顺序:
8. 部署查看
设置完成后,点击右上角的部署按钮:
如果提示有的按钮设置不正确,需要依次点进去,设置好对应的Group即可。
正确无误部署好,打开浏览器: http://node-red服务器ip:1880/ui 即可查看基础的UI界面:
在该界面上,点击对应的按钮,开发板会收到对应的控制指令,并进行LED的控制:
三、总结
只要将nRF7002-DK接入了mqtt服务,那就相当于是给一座孤岛通上了网络,从此可以方便的和外界发生联系了。
在这片分享中,使用node-red这个低代码IoT平台,来实现简单快捷的接入nRF7002-DK,并实现通过界面按钮,来控制板载LED。
当然,这里只是一个基础的展示,还可以在nRF7002-DK上接入传感器,读取传感器的数值,并通过mqtt发布出去,例如使用温湿度传感器获取环境数据,mqtt发布后,node-red可以收到,并在界面上进行呈现,从而实现更丰富的功能和界面了。