M-Design设计竞赛 - 基于Arduino Nano Matter实现空气质量检测
该项目使用了Arduino Nano Matter开发板,实现了空气质量检测的设计,它的主要功能为:支持一氧化碳、氢气、氨气、甲烷、乙醇五种气体浓度的测量;通过OLED屏幕实时显示当前气体浓度;可以通过板载的按键,实现测试气体类型的切换;可以使用手机通过BLE连接至开发板,通过BLE发送指令,实现测试气体类型的切换;可以通过BLE实时查看当前气体浓度,当气体浓度超过一定阈值时,发出警报。
标签
Arduino
开发板
M-Design
pet
更新2025-04-02
22

一、项目介绍

项目名称为基于Arduino Nano Matter实现空气质量检测

通过Nano Matter的ADC读取气体传感器输出的原始模拟量数据,将数据转化为ppm浓度格式的数据,同时可以通过IIC对OLED屏幕进行操作来显示当前气体浓度。Nano Matter板载一颗用户按键,通过按键可以实现CO、乙醇、NH3、H2、CH4五种气体类型的切换,还可以通过BLE切换气体类型,并实现BLE读取当前气体浓度数据。


二、硬件介绍

1.来自Arduino官方的Arduino Nano Matter开发板

2.来自Seeed的0.96寸OLED屏幕

3.来自DFRobot的MiCS-5524 MEMS气体传感器

4.整体硬件连接图


三、方案框图和项目设计思路介绍

image.png

使用Arduino Nano Matter板载的ADC功能读取气体传感器输出的模拟量,将该数据通过公式转换为正常的ppm浓度值,然后通过IIC操作OLED屏幕,实时显示当前浓度数据。并且可以通过GPIO读取板载按键状态,实现检测气体类型的切换,当气体浓度超过一定阈值时,RGB LED显示红灯,在正常范围内,RGB LED显示绿灯。最后,使用Silicon Labs提供的BLE协议栈实现与手机蓝牙的连接,并在APP内发送和显示数据。在APP内发送指令,可以切换检测气体类型,同样也可以在APP内实时读取到当前的气体浓度。


四、软件流程图和关键代码介绍

image.png

setup函数主要实现:

1.串口初始化、初始化GPIO并将RGB LED设置为高电平输出、按键配置为内部上拉,低电平触发中断

  Serial.begin(115200);
  while(!Serial);
  Serial.println("Silicon Labs BLE Air Quality Monitor");


  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDR, LED_BUILTIN_INACTIVE);
  digitalWrite(LEDG, LED_BUILTIN_INACTIVE);
  digitalWrite(LEDB, LED_BUILTIN_INACTIVE);


  // If the board has a built-in button configure it for usage
  pinMode(BTN_BUILTIN, INPUT_PULLUP);
  attachInterrupt(BTN_BUILTIN, btn_state_change_callback, FALLING);

2.使用硬件IIC初始化OLED屏幕

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);  // High speed I2C
u8g2.begin();

3.初始化MiCS-5524气体传感器

#define CALIBRATION_TIME   3                      // Default calibration time is three minutes
#define ADC_PIN   A0
#define POWER_PIN 10
DFRobot_MICS_ADC mics(/*adcPin*/ADC_PIN, /*powerPin*/POWER_PIN);
  while(!mics.begin()){
    Serial.println("NO Deivces !");
    delay(1000);
  }
  Serial.println("Device connected successfully !");
  uint8_t mode = mics.getPowerState();
  if(mode == SLEEP_MODE){
    mics.wakeUpMode();
    Serial.println("wake up sensor success!");
  }else{
    Serial.println("The sensor is wake up mode");
  }
  while(!mics.warmUpTime(CALIBRATION_TIME)){
    Serial.println("Please wait until the warm-up time is over!");
    delay(1000);
  }

首先初始化气体传感器,然后判断其当前状态,如果处于睡眠模式将其唤醒,接着由于气体传感器的特性,需要先预热才能保证后续得到的数据尽可能精准,推荐的预热时间至少3分钟。

4.初始化BLE协议栈

项目中使用的BLE协议栈不是Arduino提供的,而是来自Silicon Labs官方的,但是翻遍所有例程也没看见通过哪句代码对BLE协议栈初始化,可能是当开始使用Silicon Labs官方BLE协议栈时,库内部就已经初始化了。具体设置步骤如下

实测这样设置,虽然没有看见任何函数调用BLE协议栈的语句,但是BLE协议栈已经可以使用。

loop函数主要实现:

1.获取气体传感器转换出的ppm浓度数据,并将其显示在OLED屏幕上

  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.drawStr(0,10,"M-Design");
  u8g2.sendBuffer();
  if(gas_type == 0x00)
  {
    return;
  }
  gasdata = mics.getGasData(gas_type);
  Serial.print(" Current gas type is ");
  Serial.print(gas_type);
  Serial.print(" | ");
  Serial.print(gasdata,1);
  Serial.println(" PPM");


  u8g2.setCursor(0,32);
  u8g2.print("Current Gas Type : ");
  u8g2.print(gas_type);
  u8g2.sendBuffer();
  u8g2.setCursor(0,50);
  u8g2.print("Current Gas Concen:");
  u8g2.print(gasdata);
  u8g2.sendBuffer();

