Argon Powered Air Quality Monitor

Air quality.

You probably think about it more now that our clean air has turned into a permanent haze across the sky.


One thing you do have control over is the air quality inside your home. In this tutorial, I’ll show you how to build an air quality sensor from some off-the-shelf parts. (Using Particle Mesh of course!)

Everything you need. Nothing you don’t.

All the parts needed for this project.

The brains of the operation is a Particle Argon. This project is also compatible with any Particle Mesh board and also anything in the Adafruit Feather footprint.

As for the sensors, the most important is the the Honeywell HPM series PM2.5/PM10 sensor. At a high level, it counts how many particles are flying around in your air and gives you a number based on that information. If you ever see air quality data in a weather app like Weather Underground, that information is based off of a particle measurement that the HPM produces.

Second, there’s the AMS CCS811. It measures eC02 and TVOCs (total volatile organic compounds). It’s an important data point for indoor air quality. Try placing this sensor in your basement next to your furnace or boiler (if it’s not electric) and you’ll get some surprising readings when it’s running.

Finally, a Silicon Labs Si7021. This little guy has been my go to for combined humidity and temperature readings. It also provides the temperature and C02 readings for the internal calibration of the CCS811.

Fire it up

Here’s the full list of ingredients for this IoT stew:

I’ve included the Fritzing example with the source for this project. Here’s a screenshot though:

Note: the original Fritzing diagram was incorrect. Both Vin of the CCS811 and Si7021 should be connected to the 3.3V on the Particle. Thanks armor pointing out I goofed :slight_smile:

Particle doesn’t have any Fritizing parts so I had to substitute with an Adafruit Feather. So just close your eyes and pretend we’re using an Argon…

As for the particle sensor, it’s a bit of a mystery how it’s hooked up from that diagram. Here’s the pinout from the data sheet:

HPMA115S0 datasheet

We’re most concerned with the 5V, GND, RX and TX pins.

Just make the connections like so:

  • 5V -> USB
  • GND -> GND
  • RX -> TX (on the Argon)
  • TX -> RX (on the Argon).

If you did everything right, your setup will look something like this:

If you’re curious how I hooked up the HPM sensor here were the steps:

  1. Cut the pre-assembled cable in half.

  2. Remove all the unneeded wires

  3. Solder headers to the individual wires

  4. Plug em in to the breadboard

Here’s a picture of what the solder job looks like.

Soldering header to wire

I went into further detail on this in the full tutorial.

Setting Up The Firmware

For this project, I’m using Visual Code with the Particle plugins. More on how to set that up here.

TheSi7021(Temperature and Humidity)

The Si7021 is a go-to for humidity and temperature measurements due to its simplicity. The only drawback is that a read to this device, in its simplest form, uses clock stretching.

What does that mean?

Your micro controller is tied up during the time it’s waiting for the temperature and humidity data. For this application, it’s less critical considering we’re taking measurements every minute or two.

Here’s what a typical read looks like:

If you feel like you’re having déjà vu, it’s not surprising. Particle uses the same Wiring/Arudino API that we’re all familiar with.

The CSS811 (TVOCs and C02)

The CCS811 has a few more bells and whistles compared to the Si7021. In particular it has an interrupt pin. An interrupt is particularly useful because you can start a measurement, do some stuff, and then return to it later once it’s done. No blocking code makes for a happy processor.

TheHPM (Particle Sensor)

The HPM sensor unfortunately is not as nice. It uses a UART interface. In my experience UART is a pain to work with especially when you have a tight state machine. I also had trouble writing commands to it over time.

My solution?

Leave it on and running.

By default it sends data every 1 second. All you have to do is be there to listen for it. Over time this does degrade the performance of the fan. Honeywell recommends that it be controlled by a switch to preserve the device life. I ended up doing that in the fully integrated version of this project.

Gotta Get Out

The data is only so useful sitting in the micro controller on your table. You gotta get it out somehow!

I serialized everything together in a JSON blob and sent it as a single publish event. There were a few reasons for this I won’t go into here.

The creation of the blob is straight forward. Take a look below at the actual code:

I simply took each reading and inserted them into a pre-formatted string. I then sent that string using the Particle.publish command.

