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
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"); }