1234下一页
返回列表 发新帖我要提问本帖赏金: 35.00元(功能说明)

[单片机芯片] 基于CH32V307&腾讯连连小程序的二氧化碳TVOC环境监测仪

[复制链接]
17642|70
 楼主| dql2015 发表于 2022-12-22 13:04 | 显示全部楼层 |阅读模式
#申请原创#@21小跑堂

一、项目概述
现代社会,由于长期在室内活动,人们越来越关注室内空气质量。衡量室内空气质量的主要指标有:二氧化碳浓度、TVOC(总挥发性有机物)浓度、温度、湿度。研究表明:当室内CO2浓度超过1000 ppm时,人体就会出现困倦、呼吸急促、注意力缺乏等症状,当浓度逐渐升高未及时处理,还会造成脑损伤、昏倒甚至丧生。当TVOC浓度为3.0-25 mg/m3时,会产生刺激和不适,与其他因素联合作用时,可能出现头痛;当TVOC浓度大于25 mg/m3时,除头痛外,可能出现其他的神经毒性作用。温度和湿度则是最基本的环境指标,过高或过低的温湿度都会造成然提感到不适。
通风换气是改善空气质量的最简单有效的办法之一,TVOC、CO2、温读和湿度等环境监测数据可用于配置空气净化系统或智能管理通风系统运行模式,从而节省能耗,达到节能减排的目的,助力双碳目标实现。本文基于CH32V307开发板采集TVOC、CO2、温读和湿度四项环境数据,通过WIFI无线网络接入腾讯云物联网平台IoT Explorer,实现了在腾讯连连小程序上能够实时监测室内空气质量的应用。
二、系统结构
系统采用CH32V307作为控制核心,esp8266无线wifi模块用于和腾讯云物联网平台通信,SGP30模块用于采集TVOC、CO2浓度数据、SHT40模块用于采集温湿度数据,led和按键用于人机交互,用户可通过微信小程序实时查看监测数据。系统结构如图所示:
系统硬件结构.JPG

三、硬件介绍
3.1  CH32V307开发板
CH32V307是沁恒微电子基于32位RISC-V设计的高性能、低功耗互联型微控制器,产品特点:
青稞V4F处理器,最高144MHz系统主频
支持单周期乘法和硬件除法,支持硬件浮点运算(FPU)
64KB SRAM,256KB Flash
供电电压:2.5/3.3V,GPIO单元独立供电
多种低功耗模式:睡眠、停止、待机
上/下电复位、可编程电压检测器
2组18路通用DMA
4组运放比较器
1个随机数发生器TRNG
2组12位DAC转换
2单元16通道12位ADC转换,16路触摸按键TouchKey
10组定时器
USB2.0全速OTG接口
USB2.0高速主机/设备接口(480Mbps 内置PHY)
3个USART接口和5个UART接口
2个CAN接口(2.0B主动)
SDIO接口、FSMC接口、DVP数字图像接口
2组IIC接口、3组SPI接口、2组IIS接口
千兆以太网控制器ETH(内置10M PHY)
80个I/O口,可以映射到16外部中断
CRC计算单元,96位芯片唯一ID
串行2线调试接口
本次项目采用沁恒微电子官方推出的开发板,如下图所示:
ch32v307参数.JPG

3.2 SGP30传感器
SGP30是Sensirion推出的一款I2C接口低功耗、高精度多气体传感器(VOC和CO₂eq),主要参数规格如下:
SGP30参数.JPG
本次项目采用模组如下图所示,集成了电源管理和电平转换电路:
SGP30模块.jpg
3.3 SHT40传感器
SHT40是Sensirion推出的一款I2C接口低功耗、高精度数字温湿度传感器,主要参数规格如下:
SHT40参数.JPG
本次项目采用模组,集成电源和电平转换:
SHT40模块.jpg
3.4 ESP8266 WIFI
esp8266是乐鑫推出的面向物联网应用的高性价比、高度集成的 Wi-Fi MCU,本次使用模组,引出了通信串口,如图所示:
esp8266模块.JPG
3.5 系统硬件连接
ESP8266模块连接到开发板串口2(管脚PA2、PA3),SGP30连接到开发板PA4、PA5,SHT40连接到开发板PB10、PB11,如下图所示:
实物接线.jpg

