Arduino Projects

CO2 Concentration, CO2 PPM, or CO2 Levels using MQ135 Sensor & Arduino

CO2 Concentration using MQ135 Sensor and Arduino:

CO2 Concentration with MQ135 Sensor and Arduino– In this article, we will find the concentration of Carbon Dioxide using the MQ135 sensor and display it on the OLED I2C display module. So basically we will be making the Carbon Dioxide Parts per Million Meter.

* Atmospheric CO2 Level…………..400ppm

* Average indoor co2………….350-450ppm

* Maximum acceptable co2………..1000ppm

* Dangerous co2 levels………….>2000ppm

Before, we find the concentration of the Carbon Dioxide or the CO2 level first; let’s learn how to interface the OLED display module with the Arduino. If you already know how to interface the SDD1306 I2C Oled display module with the Arduino then you can jump to the end. Without any further delay let’s get started!!!



Amazon Links:

12v Adaptor:

Arduino Uno

Arduino Nano

SSD1306 128×64 Oled i2c display Module

MQ135 Gas Sensor:

Other Tools and Components:

Top Arduino Sensors:

Super Starter kit for Beginners

Digital Oscilloscopes

Variable Supply

Digital Multimeter

Soldering iron kits

PCB small portable drill machines

*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!




Interfacing I2C OLED Display with Arduino:

Now we will learn how to connect and code an I2C OLED display with an Arduino microcontroller board and we will also learn how to write a simple code to display a message on your OLED screen. Let’s begin with wiring the display since the OLED display works on I2C communication we have to connect only four wires to the Arduino. The OLED consists of four pins which are:

  • SCK
  • SDA
  • VCC
  • GND

The SCK is the clock pin, SDA is the data pin, VCC is the power pin and GND is the ground pin.  The Arduino board has SDA at pin number A4 and SCK at pin number A5.

  • Connect the SDA pin of the OLED with the pin number A4
  • Connect the SCK pin of the OLED with the pin number A5
  • Connect the VCC pin of the OLED with the 5V of the Arduino
  • Connect the ground pin of the OLED with the ground of the Arduino

co2 concentration

Libraries needed for the Oled Display Module:

Before you start the programming, first of all, make sure you download the necessary libraries; otherwise, you won’t be able to display anything on your Oled display module. The Oled display module I am using is SSD1306 which i have been using for quite a long time and seriously this is my favorite one. For this Oled display module you will need to install two libraries which are:

Now to install these libraries go to the sketch click on the include library and then click on the manage library

co2 concentration

Now just write the library name and install it

co2 concentration

We can also install the library manually from zip file. Go to sketch click on the include library and click on the zip file and insert the zip file.

co2 concentration



Now click on files and in files click on examples and in examples select Adafruit SDD1306 in which click on ssd1306_128X32_i2c

co2 concentration

I2C Oled Arduino Code:

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x32 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define NUMFLAKES     10 // Number of snowflakes in the animation example

#define LOGO_HEIGHT   16
#define LOGO_WIDTH    16
static const unsigned char PROGMEM logo_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

  // Draw a single pixel in white
  display.drawPixel(10, 10, SSD1306_WHITE);

  // Show the display buffer on the screen. You MUST call display() after
  // drawing commands to make them visible on screen!
  display.display();
  delay(2000);
  // display.display() is NOT necessary after every single drawing command,
  // unless that's what you want...rather, you can batch up a bunch of
  // drawing operations and then update the screen all at once by calling
  // display.display(). These examples demonstrate both approaches...

  testdrawline();      // Draw many lines

  testdrawrect();      // Draw rectangles (outlines)

  testfillrect();      // Draw rectangles (filled)

  testdrawcircle();    // Draw circles (outlines)

  testfillcircle();    // Draw circles (filled)

  testdrawroundrect(); // Draw rounded rectangles (outlines)

  testfillroundrect(); // Draw rounded rectangles (filled)

  testdrawtriangle();  // Draw triangles (outlines)

  testfilltriangle();  // Draw triangles (filled)

  testdrawchar();      // Draw characters of the default font

  testdrawstyles();    // Draw 'stylized' characters

  testscrolltext();    // Draw scrolling text

  testdrawbitmap();    // Draw a small bitmap image

  // Invert and restore display, pausing in-between
  display.invertDisplay(true);
  delay(1000);
  display.invertDisplay(false);
  delay(1000);

  testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}

