A More Complete Air Quality Monitor

Samples all 6 metrics the EPA uses to calculate Air Quality Index: 2.5 & 10um particulates, O3, CO, NO2, and SO2, and calculates AQI.

Public Chat
Similar projects worth following
This prototype air monitoring system measures all six pollutants the EPA factors into its composite Air Quality Index (AQI). Most air monitor projects only measure particulate matter because fully-integrated particulate counters are cheap and easy to work with, with full onboard processing and a serial interface, and do not need to be recalibrated in the field. Ozone, carbon monoxide, and nitrous oxide are equally concerning, but until recently were not measurable without expensive lab equipment. This project uses factory-calibrated gas concentration sensors designed for medical equipment by Spec-Sensors. The gas sensors are sampled by a pair of 16-bit ADCs and heavily filtered on the ESP8266. A BME280 module provides temperature, humidity, and pressure, while a Plantower PMS5003 laser particle counter reports particulates. The ESP8266 serves a webpage showing latest values and hosts a REST server for offboard data logging.

This project was inspired by a general interest in pollution in the Los Angeles area and wanting to know how air quality varies by geography. I live in a neighborhood ten miles north of downtown LA and about 1500' higher elevation, and often wonder if I'm living in LA's smog layer. I figured there must be an air quality monitoring station nearby that might provide data for my neighborhood, but was somewhat disturbed to find that the EPA has only a single air quality monitoring station in the Los Angeles basin and another in the San Fernando Valley. That's far too sparse to provide neighborhood-scale comparisons.  I'll spare you my speculation about why the EPA doesn't do a better job.

A friend at work told me about NASA's Air Quality Citizen Science program which is establishing a network of volunteers around Los Angeles and two other pilot program regions to provide much higher spatial resolution data which can serve as ground truth data for remote monitoring satellites. I now host a sensor for this network, but was surprised to find that the sensor module only senses particulate matter which count for only 2 of 6 pollutants the EPA factors into its composite Air Quality Index (AQI) indicator. I couldn't find any projects which measure gas concentrations with sufficient accuracy and stability to resolve the various grades within the AQI scale for each gas (some of which measure in parts-per-billion), short of full-blown lab equipment and incredibly expensive field deployable units.

The Spec-Sensors gas sensors turned out to be just the thing I was looking for. I consulted with Spec-Sensors to ensure I could achieve the accuracy levels needed. The neat thing about these sensors are that they are precisely calibrated at the factory and hold their calibrations indefinitely. It is still necessary, however, to precisely calibrate the host system. Specifically, the analog driver boards from Spec-Sensors float uV-scale signals on top of a carrier voltage which is approximately half the supply voltage. The driver board provides a readback pin with this carrier voltage, but sampling that pin with anything short of a lab-quality ADC with exceptionally high impedance distorts the measurement. Instead, for this project, I sampled the supply voltage and output signal from each gas sensor board for two months, then compared to data from a nearby EPA station and solved for bias voltages for each gas sensor by minimizing sum-squared error between the sampled voltages and the EPA data. I subsequently logged 2 more months of raw voltages and calibrated values and found the calibrated values to show good correlation with the nearby EPA station.

Now what do I do with this? The system is portable. I originally envisioned logging data while driving around Los Angeles, then driving up the Angeles Crest Highway into the Angeles Forest to see how the smog varies with geography. However, the signals are so faint that it's necessary to average over about 30 minutes (one hour to match EPA criteria). That would be a very long day. A network of co-calibrated modules would work much better. The prototype cost approximately $250 (the same price as a PurpleAir AS-II module which only samples particulates, by the way), cheap compared to other systems of comparable capability but not cheap enough for me to fund my own curiosity project. Maybe a gofundme or kickstarter project? I wouldn't know where to start.

JPEG Image - 98.29 kB - 03/28/2020 at 21:48


JPEG Image - 2.51 MB - 03/28/2020 at 21:11


