【野火】瑞萨RA MCU创意氛围赛+室内空气质量监测盒子
本帖最后由 Dyc. 于 2023-8-23 21:39 编辑一、背景
空气污染越来越被大家重视,人们80%的时间都是在室内度过的,室内的空气环境质量对人们的身体健康更是息息相关,有幸参加野火电子与瑞萨联合举办的创意氛围赛,借着这次能白嫖的机会:P,把手里现有的传感器在RA平台上完成一个室内空气质量监测的盒子实现,有不足之处希望各位大佬指正修改开源(减少脑细胞死亡;P),下面介绍一下整个系统的架构,以及各个模块功能作用。
二、系统设计与实现
白嫖当然申请最高配置的(野火启明RA6M5),各个传感器模块采集数据到RA6M5,进行数据处理和组包,在OLED屏山显示实时采集的数据,监测各个模块采集数据是否在合理范围,当超出安全值时,根据不同梯度范围,使蜂鸣器发出不同音调的声音进行提示。数据组包完成后,与板载的ESP8266进行通信,将采集的数据通过WiFi远程传输到Blinker服务器中,手机端访问blinker应用可查看历史和当前监测的室内环境数据信息。
三、硬件模块
整个系统分为四个模块,分别是数据采集:各个传感器感知数据, 数据处理传输:启明RA6M5(最强大脑),板载ESP8266,数据显示:0.96寸OLED屏,Blinker终端显示, 报警提示:蜂鸣器。传感器:温湿度采集 SH30、颗粒物浓度监测 PMS1003、 二氧化碳浓度监测 MH-Z19B、甲醛浓度监测 DS-HCHO,每个模块的具体性能就不一一列举了,此处省略1000字(淘宝百度很详细),这里贴几张图占位置。
SH30: DS-HCHO:
PMS1003: MH-Z19B:
其余模块都在板子上,可看野火淘宝旗舰店官方高清大图:)
四、系统实现
详情见代码:
针对上面的系统架构中各个模块在,代码中的具体实现,下面简要列举几个模块
1.这是系统进入的原始接口,实现方式是串行模式,最开始初始化系统,LED、按键、串口打印、串口通信,温度传感器、CO2、空气质量传感器、OLED、HCHO、Beep,然后进入while循环依次调用各模块数据读取接口。void hal_entry(void)
{
/* TODO: add your own code here */
LED_Init(); // LED 初始化
Key_IRQ_Init(); // KEY 外部中断初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
Debug_UART9_Init();
gxht30_init(); //白 sda:p105 黄 scl:p104
doublea = 0.0;
doubleb = 0.0;
uint8_t buff1;
uint8_t buff2;
uint8_t buff3;
uint8_t buff4;
OLED_Init(); // scl - P505 sda - P506
CO2_init();//白-> rx3: 408棕->tx3:409 红:5v黑:Gnd
int co2=0;
struct PM25 *PM_data = NULL;
PM_data = (struct PM25 *)malloc(sizeof(struct PM25));
PM25_init();// 蓝 -> rx5:p513 黑 -> tx5:p805红色:5v黄色:GND
HCHO_init(); //黄——> rx2:p302绿——>tx2:p301
// printf("start\r\n");
OLED_Clear();
R_BSP_SoftwareDelay(3, BSP_DELAY_UNITS_MILLISECONDS);
while(1)
{
LED2_ON;
if (recv_flag) {//debug
recv_flag = false;
sprintf(Send_buff, "%2x %4d %2.2f %2.2f %5d %.2f %2x", 0x42, co2, a, b, PM2_5, hcho_data, 0xff);//28
printf ("%s\r\n", Send_buff);
memset(Send_buff, 0, sizeof(Send_buff));
}
if (uart9_recv_flag) {// send data to esp8266
if (!CO2_read()) {
co2 = co2_data;
// Buzzer_co2(co2);
// printf("co2:%d\r\n", co2);
}
if(gxht30_read()) {
a = gxht30_get_temperature();
b = gxht30_get_humidity();
// printf("Temp:%.2f Humi:%.2f\r\n", a, b);
}
PM25_read(PM_data);
if (PM_data != NULL) {
// printf("PM1_0:%d PM2_5:%d PM10:%d\r\n", PM1_0, PM2_5, PM10);
}
if (HCHO_read()) {
// Buzzer_hcho(hcho_data);
// printf("hcho:%.2f %s\r\n", hcho_data, hcho_unit);
}
sprintf(buff1,"Temp:%.2f Humi:%.2f", a , b);
sprintf(buff2,"PM1.0:%dPM2.5:%d", PM1_0, PM2_5);
sprintf(buff3,"PM10:%d CO2:%d", PM10 , co2);
sprintf(buff4,"HCHO:%.2f %s", hcho_data, hcho_unit);
sprintf(Send_buff, "%2x %4d %2.2f %2.2f %5d %2x\r\n", 0x42, co2, a, b, PM2_5, 0xff);//28
// printf ("buff1:%d\n", strlen(buff1));
// printf ("buff2:%d\n", strlen(buff2));
// printf ("buff3:%d\n", strlen(buff3));
// printf ("buff4:%d\n", strlen(buff4));
LED3_ON;
uart9_recv_flag = false;
uint32_t send_leng = 0;
send_leng = strlen(Send_buff);
uart9_Write_data(Send_buff, send_leng);
memset(Send_buff, 0, sizeof(Send_buff));
LED3_OFF;
OLED_ShowString(0, 0, "Temp:", 12, 1);
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式0,填充模式;1,叠加模式
//num:数值(0~4294967295);
// void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode)
// OLED_ShowNum(26, 0, 20, 2, 12, 1);
OLED_ShowString(0, 0, buff1, 12, 1);
OLED_ShowString(0, 13, buff2, 12, 1);
OLED_ShowString(0, 26, buff3, 12, 1);
OLED_ShowString(0, 38, buff4, 12, 1);
OLED_Refresh_Gram();
}
// OLED_ShowHzStringRow(30, 0, (const char*)"启明", 1);
// OLED_ShowString(62, 0, (const uint8_t*)"R6M5", 16, 1);
// OLED_ShowHzStringRow(32, 24, (const char*)"电子发烧友", 1);
/* 判断按键 KEY1_SW2 是否被按下 */
if (key1_sw2_press)
{
key1_sw2_press = false; //标志位清零
// PM_SLEEP_ON;
// BEEP_ON = true;
OLED_Clear();
R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS); // 10ms
OLED_ON();
}
/* 判断按键 KEY2_SW3 是否被按下 */
if (key2_sw3_press)
{
key2_sw3_press = false; //标志位清零
// PM_SLEEP_OFF;
// BEEP_ON = false;
OLED_OFF();
LED3_TOGGLE; //LED3 翻转
}
R_BSP_SoftwareDelay(2, BSP_DELAY_UNITS_SECONDS);
LED2_OFF;
R_BSP_SoftwareDelay(2, BSP_DELAY_UNITS_SECONDS);
}
free(PM_data);
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
2、CO2模块
根据数据CO2数据使用手册,实现数据读取校验功能
int CO2_read()
{
uint8_t co2_tmp_buff;
memset(co2_tmp_buff, 0, sizeof(co2_tmp_buff));
int ret =0;
if (only_once_skip)
{
only_once_skip = 0;
// first data is error, so skip
Write_data(str);
R_BSP_SoftwareDelay (100, BSP_DELAY_UNITS_MILLISECONDS);
co2_err = R_SCI_UART_Read (&g_uart3_ctrl, co2_tmp_buff, 9);
assert(FSP_SUCCESS == co2_err);
memset(co2_tmp_buff, 0, sizeof(co2_tmp_buff));
R_BSP_SoftwareDelay (500, BSP_DELAY_UNITS_MILLISECONDS);
}
uint8_t i;
for (i = 0; i < 1; i++)
{
Write_data(str);
R_BSP_SoftwareDelay (100, BSP_DELAY_UNITS_MILLISECONDS);
co2_err = R_SCI_UART_Read (&g_uart3_ctrl, co2_tmp_buff, 9);
assert(FSP_SUCCESS == co2_err);
while ((UART_EVENT_RX_COMPLETE != g_uart3_event) && timeout_ms > 0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
timeout_ms--;
}
if (UART_EVENT_ERR_FRAMING == g_uart3_event)
{
__BKPT(0);
}
g_uart3_event = UART_EVENT_ERR_FRAMING;
timeout_ms = 100000;
// uint8_t j =0;//打印接收到的原始数据
// for (j =0; j < 9; j++)
// {
// printf("num:%d 0x%02x\n",j, co2_tmp_buff);
// }
co2_data = 0;
co2_data = co2_tmp_buff *256 + co2_tmp_buff;
ret = getCheckSum(co2_tmp_buff);
if (ret == 0)
{
co2_data = 0;
co2_data = co2_tmp_buff *256 + co2_tmp_buff;
// printf("%s()%d co2_data:%d\n", __func__, __LINE__, co2_data);
break;
}
printf("%s()%d CO2 check sum error\r\n",__func__, __LINE__);
i = 0;//retry only once
}
return ret;
}
3、温度传感器
根据sht30数据手册,完成数据采集读取功能
bool gxht30_read()
{
Humiture_HS3003_writeRegister(gxht30_cmd_read_interval_measure, 2);
uint8_t data = { 0 };
timeout_ms1 = 100000;
gxht30_err = R_SCI_I2C_Read(&g_sci8_i2c_ctrl, data, sizeof(data), true);//truefalse
assert(FSP_SUCCESS == gxht30_err);
while ((I2C_MASTER_EVENT_RX_COMPLETE != sci8_i2c_event) && timeout_ms1)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
timeout_ms1--;
}
if (I2C_MASTER_EVENT_ABORTED == sci8_i2c_event)
{
__BKPT(0);
}
sci8_i2c_event = I2C_MASTER_EVENT_ABORTED;
timeout_ms1 = 100000;
//check CRC, when fail, return false
if (data != crc8 (data, 2) || data != crc8 (&data, 2))
{
return false;
}
temperature = ((((data * 256.0) + data) * 175) / 65535.0) - 45;
humidity = (((data * 256.0) + data) * 100) / 65535.0;
// printf("Temp:%.2f\r\n", temperature);
// printf("Humidity:%.2f\r\n", humidity);
return true;
}4、空气质量传感器
这里使用的PMS1003,可以采集PM1.0, PM2.5, PM10等颗粒物浓度,可以根据项目需求进行调整
int PM25_read(struct PM25 *p)
{
read_flag = 0;
memset(tmp_Buff,0,sizeof(tmp_Buff));
while(1)
{
uint8_t ch;
read(&g_uart5_ctrl, &ch, 1);
if (uart_recv0_complete_flag == true && read_flag == 32)
{
uart_recv0_complete_flag = false;
int ret = 0;
read_flag = 0;
// int j =0;//PM2.5 原始数据
// for (j =0; j < read_flag; j++)
// {
// printf("num:%d 0x%02lx\n",j, tmp_Buff);
// }
ret = data_checksum(p);
if (ret == 0)
{
break;
}
printf("%s()%d PM2.5 data checksum error\r\n", __func__, __LINE__);
break;
}
}
return 0;
}5、HCHO
甲醛浓度传感器,目前使用的这个模块能采集多种有毒有害气体,根据数据手册实现了采集校验,区分气体类型功能
int HCHO_read()
{
uint8_t hcho_tmp_buff;
memset(hcho_tmp_buff, 0, sizeof(hcho_tmp_buff));
int ret =0;
uint8_t str = {0x42, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x90};
int equivalent = 1;//当量
if (hcho_only_once_skip)
{
hcho_only_once_skip = 0;
// first data is error, so skip
HCHO_Write_data(str);
R_BSP_SoftwareDelay (100, BSP_DELAY_UNITS_MILLISECONDS);
hcho_err = R_SCI_UART_Read (&g_uart2_ctrl, hcho_tmp_buff, 10);
assert(FSP_SUCCESS == hcho_err);
memset(hcho_tmp_buff, 0, sizeof(hcho_tmp_buff));
R_BSP_SoftwareDelay (500, BSP_DELAY_UNITS_MILLISECONDS);
}
uint8_t i;
for (i = 0; i < 1; i++)
{
HCHO_Write_data(str);
R_BSP_SoftwareDelay (100, BSP_DELAY_UNITS_MILLISECONDS);
hcho_err = R_SCI_UART_Read (&g_uart2_ctrl, hcho_tmp_buff, 10);
assert(FSP_SUCCESS == hcho_err);
while ((UART_EVENT_RX_COMPLETE != g_uart2_event) && hcho_timeout_ms > 0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
hcho_timeout_ms--;
}
if (UART_EVENT_ERR_FRAMING == g_uart2_event)
{
__BKPT(0);
}
g_uart2_event = UART_EVENT_ERR_FRAMING;
hcho_timeout_ms = 100000;
// int j =0;//打印HCHO原始数据
// for (j =0; j < 10; j++)
// {
// printf("num:%d 0x%02x\n",j, hcho_tmp_buff);
// }
// printf("\n");
ret = HCHOCheckSum(hcho_tmp_buff);
if (ret == 0)
{
if (hcho_tmp_buff == 0x14)
{
// 数据单位标识
if (hcho_tmp_buff == 0x01)
{
hcho_unit = "ppm";
}
else if (hcho_tmp_buff == 0x02)
{
hcho_unit = "VOL";
}
else if (hcho_tmp_buff == 0x03)
{
hcho_unit = "LEL";
}
else if (hcho_tmp_buff == 0x04)
{
hcho_unit = "Ppb";
}
else if (hcho_tmp_buff == 0x05)
{
hcho_unit = "Mg/m3";
}
else
{
hcho_unit = "unknown";
}
//当量判断
if (0x01 == hcho_tmp_buff)
{
equivalent = 1;
}
else if(0x02 == hcho_tmp_buff)
{
equivalent = 10;
}
else if(0x03 == hcho_tmp_buff)
{
equivalent = 100;
}
else if(0x04 == hcho_tmp_buff)
{
equivalent = 1000;
}
else
{
printf("%s():%d equivalent read error\r\n", __func__, __LINE__);
}
hcho_data = (float)((hcho_tmp_buff<<8) | hcho_tmp_buff);
hcho_data = hcho_data / (float)equivalent;
// printf("%s():%d hcho_data:%.2f %s\r\n", __func__, __LINE__, hcho_data, hcho_unit);
ret = 1;
break;
}
}
printf("%s():%d HCHO check sum error\r\n", __func__, __LINE__);
i = 0;//retry only once
}
ret = -1;
return ret;
}6、RA6M5与ESP8266通信
通过串口回调,实现RA6M5与ESP8266数据的通信传输功能
void uart9_Write_data(uint8_t *buffer, uint32_t datalen)
{
R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
uart9_err = R_SCI_UART_Write(&g_uart9_ctrl, buffer, datalen);
assert(FSP_SUCCESS == uart9_err);
while ((UART_EVENT_TX_DATA_EMPTY != g_uart9_event) && uart9_timeout_ms > 0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
uart9_timeout_ms--;
}
if (UART_EVENT_ERR_FRAMING == g_uart9_event)
{
__BKPT(0);
}
g_uart9_event = UART_EVENT_ERR_FRAMING;
uart9_timeout_ms = 100000;
}
void uart9_read_data(uint8_t *buffer, uint32_t datalen)
{
uart9_err = R_SCI_UART_Read (&g_uart9_ctrl, buffer, datalen);
assert(FSP_SUCCESS == uart9_err);
while ((UART_EVENT_RX_COMPLETE != g_uart9_event) && uart9_timeout_ms > 0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
uart9_timeout_ms--;
}
if (UART_EVENT_ERR_FRAMING == g_uart9_event)
{
__BKPT(0);
}
g_uart9_event = UART_EVENT_ERR_FRAMING;
uart9_timeout_ms = 100000;
}
五、界面展示
终端显示画面:
板子画面:
页:
[1]