Commit Graph
3 Commits
Author SHA1 Message Date
tfaour 51fbd0657c Never physically refresh the panel with a short/wrong-size frame
Found on hardware: the Tools Server field was pointed at Immich's own
port instead of the frame server's, so /frame/image was actually hitting
Immich and getting back a small error response (~10KB) instead of a
192,000-byte frame. epd_display_stream() logged a size-mismatch warning
but called epd_turn_on_display() anyway, physically refreshing the panel
with a buffer that was ~95% whatever was left over from before -- visible
as "garbage" on screen, overwriting a previously-good image.

epd_display_stream() now returns ESP_ERR_INVALID_SIZE instead of
refreshing when the stream doesn't supply exactly EPD_FRAME_BYTES. Since
this check happens before epd_turn_on_display() is ever called, the
pixel data that *did* arrive only ever reached the panel's internal RAM
over SPI, not the physically visible display, so aborting here leaves the
screen exactly as it was.

This also means fetch_and_display() failing now always implies the panel
was never touched -- simplified frame_client_run() accordingly (dropped
the now-always-true/false out-param that used to distinguish "failed
before vs. during streaming", and always shows the FAILED status screen
on any fetch/display error, since it's now guaranteed safe to do so).
2026-07-18 16:38:43 -04:00
tfaour 1b9226326a Fix hardware-verified bugs: EPD stack overflow and busy-wait spin
Two crashes found flashing to real hardware:

- epd_display_stream's 4KB SPI chunk buffer was a stack local, but the
  default main task stack (3584 bytes) is smaller than that alone --
  Guru Meditation stack protection fault. Made it static instead, and
  bumped CONFIG_ESP_MAIN_TASK_STACK_SIZE to 8192 for headroom in the rest
  of the boot call chain (provisioning -> QR render -> eventually the
  HTTP fetch cycle all run in this one task).

- epd_wait_busy() polled with a 1ms vTaskDelay, which rounds down to 0
  FreeRTOS ticks at the default 100Hz tick rate -- so it never actually
  blocked, tight-spinning the CPU for the panel's real refresh time
  (15-30+s for a full-color pass) and starving the idle task long enough
  to trip the 5s task watchdog. Bumped to 20ms, safely >=1 tick regardless
  of tick rate.

Also updates the EPD pin defaults to the board's actual wiring
(CLK=20 MOSI=19 CS=18 DC=9 RST=10 BUSY=11), confirmed working on hardware.
2026-07-18 14:06:54 -04:00
tfaour 85a5238724 Add epd7in3e display driver component
Ports Waveshare's official EPD_7in3e.c register/refresh sequence (the
panel has no public datasheet, so their reference driver is the source of
truth) to an ESP-IDF component using spi_master + gpio instead of the
bcm2835/RPi hardware abstraction the reference targets.

Unlike the reference driver, which toggles CS around every single byte,
this holds CS low for each logical command/data phase and DMAs pixel data
in 4KB chunks -- sending ~192,000 individual one-byte SPI transactions
would make a full refresh impractically slow.

Exposes a streaming API (epd_display_stream, pulling chunks from a
caller-supplied read_fn) so the eventual HTTP fetch path can feed the panel
without holding a full ~192KB frame in RAM, plus an in-memory convenience
wrapper (epd_display_buffer) for cases like the upcoming QR code screen
where buffering the whole frame is fine.

Wired into wifi_provisioning_start() as an init + white-clear for now, to
prove the driver builds/links/runs at the right point in the boot sequence
ahead of the actual QR code content.
2026-07-18 11:05:06 -04:00