JPEG Image - 2.55 MB - 03/28/2020 at 21:11


  • 1 × NodeMCU Lua ESP8266 ESP-12E, CP2102
  • 1 × BME280 I2C breakout Measures temperature, humidity, barometric pressure
  • 2 × ADS1115 ADC Breakout 16-bit 4-channel anadlog-digital converter, I2C
  • 1 × Proto-board 3.3V and 5V Power Supply
  • 1 × DS3231 AT24C32 I2C Real Time Clock Module

View all 10 components

  • 1

    This is the basic topology I ended up with for this prototype:

    I did not take the time to draw up a more detailed schematic, but given the modularity of the components, this should be simple to wire up point-to-point, as I did, or to design an actual PCB around.  Ultimately, this could be produced for about 2/3 the cost per unit if one were to consolidate the DC voltage regulation, esp8266, RTC, and ULPSM gas sensor breakout boards onto a single PCB.

    For this prototype, I ended up sampling the ULPSM gas sensor boards single-endedly, and separately sampled the 3.0V bus voltage in order to estimate Vref, with calibration offsets that I worked out externally by solving for best fits to data form a nearby EPA station.  I briefly experimented with sampling in differential mode but was plagued by drift.  That was before I worked out temperature compensation and a software low-pass filter, so, in retrospect, I may not have given differential mode a fair shake.  

    Others have commented that op-amps configured as differential amplifiers would have worked here.  If I pick this project up again, I will try these other methods.  I suspect that a single quad op-amp IC would be perfect, between the four ULPSM boards and one ADS1115.  The other ADS1115 would no longer be needed since the op amps produce four single-ended analog signals and there would no longer be any need to sample bus voltage.

  • 2

    Download the arduino code and accompanying index.h file to a new directory in your arduino working directory.

    Edit the arduino file to use your wifi network SSID and password, and adjust any pin assignments as needed.

    Flash using the Arduino IDE.  You'll need to use a usb cable the first time you flash your esp8266.  After that, you can flash over the air.

    Within the arduino code, the gasSensor structure contains one Vcal calibration value for each sensor.  The values in the repo version of the code work for my specific build and will not yield accurate results for your system.  You must calibrate these values either by your own method or the hacky method described below.

  • 3
    Understand the Gas Concentration Calculations

    Spec-sensors provides this Application Note:

    which describes thermal sensitivity of its gas sensors.  The Application Note provides plots showing offset and scale temperature sensitivity, and also provides a brief description of a temperature compensation process.  I was unable to figure out how to associate the temperature sensitivity scale and offset values from the included plots to these equations, even after contacting Spec-Sensors multiple times.  Ultimately, I ended up working out my own equations, based on the functional descriptions in this document.

    In order to use the data provided in the temperature sensitivity scale and offset plots, I manually fit (eyeballed) exponential curves to each sensor profile.  The curves I derived have coefficients which are provided in the gasSensors struct in the arduino code and correspond to these equations:
    TCompOffset = Coffset0*exp(Coffset1*TdegC)+Coffset2
    TCompScale = Cscale0*(1.0-exp((Cscale2-TdegC)*Cscale1))

    These scale and offset values vary with temperature over time.  Next, these scale and offset values are used in the temperature-compensated gas concentration equation which I worked out on my own:

    Gas Concentration (ppm) = ((Vgas-Vbus/2-Vcal)*1e6/Gain-TCompOffset)/Gain*(2-TCompScale) 

View all 4 instructions

Enjoy this project?



Ryan Kinnett wrote 03/30/2020 at 02:45 point

This project has been on the backburner since Sept 2019.  I don't know if I'll get back to it.  My brother was briefly interested in coding an online database and arcgis interface to show multiple stations, but we never did follow through (my fault).  A Kickstarter project would be fun but would need some up-front investment to improve and consolidate the circuit design and build just enough units to populate a map. I'm not planning to put more resources into this anytime soon but would be happy to help others with similar projects.

Since several people have written, asking for schematics, code, and instructions, I went ahead and wrote up instructions in the Details section.  I did not provide a full schematic because the entire unit is composed of modular components which just need point-to-point wiring.  Wiring, pin mapping, etc is left to the reader.  See the Files area for an image of the underside of my PCB, which is only a little embarrassing.  I also posted code to github, even though my code is a horrible mess and may be near impossible for anyone to follow.  The real innovation of this project is in the calibration process, which is far from perfect.. but the price is right.  I also wrote about this in the Details section.