获取气体浓度数据前,必须先判断当前检测的气体类型是否有效

2.判断当前气体浓度是否超出阈值,如果超出,RGB LED显示红灯,并且通过BLE传输当前数据,当气体浓度在安全范围内,RGB LED显示绿灯

  if(gasdata>200)
  {
    digitalWrite(LEDG, LED_BUILTIN_INACTIVE);
    digitalWrite(LEDB, LED_BUILTIN_INACTIVE);
    digitalWrite(LEDR, LED_BUILTIN_ACTIVE);
    send_button_state_notification();
  }
  else
  {
    digitalWrite(LEDR, LED_BUILTIN_INACTIVE);
    digitalWrite(LEDB, LED_BUILTIN_INACTIVE);
    digitalWrite(LEDG, LED_BUILTIN_ACTIVE);
  }
  delay(2000);
static void send_button_state_notification()
{
  if (!gas_notification_enabled) {
    return;
  }
  sl_status_t sc = sl_bt_gatt_server_notify_all(btn_report_characteristic_handle,
                                                sizeof(gasdata),
                                                (const uint8_t*)&gasdata);
  if (sc == SL_STATUS_OK) {
    // Serial.print("Notification sent, button state: ");
    // Serial.println(btn_state);
  }
}

按键中断函数实现:通过判断当前按键按下的次数,实现气体检测类型的切换

static void btn_state_change_callback()
{
  // If the board has a built-in button
  #ifdef BTN_BUILTIN
  // The real button state is inverted - most boards have an active low button configuration
  // btn_state = !digitalRead(BTN_BUILTIN);
  // btn_state_changed = true;


  //uint8_t current_type, last_type;
  static uint8_t cnt=0;
  if(digitalRead(BTN_BUILTIN) == 0)
  {
    cnt++;
    if(cnt > 5)
    {
      cnt = 0;
    }
  }
  switch(cnt)
  {
    case 1:gas_type=CO;break;
    case 2:gas_type=C2H5OH;break;
    case 3:gas_type=H2;break;
    case 4:gas_type=NH3;break;
    case 5:gas_type=CH4;break;
    deafult:break;
  }
  #endif // BTN_BUILTIN
}

通过BLE给开发板传输设置气体检测类型指令的相关代码

    // This event indicates that the value of an attribute in the local GATT
    // database was changed by a remote GATT client
    case sl_bt_evt_gatt_server_attribute_value_id:
      // Check if the changed characteristic is the LED control
      if (gas_type_control_characteristic_handle == evt->data.evt_gatt_server_attribute_value.attribute) {
        Serial.println("Gas Type control characteristic data received");
        // Check the length of the received data
        if (evt->data.evt_gatt_server_attribute_value.value.len == 0) {
          break;
        }
        // Get the received byte
        uint8_t received_data = evt->data.evt_gatt_server_attribute_value.value.data[0];
        // Turn the LED on/off according to the received data
        if (received_data == 0x01) {
          // set_led_on(false);
          // Serial.println("LED off");
          gas_type=CO;
        } else if (received_data == 0x02) {
          // set_led_on(true);
          // Serial.println("LED on");
          gas_type=C2H5OH;
        } else if (received_data == 0x03) {
          // set_led_on(true);
          // Serial.println("LED on");
          gas_type=H2;
        } else if (received_data == 0x04) {
          // set_led_on(true);
          // Serial.println("LED on");
          gas_type=NH3;
        } else if (received_data == 0x05) {
          // set_led_on(true);
          // Serial.println("LED on");
          gas_type=CH4;
        }
      }
      break;


五、功能展示图及说明

乙醇易挥发,空气中乙醇含量很低,当前传感器下限检测不到默认为0,亮绿灯。

使用医用酒精在手指上喷一下,靠近气体传感器,随着距离的远近,ppm浓度数据有变化,并且亮红灯。

关于BLE指令控制气体检测类型请看视频。


六、设计中遇到的难题和解决方法

设计中使用了来自DFRobot的MICS-5524 MEMS气体传感器,针对这个传感器DFRobot官方已经编写好了相对应的lib,但是当用起来的时候,发现ADC读取到的原始模拟量总是负值,并且获取到的ppm浓度也总是为0。反复验证查询发现与硬件无关,最后发现官方lib的cpp源码中有一个问题

通过ADC读取数据,但是其ADC分辨率只有10位,对应1023,当读取到的ADC原始模拟量数据稍大点,超过1023后,原始模拟量数据就会是负值,并且ppm浓度总是为0。因此目前官方lib支持的ADC是10bit,对应于UNO R3此类板子。但是Nano Matter的ADC是12bit的,且不支持修改分辨率。

因此将此处的1023更换为4095后即可正常使用该传感器,目前使用中暂未出现异常。


七、心得体会

感谢贸泽电子和硬禾学堂联合举办的活动,让更多的国外优秀板卡走进大家的视野,拓宽大家的眼界。希望此类活动以后可以层出不穷。




附件下载
air_quality.ino
团队介绍
1
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号