四、项目亮点
支持腾讯连连小程序查看监测数据(CO2浓度、TVOC浓度、温度和湿度);
支持腾讯云IoT Explorer平台实时查看上报数据信息(CO2浓度、TVOC浓度、温度和湿度);
支持腾讯连连微信公众号信息推送(CO2浓度、TVOC浓度、温度和湿度超标告警);
采用腾讯云可视化编辑器自定义腾讯连连小程序界面;

小程序界面展示如下图所示:
微信小程序界面.jpg

五、系统实现
系统实现分为3个部分,一是腾讯云平台配置,二是ESP8266腾讯云固件烧录,三是MCU端程序设计。
5.1 腾讯云平台配置
首先需要在腾讯物联网开发平台创建项目:
1新建项目.JPG
这里建立一个名为环境监测的项目:
1新建项目2.JPG
然后在该项目下面点击新建产品按钮,新建一个产品:
2新建产品.JPG
一个项目下面可以新建多个产品,这里建立名为室内二氧化碳监测的产品,设备类型为:设备,因为项目使用的是esp8266 wifi模块,因此通信方式选择:Wi-Fi,认证方式选择:秘钥认证,数据协议选择:物模型
2新建产品2.JPG
建立好产品后,会看到产品的基本信息:
2新建产品3.JPG

然后根据本项目需要实现的功能进行功能定义,腾讯云平台采用了物模型协议将现实物理世界中的设备功能进行数字化定义,便于应用更便利的管理设备。
在产品定义物模型后,设备可以根据物模型中的定义上报属性、事件,并可对设备下发控制指令,物模型协议主要包括2部分:
设备属性上报:设备端将定义的属性根据设备端的业务逻辑向云端上报。
设备远程控制:从云端向设备端下发控制指令,即从云端设置设备的可写属性。
新建自定义功能:
3新增功能.JPG
功能类型:属性
功能名称:二氧化碳
标识符:co2
数据类型:整型
读写类型:只读(读写表示该属性即可从设备端上报到云端也可从云端发起控制,只读表示该属性只从设备向云端上报)
数据范围等参数根据前文SGP30传感器芯片参数填写即可:
3新增功能2.JPG
同样的方式,再新建TVOC、温度、湿度等其它自定义功能:
3新增功能3.JPG

3新增功能4.JPG
最后,建立好的自定义功能如下所示:
3新增功能5.JPG
查看物模型json格式数据如下:
4查看物模型.JPG

建立好产品后,在进行小程序界面和MCU程序开发前,可以使用虚拟在线调试测试设计的正确性:
7设备调试.JPG

然后配置小程序界面,在交互设计选项卡选择面板配置:
6面板配置.JPG
小程序界面支持3种配置方案,这里使用傻瓜化的可视化面板:
6面板配置2.JPG
可以通过简单的拖、拽控件、关联物模型数据等方式设计自定义的小程序界面,十分方便小白使用。以CO2数据显示为例,新增一个信息展示控件,然后关联物模型co2:
6面板配置3.JPG
同样的方式设计其它控件:
6面板配置4.JPG

除了数据展示功能外,还能配置告警消息推送功能:
8数据开发.JPG
通过拖曳控件的方式完成数据流配置:
8数据开发2.JPG
设定数据过滤规则和阈值:
8数据开发3.JPG
设定报警推送消息内容:
8数据开发4.JPG
定义的报警信息如下:
8数据开发5.JPG


5.2 ESP8266腾讯云固件烧录
esp8266模块烧写腾讯云定制固件后,就能连接到腾讯云平台,实现与腾讯云平台的数据交互,能够实现WIFI账号密码配置、MQTT连接、订阅、发布等功能。
乐鑫官网提供esp8266腾讯云AT固件:
https://docs.espressif.com/projects/esp-at/zh_CN/release-v2.2.0.0_esp8266/Customized_AT_Commands_and_Firmware/Tencent_Cloud_IoT_AT/index.html
注意烧录2.2.0.0版本,实测2.3版本不能顺利连接腾讯云。
使用乐鑫提供的esptool工具烧录即可:
esp8266固件烧录.png

