Objective 1 — screen: full-scene faithful rendering on 320x240 LVGL display.
Palette matches idf_zacus display_ui reference: bg #0055AA (Workbench blue),
symbol #FF8800 (orange / font-48), title #FFFFFF (font-24 top), subtitle
#AAAAAA (font-14 bottom). All four effects implemented:
- pulse: LVGL anim opacity COVER→50 breathing on symbol (600ms half-period)
- glitch: lv_timer 120ms flicker + X-jitter on title
- gyro: lv_arc rotating ring around symbol (1200ms full rotation)
- none: static
Objective 2 — play: real WAV PCM-16 decode + I2S streaming.
- SD card mounted best-effort via bsp_sdcard_mount() on first play CMD
- WAV header parser (chunk-walker, tolerant of non-standard ordering)
- PCM samples streamed via i2s_channel_write (same s_spk_handle as TTS)
- Embedded test cue (C5-E5-G5, 570ms, 16kHz mono, ~18 KB) baked into
embedded_wav.h — proves real WAV decode + I2S path without SD card
- Graceful fallback chain: SD file → embedded cue → 880 Hz beep
main.c: cmd_exec_set_spk_handle() called after speaker_init() to pass the
I2S TX channel handle to the CMD executor.
Tested on ESP32-S3-BOX-3 (/dev/cu.usbmodem11301):
- All four screen effects confirmed via serial log + visual observation
- play embedded://cue streams 18240 bytes PCM at 16kHz confirmed
- SD mount succeeds (card present), file not found → embedded fallback OK
- No crashes, existing voice pipeline and scenario server unaffected