The cool thing is, the data won’t get sent until all three sensors have finished doing their thing. The one that takes the longest, by far, is the HPM sensor.

Webhook Madness

So we’re pushing data to Particle’s backend. Now what?

After some searching, I found one solution that I’ll share here: GoogleSheets.

Oooh yes. We’re going to directly import the data from this device into Google Sheets.

Here’s the play by play:

  • Go to Tools -> Script Editor . This should pop open a script window.

  • Create a new script. And copy the contents of what I’ve written below:

I’ve based the code originally on the post I found on the subject.

  • Then I went to Publish -> Deploy as web app
  • Remember, this app shouldn’t be used by anyone else except you. You can set Execute the app as and select yourself.
  • Finally, Who has access to the app is Anyone, even anonymous otherwise it will require authentication which would not work! If you have already published, you will have to change the Project Version to new in order for your changes to apply.
  • In the Particle console, go to the Integrations section and create a new Webhook

  • Fill in the name of the event you want to forward. In our case it’s blob
  • Enter the URL provided in Step 5
  • Make sure the request format is JSON . You can keep the default JSON format. Keep Enforce SSL enabled.
  • Make sure the device that you want to watch is defined. In my case I’m using the name that Particle gave my device hamster_turkey (awesome name, right?)
  • Go down to the bottom and click Save
  • If your Particle is programmed, you should start seeing updated data populate the Google Sheet. You can put data into graphs that update in real time.

Note: This type of data collection isn’t great for large amounts of data spanning more than a few days. It’s great though for short time trials and tests though! I originally was using IFTTT but the data collection was sporadic. Now it’s quite consistent!

You can see my live example here.

We did it!

If you’ve gotten this far. Here’s a virtual high five! :raised_hand_with_fingers_splayed:

If you want more, I go into more details on my blog. I cover things like configuring this project for Adafruit’s IoT service, methodology behind the code and more.


I have a Xenon here that I want to get working with this project. I’m particularly interested in seeing the battery life I can squeeze out of it. Will report back when I get some numbers!

The Source

Oh! I did not forget about the source. Everything for this project and the accompanying breakout board is located on my site here. Go check it out and get started!


Great project!!!

1 Like

Thanks Will! Glad you like it! :sunglasses:

I think that this is a great project. I see that you have connected a 5V to the CCS811 and Si7021. Won’t this then produce 5V logic on the I2C bus (this is what the Adafruit spec says) which is definitely not good for the Argon pins? Both these sensors can be operated from the 3V3 pin which would avoid this issue. Indeed, shouldn’t there be a level-shifter/bi-direction buffer IC on the TX and RX pins to the HPMA1150 for the same reason, accepting that this needs a 5V supply?

1 Like

This is a great question!

Your reasoning is correct. 5V logic on a 3.3V circuit would be bad news bears.

In the case of this circuit, the Adafruit boards have LDOs which bring the voltage down to 3.3V. In addition, the HPM sensor has an internal regulator that does the same. The logic on the HPM is also 3.3V.

Here’s some screenshots from the Adafruit schematics with the 3.3V step down. (These boards also have level shifters so it will acclimate to a larger range of voltages.)

If you take a look at the source files for the all-in-one board, you can see I wired everything up as you’d expect.

Thanks for replying. I did look at the schematics and I am seeing a pull-up on each of SDA and SCL to 5V/VIN for both the CCS811 and the SI7021? I understand your final circuit design avoids this issue.

