Download PDF Parts List View on YouTube Download Code

Today we are going to take a look at a multitude of air quality sensors, as well as a few temperature and humidity sensors.  Let’s see how we can use a microcontroller to monitor the quality of the air we breathe!


Air pollution is a growing concern in almost every spot on the planet, including the inside of your home. In addition to the pollutants in the surrounding air, our indoor environments contain several sources of volatile organic chemicals (VOC) and other contaminants.

Monitoring the number of specific gases in the air will allow you to take steps to improve the air quality in your home. Often a strategically placed fan can work wonders.

Today we will look at several sensors designed to measure gas levels and various aspects of air quality. These can be used to construct an air quality monitor, to create an alarm to alert you to specific gas concentrations, or to automatically activate a fan or open a vent when conditions warrant it.

These sensors can work with almost any microcontroller, as well as with many microcomputers. Some of these sensors use the I2C bus to interface with their host, while others use either a serial connection or provide a voltage output for use with an analog-to-digital converter.

Today we will be using these environmental sensors with both an Arduino and an ESP32, but everything we do today applies to just about any microcontroller.

Measuring Air Quality

There are several parameters that you can measure to determine the quality of the air you are breathing.

You can look for levels of specific gases like ozone or carbon dioxide, and the sensors we will be using are available for a wide variety of gases. We will also look at sensors that measure the size of particulate matter, tiny particles of material that are a major cause of pollution and respiratory problems.

We will also measure temperature and humidity. These are not considered to be air quality factors, but accurate temperature and humidity measurements can be used to improve the accuracy of our other sensors.  It could also be argued that an environment that is too humid or too dry is also unhealthy.

Noxious Chemicals

Several of these sensors measure the concentration of specific gases. They are made sensitive to these gases with a resistive sensor that is tuned to that specific element, or “elements,” as many of these sensors work with multiple gases.

Some gases that you want to be on the lookout for include:

  • Carbon Dioxide – Causes headaches, dizziness, difficulty breathing, and increased heart rate.
  • Carbon Monoxide – Reduces blood’s ability to carry oxygen.
  • Ozone – Triggers chest pain and throat irritation and can also worsen asthma and bronchitis.
  • Nitrogen – Although it’s part of the air we breathe, too much nitrogen can be harmful to the lungs and can worsen common viral infections.
  • Sulfur Dioxide – Irritates skin and membranes of eyes, nose, and throat.

Volatile Organic Compounds

Volatile organic compounds, or “VOCs”, are compounds that have a high vapor pressure and low water solubility.  Industrial solvents, paints, and refrigerants are primary sources of these chemicals.

VOCs are found in cleaning, disinfecting, cosmetics, degreasing, and other common household products, as well as in paints and varnishes.  They can also be emitted by solid objects, including many types of plastics used in office equipment.

Common VOCs include:

  • Benzene – Found in tobacco smoke, paint thinner, deodorizers, air fresheners, and furniture polish.
  • Formaldehyde – Used in disinfectants, furniture upholstery, carpets, and plywood.
  • Ethylene glycol – Included in cleaning agents, personal care products, and perfumes.
  • Methylene chloride – Found in spot removers, dry-cleaned clothes, fabric cleaners, and commercial solvents, as well as used in air conditioner refrigerants.
  • Tetrachloroethylene – Used in solvents, dry cleaning chemicals, and paint strippers.
  • Toluene – Used in paint, metal cleaners, and adhesives.

VOCs can cause many adverse health symptoms, including:

  • Eye, nose, and throat irritation.
  • Headaches and nausea.
  • Loss of coordination.
  • Fatigue.
  • Dizziness.
  • Allergic skin reactions.

Particulate Matter

One of the primary components of pollution is particulate matter, a mix of solid particles and liquid droplets in the air.  While some of these particles, like smoke, dust, and soot, are visible to the naked eye, others are so tiny that a microscope would be required to see them.

When measuring Particulate Matter (also called “Particle Pollution”), we reference two standards:

  • PM2.5 – Particles with a diameter of 2.5 microns or less.
  • PM10 – Particles with a diameter of 10 microns or less.

For reference, the diameter of an average human hair is about 70 microns!

