Project in progress
Atmospheric Air Analyser

Atmospheric Air Analyser © MIT

A low cost air quality monitoring device created with an aim to detect pollution hotspots caused due to traffic jams or factory emissions.

  • 22 respects

Components and supplies

Necessary tools and machines

3D Printer (generic)
09507 01
Soldering iron (generic)
Solder Wire, Lead Free
66y6421 40
Solder Flux, Soldering
26w6260 40
Multitool, Screwdriver

Apps and online services

ThingSpeak API
Ide web
Arduino IDE
Autodesk EAGLE CAD
Software used for designing the PCB which houses the sensors
Autodesk AutoCAD
Software used to design the hardware casing for the PCB (with sensors)

About this project


The journey of this project began during a 'Design Thinking' project with a motive to design a system for measurement of localized Air Quality data for detecting pollution hotspots and alerting commuters of the same. The idea was to deploy large number of low cost devices across a geographic location to get this data, analyze it and use it for traffic management and city planning. Although quite visionary and ambitious, this project received a lot of appreciation at various levels and fared well in various competitions, it couldn't really move into the implementation phase. Lack of funds, expertise and less access to technology, hampered the progress of the project. However, I decided to publish this project (although fairly late - had developed this in 2018), as an attempt to inspire interested hacksters, to not just encourage learning but also hope for people to take it forward.

Hardware Overview

The hardware in the project is broadly divided into:

  • Sensors and their interfacing with Arduino Nano
  • Arduino Nano and NodeMCU interface

In this project, the Figaro TGS2600 checks for Methane and Carbon Monoxide, Figaro TGS2602 checks for Ammonia, Hydrogen Sulfide and Toluene while the less accurate and cheap MQ135 checks for Carbon Dioxide and NOx. Although a single sensor is used to detect multiple gases, it's better to use a dedicated sensor for each gas, if you want better accuracy and reliability.

Arduino Nano and NodeMCU should cost less than about $15 in total and will do a great job for our basic requirement.

Sensors: Interfacing and Testing with Nano

Most low cost Gas Sensors available in the market are Electrochemical-type contact sensors. A sensing element is used to detect a certain gas. The sensor conductivity increases/decreases depending on gas concentration in the air. A simple electrical circuit can convert the change in conductivity to an output signal which corresponds to the gas concentration To model this, it's extremely important to understand the datasheets and the Rs/Ro Vs PPM or the Sensitivity Characteristics.

Please head over to this blog written by Davide Gironi to get a clear understanding of how to model your software and hardware to get an accurate sensor data.

For TGS2600/TGS2602 Sensor interface with Arduino

The sensor requires two voltage inputs to detect the concentration of gas: Heater Voltage (VH) and Circuit Voltage (VC). This sensor has 4 terminals. Two are dedicated to heater which maintains optimum sensing temperature in the sensor and the other two are for measurement of Output Voltage across a load resistor (RL) which is connected in series with the sensor. DC voltage is required for the circuit voltage since the sensor has a polarity. A common power supply circuit can be used for both VC and VH to fulfill the sensor's electrical requirements.

The value of the load resistor (RL) should be chosen to optimize the alarm threshold value, keeping power consumption (PS) of the semiconductor below a limit of 15mW. Power consumption (PS) will be highest when the value of Rs is equal to RL on exposure to gas (source: TGS2600 Datasheet).

Pin connection:
1: Heater
2: Sensor electrode (-)
3: Sensor electrode (+)
4: Heater

From the sensitivity curve, we obtain the scaling factor (a), and exponent (b) for our equation the ppm = a*(Rs/Ro)^b using power regression. If you don't understand this, please refer to Davide's Blog.

Also,Rs = Sensor resistance in displayed gases at various concentrations Ro = Sensor resistance in fresh air

To calculate Ro:

We know what the values of a, b are for 'air' (which is typically constant throughout) and also the Rs value, which can be calculated from:
Rs = ( Vc/Vout - 1) x RL

Hence from the equation: ppm = a*(Rs/Ro)^b we get:
Ro = Rs * sqrt(a/ppm, b)

Code to obtain Ro is present in the code section of the page. Refer to that end to see the code.

Once you obtain the Ro value, you can now proceed to calculate the final PPM value. To do so, follow these steps:

  • Convert raw analog value to Vout or Vrl Voltage value
  • Calculate the Rs value
  • Find Rs/Ro ratio
  • Obtain the PPM value by plugging the obtained values into the formula
VRL_F2600 = analogRead(F2600)*(5.0/1023.0);
Rs_F2600 = ((5.0/VRL_F2600)-1)*(RL_F2600);
ratio_F2600 = Rs_F2600/Ro_F2600;
float ppm_CO = CO_A * pow(ratio_F2600, CO_B);

This should help you obtain gas sensor value in PPM. However, as a part of calibration, you may be required to use a true value as reference and compare against the measure value.

Use: Ref. Value = Calibration Factor x Measured Value

Arduino Nano and NodeMCU interface