5.3 MCU端程序设计
MCU端软件基于腾讯物联网终端操作系统TencentOS tiny构建,TencentOS tiny提供了ch32v307的板级支持包和丰富的示例程序,MCU端软件实现传感器数据采集、无线数据接入腾讯云IoT Explorer,从而设备联网控制与数据交互功能,结构如下图所示:
软件结构.JPG
设备端主要完成ch32v307的外设初始化、传感器初始化、wifi初始化、协议栈初始化、传感器数据采集、mqtt协议数据发送等功能。基于TencentOS-Tiny物联网操作系统开发,设计了3个线程,定时采集传感器数据,控制线程,mqtt发送线程将数据通过腾讯云mqtt协议发送到云平台。
软件功能简要流程如图所示:
软件流程图.JPG

开发板提供了常用外设的驱动程序,与云平台的交互基于例子mqtt_iot_explorer.c进行开发。主函数main创建三个线程来进行不同功能的处理,线程间采用TencentOS-tiny提供的消息队列进行通知和数据交换,实现CPU资源高效利用。线程间传递的消息队列中的数据设计如下:
  1. typedef struct
  2. {
  3. int tvoc;//tvoc浓度
  4. int co2;//co2浓度
  5. float tem;//温度
  6. float hum;//湿度
  7. bool fan;//风扇启用标志
  8. bool sleep;//睡眠模式启用标志
  9. }msg_t;

main.c内容如下,主要完成初始化内核、创建消息队列、创建线程、启动内核功能,需要注意的是,由于使用了printf打印浮点数,需要使用__aligned(8)8字节对齐,否则将出现异常:
  1. #include "debug.h"
  2. #include "tos_k.h"

  3. #define MQTT_TASK_STK_SIZE          2048
  4. k_task_t mqtt_task;
  5. __aligned(8) uint8_t mqtt_task_stk[MQTT_TASK_STK_SIZE];

  6. #define CONTROL_TASK_STK_SIZE       4096
  7. k_task_t control_task;
  8. __aligned(8) uint8_t control_task_stk[CONTROL_TASK_STK_SIZE];

  9. #define DATA_COLLECT_TASK_STK_SIZE       4096
  10. k_task_t data_collect_task;
  11. __aligned(8) uint8_t data_collect_task_stk[DATA_COLLECT_TASK_STK_SIZE];

  12. extern void mqtt_entry(void *arg);
  13. extern void control_entry(void *arg);
  14. extern void data_collect_entry(void *arg);

  15. k_msg_q_t msg_q;
  16. uint8_t DataMsgPool[100];

  17. int main(void)
  18. {
  19.     Delay_Init();
  20.     USART_Printf_Init(115200);
  21.     printf("SystemClk:%d\r\n",SystemCoreClock);

  22.     printf("Welcome to TencentOS tiny(%s)\r\n", TOS_VERSION);

  23.     tos_knl_init();
  24.     tos_msg_q_create(&msg_q, DataMsgPool, 100);
  25.     tos_task_create(&mqtt_task, "mqtt_task", mqtt_entry, NULL, 4, mqtt_task_stk, MQTT_TASK_STK_SIZE, 0);
  26.     tos_task_create(&control_task, "control_task", control_entry, NULL, 5, control_task_stk, CONTROL_TASK_STK_SIZE, 0);
  27.     tos_task_create(&data_collect_task, "data_collect_task", data_collect_entry, NULL, 6, data_collect_task_stk, DATA_COLLECT_TASK_STK_SIZE, 0);
  28.     tos_knl_start();

  29.     printf("should not run at here!\r\n");
  30.     while(1);
  31. }