These small particles can enter your lungs and cause respiratory problems.

Air Quality Indicator (AQI)

The Air Quality Index, or AQI, is an attempt to create an easy-to-read standardized indication of the quality of the air.

The following chart shows the levels of the Air Quality Index:

You can calculate the AQI using an online calculator.

MQ Gas Sensors

The MQ series of gas sensors are resistive sensors that are usually purchased on a module.

These are resistive sensors known as Chemresistors, and on the module, they are wired in a simple voltage divider circuit so that the module’s voltage output is in proportion to the amount of gas detected. 

The modules also have a digital output, which is driven by a comparator. There is a potentiometer on the module that allows you to set a “trip point,” at which the state of the output will go from low to high. You can use this as an interrupt or in a stand-alone configuration for an alarm.

The sensors also have a heater, and on most models, this requires 5 volts to come up to the correct temperature. The modules include a dropping resistor to limit the heater current. These modules can consume quite a bit of current thanks to the heater, so you’ll need to consider that in your design.

There are several different types of MQ sensors; each one is sensitive to specific gases:

The hookup for all of these sensors is identical.

MQ Sensors with Arduino Uno

As the MQ series of gas sensors are designed for 5-volt operation, we will do most of our experimenting with an Arduino Uno.

As you are probably aware, the Arduino Uno has six analog inputs, A0 through A5. These can measure voltages between 0 and 5 volts, which matches the output of the gas sensors when used with a 5-volt power supply.

The Uno also has a 5-volt output, and you can use this to power the MQ gas sensor module, providing that you only intend to power one. If you wish to use multiple modules, you’ll need to provide a power supply. Allow for up to 100ma for each module, as there is a current surge while they are warming up. 

Arduino Hookup

Here is how I wired up my MQ gas sensor:

Click on the wiring diagram to open a larger image

It’s a pretty simple setup, and you can use a different analog port if you wish. Remember that A4 and A5 double as I2C ports, so if you want to use any I2C devices, such as displays or other sensors, you’ll need to avoid those two pins.

MQ Sensor Test Code

A very simple sketch can be used to see if everything is working.

As I said, it’s pretty simple! We are just reading the level at the A0 analog input on the 10-bit A/D converter and displaying the result on the serial monitor. Nothing to see here, folks!

Actually, there is something to see here! Try exposing the sensor to one of the gases it is sensitive to; I used butane from a lighter with an MQ-2 sensor. Once exposed, the voltage level, and thus the reading on the serial monitor, will increase dramatically.

Arduino MQ Sensor Library

The previous sketch was very simple, but it didn’t give us an accurate reading of the gas compounds. We need to do some math to get that reading, and fortunately, there is a library where all the hard work has been done for you!

The MQSensorsLib library simplifies using MQ gas sensors with Arduino, ESP32, and ESP8266. It works with MQ gas sensors MQ2, MQ3, MQ4, MQ5, MQ6, MQ7, MQ8, MQ9, MQ131, MQ135, MQ136, MQ303A, and MQ309A.

You can install the library directly from the Arduino IDE. Once you have it installed, open up the code examples included with it by navigating to the MQUnifiedSensor menu item in the “Examples from Custom Libraries” section of the example code. 

There are dozens of examples included, specific examples for each MQ sensor and some projects like an alcohol meter and a smoke detector.

In the MQ2 example, we can see how to use the sensor; all the sensors use similar coding. Most of the changes you would need to make are in the definitions, you might need to change the pin number of the A/D converter you’re using, for example.

You’ll also need to edit the Setup section to get the values of setA and setB. These are listed in the code comments, and they differ depending on what gas you are trying to detect.

Most of the “business end” of the code is in the Loop. The sensor is updated and can then be used in your code. The readSensor function will read the value.

Load the code and observe the output on the serial monitor. If you have a sample of the gas you are trying to detect (I used butane with an MQ-2 sensor), then you should be able to see the results change when the sensor is exposed to your sample.

MQ Sensors with ESP32

The ESP32 and other 3.3-volt microcontrollers present a bit of a challenge when using the MQ gas sensors, as most of these sensors require 5 volts for the sensor heater.

If you’re using a “raw” gas sensor, then there is no problem, as you can just wire the heater with 5 volts and use 3.3-volts for the sensor and a dropping resistor.