While hardware for sensors and their connectivity with Arduino Nano is not quite surprising and is fairly common, the Interface between Arduino Nano and NodeMCU might be intriguing to a few. The objective here was to ensure that the device be cheap and should have connectivity to the internet. The necessity of Arduino Nano arises when data from sensors is to be sampled. The gas sensors output Analog data and hence the MCU must have at least 3 analog pins (since we used 3 gas sensors here; could be scaled up to a larger number) to read this analog data.

As NodeMCU has only 1 Analog input pin (A0), it makes it alone isn't fit for the job. To solve this, the most obvious approach would be to interface an ADC (Analog-to-digital) converter and get this job done, but I chose a rather uncommon approach by interfacing a NodeMCU and a Nano (which was interfaced to the sensors). I did this because both these devices commonly available and easy to use as compared to an ADC, thus making it convenient.

To interface Nano and NodeMCU is quite simple. We make use of the "SoftwareSerial.h" library, which allows us to use any set of digital pins on the MCU as our UART bus. We directly used Nano's default Rx/Tx pins and cross-connected them with pin 6 and 5 respectively on NodeMCU to establish UART communication.

Rx (Nano) <=> Tx (Digital pin 6 on NodeMCU)
Tx (Nano) <=> Rx (Digital pin 5 on NodeMCU)
GND (Nano) <=> GND (NodeMCU)

While testing the UART communication, I realized that the entire data wasn't getting transferred and I couldn't debug it either. Hence, I decided to use the standard JSON format to transfer data. With the help of "ArduinoJson.h" library, I could pack this data, send it, store it on buffer till entire data is received, unpack it and then upload it to ThingSpeak. To understand how to implement Arduino JSON to your project, head over to the creator's website or read the documentation of the library on GitHub.

Note: The values are random and do not portray sensor data in this image.

PCB Design and Hardware Casing

Using the user-friendly EAGLE CAD, A simple PCB was designed to house the three gas sensors and also DHT11 Temperature Sensor. You can find the schematic and board files in the downloads section. Along with this, a simple hardware casing was designed using AutoCAD to house the sensor-PCB. It was then 3D Printed and has been in use ever since. Incase you need to get access to additional documents, please visit my GitHub rep on the same.


Code to read and analyse Sensor dataArduino
Upload this code to your Arduino Nano with the sensors connected to the relevant pins.
#include <ArduinoJson.h>

//Gas Sensor Pins
#define F2600 A1
#define MQ135 A2
#define F2602 A3

//Gas Sensor Load Resistance (RL)
#define RL_F2600 7.5
#define RL_MQ135 10
#define RL_F2602 7.5
/*2600 GASSES*/
float CO_A = 1.144997421;
float CO_B = -0.21687423;

float CH4_A = 1.05777824;
float CH4_B = -0.0795645;

/*2602 GASSES*/
float NH3_A = 0.92372844;
float NH3_B = -0.291578925;

float H2S_A = 0.38881036;
float H2S_B = -0.35010059;

float VOC_A = 0.3220722;
float VOC_B = -0.6007520;

/*MQ-135 GASSES*/
//MQ-135 CO2
float A_MQ135_CO2 = 112.89808; 
float B_MQ135_CO2 = -2.868463517;  

//MQ-135 NOx
float A_MQ135_NOx = 34.69756084;  
float B_MQ135_NOx = -3.422829698; 

void setup() 
Serial.begin(9600); // opens serial port, sets data rate 9600 bps

void loop() 
  //Refer to the documentation mentioned on this page to understand this better
  float VRL_F2600; 
  float Rs_F2600; 
  float Ro_F2600 = 11.26;
  float ratio_F2600;
  VRL_F2600 = analogRead(F2600)*(5.0/1023.0); 
  Rs_F2600 = ((5.0/VRL_F2600)-1)*(RL_F2600);
  ratio_F2600 = Rs_F2600/Ro_F2600;

  float ppm_CO = CO_A * pow(ratio_F2600, CO_B);
  float ppm_CH4 = CH4_A * pow(ratio_F2600, CH4_B);

  float VRL_MQ135; 
  float Rs_MQ135; 
  float Ro_MQ135 = 20.1;
  float ratio_MQ135;

  VRL_MQ135 = analogRead(MQ135)*(5.0/1023.0); 
  Rs_MQ135 = ((5.0/VRL_MQ135)-1)*(RL_MQ135); 
  ratio_MQ135 = Rs_MQ135/Ro_MQ135;
  float ppm_CO2 = A_MQ135_CO2 * pow(ratio_MQ135, B_MQ135_CO2);
  float ppm_NOx = A_MQ135_NOx * pow(ratio_MQ135, B_MQ135_NOx);

  float VRL_F2602; 
  float Rs_F2602; 
  float Ro_F2602 = 64.88;
  float ratio_F2602;
  VRL_F2602 = analogRead(F2602)*(5.0/1023.0); 
  Rs_F2602 = ((5.0/VRL_F2602)-1)*(RL_F2602); 
  ratio_F2602 = Rs_F2602/Ro_F2602;

  float ppm_VOC = VOC_A * pow(ratio_F2602, VOC_B);
  float ppm_H2S = H2S_A * pow(ratio_F2602, H2S_B);  
  float ppm_NH3 = NH3_A * pow(ratio_F2602, NH3_B);
 //Refer to the author's website: and read the documentation to understand ArduinoJSON better
  DynamicJsonBuffer jBuffer; 
  JsonObject& root = jBuffer.createObject();

  root["CO2"] = ppm_CO2;
  root["CO"] = ppm_CO;
  root["CH4"] = ppm_CH4;
  root["NOx"] = ppm_NOx;
  root["NH3"] = ppm_NH3;
  root["H2S"] = ppm_H2S;
  root["VOC"] = ppm_VOC;

