ideas.
April 25, 2026 3 min read hardwareconsumerhealthdata

DIY air quality monitor with home dashboard

A sub-$40 ESP32 sensor kit paired with a self-hosted web dashboard to track CO2, particulates, and VOCs in your home — no cloud account required.

The idea

A hardware build using an ESP32 microcontroller paired with a BME680 environmental sensor and a PMS5003 particulate sensor. The device publishes readings over MQTT to a lightweight local server that stores them in SQLite and serves a live web dashboard. You get CO2 equivalent levels, PM2.5/PM10 particulate counts, temperature, humidity, and total volatile organic compounds — the metrics that correlate with headaches, poor sleep, and reduced focus. No cloud, no subscription.

Why build this

Indoor air quality is a quiet problem. Most people don't realize their CO2 levels climb above 1500 ppm in a closed bedroom by morning, or that running a laser printer spikes PM2.5 to outdoor-pollution levels. Commercial monitors (Awair, AirThings) exist but cost $150–400 and phone home to cloud services that may disappear. The hardware cost has collapsed: an ESP32 is $5, the BME680 is $8, and the PMS5003 is under $20. The missing piece is software that collects and visualizes the data without requiring a cloud account or an existing home automation platform like Home Assistant.

The timing is also right technically. MicroPython on the ESP32 has matured to the point where the entire firmware fits in a single readable file, MQTT is the de-facto standard for this class of sensor data, and SQLite handles years of 60-second readings without breaking a sweat.

Stack sketch

  • Microcontroller: ESP32 (ESP-WROOM-32) running MicroPython. Minimal flash footprint, built-in Wi-Fi.
  • Sensors: Bosch BME680 (CO2eq, TVOC, temperature, humidity, pressure) over I2C; Plantower PMS5003 (PM1.0, PM2.5, PM10) over UART.
  • Transport: MQTT — the sensor publishes to a topic every 60 seconds. Simple, low-power, decoupled from the backend.
  • Broker: Mosquitto running in Docker on the same LAN.
  • Backend: A small Python FastAPI service subscribed to the MQTT topic, writing to SQLite via aiosqlite. Exposes a /readings endpoint.
  • Frontend: Plain HTML + Chart.js served by the same FastAPI process. No build step, no framework.
  • Alerts: FastAPI background task that checks thresholds on each reading and fires a notification via self-hosted ntfy.sh.

Scope for v1

In: - ESP32 firmware publishing all six sensor channels to MQTT every 60 seconds. - Backend storing readings in SQLite with a 90-day rolling window. - Dashboard showing the last 24 hours as line charts per metric, with color-coded status (green/yellow/red) against WHO and ASHRAE reference thresholds. - Threshold alerts via ntfy.sh for CO2 above 1000 ppm and PM2.5 above 12 µg/m³. - A /export?from=&to= endpoint returning CSV for any time range.

Out: - Multi-sensor/multi-room support (one device for v1; the MQTT topic scheme supports expansion). - Battery operation and deep-sleep power management. - Integration with Home Assistant, Grafana, or InfluxDB. - Historical anomaly detection or trend analysis.

Where it could go

Once the pipeline works, the natural first expansion is multi-room: each sensor publishes to air/<room>/readings and the dashboard gains a room-switcher. After that, the six-channel time series becomes useful for pattern detection — correlating VOC spikes with cooking, PM2.5 with outdoor weather events, or CO2 with home-office meeting density.

A lightweight rule engine ("if CO2 exceeds 900 ppm between 22:00 and 07:00, alert — crack a window before bed") would turn the raw data into actionable habit changes without needing a model. The data is already there; the rules just need a simple UI to define them.

Watch out for

The BME680's CO2eq output is an estimate derived from VOC sensing via Bosch's proprietary BSEC library, not a direct NDIR measurement. It requires a 5-day burn-in period and will read high near cooking or cleaning products. If you need accurate CO2 numbers, substitute a Sensirion SCD40 NDIR sensor (~$15 more) and adjust the I2C wiring. Also: the PMS5003 fan degrades after roughly 8,000 hours of continuous operation — build duty-cycling into the firmware from day one rather than retrofitting it after you've replaced the first unit.