But if you are using a module, which is the most common way to buy these sensors, then the sensor and its heater are already wired together on the board. 

There are three ways to get around this:

1 – Just use 3.3 Volts

Click on the wiring diagram to open a larger image

The easiest method, simply power the sensor with 3.3-volts. The heater will work, but it won’t reach its full temperature, so the reading will be somewhat inaccurate (although you may be able to recalibrate for the difference). This is certainly the easiest way to use one of these sensors!

2 – Use 5 Volts and a Potentiometer

Click on the wiring diagram to open a larger image

This method powers the module with 5 volts. The analog output is fed into a potentiometer, allowing you to limit the maximum voltage to 3.3 volts to avoid damaging the ESP32.

This method is a good compromise for accuracy, although “tuning” the potentiometer will take some time. You could replace the potentiometer with a couple of fixed resistors after determining the resistor values.

3 – Modify the Module

Click on the wiring diagram to open a larger image

For accuracy, this is the best method, but it is also the most complex. It involves modifying the module by cutting traces on its printed circuit board, so if you feel uncomfortable doing this, then this isn’t the best method.

Click on the schematic to open a larger image

What you need to do is disconnect the side of the resistive element that is currently connected to VCC. Then you can power it separately with 3.3 volts.

Click on the schematic to open a larger image

This method powers the heater with 5 volts and the sensor/divider with 3.3 volts.  The heater will come up to the correct temperature, and the sensor will output a maximum of 3.3 volts.

Using the UnifiedSensor Library with ESP32

Regardless of which method you used to hook up and power the module, you’ll need to change the UnifiedSensor library example code for it to work correctly.

The modifications are all in the declarations section at the beginning of the code, and they are as follows:

  • Change the Board to “ESP32”
  • Change the Pin as required
  • Change the Voltage_Resoloution to “3.3”
  • Change the ADC_Bit_Resoloution to “12”

The rest of the code remains the same.

PM2.5 Sensors

The PMS5003 PM2.5 sensor can sense particulate patter down to as small as 0.3 microns. It provides standardized readings at PM1.0, PM2.5, and PM10.

The sensor itself is a small box that contains a fan and a laser. The fan is to intake the air, and the laser is used to measure the size of the particles in the air.

The sensor uses a serial UART as its interface, and it is set at 9600 baud.

You can buy the same PM2.5 sensor from several vendors, the only difference between them is the cable they provide for connecting power and communications. I purchased mine from Adafruit.

PM2.5 Sensor with ESP32

The ESP32 is an ideal match for the PMS5003 as it has multiple serial ports. Processors like the Arduino Uno without multiple serial ports can still be used by employing the SoftwareSerial library.

Here is how I hooked up my module to an ESP32:

Click on the wiring diagram to open a larger image

Note that although the module is powered by 5 volts, it is 3.3-volt logic compatible, so no level conversion is required. 

I used pins GP16 and GP17 for my UART connections, but you can use different pins if you wish – just change them in the code.

ESP32 PM2.5 Code

When I first tried my PM2.5 sensor, my initial impulse was to look for a library to use with it. Adafruit does have a library for these sensors, and I installed it and tried a sample sketch.

The sketch seemed to work, at least for a whole. I left it on for a few hours, and when I checked it, the sensor had stopped responding. I reset it and tried it again, same thing – it stopped working after a few hours.

At first, I considered that I might have a bad sensor, but after checking on the internet, I saw that this is a very common problem.

The solution seems to be to use some discreet code instead of a library. A sample from seemed to work fine.

The sample was originally written to use an Arduino Uno and SoftwareSerial. I rewrote it for the UART on the ESP32.

Essentially, the code defines a data structure and then defines a function called readPMSdata. This function reads the stream and gets the data from it, which it then outputs for display on the serial monitor.

When you run this example, like most examples in this article, you’ll need to wait a while for the readings to stabilize. You really should run it first in a known “clean air” environment so that you have a reference.

I2C Sensors

The rest of the sensors that I experimented with all use an I2C interface, which makes them easy to interface with just about any microcontroller and many microcomputers.

