You ever wonder what's actually in the air you breathe every day? I've lived in Beijing for a few years now and I'm always running a couple of air purifiers in my home, but how well do they actually work? Sure some have built in sensors, but how accurate are they? Well with a little Python, a Raspberry Pi, and some moxie, we can find our own answers.
This will focus primarily on measuring PM2.5 and PM10 in the air and converting the values to an Air Quality Index (AQI). For measuring other harmful chemicals like Nitrogen Dioxide (NO2) or Carbon Monoxide (CO), see the "Adding Sensors" section at the end of the article
- Raspberry Pi Configured, Headless (I used a 3b, but anything with a USB port should do just fine, though built in wifi is recommended for portability) (~$35)
- SDS011 PM Sensor (~$15)
- Adafruit IO Account (free)
Create an account on Adafruit IO. This is a great site to collect data streams and display them on custom dashboards. After you've created an account, let's create a couple of feeds. We'll need three: a pm2.5, a pm10, and a log feed (I named mine "beijing-twofive", "beijing-ten", and "logs" respectively). After that, you can either create a dashboard now or later and play around with how you want to display the data. This is how I have mine set up.
Configure your Raspberry Pi with the installers on the product website. Once configured with either Raspbian or a Linux distribution of your choice (this project should be compatible with most distributions, but it's only been tested on Debian, Raspbian, and MacOS), install the following dependencies:
SSH into your Raspberry Pi (or login if GUI is installed and open your terminal) and download the repo in your home directory:
git clone firstname.lastname@example.org:HoukasaurusRex/RaspberryPi-AQIPi.git
Then install the python dependencies:
pip3 install -r requirements.txt
Retrieve your AdafruitIO username and key from the dashboard and add them to you env.
echo 'AIO_USERNAME="Hackerman" AIO_KEY="aio_xXxXx" CITY="beijing" AIO_LOGS="logs"' > .env
AIO_LOGS are feed names created in the AdafruitIO dashboard.
Now you can run the code. I like to use screen to save my terminal process to be accessible later, but you can just run it in your main shell as well.
screen -S aqipi
cd ~/RaspberryPi-AQIPi chmod +x run.sh sh run.sh
There won't be any output in your terminal, but you should be able to go to your AdafruitIO feeds and start seeing results!
Standard advice for locating your sensor is that it should be outside and four metres above ground level. That’s good advice for general environmental monitoring; however, we’re not necessarily interested in general environmental monitoring – we’re interested in knowing what we’re breathing in.
Choose a location where you spend most of your time or where you might be particularly interested in the general air quality (e.g. in the kitchen or garage) and place your sensor in a safe place where it won't be affected by excessive moisture or humidity.
def read_data(): pm_twofive_data =  pm_ten_data =  readings = 0 # This will take 11 data samples and use the built in `statistics` module to upload a median value # to filter out excessive data spikes in the readings while readings < 11: data =  for index in range(0, 10): datum = ser.read() data.append(datum) # Convert the readings from bytes to ints and append to the array of data samples pm_twofive = int.from_bytes(b''.join(data[2:4]), byteorder='little') / 10 pm_twofive_data.append(pm_twofive) pm_ten = int.from_bytes(b''.join(data[4:6]), byteorder='little') / 10 pm_ten_data.append(pm_ten) readings += 1 # Take a little break ☕️ sleep(1) # Calculate the AQI from ppm^2 using the US EPA API table detailed in the section below # then upload to your AdafruitIO feeds using the Adafruit_IO SDK pm_twofive_aqi = calc_aqi('pm_twofive', median(pm_twofive_data)) send_data('twofive', pm_twofive_aqi) pm_ten_aqi = calc_aqi('pm_ten', median(pm_ten_data)) send_data('ten', pm_ten_aqi) return [pm_twofive_aqi, pm_ten_aqi]
This is converting to the US EPA AQI, in order to use a different standard, you might need to tweak the formula to fit your country's AQI model.
This repo implements an exponential backoff policy to retry connections after an exponentially longer period to account for common network errors or connection issues with your sensor.
def exponential_backoff(n): return (2 ** n) + (random.randint(0, 1000) / 1000)
First testing against the air quality outside, make sure it seems to match up with IQ Air's Air Quality Index.
After a few weeks of running the sensor, it seems the sensors on my air purifiers would often underreport pm2.5 values by as much as 50% and were seldom correlated with each other. The Raspberry Pi on the other hand, filtering out for data spikes, seems to respond very reasonably to real world phenomena such as rising with the outdoor AQI and falling linearly when the purifiers are all left on high for a few hours. I have also learned how much barometric pressure differences will also increase the penetration of outdoor pollution to my home. Interestingly, poor kitchen ventilation also causes quite noticeable spikes in indoor air pollution!
I've been using this sensor a few months now and have since felt much more empowered with managing the quality of the air I breathe each day.
This project can be easily extended by adding additional sensors, such as an Ozone (O3), Carbon Monoxide (CO), Nitrogen Dioxide (NO2), or any other harmful air pollutants that might be more relevant to your area. If you do, please let me know and I'd like to compare your findings and update the AQIPi repository to extend the project.
Big thanks to Andrew Gregory from raspberrypi.org on his work providing the inspiration for this project.
You can view this article and more like it as well as sign up to my newsletter to get the latest posts on https://jt.houk.space