Oy, you’re right. I wrote that last message in a daze without going back and double checking. (I literally sat up in bed and went… “damnit!”

My main concern was the LDOs dropping out considering my input would be 3.3V. Considering it’s a trivial amount of current, my concern is moot.

I’ve tested it with Vin @ 3.3 on both and the output of the LDOs aren’t dropping out. I’m going to change this info right… now!

No worries! It just isn’t obvious and I have a (small) drawer of wounded solders with burnt out pins from previous mis-use. It would be a shame if someone follows the project (GoogleSheets stuff is great) and then burns their Argon or Xenon!

1 Like

I totally agree. I’d feel pretty terrible. Thanks again for bringing this up!

Some of you guys have asked “Why the CCS811?”

Well, I finally had the chance to polish the comparison between the BME680, CCS811 and SGP30.

Here’s a quick summary:

The above is a screenshot from the side-by-side data of the BME680, CCS811 and SGP30.

The BME680 seemed impressive at first but I’m seeing a big difference in temperature and humidity. My thinking it’s likely due to the heating elements inside keeping it a little less humid and a little more hot. (These are the compensated values by the way. The non-compensated values are even lower humidity and a hotter.)

The SGP30 is a bit of a power hog but the TVOC readings seemed to align well with the response from the BME680.

The CCS811, like we’ve seen throughout this thread, had some wacky responses using the 1.0.0 firmware. I haven’t seen that since all of my CCS811 are up to date.

It’s a toss up between all three but I’d shy away from the CCS811. If anyone here plans on building a production device, the stock is not reliable enough. (Digikey and Mouser have been out of stock lately) I’m not sure if you’d have the same problem in the EU or Asia.

Anyway my TLDR conclusions are here if anyone is interested. They include software implementation factors, hardware factors, availability factors and data output factors all rolled into one.


Regarding the higher temps of the BME680, did you run it in forced or continuous mode? I’ve only used the BME280 but it has the same issue unless you run in forced mode and limit your sampling to 1hz.

Thanks for sharing the CCS811 info, glad to know I can keep using my SGP30’s :wink:

1 Like

@Fragma thanks for the tips. I’m using the code that comes with the BME680 (from Bosch). It appears the i’m using BSEC_SAMPLE_RATE_CONTINUOUS so that may explain a ton.

I’ll have to play with it more and report back! :slight_smile:

@Fragma Looks like that did it! I changed it over at 1900

The humidity is within 1%
The temperature totally swapped whereas the BME680 seems to be daaaang close to actual. The sensors are still off by 2. ( I also think the Si7021 runs hot )

1 Like

Some of you have noticed I’ve played around with a few dashboards for this project. My favorite dashboard, by far, is Grafana. It was suggested to me by someone on another forum. It’s a minimal cost for an extra boost in functionality.

All the screen shots in my last few posts are all from Grafana. You can easily add new graphs, change the time window for a whole dashboard. Very slick!

You can even set alerts on the data. There’s a handful of messaging protocols it supports. I personally use Pushover (no affiliation).

Anyway here’s the link to how to set it up.

What does everyone else here use?

Hey all!

I previously posted about using this Argon/Xenon powered air quality monitor. But I felt like there was more to be desired. Like an enclosure, and a more straight forward way to getting feedback to the people in the room.

So I decided to make The Canary. Only that it starts chipping when things are going awry. (Not the other way around!) :penguin::dizzy_face:

It was a fun project and it’s already paid off in a few ways. It has chirped at me when the air quality in the kitchen has gotten out of hand. (Turns out cooking puts a ton of particulates in the air!) Plus, it reminds me of the current air quality when I walk by using the onboard RGB LED and PIR.

I posted the full details on Hackster. Hopefully this inspires you to build your own or improve on mine! :slight_smile:


Hey All,

I hope you’re staying safe and healthy!

Do you need an enclosure for your Particle/Feather projects? You’re in luck. The enclosure exists now. :slight_smile:

It was designed with the Air Quality Wing + HPMA115S0 combo in mind. You can use it for other Particle/Feather projects as well though!

Download link:

Click here to download all the .STL files and get printing!

The features:

  • Detachable lithium battery compartment.
    • Maximum battery size:
    • Width: ~81 mm
    • Length: ~41.9 mm
    • Height: ~10.35 mm (includes battery expansion wiggle room)
    • Secures with M2.5 screws
  • Precision openings for the Honeywell HPMA115S0
  • Dual purpose top opening for Particle RGB LED and Ventilation
  • No fastener clip top
  • Enough room for the original AQW and Reduced Size Version (v5 and newer)
  • Knock outs for antenna connection and battery connection
  • USB port cutout to run off USB power

Here are a few photos:

Also here are some shots from Jim of the first assemblies:


Ready to get printing? Click here to download all the .STL files!

If you do print it out, I’m very open to feedback. You can message me here or at my email


amazing work, Jared, and now with a matching enclosure.
Congrats again,

Thanks @gusgonnet!