CO2 sensor and real-time plotter

0 1439 Medium

A practical Arduino-based air sensor unit, which can work alone or in combination with a PC.

projectImage

Things used in this project

 

Hardware components

HARDWARE LIST
1 DFRobot I2C 16x2 Arduino LCD Display Module
1 Arduino UNO
1 5 mm LED: Red
1 5 mm LED: Green
1 SparkFun Air Quality Breakout - CCS811
1 Resistor 220 ohm
Jumper wires (generic)

Hand tools and fabrication machines

 

Soldering iron (generic)

Story

 

Introduction

 

This is a simple and fairly inexpensively air quality sensor. It measures CO2 and total VOC (volatile organic content) levels in the air.An LCD display and two status LEDs indicate the CO2 level. If desired, the unit can be connected to a computer to display the evolution of CO2 and TVOC in real-time.

 

Technical principle

 

The air quality is measured with the popular CCS 811 sensor, connected to the Arduino via I2C. The communication with the LCD display is also done via I2C (other displays and/or communication modes are certainly possible). Finally, 2 status LEDs connected to pins 2 and 3 give a direct visual indication of air quality.All components are assembled inside an upcycled chocolate box (Ferrero Rocher)! The unit is powered directly by the USB cable, which also transfers the data to the PC.

 

Schematic

 

Real-time data plotter (optional)

 

If desired, the sensor can be connected to a PC. A Processing application automatically connects to the sensor and displays the data.

 

Real-time plots on PC

Real-time plots on PC

 

Schematics

 

https://github.com/HugoMVale/ArduinoSensorCO2/blob/main/Schematic/ArduinoSensorCO2.fzz

Code

 

Arduino and Processing Code

 

https://github.com/HugoMVale/ArduinoSensorCO2

Arduino Code

CODE
/*
 * Simple air sensor device based on CCS 881. Measures CO2e and TVOC.  
 * Unit performs measurements and displays corresponding values in LCD.
 * Status LEDs indicate if CO2 value is in normal or high range.
 * All readings are sent via serial protocol to PC, where companion program 
 * displays the readings in real-time.
 * 
 * Hugo
 * 21/11/2020
 */

// Libraries 
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <I2Cdev.h>
#include "Adafruit_CCS811.h"

/* Start of USER settings */
 
// LED PINS
const uint8_t PIN_GREEN_LED = 2;
const uint8_t PIN_RED_LED = 3;

// LCD SPECS
const uint8_t LCD_ROWS = 2;
const uint8_t LCD_COLS = 16;

// CCS SENSOR CONFIGURATION
const uint16_t LowCO2 = 250;               // Minimum CO2 Level
const uint16_t MediumCO2 = 800;            // Medium CO2 Level
const uint16_t MaxCO2 = 1000;              // Maximum CO2 Level

// Periods of read and write
const uint16_t periodReadSensor = 2000;   // How often measurements are made (ms)
const uint16_t periodWritePC = 5000;     // How often values are sent to PC (ms)

//  Baud rate for serial port
const uint32_t baudRate = 57600;  

/* End of USER Settings */

// LCD Object
LiquidCrystal_I2C lcd(0x27,LCD_COLS,LCD_ROWS);

// CCS Sensor
Adafruit_CCS811 ccs;

// Global variables
float temp;
uint16_t valueCO2, valueTVOC;
uint8_t statusCCS;
uint32_t timeStart, timeNow, timeSinceStart;
bool serialState = false;

/* SETUP */
void setup() {

  // Set LED pins and turn on LEDs
  pinMode(PIN_GREEN_LED, OUTPUT);
  pinMode(PIN_RED_LED, OUTPUT);
  digitalWrite(PIN_GREEN_LED, HIGH);
  digitalWrite(PIN_RED_LED, HIGH);
    
  // Initialize Serial
  Serial.begin(baudRate);
  
  // Initialize LCD
  lcd.init();
  lcd.backlight();
  Write2LinesLCD("Hallo! :)", "CO2 sensor v1");

  // Switch off LEDs
  delay(200);
  digitalWrite(PIN_GREEN_LED, LOW);
  digitalWrite(PIN_RED_LED, LOW);
  
  // Initialize CCS sensor
  statusCCS = ccs.begin();
  if(!statusCCS){
    Write2LinesLCD("ERROR:", "Init CCS sensor");
    BlinkForeverLED(PIN_RED_LED,200);
  }

  // Calibrate temperature sensor
  while(!ccs.available());
  //temp = ccs.calculateTemperature(); // This board does not have temperature sensor
  ccs.setTempOffset(0);

  // Start counting time
  timeStart = millis();

}  


// MAIN PROGRAM
void loop() {

  // Keep track of last events
  static uint32_t timeLastRead = 0, timeLastWrite = 0; 
  
  // Read values from CCS sensor
  // Send values to LCD
  timeNow = millis();
  bool timeReadFlag = (timeNow - timeLastRead) > periodReadSensor;
  if(timeReadFlag){
    timeLastRead = timeNow;
    timeSinceStart = timeNow - timeStart;
    ReadCCS(temp, valueCO2, valueTVOC, statusCCS);
    WriteValuesLCD(temp,valueCO2,valueTVOC);
  }

  // Write "alarm" state to LCD and LEDs
  // Needs to be outside of previous if construct, so that LEDs can blink 
  StateCO2(valueCO2,timeReadFlag);

  // Check for request for sending serial data
  CheckSerial(serialState);
  
  // Send serial data if conditions are met
  bool timeWriteFlag = (timeNow - timeLastWrite) > periodWritePC;
  if(serialState && timeWriteFlag){
    timeLastWrite = timeNow;
    WriteValuesPC(timeSinceStart,temp,valueCO2,valueTVOC);
  }
   
}

