├── README.md └── arduino_sensor_main.ino /README.md: -------------------------------------------------------------------------------- 1 | ![Arduino Sensor GP2Y1010AU0F arduinosensor.tumblr.com](https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/Arduino_Logo.svg/320px-Arduino_Logo.svg.png) 2 | # [Arduino Sensor Project](http://arduinosensor.tumblr.com) 3 | ## [arduinosensor.tumblr.com](http://arduinosensor.tumblr.com) 4 | 5 | Arduino PM2.5 / PM10 Pollution Sensor based on Sharp Optical Dust Sensor GP2Y1010AU0F Check articles about air pollution and description of my arduino project at http://arduinosensor.tumblr.com/ 6 | 7 | *** 8 | 9 | ### GP2Y1010AU0F Scharp Dust Sensor 10 | 11 | Dust sensor calculates dust density based on reflected 12 | infrared light from IR diode. Light brightness coresponds 13 | to amount of dust in the air. Following program is responsible 14 | to light-up IR diode, perform dust sampling and swith-off diode, 15 | according to GP2Y1010AU0F specification. 16 | Arduino program performs sequence of several measurements, 17 | filters input data by mid point calulation based on several 18 | measurements, to avoid voltage spikes. Then it performs dust 19 | values transformation to ug/m2. Finaly calculated data could 20 | be printed to Arduino serial output or to LCD display. 21 | 22 | 23 | AQI Index could be clculated as 24 | 25 | -------------------------------- 26 | PM2.5 ug | AQI | Pollution 27 | =========|=========|============ 28 | 0-35 | 0-50 | Excelent 29 | ---------|---------|------------ 30 | 35-75 | 51-100 | Average 31 | ---------|---------|----------- 32 | 75-115 | 101-150 | Light 33 | ---------|---------|----------- 34 | 115-150 | 151-200 | Modrate 35 | ---------|---------|----------- 36 | 150-250 | 201-300 | Heavy 37 | ---------|---------|----------- 38 | 250-500 | > 300 | Serious 39 | ---------|---------|----------- 40 | 41 | Please note that this program is adapted for waveshare GP2Y1010AU0F adapter board for Arduino. 42 | Board inverts voltage to turn infrared led on/off and has signal 1/10 divider. 43 | So to turn-on the ILED you should call digitalWrite(PIN_LED, HIGH); and to switch-off digitalWrite(PIN_LED, LOW); 44 | Details & sample code you could find on waweshare WIKI page: [http://www.waveshare.com/wiki/Dust_Sensor](http://www.waveshare.com/wiki/Dust_Sensor) 45 | ![Arduino Sensor GP2Y1010AU0F arduinosensor.tumblr.com](http://www.waveshare.com/w/upload/f/fd/Dust-Sensor-intro.jpg) 46 | -------------------------------------------------------------------------------- /arduino_sensor_main.ino: -------------------------------------------------------------------------------- 1 | /* *************************************************************** 2 | * **** GP2Y1010AU0F Scharp Dust Sensor with waveshare board ***** 3 | * *************************************************************** 4 | * Dust sensor calculates dust density based on reflected 5 | * infrared light from IR diode. Light brightness coresponds 6 | * to amount of dust in the air. Following program is responsible 7 | * to light-up IR diode, perform dust sampling and swith-off diode, 8 | * according to GP2Y1010AU0F specification. 9 | * Arduino program performs sequence of several measurements, 10 | * filters input data by mid point calulation based on several 11 | * measurements, to avoid voltage spikes. Then it performs dust 12 | * values transformation to ug/m2. Finaly calculated data could 13 | * be printed to Arduino serial output or to LCD display. 14 | * http://www.waveshare.com/wiki/Dust_Sensor 15 | * *************************************************************** 16 | * AQI Index could be calculated as 17 | * -------------------------------- 18 | * PM2.5 ug | AQI | Pollution 19 | * =========|=========|============ 20 | * 0-35 | 0-50 | Excelent 21 | * ---------|---------|------------ 22 | * 35-75 | 51-100 | Average 23 | * ---------|---------|------------ 24 | * 75-115 | 101-150 | Light 25 | * ---------|---------|------------ 26 | * 115-150 | 151-200 | Modrate 27 | * ---------|---------|------------ 28 | * 150-250 | 201-300 | Heavy 29 | * ---------|---------|------------ 30 | * 250-500 | > 300 | Serious 31 | * ---------|---------|------------ 32 | */ 33 | 34 | #include 35 | #include 36 | 37 | // initialize the library with the numbers of the interface pins 38 | LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 39 | 40 | #define V_REF 5.0 //1100 arduino reference voltage for ADC 41 | #define ADC_RESOLUTION 1024.0 42 | 43 | #define PIN_LED 7 44 | #define PIN_ANALOG_OUT 0 45 | 46 | #define POWER_ON_LED_DELAY 280 47 | #define POWER_ON_LED_SLEEP 40 // not used, digital read takes about 100 microseconds 48 | #define POWER_OFF_LED_DELAY 9500 49 | #define SENSOR_PIN 0 50 | 51 | #define DISPLAY_REFRESH_INTERVAL 30 52 | #define SAMPLES_PER_COMP 10 //perform N reads to filter noise and calculate mid value 53 | #define STACK_SIZE 10 54 | #define MAX_UNSIGNED_INT 65535 55 | 56 | /////////////////////////////////////////////// 57 | // LCD data printing supported/not supported 58 | #define LCD_PRINT false 59 | /////////////////////////////////////////////// 60 | // Serial/Usb port printing supported/not supported 61 | #define SERIAL_PRINT true 62 | /////////////////////////////////////////////// 63 | // Raw data printing support. If true then raw analog data 0-1023 will be printed 64 | #define DEBUG_MODE false 65 | 66 | //by default this program is configured for Waveshare Dust Sensor board 67 | //if you use GP2Y1010AU0F only then uncomment following line 68 | //#define PURE_SHARP_GP2Y1010AU0F // uncomment it if you use GP2Y1010AU0F 69 | #if defined (PURE_SHARP_GP2Y1010AU0F) 70 | #define IR_LED_ON LOW 71 | #define IR_LED_OFF HIGH 72 | #define DIV_CORRECTION 1.0 73 | #else 74 | //in case if you use http://www.waveshare.com/wiki/Dust_Sensor 75 | #define IR_LED_ON HIGH 76 | #define IR_LED_OFF LOW 77 | #define DIV_CORRECTION 11.0 //Waveshare uses volt divider 1k/10k, so corection is needed 78 | #endif 79 | 80 | /////////////////////////////////////////// 81 | // Formulas to to calculate dust by minimum & maximum values. 82 | // According to specification: min=600mv; max=3520mv 83 | // using linear function: y = a*x + b; 84 | // and Fig. 3 Output Voltage vs. Dust Density from specification 85 | // 0.6 = a*0 + b => b = 0.6 86 | // 3.52 = a*0.5 + 0.6 => a = (3.52 - 0.6)/0.5 = 5.84 87 | // y = 5.84*x + 0.6 88 | // 89 | // Lets's inverse function, because y is unknown and x is our measured voltage 90 | // 91 | // y - 0.6 = 5.84 * x 92 | // x = (y-0.6)/5.84 93 | // x = y*1/5.84-0.6/5.84 94 | // x = 0.171*y - 0.1 95 | #define A 0.171 96 | #define B 0.1 97 | // 98 | // Please note that ADC will return value in millivolts and dust is calculated in milligrams. 99 | // x = 0.171 * y - 0.1 100 | // finally let's rewrite the formula y is Vo (voltage) x is Dust value 101 | // DUST = 0.171 * Vo - 0.1 102 | // 103 | // Vo for Arduino is calculated as: 104 | // Vo = ADC_sample * V_REF / ADC_RESOLUTION 105 | ///////////////////////////////////////////////////// 106 | // Let's calculate volt correction coefficient to use for Vo calculation later 107 | // Vo = V_correction * ADC_sample 108 | // We also need to use DIV_CORRECTION because Waveshare board uses voltage divider 1k/10k 109 | float V_correction = DIV_CORRECTION * V_REF / ADC_RESOLUTION; 110 | 111 | /////////////////////////////////////////// 112 | /// Start calibraion, because it is required to set minimum sensor output voltage 0.6V for 0 mg/m3 and 3.62V for max pollution (according to Fig3 of GP2Y1010AU0F specification) 113 | // I was unable to calibrate by minimal pollution level (because I have no clean camera for calibration) 114 | // But I was able to calibrate by upper limit, just put some stick inside sensor hole 115 | // I found that at max pollution Vo = ~ 3.1V So, I decided increase whole equation by 0.45V 116 | #define V_CORRECTION_MAX 0.45 117 | 118 | //////////////////////////////////////////////////// 119 | // Prepare stack to calculate rounded data 120 | unsigned int stack[STACK_SIZE+1];// stack is used to calculate middle value for display output 121 | unsigned int stackIter; // current stack iteration 122 | 123 | /////////////////////////////////////////////////// 124 | // Calculation refresh interval used to not print data too frequently. 125 | unsigned int refresh; 126 | #define SLEEP 10000 //sleep after print 127 | 128 | void setup() { 129 | initLCD(); 130 | 131 | if(V_REF < 4) 132 | analogReference(INTERNAL); 133 | 134 | pinMode(PIN_LED, OUTPUT); 135 | pinMode(PIN_ANALOG_OUT, INPUT); 136 | digitalWrite(PIN_LED, LOW); 137 | 138 | initStack(); 139 | 140 | if(SERIAL_PRINT) 141 | Serial.begin(9600); 142 | 143 | printInitParams(); 144 | } 145 | 146 | void printInitParams(){ 147 | print("DIV_CORRECTION"); 148 | if(LCD_PRINT) 149 | delay(1500); 150 | print(DIV_CORRECTION); 151 | delay(500); 152 | } 153 | 154 | int readRawSensorData(){ 155 | int analogData; //ADC value 0-1023 156 | digitalWrite(PIN_LED, IR_LED_ON); 157 | 158 | delayMicroseconds(POWER_ON_LED_DELAY); 159 | analogData = analogRead(SENSOR_PIN); 160 | //print(analogData); 161 | 162 | delayMicroseconds(POWER_ON_LED_SLEEP);//not used, digital read takes about 100 microseconds 163 | digitalWrite(PIN_LED, IR_LED_OFF); 164 | 165 | delayMicroseconds(POWER_OFF_LED_DELAY);//9500 166 | return analogData; 167 | } 168 | 169 | void loop(void){ 170 | if(stackIter >= STACK_SIZE) 171 | stackIter = 0; 172 | 173 | stack[stackIter] = filterVoltageNoiseFromADC(); 174 | 175 | if(refresh < DISPLAY_REFRESH_INTERVAL){ 176 | refresh++; 177 | }else{ 178 | //If refresh >= DISPLAY_REFRESH_INTERVAL then dust will be calculated and refresh updated to zero to start new cycle 179 | refresh = 0; 180 | if(DEBUG_MODE){ 181 | //print voltage 182 | print(getAverageRawSamples()); 183 | } else { 184 | print(calculateDust()); 185 | if(!DEBUG_MODE)delay(SLEEP); 186 | } 187 | } 188 | stackIter++; 189 | } 190 | 191 | int calculateDust(){ 192 | float Vo = (getAverageRawSamples() * V_correction + V_CORRECTION_MAX) * A - B; 193 | return Vo * 1000; // show result in micrograms 194 | } 195 | 196 | float getAverageRawSamples(){ 197 | //calculate middle value from stack array 198 | float midVal = 0; 199 | for(int i = 0; i < STACK_SIZE ; i++){ 200 | midVal += stack[i]; 201 | } 202 | return midVal / STACK_SIZE; 203 | } 204 | 205 | int filterVoltageNoiseFromADC(){ 206 | //perform several measurements and store to stack 207 | //for later midpoint calculations 208 | 209 | unsigned int maxDust = 0; 210 | unsigned int minDust = MAX_UNSIGNED_INT; 211 | unsigned long avgDust = 0; 212 | 213 | //perform several reads to filter noise and calculate mid value 214 | for(int i = 0; i< SAMPLES_PER_COMP;){ 215 | int dustVal = readRawSensorData(); 216 | if (dustVal >= 0){ 217 | 218 | //find max dust per sample 219 | if(dustVal > maxDust) 220 | maxDust = dustVal; 221 | 222 | //find min dust per sample 223 | if(dustVal < minDust) 224 | minDust = dustVal; 225 | 226 | avgDust += dustVal; 227 | i++; 228 | } 229 | } 230 | 231 | //additional filter for input data 232 | //don't take to consideration max & min values per sample 233 | //and save average to stack 234 | return (avgDust - maxDust - minDust) / (SAMPLES_PER_COMP - 2); 235 | } 236 | 237 | void initLCD(){ 238 | if(LCD_PRINT){ 239 | // set up the LCD's number of columns and rows: 240 | lcd.begin(16, 2); 241 | // Print a message to the LCD. 242 | lcd.print("-=Dust+Sensor=-"); 243 | } 244 | } 245 | 246 | void initStack(){ 247 | stackIter = 0; 248 | refresh = 0; 249 | for(int i = 0; i < STACK_SIZE ; i++){ 250 | stack[i] = 0; 251 | } 252 | } 253 | 254 | void print(double val){ 255 | print(String(val)); 256 | } 257 | 258 | void print(int val){ 259 | print(String(val)); 260 | } 261 | 262 | void print(String msg){ 263 | if(SERIAL_PRINT) 264 | Serial.println(msg); 265 | if(LCD_PRINT){ 266 | lcd.setCursor(0, 1); 267 | lcd.print(msg); 268 | } 269 | } 270 | --------------------------------------------------------------------------------