Actively maintained Opensource Air Quality Monitoring projects?

Hey folks,

I’ve been asked to run a citizen science project to measure PM2.5 and PM10 across the town.

As our council are finally installing LoRaWAN Gateways, I figured that I’d just grab one of the Luftdaten-style setups and use that, however it turns out things have moved on a lot since I last looked at this over 4 years ago.

I was going to use the ESP32-Paxcounter project, but I’ve had no end of issues with getting that up and running and all the libraries I’ve looked at to write my own firmware seem to be unmaintained (or at least have not been updated in over 2 years!)

Is there a “gold standard” for this kind of device that can be self-built for reasonably cheaply?

If I need to write my own firmware, that’s fine, but pointers in the direction of a library for the SDS011 that is being kept up to date with changes in the Serial library for the ESP32 (and ideally not rely on SoftwareSerial at all!) etc. would be appreciated, otherwise recommendations for other hardware are also welcome!

Thanks in advance,

Matt (formerly Mockingbird Consulting, now running a makerspace in Monmouth, Wales)

Good morning, I’m not really into the ‘gold standard’ stuff but have been somewhat active in the PM world for a while now. I think that an important question to start with, is: which PM sensor do you want to use?
The SDS011 has been the standard for quite a while, and it has been reported to be the best with respect to especially PM10; for PM2.5 it’s fine as well. However, it is highly sensitive to high humidity, and the SPS30 has started to replace it for its size and lower sensitivity to humidity. Institutes are looking for calibration factors to compensate its slightly lower performance for PM10; for PM2.5 it’s pretty good out of the box.
However, recently the SEN5x series started appearing, especially the SEN55. It is both cheaper than the SPS30 (depending on reseller) and is also able to measure VOC and NOx index (not real value, but quite indicative). Its performance is not yet very much validated, but given that they are from the same manufacturer and Sensirion’s reputation in general on recent sensors, I’ve personally acquired a bunch of them and started integrating them in my new series of AQM boxes.

Thanks - I’ve used the SDS011 in the past and was planning to use it again, the “gold standard” for me is more of a question around which sensor is best supported via active libraries at the moment as most of the ones for SDS011 appear to rely on SoftwareSerial which isn’t needed on the ESP32 devices as they have multiple hardware serial ports and the ESP32 Software Serial libraries that I’ve found don’t quite seem to offer the same interfaces as the “standard” SoftwareSerial.h

I’ll take a look at the other sensors you mention though, if they are being more actively supported than that might be a better route to take.

The SPS30 and SEN5x both work on I²C (although they should also support UART, didn’t test). The SPS30 libraries out there are quite ugly, but the SEN5x library is pretty nice and easy to use.
But you should be able to just use Serial1 on an ESP32 with almost any available pins you’d like, and don’t look for a single second at any of the SoftwareSerial stuff.

Yup, that was my assumption too, but so far my attempts to use the libraries listed on github as most recently maintained have all seemed to use SoftwareSerial.

Dok-net’s library seems to be the most actively used, however I’m struggling to get those examples to update a variable that I can then pack into an LMIC payload to send back to the things network (although admittedly that was some weeks ago, and my subconcious may have resolved it since then! :smiley: )

If other sensors are I²C and have libraries, then that’s definitely preferable as it means I can hang them off the same bus as a BME680 and worry about fewer pins, so I’ll check those out in a bit

For sensors with a serial output, I usually follow the “state machine” approach:

  • My code sends a command over serial usually as one block of bytes
  • A finite state machine then processes bytes as received back from the sensor, one-by-one, keeping track of start bytes, payload, end bytes, checksums, etc. Also taking care to not overrun any buffers etc. When it finds a valid frame it indicates it back to the caller, which can then copy the payload.

This principe allows basically “bulletproof” parsing. The state machine only handles bytes, so is not actually Arduino-specific.

I’m not using a library for this, just writing my own code, examples:
LoraWanPmSensor/sds011_protocol.cpp at master · bertrik/LoraWanPmSensor · GitHub for SDS011
LoraWanPmSensor/shdlc.cpp at master · bertrik/LoraWanPmSensor · GitHub for SPS30

Ah, ok, thanks - yours was one of the projects I stumbled upon yesterday whilst looking for an alternative to the ESP Paxcounter stuff.

I moved on from it because it hadn’t been updated in a couple of years, but if it’s working fine for you still then I may base what I’m doing on it and contribute back if I find some bugs!

Is this actively deployed at the moment?