void loop() {
}

void testdrawline() {
  int16_t i;

  display.clearDisplay(); // Clear display buffer

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn line
    delay(1);
  }
  for(i=0; i<display.height(); i+=4) {
    display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000); // Pause for 2 seconds
}

void testdrawrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testfillrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=3) {
    // The INVERSE color is used so rectangles alternate white/black
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testdrawcircle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillcircle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
    // The INVERSE color is used so circles alternate white/black
    display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn circle
    delay(1);
  }

  delay(2000);
}

void testdrawroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    // The INVERSE color is used so round-rects alternate white/black
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawtriangle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfilltriangle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
    // The INVERSE color is used so triangles alternate white/black
    display.fillTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  // Not all the characters will fit on the display. This is normal.
  // Library will draw what it can and the rest will be clipped.
  for(int16_t i=0; i<256; i++) {
    if(i == '\n') display.write(' ');
    else          display.write(i);
  }

  display.display();
  delay(2000);
}

void testdrawstyles(void) {
  display.clearDisplay();

  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("Hello, world!"));

  display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("0x")); display.println(0xDEADBEEF, HEX);

  display.display();
  delay(2000);
}

void testscrolltext(void) {
  display.clearDisplay();

  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();      // Show initial text
  delay(100);

  // Scroll in various directions, pausing in-between:
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
  delay(1000);
}

void testdrawbitmap(void) {
  display.clearDisplay();

  display.drawBitmap(
    (display.width()  - LOGO_WIDTH ) / 2,
    (display.height() - LOGO_HEIGHT) / 2,
    logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
}

#define XPOS   0 // Indexes into the 'icons' array in function below
#define YPOS   1
#define DELTAY 2

void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for(f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
    icons[f][YPOS]   = -LOGO_HEIGHT;
    icons[f][DELTAY] = random(1, 6);
    Serial.print(F("x: "));
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(F(" y: "));
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(F(" dy: "));
    Serial.println(icons[f][DELTAY], DEC);
  }

  for(;;) { // Loop forever...
    display.clearDisplay(); // Clear the display buffer

    // Draw each snowflake:
    for(f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
    }

    display.display(); // Show the display buffer on the screen
    delay(200);        // Pause for 1/10 second

    // Then update coordinates of each flake...
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][YPOS] += icons[f][DELTAY];
      // If snowflake is off the bottom of the screen...
      if (icons[f][YPOS] >= display.height()) {
        // Reinitialize to a random position, just off the top
        icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
        icons[f][YPOS]   = -LOGO_HEIGHT;
        icons[f][DELTAY] = random(1, 6);
      }
    }
  }
}

Upload the code to Arduino and the following text will be displayed on the OLED display module. If your display behaves in an abnormal way and if you can’t see anything then you should read my article on how to fix the Oled display Module issues.

co2 concentration

If your display is working then congrats you are done with the hard part. Before you jump into the project first play with the code a little, to understand how you can display different texts at different positions.



Interfacing MQ135 sensor with Arduino:

The connection of the I2C Oled display module with the Arduino remains exactly the same and we don’t need to change any wires, we can now add our MQ135 Sensor with the Arduino Uno or Arduino Nano to find the CO2 Concentration. But, before I am going to explain the wiring first let’s take a look at the MQ135 Sensor.

MQ135 Specifications:

  • Working voltage: DC 5V
  • Working Current: 150mA
  • DOUT: TTL output
  • AOUT: Analog output
  • Preheat time: Over 20s
  • Dimension: 32mm x 22m x 27mm(HIGH 27mm)

co2 concentration

