Dyc. 发表于 2023-8-8 23:48:05

【野火】瑞萨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]
查看完整版本: 【野火】瑞萨RA MCU创意氛围赛+室内空气质量监测盒子