Skip to content

Repository files navigation

Solar-Battery-Rain-Gauge

A weighing rain gauge on ESP32 — a bottle hanging from a load cell measures rainfall by weight and reports it over MQTT.

A collecting bottle hangs from a bar load cell (HX711). As rain raises the fill level, the weight rises; from the weight gain over time the evaluation side (Node-RED) computes rainfall amount and intensity. The device runs on battery/solar in the garden, spends most of its time in deep sleep and wakes adaptively more often while it is raining.

Language convention: README.de.md is the primary, authoritative version; this English README.md is kept in sync from it (GitHub default). Code, commit and PR language: English.

Hosting: primary on gitlab.brokenpipe.de (group platformio), mirrored to GitHub. Full concept/decision record in MemPalace: wing regenmesser-rain-gauge (rooms decisions + technical).

How it works

Each wake-up runs a linear cycle, then goes back to deep sleep:

  1. Power the load cell (HX711) via a gate, let it settle, take a smoothed reading
  2. Read the environment sensor AHT20 + BMP280 (temperature/humidity/pressure)
  3. Measure the battery voltage
  4. Only when needed (weight changed · heartbeat due · first boot): read the environment sensor, WiFi (static IP + cached BSSID/channel → fast connect) + MQTT: apply retained config + cmd, publish state, OTA if requested
  5. Pick the next interval adaptively and go to sleep

Adaptive: while the weight barely changes (dry) it measures rarely (dry_interval). Once the change exceeds a threshold (rain) the device switches to a fine interval (wet_interval) for a few cycles — fine resolution during rain, frugal when dry. The last value survives deep sleep in RTC memory.

Energy: WiFi is the most expensive part. Dry wakes without a weight change are therefore measure-only wakes without WiFi; the device transmits only on a change (rain) or at the latest via heartbeat (heartbeat_s), which also keeps the OTA window open. The weight is the median of several samples → robust against the bottle swinging in the wind.

Hardware

  • MCU: ESP32-WROOM-32 (bare module) on an AI-Thinker adapter board
  • Weighing: HX711 module (XFW-HX711) + bar load cell (~5 kg)
  • Environment: AHT20 + BMP280 (I²C combo module)
  • Power: 1× LiIon 3.7 V + solar (planned); low-Iq LDO concept (two LDOs in parallel: main LDO for the steady load, the second only for the WiFi current spikes, ~1 µA quiescent)
  • HX711 and sensor are powered from GPIOs only while measuring (power gating against quiescent draw)
  • Brownout detector disabled (RTC_CNTL_BROWN_OUT_REG): the WiFi current spike briefly pulls the dual-LDO voltage below the threshold — not a real undervoltage, but it otherwise caused resets mid-connect.

Pinout

Function GPIO Note
Battery voltage IO34 1M/1M divider, ADC1
HX711 DOUT IO35 input-only (ESP reads)
HX711 SCK IO32 output
HX711 power gate IO33 powers HX711 + bridge
Sensor power gate IO26 powers AHT20+BMP280
I²C SDA / SCL IO21 / IO22 AHT20 @0x38, BMP280 @0x77

Rainfall calculation

The device sends raw grams (source of truth); mm/intensity is computed on the evaluation side (Node-RED → InfluxDB/Grafana), so swapping the funnel does not require a reflash:

mm = 10 × grams / funnel_area[cm²]

A siphon drain (planned, a "Pythagorean cup" at the bottle bottom) produces a large negative weight step → treat it server-side as a reset, not as negative rain. The known empty weight after draining also enables an auto-tare to compensate load-cell drift.

MQTT

Topic prefix: ohm/garten/regenmesser

  • …/state — readings as JSON: {"grams":…,"temp_c":…,"humidity":…,"pressure_hpa":…,"vbat":…,"rssi":…,"boot":…,"version":…}
  • …/config (retained) — applied on connect and cached in RTC memory, i.e. tunable live without reflashing: dry_interval_s, wet_interval_s, heartbeat_s, change_threshold_g, wet_hold_cycles, cal_factor, plus fw_version + fw_url (see OTA)
  • …/cmd (publish retained) — plain-text commands: tare (tare the empty cell) or calibrate:<grams> (put a known mass on it). The device runs the command on its next connect, clears the topic afterwards (retained "") and reports the result retained on …/cmd/result. Calibration is two-step: first tare, then in the next cycle, with the mass on the cell, calibrate:<grams>.

OTA (wireless update)

HTTP-pull OTA, triggered via the retained config: set fw_version (≠ running version) + fw_url there, and on the next wake-up the device pulls the .bin over HTTP, flashes it and reboots. No BLE, no MQTT chunking.

Latency: OTA is only checked while WiFi is up. When it's dry that's not until the next heartbeat (heartbeat_s, default 1 h); during rain it's sooner. To update faster, lower heartbeat_s via config for a while.

The ota_deploy.sh script automates the whole flow:

./ota_deploy.sh --bump   # VERSION +1, build, serve firmware.bin,
                         # push retained config, confirm the update

Calibration

CALIBRATION_MODE 1 in src/config.h starts an interactive serial loop (115200 baud): t = tare (empty), c = calibrate with a known mass, p = show cal factor. Cal factor and tare offset are stored in NVS. In normal operation (CALIBRATION_MODE 0) the deep-sleep measurement cycle runs.

Build & flash

  • Toolchain: PlatformIO with the pioarduino platform (arduino-esp32 3.x on ESP-IDF 5.5) — see platformio.ini
  • Credentials: copy secrets.h.example to secrets.h and fill it in (WiFi, static IP, MQTT). secrets.h is excluded via .gitignore.
pio run                 # build
pio run -t upload       # flash (FTDI)

Flashing without auto-reset: an FTDI Basic only has DTR (no RTS), so enter the bootloader manually: hold IO0 → tap Reset → release IO0, then upload; after flashing tap Reset once to start the app. (After the first OTA-capable flash, further updates are wireless.)

Status / roadmap

Working (verified): HX711 measurement + calibration, AHT20, BMP280, battery voltage, adaptive deep sleep, WiFi (static IP), MQTT state, retained-config uptake, HTTP-pull OTA, ota_deploy.sh.

Implemented (still to verify on the mounted device): cmd tare/calibrate via MQTT · energy-frugal send logic (WiFi only on change/heartbeat) · HX711 median against wind noise · BSSID/channel caching in RTC for faster reconnect.

Open: siphon auto-tare (needs the mechanics mounted) · Node-RED evaluation (mm/intensity, siphon reset) → InfluxDB/Grafana · final tare with the mechanics mounted.

License

MIT — see LICENSE.

About

Weighing rain gauge on ESP32 (load cell + HX711), battery/solar, MQTT + OTA — mirror of gitlab.brokenpipe.de/platformio

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages