A comprehensive air quality monitoring system using the SparkFun ESP32-S3 Thing Plus, BMV080 particulate matter sensor, OLED display, SD card logging, and MQTT connectivity for real-time remote monitoring.
- PM1, PM2.5, and PM10 particulate matter readings
- OLED display with pollution level indicators
- RGB LED status indication (Green/Orange/Red/Purple)
- Automatic display modes based on air quality levels
- SD card logging with mean value calculations
- CSV format with timestamps
- Daily file rotation (new file every 24 hours)
- Hot-swap SD card support
- Automatic error recovery
- WiFi connectivity with auto-reconnection
- MQTT data transmission every 30 seconds
- Location-based topic structure for multi-sensor deployments
- Real-time status reporting
- Auto-reset after 48+ days to prevent millis() overflow
- Sensor obstruction detection
- Connection status monitoring
- Visual feedback on display and LED
- SparkFun ESP32-S3 Thing Plus (DEV-24408)
- SparkFun BMV080 Particulate Matter Sensor (SEN-22859)
- OLED Display (SSD1327, 128x128, I2C)
- MicroSD card (FAT32 formatted)
- LiPo Battery 2000mAh (need to be unplugged, to upload the code to the ESP32-S3 Thing Plus
- **SD Card 16/32GB ---> if oyur SD Card fails: Make sure the SD Card is fast enough and formatted correctly!
ESP32-S3 Thing Plus โ BMV080 Sensor
I2C/Qwiic connector
ESP32-S3 Thing Plus โ OLED Display
I2C (SDA: GPIO8, SCL: GPIO9)
RGB LED: GPIO46 (onboard)
SD Card: Built-in slot
| Component | Pin | Description |
|---|---|---|
| RGB LED | GPIO46 | Onboard WS2812 LED |
| SD CS | GPIO33 | SD card chip select |
| SD SCK | GPIO38 | SD card SPI clock |
| SD MISO | GPIO39 | SD card SPI data out |
| SD MOSI | GPIO34 | SD card SPI data in |
| SD Detect | GPIO48 | SD card detection |
| I2C SDA | GPIO8 | I2C data (Qwiic) |
| I2C SCL | GPIO9 | I2C clock (Qwiic) |
Install these libraries through the Arduino Library Manager:
- SparkFun_BMV080_Arduino_Library
- U8g2lib (for OLED display)
- SdFat (by Bill Greiman)
- FastLED
- WiFi (ESP32 core)
- ArduinoMqttClient
- Wire (I2C communication)
const char ssid[] = "YOUR_WIFI_SSID";
const char pass[] = "YOUR_WIFI_PASSWORD";const char broker[] = "192.168.1.100"; // Your MQTT broker IP
int port = 1883; // MQTT port
const char mqtt_username[] = "username"; // MQTT credentials
const char mqtt_password[] = "password";๐ข For multiple sensors, change the location for each device:
// Office sensor
const char location[] = "Office";
// Workshop sensor
const char location[] = "Workshop";
// Kitchen sensor
const char location[] = "Kitchen";The system uses a location/device/measurement structure:
Office/airsensor/pm1 # PM1 readings from Office
Office/airsensor/pm25 # PM2.5 readings from Office
Office/airsensor/pm10 # PM10 readings from Office
Office/airsensor/status # Air quality status from Office
Office/airsensor/obstruction # Sensor obstruction status
Workshop/airsensor/pm1 # PM1 readings from Workshop
Workshop/airsensor/pm25 # PM2.5 readings from Workshop
...
- PM Values: Float with 2 decimal places (e.g.,
15.42) - Status: String values:
good_air,medium_pollution,high_pollution,obstructed - Obstruction: Boolean as string:
trueorfalse
# All data from Office
Office/+/+
# PM2.5 from all locations
+/airsensor/pm25
# All air sensor data
+/airsensor/+
# Specific measurement from specific location
Office/airsensor/pm25- Connect BMV080 sensor to ESP32-S3 via Qwiic connector
- Connect OLED display to I2C via Qwiic connector
- Insert formatted microSD card
- Power via USB-C
- And or Connect the Battery - Unplug the Battery to upload the code!
- Install ESP32 board package
- Select "ESP32S3 Dev Module"
- Configure settings:
- USB Mode: Hardware CDC and JTAG
- USB CDC on Boot: Enabled
- Upload Mode: UART0 / Hardware CDC
- PSRAM: QSPI PSRAM
- Update WiFi credentials
- Set MQTT broker details
- Set unique location for each sensor
- Upload to ESP32-S3
For multiple sensors:
- Use the same code base
- Only change the
locationvariable for each sensor - Each sensor will automatically use different MQTT topics
| Level | PM Reading | Display | LED Color | Status |
|---|---|---|---|---|
| Good | < 15 ฮผg/mยณ | "Good Air!" | Green | good_air |
| Medium | 15-30 ฮผg/mยณ | "Some Particles" | Orange | medium_pollution |
| High | > 30 ฮผg/mยณ | "Open a Window!" | Red (blinking) | high_pollution |
| Error | Sensor blocked | Warning display | Purple | obstructed |
| Connecting | WiFi connecting | Connection status | Blue | N/A |
- File Format: CSV with headers
- Filename Pattern:
airdata_[boot]_[day].csv - Example:
airdata_1_0.csv,airdata_1_1.csv
Timestamp,PM1_Mean,PM2.5_Mean,PM10_Mean,ObstructionPercent,SampleCount
60000,12.45,15.32,18.67,0.0,60
120000,11.98,14.89,17.23,0.0,60- Sensor sampling: Every 1 second
- SD card logging: Every 1 minute (mean values)
- MQTT transmission: Every 30 seconds
- File rotation: Every 24 hours
- White blink (2x): System startup
- Blue: Connecting to WiFi
- Green: Good air quality + connected
- Orange: Medium pollution
- Red: High pollution
- Purple: Sensor error or obstruction
1. RGB LED not working
- Ensure you're using the correct ESP32-S3 Thing Plus board
- Pin 46 is correct for this board (not pin 35)
2. SD Card not detected
- Ensure card is FAT32 formatted
- Check card insertion and connections
- Try different SD card
3. WiFi connection fails
- Verify SSID and password
- Check 2.4GHz network (ESP32 doesn't support 5GHz)
- Ensure network allows new device connections
4. MQTT not connecting
- Verify broker IP and port
- Check username/password if broker requires authentication
- Ensure broker is accessible from your network
5. Sensor readings show obstruction
- Check if sensor air intake is blocked
- Ensure proper airflow around sensor
- Clean sensor if necessary
Enable serial monitor (115200 baud) to see:
- Initialization status
- WiFi/MQTT connection status
- Real-time sensor readings
- SD card operations
- Error messages
sensor:
- platform: mqtt
name: "Office Air Quality PM2.5"
state_topic: "Office/airsensor/pm25"
unit_of_measurement: "ฮผg/mยณ"
- platform: mqtt
name: "Office Air Quality Status"
state_topic: "Office/airsensor/status"[{"id":"mqtt-in","type":"mqtt in","topic":"Office/airsensor/+"}]import paho.mqtt.client as mqtt
def on_message(client, userdata, message):
topic = message.topic
value = message.payload.decode()
print(f"{topic}: {value}")
client = mqtt.Client()
client.on_message = on_message
client.connect("192.168.1.100", 1883, 60)
client.subscribe("Office/airsensor/+")
client.loop_forever()- Hardware initialization (I2C, SD, LED)
- WiFi connection (with visual feedback)
- MQTT broker connection
- Sensor initialization and calibration
- Begin monitoring loop
- Sample sensor every 1 second
- Update display with current readings
- Log mean values to SD every minute
- Transmit via MQTT every 30 seconds
- Monitor connections and auto-reconnect if needed
- WiFi disconnection: Automatic reconnection attempts
- MQTT disconnection: Reconnection with exponential backoff
- SD card removal: Hot-swap detection and reinitialization
- Sensor errors: Continue operation, report error status
This project is open source. Feel free to modify and distribute according to your needs.