Indoor air quality directly impacts your health, sleep, and cognitive function, yet most homes rely on guesswork rather than measurement. Off-the-shelf monitors can cost $150–$300 and often lock you into proprietary software. Building your own monitor gives you precise control over which pollutants you track, how you view the data, and how much you spend. This guide walks you through selecting sensors, wiring them to an ESP32 microcontroller, writing the firmware, and calibrating the device so you get reliable, actionable readings. By the end, you’ll have a fully functional air quality monitor that logs particulate matter, volatile organic compounds, temperature, and humidity to a local dashboard or Home Assistant instance.
Commercial monitors like the AirGradient Pro or PurpleAir are well-engineered but expensive. They often include sensors you don’t need while omitting ones you do. A DIY approach lets you choose the exact sensor for your concern: if you live near a highway, prioritize PM2.5; if you work with paints or solvents, prioritize VOC. Additionally, many commercial units require cloud subscriptions or send data to third-party servers. Your DIY setup can be entirely offline, storing data on an SD card or broadcasting locally via MQTT. The trade-off is time and soldering skill—expect to spend two evenings assembling and testing. But the result is a device you can repair, upgrade, and customize.
The PMS5003 is the most common and affordable PM sensor, costing around $20. It reports PM1.0, PM2.5, and PM10 with decent accuracy (±10 µg/m³ in lab conditions). Sensirion’s SPS30 costs about $35 but offers better long-term stability and a smaller form factor. Both use laser scattering and can be cleaned with compressed air. If you live in a dusty environment, the SPS30’s self-cleaning mode reduces calibration drift.
The SGP30 is a dedicated VOC sensor that outputs total VOC (TVOC) and CO₂-equivalent. It requires a 12-hour burn-in period when first powered. The BME680 combines temperature, humidity, barometric pressure, and an inexpensive MOX (metal-oxide) gas sensor. Its VOC readings are relative, not absolute, so it’s better for detecting changes than measuring precise levels. For most home labs, the SGP30 gives more actionable data, while the BME680 saves space on a single board.
The DHT22 is old but reliable, accurate to ±0.5°C and ±2% RH. It’s slow (one reading every two seconds) and uses a single-wire protocol. The BME280 is faster, smaller, and more precise (±0.3°C, ±1% RH) but costs twice as much. I recommend the BME280 if you can spare the extra $5—it integrates easily over I²C and doesn’t require timing-sensitive code.
The ESP32 is the best choice for a standalone monitor. It has built-in Wi-Fi and Bluetooth, two ADC channels for analog sensors, and deep-sleep support for battery operation. An Arduino Uno lacks Wi-Fi and runs at 5V logic, requiring level shifters for 3.3V sensors. The Raspberry Pi Pico is cheap and low-power but lacks wireless without an add-on module. For a connected monitor, the ESP32 DevKit V4 ($5) is unbeatable. For a battery-powered version, the ESP32-S2 with its native USB and low-power modes is a better fit.
If placed near an outlet, a 5V USB wall charger (2A minimum) with a micro-USB cable works fine. For remote placement (e.g., a garage or basement), use a 3.7V 18650 battery connected to a TP4056 charging module. The ESP32 in deep-sleep mode draws about 10 µA, but running Wi-Fi every 30 seconds raises average draw to 80 mA. A 3000 mAh battery lasts about 12 hours with continuous Wi-Fi, or several days if you only sync data hourly. Consider a power bank as a simple no-solder option.
Both the SGP30 and BME280 use the I²C bus. Connect VIN to 3.3V (not 5V, or you’ll destroy them). Connect GND to ground. Connect SDA to GPIO21 (default on ESP32) and SCL to GPIO22. The PMS5003 uses UART: connect its TX pin to GPIO16 (RX2 on ESP32) and VIN to 5V (it needs 5V for the fan). If you’re new to wiring, start on a breadboard: insert the ESP32 along the top rail, wire its 3.3V and GND to the power rails, then add each sensor one at a time. Test with a simple blink sketch before moving on.
Download Arduino IDE 2.0 or later. Go to File > Preferences and add "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json" to Additional Board Manager URLs. Then go to Tools > Board > Board Manager, search for ESP32, and install the package by Espressif Systems. Select "ESP32 Dev Module" from the board menu.
You’ll need three libraries: Adafruit BME280, Adafruit SGP30, and a PMS5003 library (I recommend pms5003 from pimoroni). In the Library Manager, search and install each. For the PMS5003, you may need to manually install the library from GitHub if it’s not in the manager.
Below is the key initialization section—write it inside setup() for each sensor:
BME280: Wire.begin(21,22); Adafruit_BME280 bme; bme.begin(0x76);
SGP30: Adafruit_SGP30 sgp; sgp.begin(); sgp.IAQinit();
PMS5003: SoftwareSerial pmsSerial(16, 17); PMS pms(pmsSerial); pms.begin(); (Note: GPIO17 is optional for reading from the PMS—we only need TX on 16.)
In loop(), read each sensor and concatenate into a JSON string. For example: jsonBuffer = "{\"PM2.5\":" + String(pmsData.PM_AE_UG_2_5) + ",\"TVOC\":" + String(sgp.TVOC) + .... Print the JSON to the serial monitor to verify. If you see -1 values, your wiring is likely incorrect or the sensor address is wrong.
Sensors can occasionally fail to respond. Use a timeout in your read function—if no data arrives within 2 seconds, skip that sensor and report "null" instead of crashing the loop. Add a counter: if three consecutive reads fail, trigger an LED alert or send a notification via MQTT. This prevents your monitor from freezing when, say, the PMS5003’s fan jams on a piece of lint.
Use the Arduino IDE’s Serial Monitor set to 115200 baud. You’ll see raw sensor values updating every 5–10 seconds. This is fine for debugging but not for a permanent setup.
You can host a simple web page on the ESP32 that displays real-time charts. Use the ESPAsyncWebServer library paired with a small HTML page that refreshes every 30 seconds. The page can show gauges for PM2.5, TVOC, temperature, and humidity. This method works without any external software, but it only refreshes when you actively view the page. For continuous logging, you need a database.
Install Mosquitto MQTT broker on a Raspberry Pi or PC. On the ESP32, use the PubSubClient library. Configure your Wi-Fi credentials and broker IP, then publish sensor data to topics like home/monitor/pm25 and home/monitor/tvoc. In Home Assistant, create MQTT sensors that listen to these topics. You can then build dashboards, set automations (e.g., turn on a purifier when PM2.5 > 50), and log historical data to the HA database. This is my recommended setup for long-term use.
If you don’t use Wi-Fi, add a microSD card module. Connect CS to GPIO5, MOSI to GPIO23, MISO to GPIO19, and SCK to GPIO18. Use the SD library to write a line of CSV data every minute. Format: epochtime,pm25,pm10,tvoc,temp,humidity. You can then remove the card and import into Excel or a timeseries database. This approach uses more GPIO pins but gives you full ownership of your data.
Purchase a HEPA filter or use a known clean space. Place your sensor in a sealed box with a HEPA-filtered air intake (e.g., a DIY fan-box with a MERV 13 filter). After 20 minutes, the PM2.5 reading should drop below 5 µg/m³. If it reads 10+ µg/m³, your sensor may have a debris problem or you need to recalibrate the zero point. Some PMS5003 modules have a small hole for a reset button—hold it for two seconds to zero the baseline.
The SGP30’s VOC baseline drifts over months. To recalibrate, expose it to fresh outdoor air for 24 hours (filtered through a HEPA membrane) and then send the command sgp.setIAQBaseline(...) with a known good baseline from the datasheet. Alternatively, run the sensor for 12 hours in clean air and log the baseline value, then hardcode it in firmware. Expect the TVOC to be accurate within ±30%—this is a sensor-grade measurement, not lab-grade.
Borrow a friend’s PurpleAir or buy an inexpensive second-hand AirVisual monitor. Place both monitors next to each other for 48 hours. Plot the PM2.5 readings—if your DIY device differs by more than 10 µg/m³, check for correct fan orientation (the PMS5003 needs to be oriented vertically, not flat). For temperature, a 1°C offset is normal due to internal heating from the ESP32; you can compensate by subtracting 1.0°C from the BME280 reading in software.
Once your monitor is running for a week, examine the peak PM2.5 levels during specific times—like just before sunset when people cook dinner, or during vacuuming. If PM2.5 exceeds 35 µg/m³ for more than two hours a day, consider a HEPA air purifier in that room. If TVOC spikes above 500 ppb, check for new furniture, cleaning products, or lack of ventilation. Use your DIY monitor to test: run your HVAC fan continuously for 24 hours and compare the data with a fan-off period—many homes see a 40% drop in PM2.5 just from filtered circulation. The monitor isn’t a luxury; it’s a diagnostic tool for healthier indoor spaces.
Browse the latest reads across all four sections — published daily.
← Back to BestLifePulse