制作灵感来源于国外一个叫做 urban feeds 的项目。仪器能够采集: - 温度
- 湿度
- CO2浓度
- O3浓度
- 光照强度
- 人流量
- 大于1微米空气悬浮粒子
- 大于2.5微米空气悬浮粒子
- 日期时间
- 位置信息
采用SD卡存储数据,锂电池供电,外观如图:
全部配件包括: - 密封盒 1个 115×90×55
 - Arduino UNO核心板 1块
 - SD卡模块1个及SD卡1张(容量不限,但由于FAT16文件分区格式的限制单个分区应小于2G)
 - SHT10温湿度传感器 1个
 - BH1750FVI光照传感器 1个
 - MG811二氧化碳(CO2)传感器(无需底板) 1个
 - MQ131臭氧(O3)传感器(无需底板) 1个
 - 七星座 2个
 - DSM501A粉尘传感器 1个
 - SIRF II GPS模块 1个
 - HC-SR501 人体红外感应模块 1个
 - 0.5A 单锂电池充电、升压板(保护+充电+升压+充电指示) 1块
 - 606168P 聚合物锂电池 3.7V 2800mAH 1块
 - CA3140 阻抗变换 1个
- 二极管1N4007 1个
- 3mm 发光二极管LED 绿(颜色随意) 1个
- 电容 10nF(104贴片) 1个
- 电阻 220 1个、10K(104贴片) 1个、22K 1个、10K 2个、1K 1个
- 双面洞洞板 2×8cm 1块
- 单排针 间距2.54mm 高11mm和高17mm 若干
- 彩色杜邦线建议最短20cm 若干
- KN3-3 拨动开关(六脚) 1个
- M3螺母 若干
- 6+12 M3铜柱子 若干
- M3螺丝 包括10mm和6mm(非必需) 若干
- M3空心铜柱(非必需) 若干
电路部分:
- SD卡接法请参考:arduino学习笔记18 - SD卡读写实验
- SHT10 data脚接D8,clock脚接D9
- BH1750FVI 是I2C协议,参见:arduino学习笔记27 - DS1307 RTC时钟芯片与DS18B20数字温度传感器实验中DS1307 与UNO板的接法
- MG811二氧化碳传感器阻抗变换电路如图,接A3:
 - MQ131臭氧传感器辅助电路如图,接A2:
 - DSM501A粉尘传感器接线如图,接输出1接D7,输出2接D6:
 - SIRF II GPS模块TX接D0,RX接D1
- HC-SR501 人体红外感应模块输出接A1
- 发光二极管串联220欧姆电阻接在电源地和D2之间
- 锂电池充电、升压板与锂电池接法按板子说明连接
- 六角拨动开关一组控制仪器电源开关,一组控制锂电池充电开关,两组控制状态互斥如示意图:
 - 电源供电从锂电池充电、升压板升压输出端+5v经拨动开关仪器电源一组连接到UNO Vin脚;电池充电从UNO +5v输出经拨动开关仪器充电一组接到锂电池充电、升压板充电输入端
- 从锂电池充电、升压板充电输入端引出线到A0并为A0设置10K下拉电阻
- 所有传感器(除SD卡、温湿度)的+5v输入接UNO Vin脚,SD卡和温湿度传感器接UNO +5v输出脚;所有传感器的GND和锂电池充电、升压板的充电输入、+5V升压输出的GND接UNO GND脚
仪器组装:
 
DSM501A粉尘传感器 需垂直安装
仪器内部:

- //davidce 20111211
-
- // Include
- #include <SHT1x.h>
- #include <SD.h>
- #include <Wire.h>
-
- // Digital pin
- // SHT1x
- #define ShtDataPin 8 //data
- #define ShtClockPin 9 //clock
- SHT1x sht1x(ShtDataPin, ShtClockPin);
- // system light
- #define systemLightPin 2
- //DSM501A DUST
- #define DSMPin2_5 6
- #define DSMPin1_0 7
- //SD card
- File myFile;
- #define SD_CSPin 10
- char filename[] = "result.txt";
- String order= "";
- //GPS
- #define rxPin 0 // RX PIN
- #define txPin 1 // TX TX
-
- // Analog pin
- const int powerInPin = A0; // check power state for the bettery charge
- const int infrRayPin = A1; //infrared ray
- const int o3Pin = A2; //MQ131
- const int co2Pin = A3; //MQ811
-
- // Variable
- boolean inCharge = false; //bettery charging mark
- const unsigned long warmTime = 60000; //system warmming 60000 ms
- const unsigned long scanTime = 0; //get data fre scanTime = scanTime + flashTime
- const unsigned long flashTime = 250;
- unsigned long previousMillis = 0; //前一次判断时间点
- unsigned long partMillis=0; //到溢出时计算的时间
- const unsigned long sectev = 30; // 时间间隔(秒)
- const unsigned long interval = 30000; // 时间间隔(毫秒) = sectev * 1000
- const unsigned long mintev = 30000000; // 时间间隔(微秒) =interval * 1000
- //BH1750 IIC Mode
- const int BH1750address = 0x23; //setting i2c address
- byte buff[2];
- //GPS
- int byteGPS = -1;
- char linea[300] = "";
- char comandoGPR[7] = "$GPRMC";
- int cont=0;
- int bien=0;
- int conta=0;
- int indices[13];
-
- void setup() {
- if(!SD.begin(SD_CSPin))
- {
- return;
- }
- // read the value from the power
- int powerValue = analogRead(powerInPin); // variable to store the value coming from the power
- if(powerValue>1000)
- {
- inCharge=true;
- Serial.begin(4800); //port speed for GPS
- }
- else
- {
- //check if the log file exists and add name of items to the new file
- if (!SD.exists(filename))
- {
- myFile = SD.open(filename, FILE_WRITE);
- if (myFile)
- {
- myFile.print("date_UTC");
- myFile.print(9,BYTE);
- myFile.print("time_UTC");
- myFile.print(9,BYTE);
- myFile.print("lat");
- myFile.print(9,BYTE);
- myFile.print("lon");
- myFile.print(9,BYTE);
- myFile.print("temp_C");
- myFile.print(9,BYTE);
- myFile.print("hum_PER");
- myFile.print(9,BYTE);
- myFile.print("pcs_1");
- myFile.print(9,BYTE);
- myFile.print("pcs_2_5");
- myFile.print(9,BYTE);
- myFile.print("peop_tra");
- myFile.print(9,BYTE);
- myFile.print("O3");
- myFile.print(9,BYTE);
- myFile.print("CO2");
- myFile.print(9,BYTE);
- myFile.println("light_lx");
- myFile.close();
- }
- }
- for (int i=0;i<300;i++)
- { // Initialize a buffer for received data
- linea[i]=' ';
- }
- pinMode(systemLightPin, OUTPUT);
- pinMode(DSMPin2_5, INPUT);
- pinMode(DSMPin1_0, INPUT);
- pinMode(rxPin, INPUT);
- pinMode(txPin, OUTPUT);
- Wire.begin();
- Serial.begin(4800); //port speed for transform
- digitalWrite(systemLightPin, HIGH);
- delay(warmTime);
- }
- }
-
- void loop(){
- if(!inCharge) // work state
- {
- float temp_c =sht1x.readTemperatureC();
- float humidity = sht1x.readHumidity();
-
- unsigned long currentMillis;
- boolean goloop=true;
- partMillis=0;
- unsigned long duration1_0=0;
- unsigned long duration2_5=0;
- long temp1_0=0;
- long temp2_5=0;
- unsigned long rayMark=0; //人流量计数
- double rayFreq = 0.0; //人流量频率
-
- while(goloop) //loop
- {
- currentMillis = micros();
- if(currentMillis<previousMillis)
- {
- partMillis = 4294967295 - previousMillis +1;
- previousMillis = 0;
- }
- if(currentMillis - previousMillis - partMillis < mintev)
- {
- //1.0
- if(temp1_0==0)
- {
- temp1_0=-1;
- temp1_0=pulseIn(DSMPin1_0, LOW);
- }
- if(temp1_0>0)
- {
- duration1_0 =duration1_0 + temp1_0;
- temp1_0=0;
- }
- //2.5
- if(temp2_5==0)
- {
- temp2_5=-1;
- temp2_5=pulseIn(DSMPin2_5, LOW);
- }
- if(temp2_5>0)
- {
- duration2_5 =duration2_5 + temp2_5;
- temp2_5=0;
- }
- }
- else
- {
- goloop=false;
- previousMillis=currentMillis;
- }
- //human transform
- int rayState = analogRead(infrRayPin);
- if(rayState>500)
- {
- rayMark = rayMark + 1;
- }
- }
- double per =double(duration1_0)/double(interval);// had multiply 1000
- int pcs1_0 = -1;
- pcs1_0 = per * 50.0;
- per =double(duration2_5)/double(interval);// had multiply 1000
- int pcs2_5 = -1;
- pcs2_5 = per * 50.0;
- rayFreq = double(rayMark) /double(sectev);
-
- // light
- uint16_t lightval=0;
- BH1750_Init(BH1750address);
- delay(200);
- if(2==BH1750_Read(BH1750address))
- {
- lightval=((buff[0]<<8)|buff[1])/1.2;
- }
-
- //MQ131
- int O3v=analogRead(o3Pin);
- float O3ppb=float(O3v) * 0.0049; //not realy value
- float O3mg_m3 = O3ppb * 48 / 22.4 / 1000; //need ajaust
- //MG811
- int CO2v=analogRead(co2Pin);
- float CO2ppb=float(CO2v) * 0.0049; //not realy value
-
- //GPS
- String datestr = ""; //date UTC (ddmmyy)
- String timestr = ""; //time UTC (hhmmss.sss)
- String latstr = ""; //Latitude (ddmm.mmmm)
- String lonstr = ""; //Longitude (dddmm.mmmm)
- boolean isGPSOK = false;
- bien=0;
- while(bien!=6)
- {
- byteGPS=Serial.read();
- if(byteGPS == -1)
- {
- delay(100);
- }
- else
- {
- linea[conta]=byteGPS; // If there is serial port data, it is put in the buffer
- conta++;
- if(byteGPS==13)
- {
- cont=0;
- bien=0;
- for (int i=1;i<7;i++)
- { // Verifies if the received command starts with $GPRMC
- if (linea[i]==comandoGPR[i-1])
- {
- bien++;
- }
- }
- if(bien==6) // If yes, continue and process the data
- {
- for (int i=0;i<300;i++)
- {
- if (linea[i]==',')
- { // check for the position of the "," separator
- indices[cont]=i;
- cont++;
- }
- if (linea[i]=='*')
- { // ... and the "*"
- indices[12]=i;
- cont++;
- }
- }
- String dataString;
- int outindex;
- outindex=1;
- for (int j=indices[outindex];j<(indices[outindex+1]-1);j++)
- {
- if(linea[j+1]=='A')
- {
- isGPSOK = true;
- }
- }
- outindex=8; //Date UTC (ddmmyy)
- dataString="";
- for (int j=indices[outindex];j<(indices[outindex+1]-1);j++)
- {
- dataString = dataString + linea[j+1];
- }
- datestr=dataString;
- outindex=0; //time UTC (hhmmss.sss)
- dataString="";
- for (int j=indices[outindex];j<(indices[outindex+1]-1);j++)
- {
- dataString = dataString + linea[j+1];
- }
- timestr=dataString;
- outindex=2; //Latitude (ddmm.mmmm)
- dataString="";
- for (int j=indices[outindex];j<(indices[outindex+1]-1);j++)
- {
- dataString = dataString + linea[j+1];
- }
- latstr=dataString;
- outindex=4; //Longitude (dddmm.mmmm)
- dataString="";
- for (int j=indices[outindex];j<(indices[outindex+1]-1);j++)
- {
- dataString = dataString + linea[j+1];
- }
- lonstr=dataString;
- }
- // Reset the buffer
- conta=0;
- for (int i=0;i<300;i++)
- {
- linea[i]=' ';
- }
- }
- }
- }
-
- //output result
- Serial.print(datestr);
- Serial.print(9,BYTE);
- Serial.print(timestr);
- Serial.print(9,BYTE);
- Serial.print(latstr);
- Serial.print(9,BYTE);
- Serial.print(lonstr);
- Serial.print(9,BYTE);
- Serial.print(temp_c);
- Serial.print(9,BYTE);
- Serial.print(humidity);
- Serial.print(9,BYTE);
- Serial.print(pcs1_0);
- Serial.print(9,BYTE);
- Serial.print(pcs2_5);
- Serial.print(9,BYTE);
- Serial.print(rayFreq);
- Serial.print(9,BYTE);
- Serial.print(O3ppb);
- Serial.print(9,BYTE);
- Serial.print(CO2ppb);
- Serial.print(9,BYTE);
- Serial.println(lightval,DEC);
-
- //writer the result to SD card
- myFile = SD.open(filename, FILE_WRITE);
- if (myFile)
- {
- // to sd file
- myFile.print(datestr);
- myFile.print(9,BYTE);
- myFile.print(timestr);
- myFile.print(9,BYTE);
- myFile.print(latstr);
- myFile.print(9,BYTE);
- myFile.print(lonstr);
- myFile.print(9,BYTE);
- myFile.print(temp_c);
- myFile.print(9,BYTE);
- myFile.print(humidity);
- myFile.print(9,BYTE);
- myFile.print(pcs1_0);
- myFile.print(9,BYTE);
- myFile.print(pcs2_5);
- myFile.print(9,BYTE);
- myFile.print(rayFreq);
- myFile.print(9,BYTE);
- myFile.print(O3ppb);
- myFile.print(9,BYTE);
- myFile.print(CO2ppb);
- myFile.print(9,BYTE);
- myFile.println(lightval,DEC);
- myFile.close();
-
- //flash the light
- if(isGPSOK)
- {
- digitalWrite(systemLightPin, HIGH);
- delay(flashTime);
- digitalWrite(systemLightPin, LOW);
- }
- else
- {
- digitalWrite(systemLightPin, LOW);
- delay(flashTime);
- digitalWrite(systemLightPin, HIGH);
- }
- }
- delay(scanTime);
- }
- else //bettery charging and data translation
- {
- while(Serial.available() > 0)
- {
- int incomingByte = Serial.read();
- if(incomingByte==10) //order end
- {
- if(order == "list")
- {
- myFile = SD.open(filename);
- if (myFile)
- {
- while (myFile.available())
- {
- Serial.write(myFile.read());
- }
- myFile.close();
- }
- else
- {
- Serial.println("open file failure.");
- }
- }
- else if(order.length()>0)
- {
- Serial.println("The available command is:");
- Serial.println("list");
- }
- //reset order
- order="";
- }
- else
- {
- if(incomingByte!=13)
- {
- order = order + char(incomingByte);
- }
- }
- }
- }
- }
-
- int BH1750_Read(int address)
- {
- int i=0;
- Wire.beginTransmission(address);
- Wire.requestFrom(address, 2);
- while(Wire.available())
- {
- buff[i] = Wire.receive(); // receive one byte
- i++;
- }
- Wire.endTransmission();
- return i;
- }
- void BH1750_Init(int address)
- {
- Wire.beginTransmission(address);
- Wire.send(0x10);//1lx reolution 120ms
- Wire.endTransmission();
- }
复制代码
代码说明:- 传感器需要预热时间,在变量warmTime 中设置,这里设置为60秒
- 仪器根据inCharge 变量判断系统处于采集状态或充电状态,在采集状态时如插上usb可通过arduino IDE的串口监视实现数据显示;仪器通过arduino的usb口充电,在充电的同时可通过arduino IDE的串口监视实现交互,如输入list命令可列出SD卡存储的数据
- DSM501A灰尘传感器有两路输出分别对应不同的灰尘粒径检出量,检测到灰尘是在输出口产生时间不等的低脉冲,通过计算30秒内的低脉冲率和对于关系得到灰尘粒子量(请参考DSM501A datasheet),程序中通过micros()函数提供计时,micros()每70分钟左右会归零,通过 4294967295 - previousMillis +1 得出归零前的计数加到归零后的计数上实现连续计数
- 程序通过pulseIn()返回低脉冲时间,虽然pulseIn()函数可以设置超时(默认1秒)但并不产生中断,代码中通过轮询pulseIn()的返回变量的值判读pulseIn()是否完成读取
- 人体红外线模块的脉冲电平是3.3v,通过模拟读取时设置读数大于500为有输出
- 当GPS模块可靠定位时系统状态灯常灭,采集数据时亮250ms;GPS不可靠定位时系统状态灯常亮,采集数据时灭250ms,GPS上集成电池,定位可靠与非都有日期时间输出(UTC时间,中国时区 +8小时)
- BH1750光照传感器在太阳光直接照射的情况下有可能爆表(输出为0),建议避免强光直射
关于传感器标定:
每个传感器在出厂后的性能是不同的,在实际的应用中需要进行标定。仪器中需要标定的传感器是MG811二氧化碳、MQ131臭氧和DSM501A灰尘传感器,其它的传感器是数字输出,在出厂前经过标定。标定方法参考:
数据采集与展示:
固定采集:
移动采集:
- 采集路线:
 - 温度分布:
 - 湿度分布:
 - CO2分布:
 - 1微米以上粒子:

图中红色代表高值,蓝色代表低值。
下一步工作:
MQ系列传感器辅助电路类似,仪器只需更换传感器探头和标定可实现其它气体测量
由于MQ131和MG811属于加热型传感器耗电量大,2800mAh的锂电池只能连续工作3.5小时左右,同时二氧化碳和臭氧气体不适合移动观测,下一步打算将两种气体传感器设置在固定监测仪器上,在留出的面板位置上安装1602液晶显示,仪器同时实现便携式GPS功能。
致谢:
仪器在制作过程中得到极客工坊论坛和Arduino 与 ADK(1277738)QQ群热心网友的大力帮助,他们早出晚归,谈天论地,不分主题,有问必答,畅所欲言,谢谢大家!!
完。
|