Adds runtime speaker-sync control to the relay:
- src/relay_config.h: shared Wi-Fi AP creds + ESP-NOW RelayDelayMsg
(delay_ms[JBL], delay_ms[Cardo]) on a fixed channel.
- board_sink.cpp (Board A): hosts a Wi-Fi AP "BikeAudio-Setup" + a small
web UI (two sliders, no app) at 192.168.4.1, and broadcasts the chosen
per-speaker delays to the source boards over ESP-NOW. Still A2DP sink +
I2S master. (Tune while parked — Wi-Fi/BT coexist on one radio.)
- board_source.cpp (Boards B/C): inserts an adjustable delay line between
I2S in and A2DP out — a dedicated reader task fills a ring buffer (up to
~250 ms) and the A2DP callback reads delay_frames behind the write head.
Delay arrives via ESP-NOW (per SPEAKER_ID) and is persisted to flash
(Preferences), so it survives power cycles.
- platformio.ini: source envs get -DSPEAKER_ID (0=JBL, 1=Cardo).
Lets the rider trim JBL vs Cardo timing to sync the two speakers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>