/* Reads values from CCS sensor */
void ReadCCS(float &temp, uint16_t &valueCO2, uint16_t &valueTVOC, uint8_t &statusCCS){
  if(ccs.available()){
    statusCCS = ccs.readData();
    if(statusCCS<1){
      valueCO2 = ccs.geteCO2();
      valueTVOC = ccs.getTVOC();
    }
  }
}

/* Writes CO2 state to LEDs and LCD */
void StateCO2(uint16_t valueCO2, bool timeReadFlag){
  
  static uint8_t resultLast = 0;
  uint8_t result = 1;  
  
  if (valueCO2<LowCO2){
    result = 1;
    BlinkLED(PIN_GREEN_LED,200);
    digitalWrite(PIN_RED_LED,LOW);
    if(timeReadFlag && result!=resultLast){
      WriteLineLCD("Stabilizing...",2);
    }
  }
  else if ((valueCO2>=LowCO2) && (valueCO2<=MediumCO2)){
    result = 2;
    digitalWrite(PIN_GREEN_LED,HIGH);
    digitalWrite(PIN_RED_LED,LOW);
    if(timeReadFlag && result!=resultLast){
      WriteLineLCD("Level OK",2);
    }
  }
  else if ((valueCO2>MediumCO2) && (valueCO2<MaxCO2)){
    result = 3;
    digitalWrite(PIN_GREEN_LED,HIGH);
    BlinkLED(PIN_RED_LED,1000);
    if(timeReadFlag && result!=resultLast){
      WriteLineLCD("Prepare to vent.",2);
    }
  }
  else if (valueCO2>=MaxCO2){
    result = 4;
    digitalWrite(PIN_GREEN_LED,LOW);
    BlinkLED(PIN_RED_LED,200);
    if(timeReadFlag && result!=resultLast){
      WriteLineLCD("Vent. now!",2);
    }
  }
  resultLast = result;
}

/* Makes LED blink FOREVER */
void BlinkForeverLED(uint8_t pin, uint16_t duration){
  while(true){
  digitalWrite(pin, HIGH);
  delay(duration);
  digitalWrite(pin, LOW);
  delay(duration);
  }
}

/* Makes LED blink for specified duration */
void BlinkLED(uint8_t pin, uint16_t duration){
  static byte ledState = 1;
  static unsigned long previousTime = millis();
  unsigned long currentTime = millis();
  if (currentTime - previousTime >= duration){
    previousTime = currentTime;
    ledState ^= 1;
    digitalWrite(pin, ledState);
  }
}

/* 
 *  Writes sensor values to LCD
 *  Currently only CO2 value is displayed
 *  Can be extended to display temperature and TVOC
 */
void WriteValuesLCD(float temp, uint16_t valueCO2, uint16_t valueTVOC){
  const uint8_t line = 1;
  ClearLineLCD(line);
  lcd.print("CO2:");
  lcd.setCursor(4,line-1);
  if(valueCO2<100){
    lcd.setCursor(8,0);
  }
  else if(valueCO2<1000){
    lcd.setCursor(7,0);
  }
  else{
    lcd.setCursor(6,0);
  }
  lcd.print(valueCO2);
  lcd.setCursor(11,0);
  lcd.print("ppm");
}

/* Clears LCD line and places cursor at start position */
void ClearLineLCD(uint8_t line){
  lcd.setCursor(0,line-1);
  for(uint8_t i=0; i < LCD_COLS; i++){
    lcd.print(" ");
  }
  lcd.setCursor(0,line-1);
}

/* Writes single line of text to specified LCD line */
void WriteLineLCD(String text, uint8_t line){
  ClearLineLCD(line);
  lcd.print(text);
}

/* Writes 2 lines of text to LCD */
void Write2LinesLCD(String text1, String text2){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(text1);
  lcd.setCursor(0,1);
  lcd.print(text2);
}

/* Writes values to PC */
void WriteValuesPC(uint32_t tempo, float temp, uint16_t valueCO2, uint16_t valueTVOC){
  const char sep = ' ';
  Serial.print(tempo);
  Serial.print(sep);
  Serial.print(temp);
  Serial.print(sep);
  Serial.print(valueCO2);
  Serial.print(sep);
  Serial.println(valueTVOC);
}

/* Checks if there is request for transmission */
void CheckSerial(bool &result){
  const char key = 'H';
  if(false){ // Reserved for future use
    return;
  }
  else{
      if(Serial.available()>0){
        char ch = Serial.read();
        if(ch == key){
          result = true;
          Serial.println("<H>");
        }
    }
  }
}

The article was first published in hackster, March 3, 2021

cr: https://www.hackster.io/1000volts/co2-sensor-and-real-time-plotter-5bb736

author: 1000volts

License
All Rights
Reserved
licensBg
0