Files
thermostat-hw/TODO.md
T
mattandClaude face2432e4 Update TODO.md for the completed review items
Records what d4b2ab7 did and what genuinely remains: confirming the left-edge
artefacts by eye (the timing win was measured, the visual result was not),
OTA download logic, optional backlight scheduling, and the thermostat itself.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 09:09:30 -07:00

3.8 KiB

TODO

Done

The six items from the 2026-08-03 review were completed in d4b2ab7 ("Work through the remaining review items in TODO.md"). See that commit message for the reasoning behind each. Summary:

  1. UI pause window shrunk — HTTP session kept alive between polls, timeout drops 30s→10s once warm. Measured on hardware: fetch went from ~1.3s to ~80ms, so lvgl_port_pause() no longer blocks rendering and touch for a meaningful period.
  2. Stale readings indicated — temperature dims and the status line shows age after two consecutive failures.
  3. Touch readout gated behind UI_DEBUG_TOUCH, default off.
  4. main/utils deleted — no callers.
  5. OTA partitions laid out — two 4MB slots, otadata, ~7.9MB unallocated.
  6. Cleanups — truncation logged once per response; weather task stack high-water mark measured (~2400 bytes spare of 6144).

Verified on hardware after the change: boots from the new OTA layout, three consecutive successful polls at 60s, no reboots.

Still open

1. Confirm the left-edge artefacts are actually gone

The pause window is now ~80ms instead of ~1.3s, which should make the artefacts much rarer — but this has not been confirmed by eye. The timing improvement was measured from the log; the artefacts can only be verified by watching the panel around the moment the status line updates.

If they persist, the remaining lever is pausing only around LVGL's flush (the actual PSRAM writes) rather than the whole network transaction. That is more invasive; do not start it until the current change has been observed to be insufficient.

2. OTA implementation

Partition layout exists; the download/apply logic does not. This is not a drop-in. An OTA write is a flash write, and the bounce-buffer refill ISR reads PSRAM, which shares the cache with flash on the S3 — downloading while the panel scans will fault it with "Cache disabled but cached memory region accessed". The implementation must stop the panel for the download and write, or reboot into a dedicated updater mode. See README "Partitions".

3. Backlight control (optional feature)

backlight_set() is only ever called once, with 255. A night-dim schedule or touch-to-wake is a natural small feature. Note the STC8H1K28 shares the I2C bus with the GT911, so brightness writes must stay in task context (never an ISR) and will contend with touch polls on the same bus.

4. The actual thermostat

Setpoint, HVAC relay control, scheduling and persistence are all still unimplemented — this remains a weather display. Note a separate project (~/workspace/rotaryThermo) has a PRD for building this on the CrowPanel 2.1" rotary display instead, driven by Zigbee room sensors. Decide which board hosts the thermostat before building it twice.

Facts already established — do not re-investigate

  • Build is clean on ESP-IDF v5.3 (. ~/esp/esp-idf/export.sh && idf.py build). App is ~1.4MB in a 4MB OTA slot.
  • main/app_config.h is gitignored and holds real credentials locally.
  • resp_buf bounds/NUL-termination in ambient_weather.c is correct.
  • GT911 read-then-clear-status ordering is correct.
  • Backlight duty inversion maps correctly at both endpoints (255→0x00 full, 0→0xF5 off).
  • The ° glyph exists in LVGL's built-in Montserrat fonts; the · separator that does not appears only in log strings, never on screen.
  • LVGL recursive-mutex locking is consistent across all UI entry points.
  • Init order in main.c is deliberate and load-bearing: WiFi/NVS before display_init(), backlight_init() before touch_init(). Never reorder.
  • The bounce buffer is enabled and load-bearing.
  • Board is N16R8 (16MB flash / 8MB octal PSRAM).
  • touch_get_indev(), display_get_panel() and lvgl_port_is_paused() are uncalled but deliberately kept as driver API surface.