A lightweight weather monitoring system that tracks temperature, humidity, air quality, and provides camera snapshots — using an ESP32, MQTT, and a simple web client.
This project uses an ESP32 device equipped with:
- A camera (OV5640)
- A temperature and humidity sensor (DHT11)
- An air quality sensor (MQ-135)
- A passive buzzer
The ESP32 currently connects to the local Wi-Fi network and communicates via my own MQTT broker, providing access to real-time weather data and user-triggered updates. Soon it will transition to using a SIM7670 3G module for cellular connectivity.
ESP32 publishes a JSON list of currently available sensors to esp32/available_sensors
topic.
Example payload:
[
{ "name": "temp", "type": "text/plain" },
{ "name": "humidity", "type": "text/plain" },
{ "name": "air_quality", "type": "text/plain" },
{ "name": "camera", "type": "image/jpeg" }
]
Clients subscribe to this topic on connect and use the metadata to dynamically render controls and correctly display responses.
The web client sends requests to esp32/request/<sensorName>
topic.
The <sensorName>
must match one of the available sensors.
esp32/request/camera → triggers a new photo capture
esp32/request/humidity → triggers humidity reading
esp32/request/air_quality → triggers air quality percentage
esp32/request/temp → triggers temperature reading
ESP32 will buzz once to confirm reception of any valid or invalid request.
ESP32 publishes sensor results to esp32/response/<sensorName>
topic.
- Topic pattern: esp32/response/
- Payload: Value returned by the sensor
esp32/response/temp: "23.5"
esp32/response/humidity: "69.6"
esp32/response/air_quality: "47"
esp32/response/camera: (Base64-encoded JPEG string)
Any malformed requests, unavailable sensors, or failures are published to esp32/response/error
topic.
sensor_unknown:airflow
invalid_topic:bad/request/humidity
The frontend is a static HTML + JavaScript page that:
- Connects to the MQTT broker via WebSocket
- Subscribes to
esp32/available_sensors
andesp32/response/#
topics - Renders one button per available sensor using metadata from
available_sensors
- Sends requests via MQTT on button click
- Displays each result dynamically based on the sensor type (text/plain or image/jpeg)
The client is fully decoupled from the ESP32 implementation. It relies exclusively on metadata provided via MQTT.
All ESP32 code is developed using Test-Driven Development (TDD) with separation of logic and hardware:
- Business logic is tested with PlatformIO’s
native
environment - ESP32-specific components are tested in esp32 integration tests
- All sensors are represented by test doubles (fakes/stubs) for isolated testing
- Sensor registry, MQTT dispatch, message structure, and system boot logic are tested without hardware
GPIO | Status | Used By | Purpose / Notes |
---|---|---|---|
0 | Boot pin | Must be pulled LOW to enter flash mode | |
1 | Serial | TX0 — used for programming/debug | |
2 | Boot pin | Must be LOW on boot; often used for onboard LED | |
3 | Serial | RX0 — used for programming/debug | |
4 | ❌ Occupied | Camera | D0 |
5 | ❌ Occupied | Camera | D1 |
12 | - | Must be LOW on boot — strapping pin | |
13 | ❌ Occupied | ADC2 | WiFi uses ADC2, so no reading when WiFi=on (will be freed when switching to 3G) |
14 | ❌ Occupied | Buzzer | Passive buzzer signal |
15 | ✅ Free | - | Often used for SPI CS |
16 | ✅ Free | - | Can be used; UART2 RX |
17 | ✅ Free | - | Can be used; UART2 TX |
18 | ❌ Occupied | Camera | D2 |
19 | ❌ Occupied | Camera | D3 |
21 | ❌ Occupied | Camera | XCLK |
22 | ❌ Occupied | Camera | PCLK |
23 | ❌ Occupied | Camera | HREF |
25 | ❌ Occupied | Camera | VSYNC |
26 | ❌ Occupied | Camera | SCCB SDA |
27 | ❌ Occupied | Camera | SCCB SCL |
32 | ❌ Occupied | MQ-135 | MQ-135 Air Quality sensor |
33 | ❌ Occupied | DHT11 | Temp & humidity sensor |
34 | ❌ Occupied | Camera | D6 (input only) |
35 | ❌ Occupied | Camera | D7 (input only) |
36 | ❌ Occupied | Camera | D4 (input only) |
39 | ❌ Occupied | Camera | D5 (input only) |
- ✅ Free – safe to use for sensors/peripherals
- ❌ Occupied – already used here
⚠️ Caution / Avoid – avoid unless really sure