Air quality sensor firmware for the Polverine board — an ESP32-S3 mikroBUS board with a Bosch BMV080 particulate matter sensor and BME690 environmental sensor.
| Sensor | Measurements |
|---|---|
| BMV080 | PM1, PM2.5, PM10 (mass & count) |
| BME690 (via BSEC) | Temperature, humidity, pressure, IAQ, static IAQ, CO2, bVOC, tVOC, gas % |
Sensor data is published over MQTT as JSON and can be visualized with the included Grafana dashboard.
- Docker (for building firmware)
- Python 3 with a venv (for flashing)
No ESP-IDF installation required — builds run inside the espressif/idf:v5.5.3 Docker image.
python3 -m venv .venv
.venv/bin/pip install esptool pyserialcp firmware/main/config.example.h firmware/main/config.hEdit firmware/main/config.h with your WiFi and MQTT credentials:
#define WIFI_SSID "your-ssid"
#define WIFI_PASSWORD "your-password"
#define MQTT_BROKER_URL "mqtt://your-broker:1883"make buildPut the board into flash mode (hold BOOT, press RESET, release BOOT), then:
make flashIf your serial port isn't /dev/cu.usbmodem2101, override it:
make flash PORT=/dev/cu.usbmodem1234make monitor| Command | Description |
|---|---|
make build |
Build firmware in Docker |
make clean |
Delete sdkconfig and rebuild from scratch |
make flash |
Flash bootloader + partition table + app |
make erase |
Erase entire flash (including NVS calibration data) |
make monitor |
Serial monitor (30 seconds) |
On boot the firmware connects to WiFi, then MQTT, then starts two FreeRTOS tasks:
- bmv080_task — reads the BMV080 particulate matter sensor over SPI, publishes every ~10 seconds
- bme690_task — reads the BME690 gas sensor over I2C through the Bosch BSEC library (LP mode, 3-second cycle), publishes every ~3 seconds
Data is published as JSON to MQTT topics polverine/{mac}/bmv080 and polverine/{mac}/bme690, where {mac} is the device's WiFi MAC address.
| LED | Meaning |
|---|---|
| Blue (solid) | Connecting to WiFi |
| Red (flash) | MQTT publish failed |
| Red (solid) | Fatal error (sensor init failed, etc.) |
The ESP32's self-heating raises the BME690 temperature reading well above ambient. The firmware uses the ESP32's internal CPU temperature sensor to compute a dynamic offset fed to BSEC via BSEC_INPUT_HEATSOURCE. Tune the constants in config.h (BME690_TEMP_OFFSET_BASE, BME690_TEMP_OFFSET_SCALE) by comparing against a reference thermometer.
BSEC calibration state is saved to NVS flash every 4 hours and restored on boot. The IAQ accuracy field indicates calibration progress (0 = uncalibrated, 3 = fully calibrated). Use make erase to reset calibration if needed.
The compose.yaml file contains a self-contained Docker Compose stack (Mosquitto + Telegraf + TimescaleDB + Grafana) with a pre-configured Grafana dashboard. Deploy it with Portainer or docker compose up -d.
firmware/
main/ Firmware source (main.c, sensor tasks, MQTT, LED, config)
components/ Bosch SDK libraries (BMV080, BSEC, BME69x)
compose.yaml TIG stack (Telegraf + TimescaleDB + Grafana + Mosquitto)
docs/ SDK documentation (converted to Markdown)
Makefile Build, flash, and monitor commands
Bosch SDK components under firmware/components/ and docs/ are subject to their respective licenses. See the SDK archives for details.