目前市場上的大多數空氣淨化器的傳感器採用煙塵傳感器,例如夏普的GP2Y1010AU, 該傳感器能夠檢測出大於0.8um直徑的灰塵,因爲我們現在空氣中的主要污染物爲PM2.5,所以通過一定的算法能夠大致讓其表示出當前的空氣質量指數。下圖爲CC2540最小系統與GP2Y1010AU組合起來的PM2.5檢測儀,房間裏計算出來的空氣質量指數似乎比官方通報的差一級。
下面是GPY1010AU的採樣電路與採樣方式,該傳感器通過光探測灰塵濃度,需要MCU一個PIN腳產生低脈衝,然後在低脈衝0.32ms的位置進行ADC的轉換。
下圖爲該傳感器的外圍設計電路,也就是上圖中轉接板的電路
通過一個三極管控制第3個引腳產生低脈衝,將第5引腳輸出的煙塵數據通過R14,R15進行分壓以適應MCU ADC的參考電壓。
下面是GPY1010AU在CC2540裸機上的核心代碼
#include<ioCC2540.h>
#include<stdio.h>
#include "gp2y1010au.h"
int dustPin=0;
int ledPower=2;
int delayTime=10;
int delayTime2=3;
int offTime=15000;
void delayMicroseconds(unsigned int microSec)
{
unsigned int i,j;
for(i=0;i<microSec;i++)
for(j=0;j<5;j++);
return;
}
void InitGp2y1010au(void)
{
ADCCON1 = 0x33;//軟件啓動轉換
ADCCON3 = 0x30;//AVIN0
//TR0 = 0x01; //avin, not internal
//ATEST = 0x01;
// ADCCON1 |= 0x40;//軟件啓動轉換
P1DIR |= 0X01;/*output mode*/
}
void ledPowerSet(unsigned char val)
{
LED_POWER_CTRL = val;
}
unsigned short somkeValAdcRead(void)
{
unsigned short AdValue=0;
unsigned char ADCVal_L,ADCVal_H;
//InitGp2y1010au();
//ADCCON1 = 0x33;
ADCCON3 = 0x30;//AVIN0
ADCCON1 = 0x73;
while(!(ADCCON1 & 0x80));
ADCVal_L = ADCL;
ADCVal_H = ADCH;
AdValue = ADCVal_L >>2;
AdValue |= ADCVal_H <<6;
return AdValue;
}
unsigned short smokeDetect(void){
unsigned short dustVal=0;
// ledPower is any digital pin on the arduino connected to Pin 3 on the sensor
ledPowerSet(1);
delayMicroseconds(delayTime);
dustVal=somkeValAdcRead();
delayMicroseconds(delayTime2);
ledPowerSet(0);
delayMicroseconds(offTime);
//delayMicroseconds(1000);
/*if (dustVal>36.455)
Serial.println((float(dustVal/1024)-0.0356)*120000*0.035);*/
return dustVal;
}
>
上面的代碼讀出的是ADC的碼字,需要轉換爲煙塵的濃度,轉換方法參看datasheet提供的煙塵與電壓關係曲線,如下圖所示
轉換的濃度信息爲mg/m3, 在天氣預報中的空氣質量是叫一種空氣質量指數的指標表示的,也就是AQI.
空氣質量指數(Air Quality Index,簡稱AQI),是一個用來定量描述空氣質量水平的數值。AQI的取值範圍位於0 – 500 之間。
空氣質量指數是根據各種污染物的濃度值換算出來的。要計算AQI,就需要事先確定各污染物在不同空氣質量水平下的濃度限值。例如,美國環保局(EPA)針對PM2.5的限值定義如下:
AQI的計算公式如下:
其中:
I = 空氣質量指數,即AQI,輸出值;
C = 污染物濃度,輸入值;
Clow= 小於或等於C的濃度限值,常量;
Chigh= 大於或等於C的濃度限值,常量;
Ilow= 對應於Clow的指數限值,常量;
Ihigh= 對應於Chigh的指數限值,常量。
轉換部分的核心代碼如下
unsigned short rawVal2_to_ugPerM3(unsigned short rawVal)
{
unsigned int ugPerM3Val;
unsigned int adc_mVolt;
unsigned long temp;
temp = (long)2300*(long)rawVal;
adc_mVolt = (long)temp/(long)8192;
temp = (long)10000*(long)(adc_mVolt - 625);//ug * 10times for float compute
ugPerM3Val = temp/5750;
return (unsigned short)ugPerM3Val;
}
unsigned short ugPerM3_to_AQI(unsigned short ugPerM3)
{
unsigned short AQI;
unsigned short Clow, Chigh, Ilow, Ihigh;
if((ugPerM3>C_LOW_1)&&(ugPerM3<C_HIGH_1))
{
Clow= C_LOW_1;
Chigh = C_HIGH_1;
Ilow = I_LOW_1;
Ihigh = I_HIGH_1;
}
else if((ugPerM3>C_LOW_2)&&(ugPerM3<C_HIGH_2))
{
Clow= C_LOW_2;
Chigh = C_HIGH_2;
Ilow = I_LOW_2;
Ihigh = I_HIGH_2;
}
else if((ugPerM3>C_LOW_3)&&(ugPerM3<C_HIGH_3))
{
Clow= C_LOW_3;
Chigh = C_HIGH_3;
Ilow = I_LOW_3;
Ihigh = I_HIGH_3;
}
else if((ugPerM3>C_LOW_4)&&(ugPerM3<C_HIGH_4))
{
Clow= C_LOW_4;
Chigh = C_HIGH_4;
Ilow = I_LOW_4;
Ihigh = I_HIGH_4;
}
else if((ugPerM3>C_LOW_5)&&(ugPerM3<C_HIGH_5))
{
Clow= C_LOW_5;
Chigh = C_HIGH_5;
Ilow = I_LOW_5;
Ihigh = I_HIGH_5;
}
else if((ugPerM3>C_LOW_6)&&(ugPerM3<C_HIGH_6))
{
Clow= C_LOW_6;
Chigh = C_HIGH_6;
Ilow = I_LOW_6;
Ihigh = I_HIGH_6;
}
else if((ugPerM3>C_LOW_7)&&(ugPerM3<C_HIGH_7))
{
Clow= C_LOW_7;
Chigh = C_HIGH_7;
Ilow = I_LOW_7;
Ihigh = I_HIGH_7;
}
else//full
{
return I_HIGH_7;
}
AQI = (long)(Ihigh-Ilow)*(long)(ugPerM3-Clow)/(long)(Chigh-Clow) + (long)Ilow;
return AQI;
}
/***************************
主函數
***************************/
void main(void)
{
unsigned short smokeVal;
unsigned short ugPerM3Val;
unsigned short AQIval;
//LED端口初始化
OLED_Init(); //初始化OLED
OLED_Clear();
InitGp2y1010au();
oledDisplayLabel();
while(1)
{
smokeVal = smokeDetect();
ugPerM3Val = rawVal2_to_ugPerM3(smokeVal);
AQIval = ugPerM3_to_AQI(ugPerM3Val);
gp2y1010auDisplay(AQIval);
}
}