Now to connect the MQ135 with Arduino:

  • Connect the VCC of the MQ135 Sensor with the 5V of the Arduino
  • Connect the gnd of the MQ135 with the ground of the Arduino
  • Connect the analog pin of the MQ135 with the A0 of the Arduino
  • Connect the anode of the led with the 220-ohm resistor at digital pin number 9
  • Connect the cathode of the Arduino with the ground of the Arduino

co2 concentration


CO2 Concentration Arduino Code:

/*
 * Carbon Dioxide Parts Per Million Meter
 * CO2PPM

 * Atmospheric CO2 Level..............400ppm
 * Average indoor co2.............350-450ppm
 * Maxiumum acceptable co2...........1000ppm
 * Dangerous co2 levels.............>2000ppm
 */


#include <Wire.h>                             //I2C for OLED
#include <Adafruit_GFX.h>                     //grafix library for OLED
#include <Adafruit_SSD1306.h>                 //OLED Driver


#define anInput     A0                        //analog feed from MQ135
#define digTrigger   2                        //digital feed from MQ135
#define co2Zero     55                        //calibrated CO2 0 level
#define led          9                        //led on pin 9
#define OLED_RESET   4                        //OLED reset on lin 4

Adafruit_SSD1306 display(OLED_RESET);         //create instance of OLED called display

void setup() {
  
  pinMode(anInput,INPUT);                     //MQ135 analog feed set for input
  pinMode(digTrigger,INPUT);                  //MQ135 digital feed set for input
  pinMode(led,OUTPUT);                        //led set for output
  Serial.begin(9600);                         //serial comms for debuging
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  //begin display @ hex addy 0x3C
  display.display();                          //show buffer
  display.clearDisplay();                     //clear buffer
  
}

void loop() {
  
int co2now[10];                               //int array for co2 readings
int co2raw = 0;                               //int for raw value of co2
int co2comp = 0;                              //int for compensated co2 
int co2ppm = 0;                               //int for calculated ppm
int zzz = 0;                                  //int for averaging
int grafX = 0;                                //int for x value of graph


  display.clearDisplay();                     //clear display @ beginning of each loop

  for (int x = 0;x<10;x++){                   //samplpe co2 10x over 2 seconds
    co2now[x]=analogRead(A0);
    delay(200);
  }

for (int x = 0;x<10;x++){                     //add samples together
    zzz=zzz + co2now[x];
    
  }
  co2raw = zzz/10;                            //divide samples by 10
  co2comp = co2raw - co2Zero;                 //get compensated value
  co2ppm = map(co2comp,0,1023,400,5000);      //map value for atmospheric levels

  display.setTextSize(2);                     //set text size
  display.setTextColor(WHITE);                //set text color
  display.setCursor(0,0);                     //set cursor
  display.println("CO2 Level");               //print title
  display.println(" ");                       //skip a line
  display.print(co2ppm);                      //print co2 ppm
  display.print(" PPM");                      //print units
  grafX = map(co2ppm,0,1000,0,127);           //map value to screen width
  display.fillRect(0, 52, grafX, 10, WHITE);  //print graph 400min 1000max
  display.display();                          //show the buffer
  if(co2ppm>999){                             //if co2 ppm > 1000
    digitalWrite(led,HIGH);                   //turn on led
  }
  else{                                       //if not
    digitalWrite(led,LOW);                    //turn off led
  }
}



Code Explanation of carbon dioxide CO2 concentration:

* Carbon Dioxide Parts Per Million Meter

* CO2PPM

The threshold values of the C02 are as follow:

* Atmospheric CO2 Level…………..400ppm

* Average indoor co2………….350-450ppm

* Maximum acceptable co2………..1000ppm

* Dangerous co2 levels………….>2000ppm

*/

We will require three libraries for interfacing OLED with Arduino which we have explained above in detail

//—————————————————————————————————————

//                                                  LIBRARIES

//—————————————————————————————————————

#include <Wire.h>                             //I2C for OLED

#include <Adafruit_GFX.h>                     //grafix library for OLED

#include <Adafruit_SSD1306.h>                 //OLED Driver

The data from the MQ135 Sensor is taken from the analog pin of the MQ135 which is connected with A0 of the Arduino and is stored in variable anInput

