共计 11287 个字符,预计需要花费 29 分钟才能阅读完成。
本文由 RT-Thread 论坛用户 @纯白酱原创公布:https://club.rt-thread.org/as…
我的项目形容
应用新唐公司的 NUC980,开发一款室内环境监测平台。采集端通常位于家庭中的室内,采集传感器数据,如温湿度数据,光照数据,空气质量数据等,读取实现后,打包成 json 格局的数据,通过以太网,应用 http post 形式传输传感器数据。
设施清单
主控板:NUC980-IOT
传感器扩大板:板载多种传感器,基于立创 EDA 制作,已开源,开源地址为:立创 EDA- 传感器扩大板
服务器:基于腾讯云搭建的一款云服务器,运行的是 Windows server 2019,已装置 thingsboard 开源物联网云平台。
传感器扩大板简介
定位模块:基于华大北斗的 TAU1202,默认波特率为 115200,双频定位,亚米级定位,定位成果优异。
光照度:基于 vishy 的 vcnl4040
温湿度:基于盛思锐的 SHT30
二氧化碳浓度 / 空气质量指数(TVOC):基于盛思锐的 SGP30
PM2.5:基于攀藤科技的 PMS7003
甲醛:基于达特的 WS-K- S 甲醛传感器模组
大气压强:基于歌尔电子的 SPL06-007(能够等效代替 SPL06-001)
姿势:基于 invensense 的 mpu-6050
重要提醒
1、服务器局部,运行的 thingsboard 社区版,能够自用 / 商用,本次是运行在腾讯云的服务器上的,也能够运行在树莓派等嵌入式 linux 平台上,不便用户治理传感器数据,确保传感器数据不流通至外网。
2、传感器扩大板反对多种传感器,通信接口为 I2C 和串口。本次只用到 I2C 接口,且 I2C 接口的姿势传感器并未应用(因为感觉室内不须要监测本身的姿势数据)。串口接口的传感器并未应用。
测试截图
开发流程
采集端开发流程
一、RT-Thread Studio 下载并装置
在 https://www.rt-thread.org/page/studio.html 中,下载并装置 RT-Thread Studio,并提前注册好 RT-Thread 账号,并在装置好后登录 RT-Thread Studio
二、装置开发板资源包
1、关上 RT-Thread Studio
2、进入主页面后,点击 SDK Manager, 能够装置本次流动的开发板 NUC980-IOT 的反对包
3、拖动右侧的滑块到上面,抉择 NK-980IOT, 装置 BSP 资源包
4、应用两根 microusb 数据线,别离连贯两根 USB 接口(在左上方),将拨码开关均调节至 off 模式,即 boot from usb 模式,此模式能够下载程序用,例如下载到 DDR 中间接运行程序,不便程序调试阶段,不间断更新程序。或者下载到板载 SPI NAND 中,并通过调节拨码开关均至 ON,按图中最左侧 RESET 按键后,即可从 SPI NAND 中运行程序,即失常收回给客户时,运行程序的模式。
两个 USB 接口别离为虚构串口和调试下载的性能,同时连贯,不便观测调试数据和下载程序。
三、创立工程
抉择文件 - 新建 -RT-THhread 我的项目,创立基于开发板资源包的工程
点击基于开发板,并抉择开发板为 NK-980IOT,自定义取名工程名称,留神不要与现有的工程名称反复,填写结束后,点击实现,并期待 IDE 创立工程,约半分钟到两分钟左右,具体工夫由开发者的电脑性能决定。
四、编译程序
创立好工程后,双击 project name/application/main.c,能够看到 main.c 中有一个点灯例程。
图中定义了板载的三个 LED 对应的引脚,其中,PG15 引脚对应板载的绿色 LED 灯,然而 PG15 引脚上电默认为 JTAG 性能,并非一般 GPIO,须要在 project name/board/nu_pin_init.c 文件中,初始化该引脚,零碎会主动调用该函数
初始化后,板载的三个 LED 灯则均能失常闪动
点击左上角的编译按钮,即可编译好程序,并在 project name/Debug 目录下生成 rtthread.bin 文件,初始 demo 工程,通常编译用时在一分钟左右,工夫由电脑 CPU 性能决定。
五、下载程序
下载 NU_Wrtier 软件,链接:https://gitee.com/OpenNuvoton/NUC980_NuCWriter.git,能够 git clone 该仓库,并在 NUC980_NuCWriter/ Release / Win64 中运行。
下载好后,运行软件,并参考 https://github.com/RT-Thread/rt-thread/tree/master/bsp/nuvoton/nk-rtu980,readme.md 中,下载程序到 DDR 的内容。
应用 NuWriter 将 rtthread.bin 下载到 SDRAM 中,而后运行它。
抉择类型:DDR/SRAM << Press Re-Connect >> 抉择文件:指定您的 rtthread.bin 文件(在 project name/Debug 目录下)。执行地址:0x0 选项:下载并运行 << Press Download >> Enjoy!!!
六、正式开发程序
依据本次我的项目需要,确定须要应用的传感器驱动程序和设施驱动程序和数据处理驱动程序,目录如下:
序号 | 名称 | 性能 | 是否已适配软件包? |
---|---|---|---|
1 | 温湿度传感器 | 通过 I2C,读取温湿度传感器数据 | 是 |
2 | 光照度传感器 | 通过 I2C,读取光照度传感器数据 | 否,须要自行依据编写驱动程序 |
3 | 空气质量传感器 | 通过 I2C,读取空气质量传感器数据 | 是 |
4 | CJSON | 将读取出的各项传感器数据处理为 JSON 格局 | 是 |
5 | 以太网驱动 | 通过以太网,传输数据到指定地址 | 是 |
6 | HTTP POST | 基于 TCP 通信,通过 HTTP 协定,POST 办法,向指定地址发送解决好的传感器数据(JSON 格局) | 是 |
根据上述剖析,别离增加各项软件包:
双击 RT-Thread Setting
增加如图的软件包
在硬件栏中使能传感器通信接口 I2C2
使能结束后,在 main.c 中增加初始化 sgp30 的相干函数
#define SGP30_I2C_BUS_NAME "i2c2"
#define SGP30_I2C_ADDRESS 0x58
static int rt_hw_sgp30_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.type = RT_SENSOR_INTF_I2C;
cfg.intf.dev_name = SGP30_I2C_BUS_NAME;
cfg.intf.user_data = (void *) SGP30_I2C_ADDRESS;
rt_hw_sgp30_init("sg3", &cfg);
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_sgp30_port);
依据论坛,及官网文档,别离调用软件包,并读取传感器数据。因为 vcnl4040 光照度传感器,并未有软件包适配,咱们依据 RT-Thread 对立的 I2C 通信驱动,编写读取程序:
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-07-09 coolwhite the first version
*/
#include <rtthread.h>
#define DBG_ENABLE
#define DBG_LEVEL DBG_LOG
#define DBG_SECTION_NAME "VCNL4040"
#define DBG_COLOR
#include <rtdevice.h>
#include <stdio.h>
#include <ulog.h>
#include <rtdbg.h>
#define HW_ADR 0x60
rt_uint32_t test;
rt_uint32_t Illuminance_als;
static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C 总线设施句柄 */
rt_uint8_t l;
rt_uint8_t h;
static rt_err_t vcnl4040_write(struct rt_i2c_bus_device *bus, uint8_t hwadr, uint8_t reg, rt_uint8_t *data)
{rt_uint8_t buf[3];
struct rt_i2c_msg msgs;
rt_uint32_t buf_size = 1;
buf[0] = reg; //cmd
if (data != RT_NULL)
{buf[1] = data[0];
buf[2] = data[1];
buf_size = 3;
}
msgs.addr = hwadr;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = buf_size;
/* 调用 I2C 设施接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{return RT_EOK;}
else
{return -RT_ERROR;}
}
rt_err_t read_regs(struct rt_i2c_bus_device *bus, uint8_t reg, rt_uint8_t *data)
{struct rt_i2c_msg msgs[2];
rt_int8_t res = 0;
msgs[0].addr = HW_ADR; /* Slave address */
msgs[0].flags = RT_I2C_WR; /* Write flag */
msgs[0].buf = ® /* Slave register address */
msgs[0].len = 1; /* Number of bytes sent */
msgs[1].addr = HW_ADR; /* Slave address */
msgs[1].flags = RT_I2C_RD; /* Read flag */
msgs[1].buf = data; /* Read data pointer */
msgs[1].len = 2; /* Number of bytes read */
if (rt_i2c_transfer((struct rt_i2c_bus_device *) bus, msgs, 2) == 2)
{res = RT_EOK;}
else
{res = -RT_ERROR;}
}
void vcnl4040_init(const char *name)
{
/* 查找 I2C 总线设施,获取 I2C 总线设施句柄 */
i2c_bus = (struct rt_i2c_bus_device *) rt_device_find(name);
if (i2c_bus == RT_NULL)
{rt_kprintf("can't find %s device!\n", name);
}
else
{rt_uint8_t cmd[2];
cmd[0] = 0x00;
cmd[1] = 0x00;
vcnl4040_write(i2c_bus, HW_ADR, 0x00, cmd);
rt_kprintf("vncl4040 init sussess at %s", name);
rt_thread_mdelay(500);
}
}
void test_vcnl4040()
{rt_uint8_t data[2];
data[0] = 0x00;
data[1] = 0x00;
rt_uint8_t data1[2];
data1[0] = 0x00;
data1[1] = 0x00;
rt_uint8_t als_l;
rt_uint8_t als_h;
read_regs(i2c_bus, 0x09, data1);
l = data[0];
h = data[1];
als_l = data1[0];
als_h = data1[1];
rt_uint16_t als_data;
als_data = als_h;
als_data <<= 8;
als_data += als_l;
Illuminance_als = als_data / 10;
LOG_I("Illuminance_als=%d\n",Illuminance_als);
}
在 application 文件夹中,创立一个 cloud_send.c 文件,在其中,调用 cjson 软件包,并解决传感器数据为 JSON 格局:
char *thingsboard_sensor_send;
cJSON *csensor = cJSON_CreateObject();
cJSON *cbaro = cJSON_CreateNumber(baro);
cJSON *cco2concentration = cJSON_CreateNumber(co2concentration);
cJSON *ctvocconcentration = cJSON_CreateNumber(tvocconcentration);
cJSON *chumidity = cJSON_CreateNumber(humi1);
cJSON *ctemperature = cJSON_CreateNumber(temp1);
cJSON *cIlluminance_als = cJSON_CreateNumber(Illuminance_als);
cJSON_AddItemToObject(csensor, "Illuminance_als", cIlluminance_als);
cJSON_AddItemToObject(csensor, "baro", cbaro);
cJSON_AddItemToObject(csensor, "co2concentration", cco2concentration);
cJSON_AddItemToObject(csensor, "tvocconcentration", ctvocconcentration);
cJSON_AddItemToObject(csensor, "humidity", chumidity);
cJSON_AddItemToObject(csensor, "temperature", ctemperature);
thingsboard_sensor_send = cJSON_Print(csensor);
解决好传感器数据后,调用 webclient 软件包,发送传感器数据到指定地址:
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-05-05 11618 the first version
*/
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-08-03 chenyong the first version
*/
#include <string.h>
#include <rtthread.h>
#include <webclient.h>
#include <cJson.h>
#define POST_RESP_BUFSZ 10240
#define POST_HEADER_BUFSZ 10240
#define POST_LOCAL_URI "http://124.220.198.111:8080/api/v1/QLYe1sufLVvb2e2K3lFb/telemetry"
extern double temp1;
extern double humi1;
extern double baro;
extern int32_t co2concentration;
extern int32_t tvocconcentration;
extern rt_uint32_t Illuminance_als;
/* send HTTP POST request by common request interface, it used to receive longer data */
static int webclient_post_comm(const char *uri, const void *post_data, size_t data_len)
{
struct webclient_session* session = RT_NULL;
unsigned char *buffer = RT_NULL;
int index, ret = 0;
int bytes_read, resp_status;
buffer = (unsigned char *) web_malloc(POST_RESP_BUFSZ);
if (buffer == RT_NULL)
{rt_kprintf("no memory for receive response buffer.\n");
ret = -RT_ENOMEM;
goto __exit;
}
/* create webclient session and set header response size */
session = webclient_session_create(POST_HEADER_BUFSZ);
if (session == RT_NULL)
{
ret = -RT_ENOMEM;
goto __exit;
}
/* build header for upload */
webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data));
webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n");
/* send POST request by default header */
if ((resp_status = webclient_post(session, uri, post_data, data_len)) != 200)
{rt_kprintf("webclient POST request failed, response(%d) error.\n", resp_status);
ret = -RT_ERROR;
session == RT_NULL;
goto __exit;
}
__exit: if (session)
{webclient_close(session);
session == RT_NULL;
}
if (buffer)
{web_free(buffer);
}
session == RT_NULL;
return ret;
}
/* send HTTP POST request by simplify request interface, it used to received shorter data */
static int webclient_post_smpl(const char *uri, const char *post_data, size_t data_len)
{
char *response = RT_NULL;
char *header = RT_NULL;
size_t resp_len = 0;
int index = 0;
webclient_request_header_add(&header, "Content-Length: %d\r\n", strlen(post_data));
webclient_request_header_add(&header, "Content-Type: application/octet-stream\r\n");
if (webclient_request(uri, header, post_data, data_len, (void **) &response, &resp_len) < 0)
{rt_kprintf("webclient send post request failed.");
web_free(header);
return -RT_ERROR;
}
rt_kprintf("webclient send post request by simplify request interface.\n");
if (header)
{web_free(header);
}
if (response)
{web_free(response);
}
return 0;
}
void thingsboard_post_test()
{
char *uri = RT_NULL;
char *thingsboard_sensor_send;
cJSON *csensor = cJSON_CreateObject();
cJSON *cbaro = cJSON_CreateNumber(baro);
cJSON *cco2concentration = cJSON_CreateNumber(co2concentration);
cJSON *ctvocconcentration = cJSON_CreateNumber(tvocconcentration);
cJSON *chumidity = cJSON_CreateNumber(humi1);
cJSON *ctemperature = cJSON_CreateNumber(temp1);
cJSON *cIlluminance_als = cJSON_CreateNumber(Illuminance_als);
cJSON_AddItemToObject(csensor, "Illuminance_als", cIlluminance_als);
cJSON_AddItemToObject(csensor, "baro", cbaro);
cJSON_AddItemToObject(csensor, "co2concentration", cco2concentration);
cJSON_AddItemToObject(csensor, "tvocconcentration", ctvocconcentration);
cJSON_AddItemToObject(csensor, "humidity", chumidity);
cJSON_AddItemToObject(csensor, "temperature", ctemperature);
thingsboard_sensor_send = cJSON_Print(csensor);
uri = web_strdup(POST_LOCAL_URI);
if (uri == RT_NULL)
{rt_kprintf("no memory for create post request uri buffer.\n");
}
webclient_post_comm(uri, (void *) thingsboard_sensor_send, rt_strlen(thingsboard_sensor_send));
if (uri)
{web_free(uri);
}
cJSON_Delete(csensor);
thingsboard_sensor_send=RT_NULL;
rt_free(thingsboard_sensor_send);
rt_thread_mdelay(1000);
}
#ifdef FINSH_USING_MSH
#include <finsh.h>
//MSH_CMD_EXPORT_ALIAS(thingsboard_post_test, thingsboard_post_test, webclient post request test.);
#endif /* FINSH_USING_MSH */
七、连贯硬件并再次下载程序
将传感器扩大板与 NK-980IOT 开发板,应用杜邦线连贯,并插上网线,应用 USB 数据线,连贯电脑并下载程序,下载程序步骤能够参考上文,不再赘述。
下载实现后,采集端开发告一段落。
云平台开发流程
Thingsboard 一款开源的云平台,官网地址为:https://thingsboard.io,用户可在官网,学习到 thingsboard 的下载(社区版),装置,与应用。
一、登录物联网云平台
登录 thingsboard 云平台(链接:http://124.220.198.111:8080)链接依据本人服务器部署状况,会有所不同。初学者倡议部署在树莓派上,硬件老本较低,传感器数据不易流出外网。
在登录页面,输出用户名和明码(须要以总管理员的身份创立)
二、创立设施
输出正确的用户名和明码后,登录,即可看到云平台的整体配置界面:
咱们为了让采集端接入云平台通信,须要先创立设施,点击设备选项:
点击右上角的创立设施:
自定义名称,并间接点击增加
三、查看设施凭证
单击 test_nuc980,抉择复制设施拜访令牌
也能够点击治理凭据,手动复制拜访令牌
四、在采集端中填写拜访令牌
thingsboard 反对多种通信协议,在社区版中,通常应用的是 HTTP 协定、MQTT 协定、COAP 协定,本次我的项目次要应用 HTTP 协定,其余协定请依据官网文档,自行学习。
通过 HTTP 协定上传传感器数据的通常 API 接口为:
http(s)://host:port/api/v1/$ACCESS_TOKEN/telemetry
其中,http(s)://host:port 替换为通常你部署的服务器地址,能够是公网地址,也能够是内网地址。
$ACCESS_TOKEN 即拜访令牌,比方 test_nuc980 设施,对应的拜访令牌为:yUtSRivycEvXiamNMC2F
确定好传输地址后,在采集端工程:project name/application/cloud_send.c 中,替换默认地址:
五、设施遥测数据查看
烧录好采集端工程,并实现硬件接线后,即可在云平台查看传输的传感器数据
单击设施名称:
点击最新遥测数据:
能够看到采集端数据曾经发送到云平台,依据最近更新工夫,能够观测数据传输是否出现异常。
六、设施数据仪表盘配置
在最新遥测数据中查看,并不能直观的展现出设施数据的长时间状况下的变化趋势等,所以咱们须要配置一个仪表盘,以便查看数据。
在主菜单中,单击仪表盘:
点击右上角的“+”,新建一个仪表盘:
输出题目,即可增加仪表盘
增加实现后,点击关上仪表板:
点击右下角的功能键,并抉择创立一个新的部件:
为了更好的展现温湿度、光照度等数据的长时间变化趋势度,抉择 chart 部件:
抉择工夫序列图部件:
单击增加数据源:
配置实体别名,即绑定设施:
配置结束后,在键名处即可找到对应的传感器数据名称,键名次要由采集端定义,如需批改,请在采集端批改键名与对应的传感器数据:
本次抉择显示温度数据,即键名为 temperature 的数据,单击蓝色圆圈,可定制折线图的折线色彩:
配置好喜爱的色彩后,点击设置,设置该折线图的名称,在此,咱们将其命名为温度:
点击增加即可,并随后点击右下角的确认更改:
即可看到温度的数据变动曲线:
其余传感器数据的配置形式能够参照上文,以此类推。
七、查看更长时间的传感器数据变动曲线
单击右上角的工夫按钮:
能够查看更多工夫维度的传感器数据变动折线图(默认为 1 分钟):
配置好后,折线图即产生变动:
流程演示
采集端流程:
云平台端流程:
视频演示
正在编辑,稍后上传
开源链接
链接: https://pan.baidu.com/s/1J_Lh… 提取码: 6sey 复制这段内容后关上百度网盘手机 App,操作更不便哦
– 来自百度网盘超级会员 v5 的分享
心得体会
NUC980 芯片,之前并未关注过,通过本次流动,让我理解到 RT-Thread 在 NUC980 上的运行及其劣势所在:开发便当,资源十分丰盛(多路串口,多路 USB 等等),在疫情三年,芯片市场动荡的状况下,NUC980 无望局部取代价格极度暴涨的 STM32,在工业管制,数据采集等畛域取得更大的市场。
备注
thingsboard 是一款开源的物联网平台,本次流动所展现的云平台数据,在以下链接中能够看到:NUC980 室内环境监测仪表盘,如果有大家有须要测试或者理解这款云平台,能够在文章评论区留言,我能够凋谢肯定数量的测试账号,不便大家体验。