Particular: A Particulate Air Quality Sensor (Shallow Thoughts)

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Fri, 17 Apr 2026

Particular: A Particulate Air Quality Sensor

We're thinking about replacing our ancient fireplace with a modern wood stove. There are lots of reasons, but one is that the house smells smoky when we use the fireplace (which is pretty much every night in winter), and I can't help wondering what all that smoke is doing to my lungs.

Dave insists that the smoke all gets sucked up the chimney and I shouldn't worry about it. I tried to look it up, but it seems like there's hardly any published research on that (or maybe I was just choosing the wrong search terms).

[a boxy blue air quality sensor in a crate made out of popsicle sticks, with a microcontroller with screen on top] But why not actually measure it? I've occasionally wanted a particulate matter sensor anyway; we get a lot of wildfire smoke here in New Mexico most summers (sometimes from local fires, sometimes from as far away as California or Canada) and sometimes the air quality can get pretty bad.

Of course you can buy ready-to-go air quality sensors. But what's the fun in that, when you can make your own for about half the price? (If you don't count the value of your time, that is.)

Hardware

Web research led me to the PMS5003, which seems to be well regarded and isn't too expensive. It measures PM1.0, PM2.5 and PM10.0, meaning particles of size 1 μm, 2.5 μm and 10 μm.

Unfortunately, it uses serial to communicate, not I2C, so you need a processor with UART capability. I was hoping to use the PyPortal, because it has that lovely display I can use to show all those different particle sizes. The specs say it does UART, but I couldn't find anything online about how. But after a little help from a wonderful person on the Adafruit forums, I learned that the PyPortal uses the D4 connector for RX and the D3 for TX (either one can provide power and ground). The PMS5003 only needs TX, so it only needs the three wires from the D4 connector.

The Adafruit version of this sensor comes with a breadboard adapter, but they were out of stock, so I was using a sensor purchased on Amazon without an adapter. The sensor itself comes with a 8-pin ribbon cable with a tiny connector on each end (a "Molex picoblade connector"). My first attempts to wire it up by cutting off one end, stripping the wires and using test clips didn't work, but once I bit the bullet and soldered breadboard-friendly pins to the end of the wire, the Adafruit library worked fine. So I crimped up a new Stemma cable for the PyPortal end, and soldered a female 5-pin header to the other end that I could plug the breadboard adaptor into.

I wanted to add another sensor, an SGP41 that measures volatiles NOX and VOX, which uses I2C. Fortunately, the PyPortal's I2C isn't used by the PMS. But the SGP41 sensor I got (again, not from Adafruit because it's out of stock there), though it works fine with the Arduino library, doesn't work with Adafruit's CircuitPython code. My SGP41 seems to have an I2C address of 0x48, not the 0x59 that the library expects, but if I initialize it with address=0x48, it fails with a CRC error. Adafruit's SGP41 Arduino library also expects 0x59 as the address, but somehow it works anyway. I have no idea why they're different; I've given up, at least for the moment, with getting the PyPortal to talk to the SGP41, because I wanted to get the PMS5003 going before fire season was completely over. I'll work on the SGP41 problem separately.

A box

[a boxy blue air quality sensor in a crate made out of popsicle sticks, with a microcontroller with screen on top] I usually like cardboard boxes for project cases, especially since we accumulate lots of small cardboard boxes that hold R/C plane batteries. But in this case, air quality sensors need to be open to the air, not shut up in a box.

I decided a wood crate made of popsicle sticks sounded like just the ticket. First I glued a popsicle stick frame the right size for the bolts on the PyPortal (I had to shave down one of the sticks a little with a Dremel tool to accommodate some hardware that protrudes a little past the line between the mounting holes). Then I glued a lower frame the same size as that upper frame, with cross-rails for the PMS sensor to sit on, and connected the upper and lower frames with pieces of a dowel and some screws. I used double-stick tape to attach the PMS sensor to the lower frame. Nice and easy! I'm not worrying about space for a battery; I'll use this mostly in the house, so I'll just plug it into a 5v supply, and if ever I need to take it outside, I can use a portable power source.

Software

Adafruit has a tutorial on the PMS5003 that covers CircuitPython. I added the SGP41 code (that it turned out I wasn't able to use), plus an extra section that lets it report JSON data via a CGI script that saves a time-stamped record. I'll be able to start it up, let it run for a bit, then start a fire in the fireplace and see how things change. Or let it run all day and all night while there's forest fire smoke in the air, and keep a record of how the air changes over time.

The source is at: particular on GitHub.

Eventually I'll add a nicer display with colors and big fonts, and maybe even do something with the PyPortal's touchscreen. But for now, I think it will be useful even with just the printouts on the CircuitPython console.

So, will we replace our fireplace with a wood stove? That turns out to be a remarkably complicated thing to do (it's not clear it's even possible to do it legally to code, when starting with a 1975 house, without completely rebuilding that end of the house). But we'll see. Maybe the particulate readings will help with motivation, if we have a few more nights cold enough to need a fire.

Tags: , ,
[ 15:09 Apr 17, 2026    More hardware | permalink to this entry | ]

Comments via Disqus:

blog comments powered by Disqus