CO2 Concentration, CO2 PPM, or CO2 Levels using MQ135 Sensor & Arduino
Table of Contents
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:
Arduino Nano USB-C Type (Recommended)
SSD1306 128×64 Oled i2c display Module
*Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.
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
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
Now just write the library name and install it
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.
Now click on files and in files click on examples and in examples select Adafruit SDD1306 in which click on ssd1306_128X32_i2c
I2C Oled Arduino Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
/************************************************************************** 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.
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)
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 Arduino Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
/* * 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.
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