Some of these sensors read aspects of air quality, while others read temperature and humidity. As many sensors need to be calibrated with accurate temperature and humidity readings, this will improve accuracy.

I2C Sensor Hookup (all sensors)

The hookup for all the sensors is identical and is shown below.

Click on the wiring diagram to open a larger image

I used pins GP21 and GP22 for my I2C bus, as this is the default for the ESP32. You’ll need to be sure that your ESP32 module brings out these connections, as some modules don’t.

While it is possible to use virtually any two pins for I2C on the ESP32, I chose the default ones for a reason. The sensors we are about to use all use libraries, and the libraries expect the I2C bus to be on the default pins. To change pins, you would need to modify the libraries, something that is usually best avoided.

BME280 – Temperature & Humidity

The BME280 is a sensor for temperature, humidity, and air pressure. 

I bought a BME280 module from Adafruit. This module can be used with either the I2C or SPI bus, using the same connections for both.  Note that the SCL (Clock) pin is labeled “SCK” and the SDA (Data) pin is “SDI”.

BME680 – Temperature & Humidity

The BME680 is very similar to the BME280. As with its predecessor, it measures temperature, humidity, and air pressure.

The module I purchased was from DFRobot, which uses both the SPI and I2C bus. Unlike the Adafruit BME280, this module has separate connections for SPI and I2C.

AHT20 – Precision Temperature & Humidity

Another temperature and humidity sensor, the AHT20 is a precision device that can make very accurate measurements quickly.

The module I purchased was from Adafruit, which uses the I2C bus for communications.

CCS811 – Air Quality

The CCS811 is a very popular air quality sensor. It measures CO2 and TVOC and has a warm-up time of only 15 seconds.

I picked up a module from DFRobot. It has an I2C interface and is also capable of generating an interrupt when the air quality is reduced to a preset level.

SGP30 – Air Quality

The SGP30 is an air quality sensor that measures CO2 and TVOC.  It is an accurate device, however, it requires a 24-hour warm-up period before it delivers accurate readings.  You can reduce that time for subsequent readings by storing a baseline reading in nonvolatile memory.

The Adafruit module I bought has an I2C interface. It also has a 1.8-volt reference output.

SGP40 – Air Quality

The SGP40 is an improved version of the SGP30. The primary improvement is that it only takes 10 seconds to warm up, a drastic reduction from the 24 hours required for the SGP30.  It outputs values for the VOC Index, a scale similar to the Air Quality Index, as well as Ethanol readings.

As with all the previous sensors, this is an I2C-connected device. The sensor I purchased is from DFRobot.

ESP32 Multi-Sensor Sketch

One thing that you’ve probably noticed by now is that none of these sensors measure every parameter associated with air quality. Instead, they are specialized devices that focus on one or two measurements. 

So to completely analyze the quality of the air, we will require multiple sensors.  And that’s exactly what we are going to do.

For this last experiment, I hooked up the following devices to my ESP32:

  • PM2.5 Particulate Matter Sensor.
  • SGP30 Air Quality Sensor.
  • BM680 Temperature, Humidity & Air Pressure Sensor.
  • AHT20 Temperature and Humidity sensor.
  • SGP40 Air Quality Sensor

Obviously, there is some redundancy between a few of the sensors. The reason for using two temperature and humidity sensors is that the AHT20 is more precise than the BM680, but the BM680 includes air pressure.

The hookup for all the devices is identical to the experiments we have already run, and the PM2.5 sensor uses the UART connection while the others are all connected via I2C.

Multi-Sensor Code

The code I used is pretty well a combination of all the sample routines plus a few additions.  Two important aspects of it are:

  • It stores a pair of baseline values in EEPROM for the SGP30.  This way, it doesn’t need 24 hours to stabilize after running the first time.
  • It only prints to the serial monitor when the PM2.5 sensor is ready. This sensor is slower than the other ones.

Here is the code:

Most of this is pretty straightforward.  One section that you may not be familiar with is the use of the Preferences library. The Preferences library is included with the ESP32 installation on the Arduino IDE, so it’s already there for you. It is the modern method (it replaces the EEPROM library) of saving data to EEPROM. It saves data as a name-value pair.