//Minimum delay required for ThingSpeak to update is 16 seconds 
Code to upload Sensor Data to ThingSpeakArduino
Upload this code to your NodeMCU. This will read data from the UART bus and send it to your ThingSpeak channel.
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <SoftwareSerial.h>
#include "ThingSpeak.h"

SoftwareSerial mySerial(5, 4);
WiFiClient client; // Creating WiFiClient Object

//ThingSpeak Channel's API Keys
unsigned long myChannelNumber = enterChannelNumber;
const char * myWriteAPIKey = "enterYourWriteKey";

//Add your WiFi credentials here
#define WIFI_SSID "yourSSID"
#define WIFI_PASSWORD "yourPWD"

void setup() {
  while (WiFi.status() != WL_CONNECTED) {
  Serial.print("connected: ");

void loop() {
  // Check WiFi Status
  while (mySerial.available())
    //To understand this section better, refer to the author:
    const size_t capacity = JSON_OBJECT_SIZE(7) + 100;
    DynamicJsonBuffer jsonBuffer(capacity);
    JsonObject& root = jsonBuffer.parseObject(mySerial);
    if (!root.success()) {
      Serial.println("parseObject() failed");

    float CO2 = root["CO2"];
    float CO = root["CO"];
    float CH4 = root["CH4"];
    float NOx = root["NOx"];
    float NH3 = root["NH3"];
    float H2S = root["H2S"];
    float VOC = root["VOC"];

    Serial.print(CO2, 5);  Serial.print(",");
    Serial.print(CO, 5);  Serial.print(",");
    Serial.print(CH4, 5);  Serial.print(",");
    Serial.print(NOx, 5);  Serial.print(",");
    Serial.print(NH3, 5);  Serial.print(",");
    Serial.print(H2S, 5); Serial.print(",");
    Serial.println(VOC, 5);
//Sending Gas Data to ThingSpeak
    ThingSpeak.setField(1, CO2);
    ThingSpeak.setField(2, CO);
    ThingSpeak.setField(3, CH4);
    ThingSpeak.setField(4, NOx);
    ThingSpeak.setField(5, NH3);
    ThingSpeak.setField(6, H2S);
    ThingSpeak.setField(7, VOC);
    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
Code to find Ro ValueArduino
Upload this code with one of the sensors connected to Nano and find the Ro value for that sensor
float RL = 10;  //The value of resistor RL 
float analog_value =0;
float VRL;
float Rs;
float Ro,k;

void setup() //Runs only once
Serial.begin(9600); //Initialise serial COM for displaying the value

void loop() 
for(int test_cycle = 1 ; test_cycle <= 200 ; test_cycle++) 
//Read the analog output of the sensor for 200 times

analog_value = analog_value + analogRead(A3); //add the values for 200

analog_value = analog_value/200.0;
Serial.print("Analog value at fresh air = ");

//Convert analog value to voltage
VRL = analogRead(A1)*(5.0/1023.0);

//RS = ((Vc/VRL)-1)*RL is the formulae we obtained from datasheet
Rs = ((5.0/VRL)-1) * RL;
Serial.print("Rs at fresh air = ");

//Rs/Ro is k as we obtained from graph of datasheet
Ro = Rs/k;
Serial.print("Ro at fresh air = ");
Serial.println(Ro); //Display calculated Ro
My Github Rep
Find all source codes and files at my Github repository

Custom parts and enclosures

PCB Files
Find the EAGLE CAD design files (.brd/.sch)
Board Image generated on EAGLE CAD
First floor sensor room f qaudf2dhdk


Schematic 1
Simple Schematic diagram showing connections between various components
Schematic 1 mg1e2qcgdx


Similar projects you might like

Air Quality Monitor

Project showcase by Mustafa Hesham

  • 40 respects

Arduino Air Quality Monitor with DSM501A Sensor

Project tutorial by Mirko Pavleski

  • 1 comment
  • 22 respects

Indoor Air Quality and Garbage Monitoring System

Project tutorial by Guillermo Guillen

  • 26 respects

DIY Air Humidifier with Backlight Controlled by Alexa

Project tutorial by Andrii Romanenko

  • 8 respects
Add projectSign up / Login