CLAUDE.md 3.7 KB

CLAUDE.md

Firmware for a desk clock / thermometer on the WaveShare ESP32-S3-RLCD-4.2. README.md is the product spec — layout, update rates and error behaviour are defined there and should be treated as requirements, not suggestions.

Build

pio run              # build
pio run -t upload    # flash over USB-C
pio device monitor   # 115200 baud

include/secrets.h is gitignored. Copy include/secrets.h.example to it before the first build; the build fails without it.

Hardware facts

Confirmed from the vendor examples in waveshareteam/ESP32-S3-RLCD-4.2 (02_Example/Arduino/), not from the wiki — the wiki does not publish a pinout.

Function Pin / value
LCD controller ST7305, 300 × 400 mono reflective, SPI
LCD SCK / MOSI / DC / CS / RST / TE 11 / 12 / 5 / 40 / 41 / 6
I²C SDA / SCL 13 / 14
PCF85063 RTC 0x51
SHTC3 temp/humidity 0x70
Module ESP32-S3-WROOM-1-N16R8 — 16 MB flash, 8 MB octal PSRAM

The panel is native 300 wide × 400 high. The app drives it rotated (U8G2_R1), so all drawing code works in a 400 × 300 landscape frame. Any new layout constant belongs in include/config.h, not inline in display.cpp.

board_build.arduino.memory_type = qio_opi in platformio.ini is what selects octal PSRAM. Dropping it silently disables PSRAM at boot.

Layout

lib/ST7305_U8g2/ is vendored verbatim from the vendor's 10_U8G2_Test example (Apache-2.0). Treat it as third party: fix bugs upstream-style, keep local changes minimal and commented.

src/main.cpp          startup order + the loop; owns the indoor reading
src/display.{h,cpp}   the only code that touches the panel
src/rtc.{h,cpp}       PCF85063 driver, stores UTC
src/temperature.{h,cpp}  SHTC3 driver
src/network.{h,cpp}   Wi-Fi association + NTP, writes the RTC
src/outdoor.{h,cpp}   HTTP poll of the remote sensor
include/config.h      pins, layout, intervals, timezone

Design decisions worth preserving

The RTC holds UTC, never local time. Presentation goes through localtime() with TIMEZONE_POSIX, so a daylight-saving change never requires a write to the chip. Anything that writes the RTC must convert to UTC first.

Boot order is deliberate. Panel → RTC → SHTC3 → first draw → then Wi-Fi. A useful screen appears in about a second even if the network never associates. Do not move network::begin() earlier.

Nothing in loop() blocks on the network. network::loop() owns its own retry timing (WIFI_RETRY_MS); the clock and indoor reading must keep updating through an outage.

The panel is refreshed only when a rendered string changes, not when a value changes. display::update() formats everything to text and compares against what is on screen, which is why a 21.54 → 21.51 drift causes no refresh. This is the mechanism behind the README's "avoid unnecessary full-screen refreshes"; keep it if you rework the renderer.

Missing data draws as --.- / --:--, never as a zero or a blank. A stale outdoor reading keeps its last value on screen with a dashed rule under it — the README is explicit that the old number is preferable to an error value.

Fonts

All from U8g2's built-in set; no font files are shipped. FONT_TIME is logisoso92_tn, which is digits, colon, period and minus only — it cannot render letters. FONT_TEMP must stay a _tf variant so ° (U+00B0) resolves, and temperatures must be drawn with drawUTF8, not drawStr.

Not implemented

Everything under README "Future Features", plus: light sleep between refreshes (the "low power consumption" goal is currently unaddressed — the loop spins), partial-region panel updates, and the TE pin.