66e72cebcc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
5.4 KiB
Markdown
48 lines
5.4 KiB
Markdown
# ESP32-P4-ETH AES67 streamer — hardware & project notes
|
||
|
||
Core AES67 parts (PTP, TX, SDP/SAP, network/VLAN, syslog, web UI base, config schema, component layout) are in **aes67-core-base.md**. This file covers the board and the project-specific parts.
|
||
|
||
## Board: Waveshare ESP32-P4-ETH (bought as ESP32-P4-POE-ETH, i.e. with PoE add-on module)
|
||
- ESP32-P4, dual-core RISC-V HP @ 360 MHz + LP core, 32 MB in-package PSRAM, 32 MB NOR flash (confirmed with `esptool.py flash_id`)
|
||
- **No Wi-Fi/BT** on this variant (the -WIFI6- variant adds an ESP32-C6). Ethernet only.
|
||
- Ethernet: IP101GRI PHY, RMII, 100 Mb, 50 MHz ref clock from PHY (25 MHz xtal), PHY addr 1
|
||
- REF_CLK 50 (EMAC_CLK_EXT_IN), TX_EN 49, TXD0 34, TXD1 35, CRS_DV 28, RXD0 29, RXD1 30, MDC 31, MDIO 52, PHY_RST/power 51
|
||
- Audio: ES8311 codec (I2C 0x18), NS4150B amp (PA enable GPIO53), onboard mic
|
||
- I2S MCLK 13, BCLK 12, LRCK 10, DOUT(to codec) 9, DIN(from codec) 11 — Waveshare wiki lists 9/11 swapped vs ESPHome/espp; verify
|
||
- I2C SDA 7, SCL 8 (shared with DSI touch / CSI SCCB)
|
||
- Also: MIPI-CSI + MIPI-DSI FPC, USB 2.0 HS OTG (4-pin), USB-C UART, microSD 4-bit (CLK43 CMD44 D0-D3 39-42), 2x20 header
|
||
- ESP-IDF >= 5.3.1 per Waveshare.
|
||
- **Chip revision caveat** (buyer review): some boards ship engineering silicon v1.x (not v3.x). IDF 5.5+ defaults to rev3 — enable `CONFIG_ESP32P4_SELECTS_REV_LESS_V3` (check `esptool.py chip_id` output first). Binaries are not interchangeable between rev <3 and rev 3.
|
||
- Crystal-based clock: fine as PTP GM on a closed network (±tens of ppm).
|
||
- Sources: Waveshare wiki (waveshare.com/wiki/ESP32-P4-ETH), ESPHome device page (devices.esphome.io/devices/waveshare-esp32-p4-eth), espp board docs (esp-cpp.github.io/espp/dev_boards/waveshare/esp32_p4_eth.html).
|
||
|
||
## Project pipeline
|
||
source (cspot Spotify Connect | HLS player) -> decode (Vorbis/AAC/MP3) -> SRC 44.1k->48k -> PSRAM ring buffer -> volume/gain -> aes67_tx pull callback
|
||
- Volume is applied after the ring (in the pull callback) so a change is heard at once, not one ring length (4 s) later; ramped over one packet to avoid zipper noise. Curve: amplitude = (volume/100)^3 (about -18 dB at 50 %, 0 = mute). The built-in test signals (modes "tone", "pink") are not affected.
|
||
- Spotify Connect: cspot (feelfreelinux/cspot), needs Premium, zeroconf via mDNS (on the internet interface when VLAN split). Spotify occasionally changes auth for 3rd-party clients — expect breakage risk.
|
||
- HLS: HTTPS (mbedTLS), m3u8 parse (master->media playlist), segment fetch, TS demux or fMP4/ADTS, AAC decode (esp_audio_codec / esp-gmf).
|
||
- Stereo L24/48k at 1 ms is about 3 Mbit/s on the wire; 100 Mb has plenty of headroom.
|
||
|
||
## Source mode "auto" (Spotify with HLS failover)
|
||
- Spotify Connect stays advertised the whole time. The HLS player runs whenever Spotify is not active.
|
||
- Fail over to HLS when there is no Spotify session for `failover_delay_s` seconds. With `failover_on_pause`, a paused or stopped Spotify session also counts as inactive.
|
||
- Switch back to Spotify as soon as it starts playing. Stop HLS so it doesn't use bandwidth or CPU.
|
||
- Short crossfade or ramp (~20–50 ms) at the switch to avoid clicks. The AES67 stream and SDP are unaffected.
|
||
- Firmware needs Spotify events: cspot connect, disconnect, play, pause.
|
||
|
||
## Project config group
|
||
- source: {mode: spotify|hls|auto|tone|pink|off, spotify_name, spotify_bitrate, hls_url, autoplay, gain_db, failover_delay_s, failover_on_pause, spotify_client_id, spotify_client_secret}
|
||
- spotify_client_id / spotify_client_secret: each user's own Spotify developer app (developer.spotify.com, Premium account), needed by the maintained cspot fork (philippe44/cspot) since Spotify's 2025 API restrictions. The secret is write-only (never returned by GET /api/config). spotify_state reports "no client credentials" while either is missing.
|
||
- Project status fields: active_source, source_state, spotify_state, buffer_ms; spotify_error (last session error with the uptime it happened at, e.g. "Can't connect to spotify servers (at 412 s)"; only present after one)
|
||
- mode "tone": the core's 1 kHz / -18 dBFS test tone, phase-locked to PTP (commissioning, e.g. Riedel import tests). "pink": the core's pink noise, -18 dBFS RMS (peaks about -6 dBFS), same on L and R (level/EQ checks). Neither is affected by volume or gain. "off": silence.
|
||
|
||
## Player control API (project routes)
|
||
- GET /api/player -> {source, forced, state(playing|paused|stopped|buffering|idle), artist, title, album, position_ms, duration_ms, volume(0-100), can:{pause,next,prev,seek}}
|
||
- POST /api/player/{play|pause|toggle|stop|next|prev} (no body); every POST returns the same JSON as GET /api/player
|
||
- POST /api/player/seek {ms}; POST /api/player/volume {value} or {delta}
|
||
- POST /api/player/source {source: spotify|hls|off|config}: runtime override, not saved; "config" returns to the configured mode
|
||
- POST /api/player/url {url}: play an m3u8 now (runtime, not saved)
|
||
- On Spotify, commands go to cspot (the Spotify app stays in sync, including volume). stop = pause (the session stays). prev restarts the track after its first 3 s (cspot, like the apps). After next/prev the response can still show the old track; the new one follows within ~1 s. On HLS (live): pause/stop = stop fetching, resume = rejoin at live edge (a pause lasts until the next source switch); next/prev/seek return 409 and `can` reflects that. Tone/off, or Spotify without a session: transport returns 409.
|
||
- Volume = runtime player volume; source.gain_db is a fixed trim on top.
|
||
- No auth yet: LAN only.
|