Skip to content
Kira

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_OFF
nivel_tinaco/{mac}/pump_state ESP32 → HA pump state
nivel_tinaco/{mac}/status ESP32 → HA online / offline

The 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.