NUC980开发板DIY项目大挑战:室内环境采集监测系统

本文由RT-Thread论坛用户@纯白酱原创发布:https://club.rt-thread.org/ask/article/75b0b1edc9ec7289.html

#项目描述
使用新唐公司的NUC980,开发一款室内环境监测平台。采集端通常位于家庭中的室内,采集传感器数据,如温湿度数据,光照数据,空气质量数据等,读取完成后,打包成json格式的数据,通过以太网,使用http post方式传输传感器数据。
#设备清单
主控板:NUC980-IOT
传感器扩展板:板载多种传感器
服务器:基于腾讯云搭建的一款云服务器,运行的是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接口的姿态传感器并未使用(因为感觉室内不需要监测自身的姿态数据)。串口接口的传感器并未使用。
#测试截图
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第1张图片
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第2张图片
#开发流程
##采集端开发流程
###一、RT-Thread Studio下载并安装
在https://www.rt-thread.org/page/studio.html中,下载并安装RT-Thread Studio,并提前注册好RT-Thread账号,并在安装好后登录RT-Thread Studio
###二、安装开发板资源包
1、打开RT-Thread Studioccb65c3e1812a315b5b758b98cb415ca.png

2、进入主页面后,点击SDK Manager,可以安装本次活动的开发板NUC980-IOT的支持包
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第3张图片

3、拖动右侧的滑块到下面,选择NK-980IOT,安装BSP资源包
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第4张图片

4、使用两根microusb数据线,分别连接两根USB接口(在左上方),将拨码开关均调节至off模式,即boot from usb模式,此模式可以下载程序用,例如下载到DDR中直接运行程序,方便程序调试阶段,不间断更新程序。或者下载到板载SPI NAND中,并通过调节拨码开关均至ON,按图中最左侧RESET按键后,即可从SPI NAND中运行程序,即正常发出给客户时,运行程序的模式。
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第5张图片

两个USB接口分别为虚拟串口和调试下载的功能,同时连接,方便观测调试数据和下载程序。
###三、创建工程
选择文件-新建-RT-THhread项目,创建基于开发板资源包的工程
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第6张图片

点击基于开发板,并选择开发板为NK-980IOT,自定义取名工程名称,注意不要与现有的工程名称重复,填写完毕后,点击完成,并等待IDE创建工程,约半分钟到两分钟左右,具体时间由开发者的电脑性能决定。
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第7张图片

###四、编译程序
创建好工程后,双击project name/application/main.c,可以看到main.c中有一个点灯例程。
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第8张图片

图中定义了板载的三个LED对应的引脚,其中,PG15引脚对应板载的绿色LED灯,但是PG15引脚上电默认为JTAG功能,并非普通GPIO,需要在project name/board/nu_pin_init.c文件中,初始化该引脚,系统会自动调用该函数
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第9张图片

初始化后,板载的三个LED灯则均能正常闪烁
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第10张图片

点击左上角的编译按钮,即可编译好程序,并在project name/Debug目录下生成rtthread.bin文件,初始demo工程,通常编译用时在一分钟左右,时间由电脑CPU性能决定。
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第11张图片

###五、下载程序
下载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!!!
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第12张图片
###六、正式开发程序
根据本次项目需求,确定需要使用的传感器驱动程序和设备驱动程序和数据处理驱动程序,目录如下:

序号 名称 功能 是否已适配软件包?
1 温湿度传感器 通过I2C,读取温湿度传感器数据
2 光照度传感器 通过I2C,读取光照度传感器数据 否,需要自行根据编写驱动程序
3 空气质量传感器 通过I2C,读取空气质量传感器数据
4 CJSON 将读取出的各项传感器数据处理为JSON格式
5 以太网驱动 通过以太网,传输数据到指定地址
6 HTTP POST 基于TCP通信,通过HTTP协议,POST方法,向指定地址发送处理好的传感器数据(JSON格式)

根据上述分析,分别添加各项软件包:

双击RT-Thread Setting
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第13张图片
添加如图的软件包
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第14张图片
在硬件栏中使能传感器通信接口I2C2
NUC980开发板DIY项目大挑战:室内环境采集监测系统_第15张图片
使能完毕后,在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 
#define DBG_ENABLE
#define DBG_LEVEL DBG_LOG
#define DBG_SECTION_NAME  "VCNL4040"
#define DBG_COLOR
#include 
#include 
#include 
#include 



#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 

#include 
#include 
#include 
#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 
//MSH_CMD_EXPORT_ALIAS(thingsboard_post_test, thingsboard_post_test, webclient post request test.);
#endif /* FINSH_USING_MSH */

###七、连接硬件并再次下载程序
将传感器扩展板与NK-980IOT开发板,使用杜邦线连接,并插上网线,使用USB数据线,连接电脑并下载程序,下载程序步骤可以参考上文,不再赘述。
下载完成后,采集端开发告一段落。

你可能感兴趣的:(RT-Thread,物联网,人工智能,iot,嵌入式,rtthread)