8485ecb5b7
play/pause/toggle/stop/next/prev. Spotify commands run on the session task (same thread as cspot's frame handling) and the POST waits until they ran, so it answers the new state. HLS pause stops fetching and resume rejoins at the live edge; next/prev on HLS and anything on tone/off return 409. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
5.1 KiB
Markdown
48 lines
5.1 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 tone (mode "tone") is 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|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
|
||
- mode "tone": the core's 1 kHz / -18 dBFS test tone, phase-locked to PTP (commissioning, e.g. Riedel import tests). "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.
|