If anybody finds any piece of this project remotely useful, please let me know.


  Are you sure? yes | no

syahrilmahyuddin wrote 12/22/2019 at 09:23 point

Hi Ryan, I think your project is very useful, I want to make it as my final project on campus, can you guide me to make it? thank you!

  Are you sure? yes | no

Robert wrote 09/05/2019 at 10:36 point

Hey Ryan,  This is a great project.  I live in Kathmandu and have been looking for something that does exactly this.  I'm new to this so forgive the naive question, but how can a get a copy of all the code to put something like this together for myself?  And would it be possible to give detailed step by step instructions?  I'd like to give this a go and if I could get it to work, I know a lot of people in the ExPat community here would be grateful for having something accurate in their house.  Thank you!

  Are you sure? yes | no

Ryan Kinnett wrote 09/05/2019 at 20:29 point

Hi Robert, thanks for the compliments.  I would be happy to help if you want to go this route.  I'll post a functional diagram and some photos of the point-to-point wiring in a couple days.

  Are you sure? yes | no

Tim Rightnour wrote 09/08/2019 at 13:01 point

Looking forward to seeing that.  I'm thinking of building something very similar!

  Are you sure? yes | no

DenisZanette wrote 03/05/2022 at 17:41 point

hi Ryan, it is possible to have a wiring diagram of your project. possibly a fritzing. thank you

  Are you sure? yes | no


[this comment has been deleted]

Ryan Kinnett wrote 09/05/2019 at 20:27 point

Hi Teodor, 

I wrote to Spec-Sensors about that external voltage divider and some other questions that arose from the examples in that repo, and they explicitly recommended not reading the Vref pin at all.  I tried it anyway; soldered those resistors around the OIII Vref pin and tested for a few days.  I was still seeing a ton of noise and a long period drift that swamped the signal I was expecting based on the nearest EPA station.  At that time, I had not yet worked out my time-weighted averaging scheme or temperature compensation, so I'm not sure I gave it a fair shake.  If I revisit this circuit, I'll put differential instrumentation amplifiers between the ULPSMs and the ADCs.

  Are you sure? yes | no

Ryan Kinnett wrote 09/04/2019 at 18:41 point

I was previously unaware of this similar project by @Radu Motisan  who did an amazing job with packaging and software dashboard:

I'm curious how the Windsen gas sensors compare to the Spec-Sensors ones I'm using.

  Are you sure? yes | no

Ryan Kinnett wrote 09/04/2019 at 19:03 point

FWIW, the Winsen sensors with built-in digitization and UARTs run ~$72 each, compared to $75 ea for Spec-Sensors' integrated digital boards.

  Are you sure? yes | no

Ryan Kinnett wrote 09/04/2019 at 19:06 point

Radu now sells the integrated modules for $1499.  The parts cost should be a small fraction of that.  I guess you're paying for engineering time, their back-end data visualization dashboards, and calibration.  Interesting to know it's marketable.

  Are you sure? yes | no

Kris wrote 09/04/2019 at 00:35 point

Interesting project. Why not use the digital versions of the Spec-Sensors? Each sensor is $25 more - is that worth the added accuracy and convenience?

  Are you sure? yes | no

Ryan Kinnett wrote 09/04/2019 at 18:41 point

Thanks Kris.  Good question.  Before committing to this project, I contacted Spec-Sensors to ask whether I could achieve ppb-scale precision from their O3 sensor with either of their driver boards.  They suggested using the ULPSM board with a higher resolution ADC, 16 bits or better. I also wasn't sure how to interface four peripheral serial boards from the ESP8266; maybe bit-banging or softwareserial would have worked, or maybe a multi-uart IC between the ESP and the sensor boards.  It would be great if Spec-Sensors made an addressable bus version (I2C, etc).  If I were to redesign this for production, I would just get the bare sensor boards from Spec-Sensors for $20 each and implement the driver circuits on a consolidated main board along with higher quality ADCs, voltage regulation, etc.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates