A four-screen oil-tank dashboard for the ESP32-2432S028R "CYD" (Cheap Yellow Display, 2.8" 240×320 ILI9341 with resistive touch). Subscribes to a KeroTrack v2 MQTT broker and surfaces tank level, consumption, cost, and current weather in a touch-cycling UI.
Originally forked from Aura — the
weather screen reuses Aura's icon set and Latin Montserrat font files (see
KeroDisplay/kerodisplay/WEATHER_ASSETS.md for attribution).
The display cycles through four screens with an asymmetric auto-rotate (60s on screen 1, 30s on the others). Tap anywhere to advance and reset the hold timer for the new screen.
| # | Screen | Hero | Supporting data |
|---|---|---|---|
| 1 | At-a-glance | 73% (colour-coded green/amber/red) |
litres remaining, level bar, "Empty 23 Aug 2026 / 47 days" |
| 2 | Consumption | 285 L used since refill |
days since refill, hot-water vs heating split bar with daily L breakdown, avg L/day burn, "↑ refill detected today" flag |
| 3 | Cost | 243 avg monthly (gold tint, (GBP) cue) |
weekly + annual cost, cost-to-fill cell with 500L order-ready threshold (green tint + pip when ≥ 500L), current p/L |
| 4 | Weather | 12° current temperature with condition icon |
feels-like, condition descriptor, location, 3-day forecast strip with icons + day + high/low |
Bottom dot indicator (*..., .*.., ..*., ...*) shows which screen is
active. Order: 1 → 2 → 3 → 4 → 1.
A 36px status bar sits across the top of every screen:
- WiFi icon (dims when disconnected)
- MQTT state dot (green = connected, amber = reconnecting, red = down)
- NTP clock
HH:MMin centre (blank until first sync; auto-applies UK BST/GMT) - Last-update age on the right (
2m ago,42m ago,3h ago)- Default light blue, turns amber after 10 min stale, red after 60 min
- Leak detected: when MQTT publishes
leak_detected: "y", a red ribbon with a soft pulsing background appears below the status bar on every screen. Content shifts down 28px while active. - Refill detected: subtle
↑ refill detected todayline on the consumption screen whenrefill_detected: "y".
- Board: ESP32-2432S028R "CYD" (240×320 portrait ILI9341, XPT2046 touch)
- Partition scheme: Huge App (3MB No OTA/1MB SPIFFS) — required, the sketch with all weather assets does not fit on the default partition
This is an Arduino IDE project. There is no CLI build system; .ino
compilation goes through the IDE.
-
Arduino IDE setup:
- Board: "ESP32 Dev Module"
- Tools → Partition Scheme: "Huge App (3MB No OTA/1MB SPIFFS)"
- Tools → Port: your CYD's COM port
-
Required libraries (Arduino Library Manager):
- ArduinoJson 7.4.1
- HttpClient 2.2.0
- TFT_eSPI 2.5.43
- WiFiManager 2.0.17
- XPT2046_Touchscreen 1.4
- lvgl 9.2.2
- PubSubClient
-
Library config override (important): The
lvgl/,TFT_eSPI/, andPubSubClient/folders at the repo root are patched header files, not vendored library copies. After installing the libraries through Library Manager, copy these into the user's installed Arduino libraries:Copy-Item lvgl\src\lv_conf.h "$HOME\Documents\Arduino\libraries\lvgl\src\lv_conf.h" -Force Copy-Item TFT_eSPI\User_Setup.h "$HOME\Documents\Arduino\libraries\TFT_eSPI\User_Setup.h" -Force Copy-Item PubSubClient\src\PubSubClient.h "$HOME\Documents\Arduino\libraries\PubSubClient\src\PubSubClient.h" -Force
The CYD-specific TFT pin mapping and the LVGL feature configuration (image widget, fonts, etc.) live in those headers. Edits to them only take effect on the device after the file is copied into the Arduino libraries folder.
-
Open and upload:
KeroDisplay/kerodisplay/kerodisplay.ino→ Sketch → Upload.Watch the boot log on Serial Monitor at 115200 baud.
On first boot (or if the saved WiFi credentials are wrong), the device launches a WiFiManager captive portal:
- Connect from a phone to the open WiFi network
KeroTrack. - Browser opens to
http://192.168.4.1(or scan for a captive-portal notification). - Pick your home WiFi and enter MQTT broker / port / user / password.
- Save. The device prompts you to power-cycle, then boots into normal mode.
Once on WiFi, the device runs an always-on settings server. Visit
http://<device-ip>/ from any browser on the LAN — the IP is printed on
serial at boot, e.g.:
Settings page: http://192.168.1.42
The settings page has two sections:
- MQTT — change broker, port, username, password. Submitting reboots.
- Weather — type a place name (city/town/postcode) and click "Search
and change". The device hits the
Open-Meteo geocoding API
and presents the matches as radio buttons. Pick one, click "Save and
reboot". The chosen name + lat/lon are persisted to NVS.
- Leave the field blank and search to disable the weather screen (screen 4 will show "Weather not configured").
Subscribed by the device. All published by the KeroTrack v2 backend.
| Topic | Used for |
|---|---|
oiltank/level |
percentage, litres, leak/refill flags, cost-to-fill, ppl, litres-to-order, current temperature |
oiltank/analysis |
empty date, days remaining, days since refill, total used since refill, avg daily consumption, hot-water vs heating breakdown |
oiltank/cost_analysis |
avg daily/weekly/monthly/annual cost, latest refill amount/cost/ppl |
A non-blocking reconnect helper runs in loop() with exponential backoff
(1s → 2s → 5s → 15s → 30s → 60s, capped) so transient broker outages don't
freeze the UI.
Powered by Open-Meteo — no API key needed.
A FreeRTOS task wakes every 15 minutes, fetches current + 3-day forecast,
and uses lv_async_call to refresh the UI on the LVGL thread.
Saved location format: Surrey, GB (city + ISO country code, kept short
to fit the screen). Long names are dot-truncated. The 26-icon set covers
all WMO weather codes; icons swap between day/night variants based on the
API's is_day flag.
KeroTrack-Display/
├── KeroDisplay/
│ └── kerodisplay/
│ ├── kerodisplay.ino # entire firmware (single-file sketch)
│ ├── icon_*.c # 26 weather icons (LVGL bitmap arrays)
│ ├── lv_font_montserrat_latin_*.c # Latin-1 font variants (5 sizes)
│ └── WEATHER_ASSETS.md # attribution for icons/fonts
├── lvgl/
│ └── src/lv_conf.h # patched LVGL config (fonts, image widget)
├── TFT_eSPI/
│ └── User_Setup.h # patched TFT pin mapping for CYD
├── PubSubClient/
│ └── src/PubSubClient.h # patched MQTT buffer size
├── docs/
│ └── superpowers/ # design specs and implementation plans
└── README.md / CLAUDE.md
- The sketch is structured around three event sources sharing global state: an LVGL UI loop, an MQTT callback, and a FreeRTOS weather task.
- Screen widgets are pre-built once during
setup()with cached pointers in per-screen structs (s1,s2,s3,s4) — no fragile child-index lookups in update paths. - Reusable
create_chrome()helper builds the status bar + hidden leak ribbon + content container + dot indicator on every screen. update_oiltank_ui()runs on every received MQTT message and dispatches to per-screen update functions; the weather task feedsupdate_screen4()separately vialv_async_call.
- Aura — original parent project, source of the weather icons and Latin Montserrat fonts. Aura's 3D-printed case from MakerWorld fits this project unchanged.
- Google Weather Icons — origin of the icon artwork (used with attribution).
- Open-Meteo — weather + geocoding APIs.
- LVGL, TFT_eSPI, WiFiManager, PubSubClient, ArduinoJson, XPT2046_Touchscreen.
- KeroTrack — the backend that feeds this display.
GPL-3.0 (inherited from Aura, the parent project). Note that the weather
icons in KeroDisplay/kerodisplay/icon_*.c originate from Google and are
not GPL-licensed — see WEATHER_ASSETS.md for details.