Files
aes67-ESP32-P4/docs/hardware-and-design-notes.md
T
bsncubed d3897f3179 Step 7.1: player plumbing (PSRAM ring -> AES67 TX), test tone mode
- main/audio_ring: SPSC ring of interleaved int32 frames in PSRAM
  (4 s at 48 kHz stereo), lock-free with acquire/release counters; the
  TX pull callback never blocks.
- main/player: source selection from source.mode and the pull callback
  for aes67_tx. tone -> the core's PTP-phased 1 kHz tone; off -> silence;
  hls/spotify -> ring with 1 s prefill (silence while buffering is not an
  underrun; running dry is, and prefills again). Status fields
  active_source, source_state, spotify_state, buffer_ms. source config
  applies live.
- New source.mode "tone" (validation, UI dropdown, doc) for commissioning.
- Temporary 440 Hz producer in hls mode (until the HLS player exists).
- Verified: tone 999.7 Hz -18 dBFS; off silent; hls 440.0 Hz with max
  sample step 60816 (ideal sine 60825, i.e. no discontinuities), buffer
  3983 ms, 0 underruns; spotify silent/not implemented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:36:15 +10:00

4.2 KiB
Raw Blame History

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 -> volume/gain -> PSRAM ring buffer -> aes67_tx pull callback

  • 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}
  • 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). On HLS (live): pause = stop fetching, resume = rejoin at live edge; next/prev/seek return 409 and can reflects that.
  • Volume = runtime player volume; source.gain_db is a fixed trim on top.
  • No auth yet: LAN only.