Firmware · Hardware · Web
ESP32 water level sensor for tanks, wired into Home Assistant
Complete firmware to measure water tanks and cisterns with ultrasound: web dashboard, captive portal, MQTT with Home Assistant, ESP-NOW remote sensors and OTA updates.
- Year
- 2025–2026
- Status
- Published on GitHub
- Role
- Firmware, embedded web and testing
- Repository
- GitHub
01 · Problem
Know how much water is left in the rooftop tank or the cistern without climbing up to check, and let the house know when it is filling, using cheap hardware and no vendor cloud app.
02 · Approach
Modular C++ firmware built with PlatformIO for a Heltec WiFi Kit 32 and an HC-SR04 sensor: averaged readings, adaptive intervals based on container type and volume, fill and drain detection, and an embedded website that is minified and gzipped on every build.
03 · Result
Dashboard with an animated SVG container, captive portal in Spanish, English and French, ESP-NOW remote sensors with no router, Home Assistant auto-discovery (including the pump switch) and OTA. It ships with 206 automated tests (141 in C++ with Unity and 65 in JavaScript); the web assets go from 342 KB down to 60 KB served.
04 · Stack
- C++
- ESP32 (Heltec WiFi Kit 32)
- PlatformIO
- ESPAsyncWebServer
- MQTT
- Home Assistant
- ESP-NOW
- ElegantOTA
- Unity
Architecture
The entry point is an AppManager singleton that coordinates independent modules. Each piece lives in its own library and talks through interfaces:
| Pattern | Where |
|---|---|
| Singleton | AppManager, the single coordinator |
| Facade | ApplicationConfig wraps 5 domain configs |
| Strategy | ISensor: swappable sensors |
| Factory | SensorManager creates sensors at runtime |
| Observer | MQTT and ESP-NOW callbacks |
| Adapter | UnifiedLogger unifies Serial and WebSerial |
Home Assistant integration
The device announces itself through MQTT Discovery. It publishes its state as JSON and listens for commands; when Home Assistant turns the pump on, the sensor switches to a fast reading interval to follow the fill.
nivel_tinaco/{mac}/state ESP32 → HA sensor data (JSON)nivel_tinaco/{mac}/cmd HA → ESP32 FILLING_ON / FILLING_OFFnivel_tinaco/{mac}/pump_state ESP32 → HA pump statenivel_tinaco/{mac}/status ESP32 → HA online / offlineThe website fits on the chip
Pages live in LittleFS. A pre-build script minifies HTML, CSS and JS and produces the .gz version served to the browser:
source 342 KB → minified 248 KB → gzip 60 KB (82.5 % smaller)The partition table reserves two app slots (one active, one for OTA); the firmware uses 89.4 % of its 1.5 MB slot.