数据采集线程实现对SGP30、SHT40传感器的初始化,定时将采集数据放入消息队列:
  1. void data_collect_entry(void *arg)
  2. {
  3.     uint16_t TVOC = 0, CO2 = 0;
  4.     uint8_t ID[6]={0};
  5.     while(sgp30_init() < 0)
  6.    {
  7.        printf(" sgp30 init error\r\n");
  8.        tos_sleep_ms(1000);
  9.    }

  10.    if(sgp30_get_serial_id(ID) < 0)
  11.    {
  12.        printf(" sgp30 read serial id error\r\n");
  13.    }else
  14.    {
  15.        printf("SGP30 Serial number: ");
  16.        for(int i = 0; i < 6; i++)  printf("%02X", ID[i]);
  17.        printf("\r\n");
  18.    }
  19.    // SGP30模块开机需要一定时间初始化,在初始化阶段读取的CO2浓度为400ppm,TVOC为0ppd且恒定不变。
  20.    printf("wait for sgp30 init");
  21.    fflush (stdout);
  22.    do
  23.    {
  24.         if(sgp30_read(&CO2, &TVOC) < 0)
  25.         {
  26.            printf("spg30 read error\r\n");
  27.         }
  28.         else
  29.         {
  30.            printf("-");
  31.            fflush (stdout);
  32.         }
  33.         tos_sleep_ms(500);
  34.    }while(TVOC == 0 && CO2 == 400);

  35.    printf("sgp30 init ok\r\n");

  36.    int16_t error = 0;
  37.    sensirion_i2c_hal_init();
  38.    uint32_t serial_number;
  39.    int32_t temperature;
  40.    int32_t humidity;

  41.    error = sht4x_serial_number(&serial_number);
  42.    if (error)
  43.    {
  44.        printf("Error executing sht4x_serial_number(): %i\r\n", error);
  45.    }
  46.    else
  47.    {
  48.        printf("SHT40 Serial number: %u\r\n", serial_number);
  49.    }

  50.    msg_t msg={0};

  51.    while (1)
  52.    {
  53.        if(sgp30_read(&CO2, &TVOC) < 0)
  54.        {
  55.            printf(" sgp30 read error\r\n");
  56.        }
  57.        else
  58.        {
  59.            printf("CO2:%5dppm TVOC:%5dppb\r\n", CO2, TVOC);
  60.        }
  61.        error = sht4x_measure_high_precision(&temperature, &humidity);
  62.        if (error)
  63.        {
  64.            printf("Error executing sht4x_measure_high_precision(): %i\n",error);
  65.        }
  66.        else
  67.        {
  68.            msg.tvoc = TVOC;
  69.            msg.co2 = CO2;
  70.            msg.tem = temperature / 1000.0f;
  71.            msg.hum = humidity / 1000.0f;
  72.            tos_msg_q_post(&msg_q,(void*)&msg);
  73.            printf("Temperature:%0.2f℃,Humidity:%0.2f %%RH\n",temperature / 1000.0f, humidity / 1000.0f);
  74.        }
  75.        tos_sleep_ms(3000);
  76.    }
  77. }

对接云平台,修改mqtt_iot_explorer.c文件开头的三元组信息产品 ID(ProductId)、设备名(DeviceName)、设备密钥(DeviceSecret)其中设备密钥由平台生成:
  1. #define PRODUCT_ID              "test"
  2. #define DEVICE_NAME             "test"
  3. #define DEVICE_KEY              "test"

然后修改要接入WIFI的名称和密码:
  1. esp8266_tencent_firmware_join_ap("ssid", "passwd");

mqtt线程首先完成esp8266初始化、wifi协议栈初始化、mqtt订阅主题等功能。当接收到消息后,将采集数据依据消息发布使用的物模型Topic打包,然后通过MQTT发送出去。属性上报主题格式如下所示:
$thing/up/property/${deviceID}/${deviceName}
根据腾讯云平台配置的物模型将数据打包为json格式上传,消息体json格式如下:
  1. #define REPORT_DATA_TEMPLATE    "{\\"method\\":\\"report\\"\\,\\"clientToken\\":\\"00000001\\"\\,\\"params\\":{\\"tvoc\\":%d\\,\\"co2\\":%d\\,\\"fan\\":%d\\,\\"sleep\\":%d\\,\\"temperature\\":%0.1f\\,\\"humidity\\":%0.1f\\}}"
通过snprintf函数将两者连接起来:
  1.     memset(report_topic_name, 0, sizeof(report_topic_name));
  2.     size = snprintf(report_topic_name, TOPIC_NAME_MAX_SIZE, "$thing/up/property/%s/%s", product_id, device_name);
将传感器数据格式化输入到payload:
  1.         memset(payload, 0, sizeof(payload));
  2.         snprintf(payload, sizeof(payload), REPORT_DATA_TEMPLATE, tvoc,co2,0,0,tem,hum);
