Files
aes67-ESP32-P4/docs/hardware-and-design-notes.md
T
bsncubed bbfd3506bf Step 0: IDF project skeleton for esp32p4 with core component stubs
- Board checked with esptool: ESP32-P4 rev v1.3 (engineering silicon),
  32 MB GigaDevice flash. Rev <3 support and 32 MB set in sdkconfig.defaults.
- OTA partition layout: nvs, otadata, phy_init, ota_0/ota_1 (6 MB each,
  below 16 MB), coredump; no factory app. Partition table at 0x10000 so
  the bootloader can grow.
- App rollback enabled in the bootloader from the start.
- PROJECT_VER from git describe --tags --always --dirty.
- Empty stubs for the nine aes67_* core components; main logs version
  and chip revision.
- Built with ESP-IDF v5.5.5; not yet flashed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 22:47:21 +10:00

4.0 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|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

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.