Arduino-based air quality monitor - new version
In this video I show you an extended version of the previous (https://youtu.be/FahmF_p4xJc) air quality monitor. I added a new sensor, so now I can measure pressure and humidity ("weather features"). I cleaned up the previous code and I added an OLED display to the ccircuit, so the data can be monitored without having a computer connected to the Arduino.
Wiring diagram
This project contains three i2C devices: OLED display, CCS811 air quality sensor, BME280 temperature/air pressure/humidity sensor. They are all connected to the same pins: A4 (SDA) and A5 (SCL). The dust sensor is connected to the Arduino in two ways. One digital pin is switching the infrared LED on and off, and an analog pin is measuring the output voltage which is proportional to the amount of dust in the dust sensor.
Arduino source code
//Info: GP2Y1010AU0F Dust sensor //The dust sensor reaches the maximum output pulse 0.28 ms after the LED was turned on //This sensor is based on voltage measurement //CCS811 is a thermometer-VOC meter and uses a premade library -I2C //BME280 is a thermometer, humidity and pressure sensor - I2C //OLED: any OLED display can be used, this particular tutorial uses the 128x32 type - I2C //OLED Libraries #include "SSD1306Ascii.h" #include "SSD1306AsciiAvrI2c.h" #define I2C_ADDRESS 0x3C //Address #define RST_PIN -1 //For OLED with no reset pin SSD1306AsciiAvrI2c display; #include <Wire.h> //VOC sensor #include "Adafruit_CCS811.h" //We load the library for the gas sensor #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> Adafruit_CCS811 ccs; //BME280 Adafruit_BME280 bme; // I2C //Use these #define lines if you want to use it with SPI //#define BME_SCK 13 //#define BME_MISO 12 //#define BME_MOSI 11 //#define BME_CS 10 #define SEALEVELPRESSURE_HPA (1013.25) //------------------------------------------- //variables float co2Amount; //Amount (ug/m^3) of CO2 float CCSTemp; //temperature measured by the CCS811 //Dust sensor int dustmeasurePin = A3; //The output of the dust sensor is connected to A3 (AD converter pin) int dustLEDPin = 2; //The IR pin inside the dust sensor is connected to D2 (digital output pin) float outBits = 0; //AD-converter raw output float dustDensity = 0; //dust density, based on the formula //BME280 - Temperature, humidity and pressure sensor. float BME280Temp; float BME280Humidity; float BME280Pressure; float BME280Altitude; unsigned long delayTime; //delaytime for BME280 library //String for storing the formatted output data (serial port) String outputData; void setup() { Serial.begin(9600); //Start serial Serial.println("*Dust and VOC sensor"); //Print message. I use '*' to tell the receiver software that this is not a measurement data //CCS if(!ccs.begin()) //if we are not able to start the VOC sensor, print the following message { Serial.println("* Failed to start sensor! Please check your wiring."); while(1); //And hold the code here } while(!ccs.available()); //endofCCS float temp = ccs.calculateTemperature(); //calculate the temperature ccs.setTempOffset(temp - 25.0); //set the offset using the //BME280 unsigned status; status = bme.begin(0x76); if (!status) { Serial.println("*Could not find a valid BME280 sensor, check wiring, address, sensor ID!"); Serial.print("*SensorID was: 0x"); Serial.println(bme.sensorID(),16); Serial.print("* ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n"); Serial.print("* ID of 0x56-0x58 represents a BMP 280,\n"); Serial.print("* ID of 0x60 represents a BME 280.\n"); Serial.print("* ID of 0x61 represents a BME 680.\n"); while (1) delay(10); } delayTime = 1000; //endofBME280 //OLED part #if RST_PIN >= 0 display.begin(&Adafruit128x32, I2C_ADDRESS, RST_PIN); #else // RST_PIN >= 0 display.begin(&Adafruit128x32, I2C_ADDRESS); #endif // RST_PIN >= 0 //Call oled.setI2cClock(frequency) to change from the default frequency. display.setFont(System5x7); display.set1X(); display.clear(); //--endofOLED---- pinMode(dustLEDPin,OUTPUT); //the pin for the dust sensor's LED is set as an output } void loop() { //we do this part outside of the serial as well, since we need to measure anyway if we want to see the info on the OLED measureDust(); delay(500); measureVOCs(); delay(500); MeasureBME280(); delay(500); //Printing the data on the OLED refreshOLED(); delay(1000); // //We only enter this part, if we sent a character "S" to the serial //The printing can be stopped by sending an "N" if (Serial.available() > 0) //if there's something on the serial { char commandCharacter = Serial.read(); //we use characters (letters) for controlling the switch-case switch (commandCharacter) { case 'S': //S: start while(Serial.read() != 'N') //while we don't send N through the serial, the following functions are looping: { measureDust(); delay(500); measureVOCs(); delay(500); MeasureBME280(); delay(500); //Printing the data on the OLED refreshOLED(); delay(1000); printFormattedData(); //prints the pre-formatted line on the serial port } break; default: // break; } } } void measureDust() { digitalWrite(dustLEDPin,LOW); //turn ON the LED delayMicroseconds(280); // wait 0.28 ms = 280 us outBits = analogRead(dustmeasurePin); //measure the peak of the output pulse digitalWrite(dustLEDPin,HIGH); //turn OFF the LED /* If you want to get the converted data on the Arduino terminal, //uncomment this part and replace the outbits to dustDensity in printFormattedData() dustDensity = 1000* ( 0.17 * ((5.0 / 1024) * outBits) - 0.1); //dust density in ug/m^3 */ } void measureVOCs() { if(ccs.available()) //If we can communicate with the VOC sensor { if(!ccs.readData()) //if we are not reading (i.e. the devide is available) { co2Amount = ccs.geteCO2(); //read CO2 ccs.getTVOC(); //read temperature CCSTemp = ccs.calculateTemperature(); //calculate temperature } else { Serial.println("ERROR!"); //Print error message while(1); //wait here } } } void printFormattedData() //Formatting the output so the receiver software can process it { outputData = (String)CCSTemp + '\t' + (String)BME280Temp + '\t' + (String)bme.readHumidity() + '\t' + (String)BME280Pressure + '\t' + (String)outBits + '\t' + (String)co2Amount; //for the output Serial.println(outputData); //print the formatted line //Format on serial output: Temp1 (CCS) /tab/ Temp2 (BME) /tab/ Humidity /tab/ Pressure /tab/ Dust /tab/ CO2 /newline/ //outBits can be replaced to dustDensity to print the converted data instead of the raw data. //dustDensity = 1000* ( 0.17 * ((5.0 / 1024) * outBits) - 0.1); //dust density in ug/m^3 } void MeasureBME280() { //Reading all the available values from the BME280 chip BME280Temp = bme.readTemperature(); BME280Humidity = bme.readHumidity(); BME280Pressure = bme.readPressure() / 100.0F; BME280Altitude = bme.readAltitude(SEALEVELPRESSURE_HPA); } void refreshOLED() { //128x32 OLED //1st line of the OLED display.clear(); display.setCursor(0, 0); display.print("T: "); display.print(BME280Temp); display.print("C H: "); display.print(BME280Humidity); display.println("%"); //2nd line display.setCursor(0, 1); display.print("P: "); display.print(BME280Pressure); display.print(" A: "); display.print(BME280Altitude); display.println("m"); //3rd line display.setCursor(0, 2); display.print("CO2: "); display.print(co2Amount); display.print(" ppm"); //4th line display.setCursor(0, 3); display.print("D: "); display.print(1000* ( 0.17 * ((5.0 / 1024) * outBits) - 0.1)); display.println(" ug/m3"); }