A unified ESP8266 firmware that combines three sensor projects into a single codebase with a runtime-selectable plugin system.
Measures distance using a 4-20mA analog sensor (e.g. SWK-LT100) connected to the A0 pin. Calculates relative fill percentage and absolute water depth. Publishes relative, absolute, and measured values to MQTT.
Wiring: Sensor output → 4-20mA to 0-3.3V converter → A0
The converter circuit uses a 160Ω precision resistor (V = I × R → 4mA = 0.64V, 20mA = 3.2V). Optionally add a 10nF capacitor for noise filtering and a 3.6V Zener diode for overvoltage protection.
4-20mA Sensor Loop
| |
(-) (+) ----+---------+--------+-------> A0
| | | |
| 160Ω 10nF 3.6V Zener
| | | |
(-) (+) ----+---------+--------+-------> GND
Measures distance using an HC-SR04 / JSN-SR04T ultrasonic sensor. Calculates relative fill percentage and absolute depth. Publishes relative, absolute, and measured values to MQTT.
Wiring: TRIG → D1, ECHO → D2, 5V from VU pin, GND to any G pin.
Bridges Geiger-Müller tube detectors (GGreg20_V3, RadiationD-v1.1 / CAJOE, etc.) to MQTT. Calculates CPM and dose rate (µSv/h). Two-page OLED display with graph and info, toggled by the FLASH button.
Wiring: Detector signal → D2. ESP8266 provides 3.3V (3V pin) or 5V (VU pin) to the detector board.
Parameters:
Tube Factor— CPM to µSv/h conversion. ~123 for J305, ~175 for CBM-20.Tube Dead Time— GM tube dead time in µs for saturation correction at high count rates (paralyzable modelm = n / (1 - n·τ)). ~90 for J305,0disables.Alert Threshold— when the 5-minute average dose reaches this µSv/h, the device exposes analertflag (HA binary sensorElevated Radiation).Graph Bar Seconds— seconds per graph bar on OLED. 665s ≈ 11min gives ~24h history.
- NodeMCU ESP8266 Development Board (recommend the variant with built-in 0.96" OLED)
- A sensor appropriate for the chosen plugin
- Optional: external 128x64 OLED display —
D5asSCL,D6asSDA
- Flash firmware and LittleFS filesystem (see below).
- On first boot,
WiFiManagercreates an AP namedESP_<ChipID>. Connect to it and configure your WiFi network. The device restarts automatically. - Navigate to the device's IP address (shown on the OLED and Serial) to open the admin dashboard.
- Optionally set a Name under Device — this is what Home Assistant shows. Left empty, the plugin's own name is used (e.g. "ESP Analog Distance Meter").
- Select the active plugin, configure sensor and MQTT parameters, then Save & Restart.
HomeAssistant autodiscovery is published automatically — the device should appear under the MQTT integration.
Everything the device publishes lives under a single root topic — the configured device name:
<device_name>/stat/<plugin_id> measurements (state topic, overridable)
<device_name>/diag diagnostics (heap, RSSI, uptime)
<device_name>/availability LWT: "online" / "offline"
Payload examples:
Distance meters:
{ "relative": 0.73, "absolute": 1.12, "measured": 1.51,
"raw": 1.55, "connected": true,
"volume": 2340, "flow_rate": 0.42, "usage_24h": 187.3 }(volume, flow_rate and usage_24h only when Total Water Volume is configured.)
Radiation counter:
{ "cpm": 42, "dose": 0.34, "dose_avg_5m": 0.29, "counts_total": 183412, "alert": false }The radiation counter also serves its graph history at GET /api/v1/chart,
rendered as a bar chart in the admin dashboard.
Diagnostics (all plugins), topic: <device_name>/diag, published every 30 s:
{ "state": "active", "heap": 41520, "rssi": -62, "uptime_s": 3602, "version": "v2026.09.03.2214" }HomeAssistant gets diagnostic sensors for heap, RSSI and uptime from this topic
automatically. The firmware version is exposed as the HA device's sw_version.
For clangd-based code intelligence (autocompletion, error checking), run the setup script after cloning:
./setup-ide.shThis generates compile_commands.json and .clangd with the correct toolchain paths for your machine. Restart the clangd language server afterwards. Re-run after changing platformio.ini dependencies.
Native unit tests run on the host machine using GoogleTest. They cover calculators, logger, and plugin registry — no hardware required.
docker build -f Dockerfile.test -t esp-tests .
docker run --rm esp-testsRequires g++ on PATH:
pio test -e native -vTests run automatically on every push/PR via GitHub Actions (see .github/workflows/ci.yml).
Requires PlatformIO.
# Build
pio run -e nodemcu
# Flash firmware
pio run -t upload -e nodemcu
# Flash LittleFS filesystem (web UI)
pio run -t uploadfs -e nodemcuBuild the images locally, then upload them from the device's admin page (Configuration → Firmware Update). Uploads go over plain HTTP, so they do not need the contiguous heap a TLS download would require.
# Firmware -> .pio/build/nodemcu/firmware.bin
pio run -e nodemcu
# Filesystem/web UI -> .pio/build/nodemcu/littlefs.bin
pio run -e nodemcu -t buildfsPick Firmware or Filesystem, select the matching .bin, and press
Upload & flash. The device restarts when the upload completes.
Release builds of both images are also attached to every GitHub release by
.github/workflows/release.yml, so they can be downloaded and uploaded the
same way.
Replace <DEVICE_IP> with the device's IP address. Note this needs a
contiguous 4 KB heap block for Update.begin(), which is not always available
while the web server and MQTT are running:
pio run -t upload -e nodemcu_ota --upload-port <DEVICE_IP>
pio run -t uploadfs -e nodemcu_ota --upload-port <DEVICE_IP>







