ESP32 + Cheap Yellow Display (CYD) battery monitor for FoxESS inverters using the FoxESS Cloud API.
This firmware:
- connects to Wi‑Fi,
- signs and sends authenticated requests to FoxESS Cloud,
- pulls
SoC,batChargePower, andbatDischargePower, - renders a full-screen battery UI on an ILI9341 TFT,
- auto-adjusts backlight from an LDR,
- schedules polling to align with FoxESS 5-minute telemetry cadence.
This is a single-purpose wall-display firmware, not a generic dashboard framework.
It is designed to run continuously on a CYD/ESP32 display device and show:
- current battery percentage,
- charge/discharge direction,
- rough time-to-full / time-to-reserve,
- timestamp of the latest inverter sample.
The core behavior lives in FoxESS-CYD-BatMon.ino.
- ESP32-based Cheap Yellow Display (ILI9341 320x240 SPI TFT).
- CYD onboard LDR (wired to ADC in this sketch) for ambient-light backlight control.
- Wi‑Fi network with internet access to
https://www.foxesscloud.com.
From FoxESS-CYD-BatMon.ino:
- TFT backlight:
GPIO21 - TFT CS/DC/MISO/MOSI/SCLK/RST:
15/2/12/13/14/-1 - LDR input:
GPIO34
If your CYD revision differs, change these defines at the top of the sketch.
config.h provides:
SECRET_SSIDSECRET_PASSSECRET_TOKENSECRET_DEVICESN
At boot, firmware builds:
FULL_URL = https://www.foxesscloud.com/op/v0/device/real/query- JSON body:
{"sn":"<deviceSN>","variables":["SoC","batChargePower","batDischargePower"]}
GetAuthor::get_signature(...) builds an MD5 signature using:
- API URL path,
- token,
- timestamp,
with
\r\nseparators. The signature is sent in HTTP headers (signature,timestamp,token,lang).
Update cycle (mainUpdate()):
- ensure Wi‑Fi connected,
- POST request,
- parse JSON (
payloadJSON), - derive display text (
stringifyState), - redraw battery widgets,
- compute next poll time (
scheduleNextUpdate).
Instead of fixed polling, the sketch reads FoxESS sample timestamp (result[0].time), compares it to current UTC from NTP, and schedules next request for:
- ~10 seconds after the next expected 5-minute data publish.
If data is stale or malformed, retry falls back to 15 seconds.
- Background image is stored in
fullscreen.has RLE565 blob and rendered once. - Battery bar colors:
- blue = charging,
- green = normal,
- red = low battery (
SoC <= 30).
- Status text rules:
- charging: “Full in X Hrs Y Mins”
- discharging above reserve: “Empty in X Hrs Y Mins”
- near idle: “Battery is Idle”
- at reserve floor (
SoC <= 20and idle): “At reserve floor”
Every second, 10 ADC samples are averaged.
- very dark (
<20): backlight duty increased, - bright (
>100): backlight duty reduced.
PWM uses ESP32 core 3.x LEDC API (ledcAttach, ledcWrite(pin, duty)).
Edit config.h:
#define SECRET_SSID "your_wifi_ssid"
#define SECRET_PASS "your_wifi_password"
#define SECRET_TOKEN "your_foxess_api_token"
#define SECRET_DEVICESN "your_foxess_device_serial"Install board + libraries used by the sketch:
- ESP32 board package
ArduinoJsonNTPClientAdafruit GFX LibraryAdafruit ILI9341MD5library compatible withMD5::make_hash/make_digest
Then open FoxESS-CYD-BatMon.ino, select your ESP32 board/port, and upload.
- HTTPS client uses
client.setInsecure(), so TLS cert chain is not validated. - A 20s HTTPS timeout is configured.
- NTP is started once in
setup()and reused. - Initial screen update runs in
setup(), then continues by scheduled polls.
- JSON field extraction assumes
datas[0..2]order remains fixed for SoC/charge/discharge. If API ordering changes, values will map wrong until parsing is hardened. - Time-to-full/empty is a linear estimate from current instantaneous power and fixed usable capacity (
USABLE_WH = 7864), so it is approximate only. setInsecure()is acceptable for hobby LAN display use, but not ideal for strict security requirements.
FoxESS-CYD-BatMon.ino— main firmware logic (Wi‑Fi, API, parsing, scheduling, UI, LDR).config.h— local secrets.fullscreen.h— compressed background image renderer/data.README.md— project documentation.
Use this project when you want a dedicated, always-on local display of FoxESS battery state without opening apps or browser dashboards.
It is not trying to be:
- a full home-energy EMS,
- a historical logger,
- a control surface for inverter settings.