#define anInput     A0                        //analog feed from MQ135

#define digTrigger   2                        //digital feed from MQ135

Now this is the atmospheric value of carbon dioxide

#define co2Zero     55                        //calibrated CO2 0 level

#define led          9                        //led on pin 9

#define OLED_RESET   4                        //OLED reset on lin 4

//—————————————————————————————————————

//                                                LIBRARY CALL

//—————————————————————————————————————

Adafruit_SSD1306 display(OLED_RESET);         //create instance of OLED called display

//—————————————————————————————————————

//                                                  SETUP

//—————————————————————————————————————

void setup() {

pinMode(anInput,INPUT);                     //MQ135 analog feed set for input

pinMode(digTrigger,INPUT);                  //MQ135 digital feed set for input

pinMode(led,OUTPUT);                        //led set for output

Serial.begin(9600);                         //serial comms for debuging

display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  //begin display @ hex addy 0x3C

display.display();                          //show buffer

display.clearDisplay();                     //clear buffer

}

//—————————————————————————————————————

//                                               MAIN LOOP

//—————————————————————————————————————

void loop() {

This integer will save the concentration of the of the carbon dioxide

int co2now[10];                               //int array for co2 readings

int co2raw = 0;                               //int for raw value of co2

The compensated value of the carbon dioxide will be saved in co2comp

int co2comp = 0;                              //int for compensated co2

int co2ppm = 0;                               //int for calculated ppm

intzzz = 0;                                  //int for averaging

intgrafX = 0;                                //int for x value of graph

This command will clear the display

display.clearDisplay();                     //clear display @ beginning of each loop

Now through this loop we will collect the samples of the carbon dioxide in 2 seconds

for (int x = 0;x<10;x++){                   //sample co2 10x over 2 seconds

co2now[x]=analogRead(A0);

delay(200);

}

After filling up of the array then we are going to add the sample together we will create another for loop

for (int x = 0;x<10;x++){                     //add samples together

zzz=zzz + co2now[x];

}

Now in the zzz variable, we have added all the samples of the carbon dioxide we will divide it by 10 to obtain the average value

co2raw = zzz/10;                            //divide samples by 10



Then we calculate the compensated value

co2comp = co2raw – co2Zero;                 //get compensated value

As we know that analog input create value are from 0 to 1023 we will used the map function the minimum value will be 400 and the maximum value will be 5000 for the ppm value

co2ppm = map(co2comp,0,1023,400,5000);      //map value for atmospheric levels

display.setTextSize(2);                     //set text size

display.setTextColor(WHITE);                //set text colo

display.setCursor(0,0);                     //set cursor

display.println(“CO2 Level”);               //print title

display.println(” “);                       //skip a line

display.print(co2ppm);                      //print co2 ppm

display.print(” PPM”);                      //print units

grafX = map(co2ppm,0,1000,0,127);           //map value to screen width

display.fillRect(0, 52, grafX, 10, WHITE);  //print graph 400min 1000max

display.display();                          //show the buffer

if(co2ppm>999){                             //if co2 ppm > 1000

digitalWrite(led,HIGH);                   //turn on led

}

else{                                       //if not

digitalWrite(led,LOW);                    //turn off led

}

}

After uploading the code to the Arduino we will get the concentration of the carbon dioxide. You will see the CO2 Level on the OLED display module in the PPM value.

co2 concentration


Related article:

IoT Smoke Detector using Nodemcu ESP8266

Arduino Gas leakage Detection with SMS Alert

Engr Fahad

My name is Shahzada Fahad and I am an Electrical Engineer. I have been doing Job in UAE as a site engineer in an Electrical Construction Company. Currently, I am running my own YouTube channel "Electronic Clinic", and managing this Website. My Hobbies are * Watching Movies * Music * Martial Arts * Photography * Travelling * Make Sketches and so on...

One Comment

  1. Actually it’s not showing the CO2 level in display it’s just printing CO2 level but not showing the reading of CO2 . Can I get details about it

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button