The west coast fires of September 2020 produced massive amounts of smoke and air pollution rose to dangerous levels. I stumbled upon the PurpleAir PA-II air quality sensor which was providing much of the state-wide air quality data. Internally it uses a PMS5003 and I picked one up for $20 on eBay.


The goal of this project was to create a internet connected air quality sensor that records data in real time. This sensor was also used to test the effectiveness of a cheap non-HEPA filter and a true HEPA filter. The cheap filter appeared to work quite well for this type of pollution.

The sensor can detect particles down to 0.3 microns, it sends data over serial. Some models support I2C as well. There are many libraries that can translate the binary data, I ended up using Adafruit_PM25AQI. I used only PM1.0 and PM2.5 data since PM10 seems to be extrapolated from PM2.5 on this device.

The sensor is connected to an ESP32 which sends data over Wi-Fi to a cloud server running node and MySQL. As data is received the node app records timestamped data into a database. Node can update graphs in real time using a WebSocket connection to a browser. Historical data can been viewed as well.

The code for the ESP32 is simple, every 5 seconds data is read and data is sent to the node endpoint. A key is used to prevent false data and identify the device.

#include "Adafruit_PM25AQI.h"
#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid = "SSID";
const char* password = "PASSWORD";

Adafruit_PM25AQI aqi = Adafruit_PM25AQI();

void setup() {
  while (!Serial) delay(10);
  WiFi.begin(ssid, password);
  Serial.println("Connecting to wifi");
  while(WiFi.status() != WL_CONNECTED) {
  Serial.print("Connected to WiFi, IP Address: ");


  if (!aqi.begin_UART(&Serial2)) {
    Serial.println("Could not find PM 2.5 sensor");

void loop() {
  PM25_AQI_Data data;
  if (! {
  if(WiFi.status() == WL_CONNECTED){
    HTTPClient http;
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    String httpRequestData = "key=gj29xh3ngfw32pz84ns"
    + "&pm1=" + String(data.pm10_env)
    + "&pm25=" + String(data.pm25_env);
    int httpResponseCode = http.POST(httpRequestData);
    Serial.print("HTTP Response code: ");
  else {
    Serial.println("WiFi Disconnected");


To be continued