The temperature data from the AHT20 is used to improve the accuracy of the SGP30 and SGP40 sensors.

As with the original ASGP30 demo, this code establishes a baseline after every 30 readings. Since we don’t want to write to EEPROM unless it is actually necessary, we check to see if the new baseline values differ from the previous ones. We only write data to the EEPROM if the values are not equal.

Running the Multi-Sensor Demo

Load the code onto your ESP32 and open your serial monitor. There will be a delay at the beginning as the sensors are allowed time to stabilize.

On the first run, you’ll have to wait about 10 minutes (or more) for the readings from the SG30 to stabilize. Ideally, you’ll run this in an area with clean air so that the baseline is established properly.

After that, the device should continue to run and display air quality. You can experiment by “fouling” the air up to see the results. Just don’t do anything unsafe!


A project like this could be expanded to have a display or to use the WiFi capabilities of the ESP32 to create a remote or IoT device.


Measuring and identifying sources of air pollution is the first step in making your air more breathable.  The sensors presented here can assist you with this and can also help determine if adding fans, ventilation, or even an electronic air cleaner would fix the situation.

So breathe easy – you’ve got technology on your side!

Parts List

Here are some components that you might need to complete the experiments in this article. Please note that some of these links may be affiliate links, and the DroneBot Workshop may receive a commission on your purchases. This does not increase the cost to you and is a method of supporting this ad-free website.




Code used in Article – All the code used in this article in a handy ZIP file.

Adafruit BME280 – The Adafruit BM280 Temperature, Humidity & Air Pressure sensor.

Adafruit AHT20 – The Adafruit AGT20 temperature and humidity module.

DFRobot SGP30 – The DFRobot SGP30 Air Quality sensor.

DFRobot CCS811 – The DFRobot CCS811 Air Quality sensor.

DFRobot BME680 – The DFRobot BME680 Temperature, Humidity & Air Pressure sensor.

Adafruit SGP30 – The Adafruit SGP30 Air Quality Sensor.


Measure Air Quality with Microcontrollers
Measure Air Quality with Microcontrollers
Article Name
Measure Air Quality with Microcontrollers
Measure the quality of the air you are breathing using an assortment of air quality sensors. You'll learn how these sensors work and how to use them with an ESP32 and Arduino.
Publisher Name
DroneBot Workshop
Publisher Logo
Tagged on:     
Notify of

Inline Feedbacks
View all comments
1 year ago

Awsome…. I was thinking to make one. Need to learn connecting a display. Thinking about color LCD display…

Forrest W
1 year ago

Great work and instructions. I appreciate your time and effort. I did want to ask about the table describing the MQ snesors and their purposes. MQ-7 and MQ-9 are both for Carbon Monoxide. Are both for CO only? Or perhaps it was a typo and one of the two are for CO2 (Carbon Dioxide)?

Steven Anon
1 year ago
Reply to  Forrest W

I did some brief research – both the MQ7 and MQ9 read CO
CO2 can be read from MQ-135

1 year ago

A great tutorial on air quality sensors. I would really like to get this working on Bluetooth to my phone but with VT100 terminal emulation on my Android phone. I think Bluetooth would work fine with an HC05 Bluetooth adapter. However, I have tried numerous Android terminal apps but none work with VT100. VT100 would allow the info to be displayed without scrolling. The text would remain in place and the values (numbers) would simply change periodically. I can get VT100 to work on my PC by using Tera Term and the VT100.h library. I have made an MQ-5 gas… Read more »

Kevin T
1 year ago

Hi Bill. This project/video was very timely for me. I was wanting to build a dust sensor that will turn on an air filter in my woodworking shop when dust levels increase due to cutting or sanding as well as turn off my minisplit A/C until the dust levels subside. I used your code for the PM2.5 sensor (Plantower PMS5003) instead of the Adafruit library and it ran for about a day-and-a-half before it quit working. I’m monitoring it on an Arduino Cloud IOT Dashboard which displays a constant reading on failure although I suspect that if it were attached… Read more »

6 months ago
Reply to  Kevin T

I have the same problem, I have the Plantower PMS5003 sensor with the same code and sometimes it works one day, other days it doesn’t work, does anyone have the solution?

Last edited 6 months ago by YSerna