Budget ESP32-S3 port of gigar1-shelly-zendure-monitor — same features, ~1/3 the cost. Tested on hardware: 24 h+ stable over a full day/night cycle with the LovyanGFX display driver.
An independent, cloud-free monitor and safety watchdog for a home battery setup, running on an ESP32-S3 with an 800×480 display. It reads a Shelly Pro 3EM (grid meter) and a Zendure SolarFlow battery read-only over their local HTTP APIs, shows live data and a daily energy balance, raises a visible alarm on misbehaviour, and serves its own JSON API.
Live dashboard on the ELECROW 7″ panel: grid total, per-phase L1/L2/L3, Zendure SoC/output, daily energy balance.
This is the cheaper sibling of the Arduino GIGA R1 version — same features, ~1/3 the cost.
- ELECROW 7″ HMI ESP32 Display, 800×480 RGB TFT — ESP32-S3-WROOM-1, 8MB PSRAM,
RGB parallel panel (driver EK9716BD3), GT911 capacitive touch. ELECROW sells the
same board under two names over time: "ESP32 Display / Wizee" and "CrowPanel ESP32
HMI 7.0". (The titles say "ESP32 / LX6" but it is an ESP32-S3.)
- 800×480 — same resolution as the GIGA display, so the layout carries over 1:1
- Shelly Pro 3EM (3-phase grid meter, on the LAN)
- Zendure SolarFlow (e.g. 2400 / 2400 Pro, on the LAN via WiFi)
- Power: ~450 mA @ 5 V (~2.25 W) in operation — any USB port or a small 5 V charger (≥ 500 mA, 2.5–10 W) is plenty. No 20 W+ charger needed.
- WiFi (ESP32 Arduino core) — native;
WiFiClient/WiFiServer - ArduinoJson (>= 7.x)
- LovyanGFX 1.2.24 — RGB parallel panel driver.
Version pairing matters: use 1.2.x with core 2.0.17 (this release), or
1.1.12 with core 2.0.3 (previous release, see the
v1.1.0tag). 1.1.x won't compile on the newer core and 1.2.x won't compile on 2.0.3 — keep the pair matched. NOT TFT_eSPI — ELECROW confirms the 4.3″/5.0″/7.0″ HMI displays don't use it. No LVGL needed — LovyanGFX's Adafruit-GFX-style API matches the GIGA drawing helpers, so the monitor draws directly.- Why LovyanGFX, not Arduino_GFX? The first port used Arduino_GFX 1.2.8 and crashed every few hours: the RGB framebuffer DMA in PSRAM corrupted WiFi memory under normal load (PANIC / heap corruption). LovyanGFX's PSRAM/DMA management fixes it — identical sketch logic, 24 h+ stable.
esp_task_wdt(ESP-IDF, built in) — hardware task watchdog (120 s)
Recommended toolchain (v2.0.0): core 2.0.17 + LovyanGFX 1.2.24 — validated on hardware (24 h+ stable, rode through a 12-min WiFi outage, daily balance preserved across reboot/power-cut). It brings ongoing WiFi/stability fixes and the newer IDF (4.4.2+, RGB bounce-buffer available) over the original 2.0.3. The earlier 2.0.3 + LovyanGFX 1.1.12 combo remains a working fallback at the
v1.1.0tag. Stay on a 2.0.x core — core 3.x changes APIs this sketch relies on.
ELECROW ships its own library bundle (LovyanGFX, Arduino_GFX, gt911-arduino touch, lvgl, …) and a factory test program for this panel:
- Libraries: https://www.elecrow.com/download/product/ESP32_Display/Arduino_Libraries.zip
- Setup tutorial (4.3″/5.0″/7.0″): https://www.youtube.com/watch?v=iKJesBu_cg4
RGB panels need correct timing (hsync/vsync/pclk/porches). This sketch uses the LovyanGFX config for the ELECROW 7″ panel (Sunton-8048S070 timing, PCLK=GPIO0, 8 MHz). If a different panel batch shows shift/stripes, adjust the porches /
pclk_idle_highin theLGFXclass.
- Live grid power: total + per phase L1/L2/L3 (W / V / A)
- Zendure status (read-only): SoC, output, acStatus — incl. "API hung / OFFLINE"
- Daily energy balance [kWh] since midnight (auto-reset)
- Watchdog/alarm: grid-dumping, deep discharge, BMS-critical (independent cell-V/temp check), debounced against device-reboot transients
- Device health (Shelly/Zendure: ok / hung / offline)
- Heartbeat + hardware watchdog (self-supervision), robust WiFi reconnect
- JSON API:
/status,/balance(cell-balancing history),/daily(30-day diary) - Own IP shown on the display
- Daily balance survives reboots & power loss (NVS) — see Persistence & resilience
The daily energy balance is kept in NVS (the ESP32's non-volatile flash key-value store): on every reboot — including a real power cut — the board restores the day's baseline if the stored entry is from the same local day, so the saldo continues instead of resetting to zero. A fresh baseline is written only ~once per day (at the midnight rollover or the first start of a new day), so flash wear is negligible (~1 write/day). Validated on hardware across a 12-minute network outage and a cold power-cycle.
The /status API gained matching diagnostics:
| Field | Meaning |
|---|---|
base_restored |
true if the day's saldo was restored from NVS after a reboot |
boots |
persistent boot counter — reveals unobserved reboots |
min_free_heap |
lowest free internal heap since boot (leak indicator) |
rssi |
current WiFi signal strength (dBm) |
If you also run the local
zero-feed-in controller
(a regler + MQTT broker on separate boards), this monitor can supervise it and show a
small CONTROL OK (green) / … DOWN (red) tile in the top bar — a glanceable,
always-on health light on independent, diverse hardware.
- Polls the controller's and broker's read-only
/statusevery 20 s. - Turns red — showing which side is silent (
REGLER DOWN/BROKER DOWN/CONTROL DOWN) — after more thanCONTROL_FAIL_N(3) consecutive misses (~80 s; setCONTROL_FAIL_N = 2for ~60 s). - Off by default. If you use the Zendure's own cloud/HEMS control (no local controller), leave it off so you never see a false red. When disabled, none of this code compiles — the monitor is byte-for-byte unchanged.
To enable: set #define CONTROL_WATCH_ENABLE 1 in the sketch and add the two IPs
(SECRET_REGLER_HOST, SECRET_BROKER_HOST) to arduino_secrets.h (see the example
file). Read-only, like everything else here. The tile state is also exposed in /status
(ctrl_state, ctrl_fail_r, ctrl_fail_b).
- Copy
shelly_monitor_esp32/arduino_secrets.example.h→shelly_monitor_esp32/arduino_secrets.h - Fill in all four values: WiFi SSID/password + local IPs of the Shelly and Zendure.
Give each device (and this board) a fixed IP (DHCP reservation) so addresses stay stable.
(The two extra
SECRET_REGLER_HOST/SECRET_BROKER_HOSTentries are only needed if you enable the optional Control-Watch tile.) - Arduino IDE board settings (ELECROW, confirmed for the 7″ HMI display):
- Boards Manager → install esp32 by Espressif, version 2.0.17
(URL:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json) — pair with LovyanGFX 1.2.24. (For the 2.0.3 + 1.1.12 fallback, see thev1.1.0tag.) - Board: ESP32S3 Dev Module
- PSRAM: OPI PSRAM · Flash Mode: QIO 80MHz
- Flash Size: depends on the ESP32-S3-WROOM-1 variant on the shield — N16R8 → 16MB (the guide's default) or N4R8 → 4MB. PSRAM is 8MB either way. Check the metal-shield label; if upload fails with 16MB, try 4MB.
- Partition Scheme: Huge APP (3MB No OTA/1MB SPIFFS)
- CPU Frequency: 240MHz (WiFi) · Upload Speed: 921600
- Boards Manager → install esp32 by Espressif, version 2.0.17
(URL:
- Upload: if the IDE can't enter download mode, hold BOOT, press RESET
(serial shows
waiting for download), then upload; press RESET to run.
arduino_secrets.h is git-ignored — credentials are never committed.
📋 Step-by-step flashing checklist: docs/FLASHING.md
Read-only monitor for display purposes, not a certified safety device. The real cell protection is the battery's own BMS. Use at your own risk.
MIT — Copyright (c) 2026 Reinhard Jesolowitz
