ESP Projects

Build a PM2.5 air quality monitor with a PMS5003 and ESPHome

Fine particles from cooking, candles, wildfire smoke and dust are invisible, but a $20 laser particle counter can see them. This build reports PM1, PM2.5 and PM10 plus the US Air Quality Index to Home Assistant, which can then switch on an air purifier for you.

What PM2.5 is

PM2.5 is the mass of particles smaller than 2.5 micrometers in each cubic meter of air, in µg/m³. They're small enough to get deep into the lungs, which is why they're the headline number in air quality reports. The PMS5003 draws air through with a small fan and counts particles by the laser light they scatter.

Parts

  • An ESP32 DevKit and a 5 V USB supply.
  • A Plantower PMS5003. Buy one that comes with its breakout adapter and cable: the sensor's own connector is a tiny 1.25 mm plug.
  • Optional: a BME280 for temperature and humidity, which helps you make sense of the readings (see below).

Wiring

PMS5003 (adapter)ESP32
VCC5V (VIN)
GNDGND
TXGPIO39
RXGPIO23
SET, RESETLeave unconnected

The fan needs 5 V, but the data pins work at 3.3 V, so they connect straight to the ESP32 with no level shifter. The BME280, if used, goes on the I2C pins: SDA to GPIO21, SCL to GPIO22.

Wiring diagram
Wiring diagram. Click to open full size.

Build this config in the generator Pick your board, adjust pins, and copy a ready-to-flash config with fresh keys.

The ESPHome config

uart:
  - id: pms_uart
    tx_pin: GPIO23
    rx_pin: GPIO39
    baud_rate: 9600

i2c:
  sda: GPIO21
  scl: GPIO22

sensor:
  - platform: pmsx003
    uart_id: pms_uart
    type: PMSX003
    pm_1_0:
      name: "PM1.0"
    pm_2_5:
      id: pm25
      name: "PM2.5"
    pm_10_0:
      id: pm10
      name: "PM10"
    update_interval: 120s
  - platform: aqi
    name: "Air quality index"
    pm_2_5: pm25
    pm_10_0: pm10
    calculation_type: AQI
  - platform: bme280_i2c
    address: 0x76
    temperature:
      name: "Temperature"
    humidity:
      name: "Humidity"
    pressure:
      name: "Pressure"
    update_interval: 60s

With update_interval over 30 seconds and the ESP32's TX wired to the sensor, ESPHome puts the fan to sleep between readings. The fan and laser wear with use, so ESPHome recommends 120 seconds or more.

What the numbers mean

These are the US EPA's PM2.5 breakpoints, updated in 2024. The AQI sensor in the config converts readings to this scale for you.

PM2.5 (µg/m³)AQICategory
0.0–9.00–50Good
9.1–35.451–100Moderate
35.5–55.4101–150Unhealthy for sensitive groups
55.5–125.4151–200Unhealthy
125.5–225.4201–300Very unhealthy
225.5 and up301+Hazardous

The official AQI uses 24-hour averages. A live indoor reading jumps around: frying food can briefly push PM2.5 into the hundreds. Use the trend, not a single spike.

Placement and quirks

  • Keep the inlet and outlet clear, out of direct sun and drafts, and not right next to the stove unless you want a cooking monitor.
  • High humidity reads high. Laser counters see fog and water droplets as particles, so readings inflate when humidity is very high. That's what the BME280 is for: if PM2.5 and humidity rise together on a damp morning, it's probably moisture.
  • Cheap sensors vary from unit to unit. They're good for trends and comparisons, not lab-grade measurements.

Home Assistant automations

  • Run the purifier: turn on a smart-plug air purifier when PM2.5 stays above 12 µg/m³ for 10 minutes, and off when it's below 5 for 20 minutes.
  • Smoke alert: notify when the AQI goes over 100 — useful in wildfire season to know when to close windows.
  • Pair it with the CO2 monitor: CO2 tells you when to bring fresh air in, PM2.5 tells you when not to.