You shouldn’t need the un-Gold standard that is the over-hyped BME680, now upgraded to the BME688 “the first gas sensor with Artificial Intelligence (AI)” - however I believe if you don’t want it to argue at a meta-physical level about the meaning of, well, anything, you can put it in to Machine Learning mode which may have some use - but given the whole “International Air Quality” marketing ploy of the BME team, YMMV.

I too have some SEN55’s mostly on the grounds they come from Sensirion but also they provide the airflow mechanism as well as temp, humidity, NOx & VOC - so pretty much a complete unit.

I find the Arduino library landscape has become somewhat bitty with no audit before inclusion in the “official” list - perhaps between us all we can create something useful & given the IAQ debates, well documented.

For more recent ESP32’s that have an SX1262 you may want to have a look at RadioLib that I hear is “quite good” as LMIC still doesn’t have support for that radio series.

As feeding these sensors some chemicals tends to distort the sensor for the longer term, I’d be happy to torture test a few modules to get some independent readings - can’t do calibrated but I can offer them up to various things that cause me headaches (literally) - like paint fumes, laser cut acrylic, petrol, diesel, butane, propane, a fair few solvent based glues etc - let’s just say Beaker would last about ten seconds in my workshop.

For indoor units, CO2 levels are hugely useful - many building management systems will happily allow the air circulation to accumulate productivity killing levels of CO2 due to cost savings on electricity - almost funny, turn down the air exchange and then reduce your staff to slow moving slugs by 3pm which starts happening well before the official limit is reached.

1 Like

In fairness, these are going to be outdoor sensors, so the BME680 is probably overkill as you say and I should be able to get away with 280 instead.

I’ve managed to get @bertrik’s code compiled and uploaded to a TTGO v2.1 so I’ll play with that first, then I’ll expand out from there, but for now I need to go and cook the Easter dinner!

Interested: what price (range) would you consider this?

I’ve priced based on the SDS011 which are around £15-£20 on AliExpress, but anything below £40 for a project like this one is a winner in my books.

I saw that the SEN5x is a lot more expensive, so for this current project I won’t be able to use it, but I’ll definitely keep it in mind for future ones!

My favourite Dutch reseller has it listed for €30 ≈ £26, although you’d have to pay the Brexit fees unless you drive across the Canal which probably breaks some laws that I never read.

1 Like

I’ve upgraded WOPR’s search engine:


1 Like

I can laugh about it now, but it’s one of two reasons why my consultancy had to close in 2020… :frowning:

I’ve got a colleague in Holland, I guess I could find a reason to visit them and pick a few up whilst I’m out there! :smiley:

Oh, great find, thanks!

I’ll continue with the SDS011 for now, but if I don’t get anywhere with that then I’ll change to the SEN55!

1 Like

Please tell me how you got over that hump - I’m some way from laughing …

Well, we nearly lost our house as a result of my company having to close, and the prospect of being homeless definitely puts things into perspective to a point where the only thing you can do with a decision as monumentally stupid as leaving the largest trading bloc that you have a relationship with is point and laugh at it, otherwise you start to contemplate some very dark things…

Anyway, this forum isn’t the place to be discussing that really, I’ll try out some of the suggestions later on today and see how I get on.

So I’ve loaded up your code onto a TTGO v2.1 and the code loads and sees the sensor, but doesn’t appear to be pulling any data back from it.

The BME280 that’s attached via I²C is sending data back fine, so it’s not an issue with the LoRaWAN connection.

This is similar to the issue I’ve seen elsewhere though, so I’m now wondering if both my SDS011 sensors are broken…

Do you know if there’s a way to tell whether the SDS011 is working or not?

Be aware that the SDS011 requires 5V input but uses 3V3 logic… and are you sure you didn’t accidentally mix the Tx/Rx pins? You can always try swapping them…

Yeah, connection is as follows:

+v → dedicated 5v power supply
GND → Shared with ESP32 and power supply GND
TX → RX on ESP32
RX → TX on ESP32

The SDS011 is picked up by the code:

Found SDS011
SDS011: 5443, 2018-11-16
>>> E_IDLE
4289573: EV_TXSTART
4605240: EV_RXSTART
4610670: EV_JOINED
>>> E_SEND
SF 7, cycle 0 / interval 1
Sending on port 1, data 0A686E0B6700D80C732684
20629631: EV_TXSTART
>>> E_IDLE
20945364: EV_RXSTART
21007865: EV_RXSTART

the fan spins up on the SDS011, data is sent from the ESP32 to TTN, but the payload is only the data from the BME280:

If I swap TX/RX then the sensor doesn’t get found by the code, so I’m confident they’re connected correctly.

I guess the only thing left is to find the USB interface and test it that way instead.