最后调用
  1. tos_tf_module_mqtt_pub(report_topic_name, QOS0, payload)
函数实现MQTT消息的发布。
推送传感器数据到腾讯云物联网开发平台IoT Explorer的mqtt线程如下:
  1. void mqtt_demo_task(void)
  2. {
  3.     int ret = 0;
  4.     int size = 0;

  5.     mqtt_state_t state;

  6.     char *product_id = PRODUCT_ID;
  7.     char *device_name = DEVICE_NAME;
  8.     char *key = DEVICE_KEY;

  9.     device_info_t dev_info;
  10.     memset(&dev_info, 0, sizeof(device_info_t));

  11.     /**
  12.      * Please Choose your AT Port first, default is HAL_UART_2(USART2)
  13.     */
  14.     ret = esp8266_tencent_firmware_sal_init(HAL_UART_PORT_2);

  15.     if (ret < 0) {
  16.         printf("esp8266 tencent firmware sal init fail, ret is %d\r\n", ret);
  17.     }

  18.     esp8266_tencent_firmware_join_ap("HUAWEI-DQL", "dql8837150");

  19.     strncpy(dev_info.product_id, product_id, PRODUCT_ID_MAX_SIZE);
  20.     strncpy(dev_info.device_name, device_name, DEVICE_NAME_MAX_SIZE);
  21.     strncpy(dev_info.device_serc, key, DEVICE_SERC_MAX_SIZE);
  22.     tos_tf_module_info_set(&dev_info, TLS_MODE_PSK);

  23.     mqtt_param_t init_params = DEFAULT_MQTT_PARAMS;
  24.     if (tos_tf_module_mqtt_conn(init_params) != 0) {
  25.         printf("module mqtt conn fail\n");
  26.     } else {
  27.         printf("module mqtt conn success\n");
  28.     }

  29.     if (tos_tf_module_mqtt_state_get(&state) != -1) {
  30.         printf("MQTT: %s\n", state == MQTT_STATE_CONNECTED ? "CONNECTED" : "DISCONNECTED");
  31.     }

  32.     size = snprintf(report_reply_topic_name, TOPIC_NAME_MAX_SIZE, "$thing/down/property/%s/%s", product_id, device_name);

  33.     if (size < 0 || size > sizeof(report_reply_topic_name) - 1) {
  34.         printf("sub topic content length not enough! content size:%d  buf size:%d", size, (int)sizeof(report_reply_topic_name));
  35.     }
  36.     if (tos_tf_module_mqtt_sub(report_reply_topic_name, QOS0, default_message_handler) != 0) {
  37.         printf("module mqtt sub fail\n");
  38.     } else {
  39.         printf("module mqtt sub success\n");
  40.     }

  41.     memset(report_topic_name, 0, sizeof(report_topic_name));
  42.     size = snprintf(report_topic_name, TOPIC_NAME_MAX_SIZE, "$thing/up/property/%s/%s", product_id, device_name);

  43.     if (size < 0 || size > sizeof(report_topic_name) - 1)
  44.     {
  45.         printf("pub topic content length not enough! content size:%d  buf size:%d", size, (int)sizeof(report_topic_name));
  46.     }
  47.     void *MsgRecv;
  48.     msg_t msg;
  49.     int tvoc;
  50.     int co2;
  51.     float tem;
  52.     float hum;
  53.     while (1)
  54.     {
  55.         //GPIO_SetBits(GPIOE,GPIO_Pin_5);
  56.         //tos_sleep_ms(2000);
  57.         //GPIO_ResetBits(GPIOE,GPIO_Pin_5);

  58.         tos_msg_q_pend(&msg_q, &MsgRecv, TOS_TIME_FOREVER);
  59.         memcpy(&msg,MsgRecv,sizeof(msg_t));
  60.         tos_mutex_pend(&mutex);
  61.         printf("tvoc:%d co2:%d temp:%d hum:%d\r\n",msg.tvoc,msg.co2,msg.tem,msg.hum);
  62.         tos_mutex_post(&mutex);
  63.         /* use AT+PUB AT command */

  64.         tvoc = msg.tvoc;
  65.         co2 = msg.co2;
  66.         tem = msg.tem;
  67.         hum = msg.hum;

  68.         memset(payload, 0, sizeof(payload));
  69.         snprintf(payload, sizeof(payload), REPORT_DATA_TEMPLATE, tvoc,co2,0,0,tem,hum);
  70.         if (tos_tf_module_mqtt_pub(report_topic_name, QOS0, payload) != 0)
  71.         {
  72.             printf("module mqtt pub fail\n");
  73.             break;
  74.         }
  75.         else
  76.         {
  77.             printf("module mqtt pub success\n");
  78.         }
  79.     }
  80. }

程序运行正常的情况下,在云平台调试界面就可以看到设备发送的数据:
7设备调试2.JPG

进入腾讯连连小程序连接设备后也能看到之前设计的小程序界面:


小程序收到超标告警消息:
报警.jpg

六、视频演示
【CH32V307&tencentos-tiny接入腾讯云平台小程序数据采集展示】 https://www.bilibili.com/video/BV1xD4y177yd/?share_source=copy_web&vd_source=4b9c499d8564e7ad9169a2c39fa19a17


七、总结
通过本项目体验了国产MCU、国产RTOS、国产IDE生态,了解了沁恒RISC-V内核微控制器 CH32V307VCT6应用、了解了国产物联网操作系统TencentOS-tiny和国产集成开发工具MounRiver Studio,感受到了国货的崛起。
由于本人能力水平不足,文章写的有误或者不清楚的地方敬请见谅。
八、工程源码
由于大小限制,替换TencentOS-tiny\board\TencentOS_Tiny_CH32V307_EVB即可。
test.rar (21.56 KB, 下载次数: 74)




打赏榜单

21小跑堂 打赏了 35.00 元 2022-12-29
理由:恭喜通过原创审核,期待您更多的原创作品~

昨天 发表于 2022-12-23 17:28 | 显示全部楼层
同样用26个字母,你为啥这么优秀。
changle11sdo5 发表于 2022-12-28 14:25 来自手机 | 显示全部楼层
昨天 发表于 2022-12-23 17:28
同样用26个字母,你为啥这么优秀。

用26个字母当然秀不起来,你得加上阿拉伯数字和符号
caigang13 发表于 2023-1-4 00:12 来自手机 | 显示全部楼层
差点看成了连连看
qidiao 发表于 2023-1-4 12:22 | 显示全部楼层
噢 竟然看完了
belindagraham 发表于 2023-1-5 10:43 | 显示全部楼层
这个信息的推送怎么实现的?              
jonas222 发表于 2023-1-5 14:55 | 显示全部楼层
腾讯云有AT固件,用起来也简单很多。
i1mcu 发表于 2023-1-5 16:31 | 显示全部楼层
这个怎么连接的微信小程序呢              
xiaoyaodz 发表于 2023-1-6 13:21 | 显示全部楼层
以后可以应用在智能家居上
              
averyleigh 发表于 2023-1-6 14:31 | 显示全部楼层
环境监测,常用设计              
tabmone 发表于 2023-1-6 15:12 | 显示全部楼层
非常赞的设计,ch32的性能发挥出来了。
hudi008 发表于 2023-1-6 16:51 | 显示全部楼层
这个写的确实非常详细了。              
pl202 发表于 2023-1-6 19:40 | 显示全部楼层
能不能实现网络告警呢?              
sanfuzi 发表于 2023-1-9 12:39 | 显示全部楼层
这个腾讯云是怎么配置的?              
burgessmaggie 发表于 2023-1-9 14:00 | 显示全部楼层
自己开发的小程序怎么关联上呢?              
cashrwood 发表于 2023-1-9 14:49 | 显示全部楼层
可以实现信息的推送吗?              
belindagraham 发表于 2023-1-9 15:49 | 显示全部楼层
这个可以应用型比较好。              
sdCAD 发表于 2023-1-9 17:26 | 显示全部楼层
腾讯云物联网平台是否有coap协议?
juliestephen 发表于 2023-1-9 17:55 | 显示全部楼层
腾讯连连小程序是独立开发吗?              
mnynt121 发表于 2023-2-4 11:25 | 显示全部楼层
现在开发物联网简单好多了。              
您需要登录后才可以回帖 登录 | 注册

本版积分规则

104

主题

384

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部