Naar inhoud springen

Gebruiker:Stitch/Snap

Uit Hack42

Omdat LouderESP32Plus op dit moment niet werkt moet je e.e.a. doen. Hier zijn de instructies voor je LLM:


LouderESP32Plus / Snapcast handoff

==================================

Last verified: 2026-08-29 (Europe/Amsterdam)

Workspace: /Users/stitch/louder

1. Final result

---------------

Five LouderESP32Plus boards now run a locally patched native

esparagus-snapclient v0.0.4 firmware. They no longer run ESPHome. All five

connect to the Snapserver at 192.168.42.50 and play the workstation user's

desktop audio in sync.

The important final settings are:

* Native esparagus-snapclient, not the ESPHome Snapclient component.

* Audio: FLAC, 44100 Hz, signed 16-bit stereo.

* Snapserver buffer: 250 ms.

* Snapserver FLAC chunk size: 40 ms.

* Desktop capture blocks: 20 ms.

* A permanent -14 dB TAS58xx DSP channel-gain cap is applied independently to

  the left and right outputs during every boot.

* Snapserver retains an independent volume and mute value for each board. The

  values are expected to change when users move the volume controls; they are

  not firmware constants.

* Snapserver and desktop capture start automatically after server boot.

* The normal workstation audio devices were not deleted or replaced. The

  server captures the monitor of bar's current default desktop output.

The native firmware is materially more reliable than the ESPHome Snapclient

implementation tested before it. Keep the native design unless the ESPHome

audio implementation is substantially changed upstream.

2. Device inventory and stable identities

-----------------------------------------

Unit  Hostname             Wi-Fi MAC          Last known IP

----  -------------------  -----------------  --------------

1     LouderESP32Plus1     B0:CB:D8:8D:36:98  192.168.42.205

2     LouderESP32Plus2     80:F3:DA:C6:FF:D8  192.168.42.171

3     LouderESP32Plus3     B0:CB:D8:8D:5E:90  192.168.42.177

4     LouderESP32Plus4     B0:CB:D8:8D:39:6C  192.168.42.23

5     LouderESP32Plus5     B0:CB:D8:8D:65:34  192.168.42.202

Snapserver identifies clients by MAC. DHCP addresses may change; the MAC and

the CONFIG_SNAPCLIENT_NAME compiled into each board are the important values.

WARNING: the working sdkconfig and most recent build artifact currently say

LouderESP32Plus5. Do not flash that application binary onto another unit

without changing CONFIG_SNAPCLIENT_NAME and rebuilding it. A previous mistaken

flash made physical unit 1 identify as unit 5, creating two sessions with one

Snapcast identity and misleading the diagnosis.

3. Source trees and repository state

------------------------------------

Outer firmware repository:

  /Users/stitch/louder/esparagus-snapclient

  origin: https://github.com/sonocotta/esparagus-snapclient.git

  base revision: 82f7743 (Features/fix tas58xx boot loop (#23))

Firmware submodule containing the substantive patches:

  /Users/stitch/louder/esparagus-snapclient/snapclient

  origin: https://github.com/CarlosDerSeher/snapclient

  base revision: 5cda3a75ed97572868a4303e35ffcf88244b6105 (v0.0.4)

The changes are intentionally still uncommitted. Do not run git reset, clean,

submodule update, or replace these directories without first saving the diff.

Show all relevant changes with:

  git -C /Users/stitch/louder/esparagus-snapclient diff

  git -C /Users/stitch/louder/esparagus-snapclient/snapclient diff

  git -C /Users/stitch/louder/esparagus-snapclient/snapclient status --short

The submodule build directories are untracked:

  snapclient/build-louder-esp32-plus-fixed/

  snapclient/build-louder-esp32-plus-fixed-5.5.1/

The authoritative build is the latter, made with ESP-IDF 5.5.1.

4. Native firmware patches: what changed and why

------------------------------------------------

4.1 Correct TAS5805M/TAS5825M I2C address

File:

  snapclient/components/custom_board/tas5805m/include/tas5805m_reg_cfg.h

Changed:

  #define TAS5805M_ADDRESS 0x2D

To:

  #define TAS5805M_ADDRESS CONFIG_DAC_I2C_ADDR

The LouderESP32Plus profile sets CONFIG_DAC_I2C_ADDR=0x4C. The driver had

ignored the board profile and always attempted 0x2D, so the DAC could not be

configured correctly. Using the Kconfig value also avoids hard-coding this

board's address into the generic driver.

4.2 Make DAC initialization fail cleanly instead of aborting

File:

  snapclient/components/custom_board/tas5805m/tas5805m.c

The HI-Z transition previously used:

  ESP_ERROR_CHECK(tas5805m_set_state(TAS5805M_CTRL_HI_Z));

It now saves the return code, logs esp_err_to_name(ret), and returns the error.

The muted PLAY transition likewise assigns ret before checking it:

  ret = tas5805m_set_state(TAS5805M_CTRL_MUTE | TAS5805M_CTRL_PLAY);

Why: an I2C/DAC error must not turn into an unexplained ESP_ERROR_CHECK abort

and boot loop. Audio is kept muted while the device is initialized.

4.3 Permanent -14 dB left/right hardware safety cap

File:

  snapclient/components/custom_board/tas5805m/tas5805m.c

Immediately after entering muted PLAY, initialization now does:

  ret = tas5805m_set_channel_gain(TAS5805M_EQ_CHANNELS_LEFT, -14);

  if (ret == ESP_OK) {

    ret = tas5805m_set_channel_gain(TAS5805M_EQ_CHANNELS_RIGHT, -14);

  }

  if (ret != ESP_OK) {

    ESP_LOGE(TAG, "%s: Failed to apply -14 dB channel safety cap: %s",

             __func__, esp_err_to_name(ret));

    return ret;

  }

The client logs "Left and right channel safety cap set to -14 dB" on success.

Why: the amplifiers and speakers can be painfully loud. During the earlier

ESPHome investigation one unit emitted a very loud high-frequency sound. This

cap is below and independent of Snapserver's ordinary 0-100% volume control,

so a Snapserver/UI value of 100% is still capped by the DSP. Do not remove this

without re-evaluating the installation's acoustic safety.

4.4 Increase TAS58xx fault-monitor stack

File:

  snapclient/components/custom_board/tas5805m/tas5805m.c

Changed the tas5805m_faults FreeRTOS task stack from 2048 to 3072 bytes.

Why: the original stack was marginal for fault decoding/logging and could

contribute to instability. The board has PSRAM and the extra 1 KiB is cheap.

Clock-fault messages at startup can be transient while I2S is not yet running;

the task clears them.

4.5 Safer fallback volume

File:

  snapclient/components/audio_hal/include/audio_hal.h

Changed:

  #define AUDIO_HAL_VOL_DEFAULT 70

To:

  #define AUDIO_HAL_VOL_DEFAULT 50

Why: if no stored Snapserver state has arrived yet, the fallback must not start

at 70%. Snapserver subsequently sends and restores the per-client value.

4.6 Correct mDNS IPv4 address selection

File:

  snapclient/main/connection_handler.c

The old code inspected only the first address attached to each mDNS result.

The Snapserver advertises multiple addresses and commonly returns IPv6 entries

before its IPv4 entry. This build has CONFIG_SNAPCLIENT_CONNECT_IPV6 disabled,

so the old code could report that no valid IP existed even though

192.168.42.50 was present later in the same address list.

The new code walks every mdns_result_t and every mdns_ip_addr_t in each result,

selects an address matching the configured family, then copies the address,

network interface, and port from that selected result. This reliably selects

192.168.42.50:1704.

4.7 Existing upstream safety behavior relied upon

The current upstream player code primes I2S with silence before enabling audio.

That behavior is visible as "Priming I2S with silence" in the boot log. It was

not part of the local git diff, but it is an important reason to remain on the

current submodule revision or later code that preserves this behavior.

5. LouderESP32Plus build configuration

--------------------------------------

Configuration file:

  /Users/stitch/louder/esparagus-snapclient/configs/sdkconfig.louder-esp32-plus

Important values:

  CONFIG_SNAPCLIENT_NAME="LouderESP32PlusN"  (change N for each board)

  CONFIG_SNAPCLIENT_USE_TIMEFILTER=y

  CONFIG_SNAPSERVER_USE_MDNS=y

  CONFIG_SNAPCLIENT_CONNECT_IPV6 is disabled

  CONFIG_WEB_PORT=80

  CONFIG_AUDIO_BOARD_CUSTOM=y

  CONFIG_DAC_TAS5805M=y

  CONFIG_DAC_I2C_SDA=21

  CONFIG_DAC_I2C_SCL=27

  CONFIG_DAC_I2C_ADDR=0x4C

  CONFIG_MASTER_I2S_MCLK_PIN=0

  CONFIG_MASTER_I2S_BCK_PIN=26

  CONFIG_MASTER_I2S_LRCK_PIN=25

  CONFIG_MASTER_I2S_DATAOUT_PIN=22

  CONFIG_PIN_DAC_PWDN=33

  CONFIG_DAC_TAS5805M_EQ_SUPPORT=y

  CONFIG_USE_DSP_PROCESSOR=y

  CONFIG_SNAPCLIENT_USE_SOFT_VOL is disabled (hardware DAC volume is used)

  CONFIG_SPIRAM=y, quad/80 MHz

  CONFIG_FREERTOS_HZ=1000

The image header and partition table use 4 MB even though boot logs detect an

8 MB flash chip. The warning about detected 8 MB versus configured 4 MB is

expected. The OTA layout in partitions.csv is:

  nvs       80 KiB

  otadata    8 KiB

  phy_init   4 KiB

  ota_0   1984 KiB

  ota_1   1984 KiB

6. Rebuilding a device image

----------------------------

Use ESP-IDF 5.5.1. Before each build, change CONFIG_SNAPCLIENT_NAME in

configs/sdkconfig.louder-esp32-plus to the physical target's correct name.

Using apply_patch for this edit makes the identity change visible and auditable.

A reproducible Docker build from the outer repository is:

  cd /Users/stitch/louder/esparagus-snapclient

  docker run --rm \

    -v "$PWD/snapclient:/project" \

    -v "$PWD/configs:/project/configs:ro" \

    -w /project \

    espressif/idf:v5.5.1 \

    /bin/bash -lc \

    '. /opt/esp/idf/export.sh && \

     cp configs/sdkconfig.louder-esp32-plus sdkconfig && \

     idf.py -B build-louder-esp32-plus-fixed-5.5.1 reconfigure build'

Do not use build-local.sh unchanged: it still names the older v5.1.1 Docker

image in its script body. The VS Code task and GitHub workflow use v5.5.1.

Current output directory:

  /Users/stitch/louder/esparagus-snapclient/snapclient/

    build-louder-esp32-plus-fixed-5.5.1/

The most recent snapclient.bin is for LouderESP32Plus5. Its recorded SHA-256 at

handoff time is:

  ac52c34e11e0d648a9fe021ef00b1616c0a51ecc2f7ba39332efeeeb5d69cfa7

Other current image hashes:

  bootloader.bin:

    c9831717eb2b8eb3058cf7e3a74521448acde764a91fc7f0dd27087e7a685428

  partition-table.bin:

    f8a731f7f8f59c3826ea978062eedc49f822465de138718c794b5d7fce18abcf

  ota_data_initial.bin:

    7d2c7ac4888bfd75cd5f56e8d61f69595121183afc81556c876732fd3782c62f

7. Flashing, OTA, provisioning, and serial diagnostics

------------------------------------------------------

7.1 Full USB recovery

Use this when replacing ESPHome, recovering an invalid partition layout, or

when OTA cannot be trusted. Determine the actual port first; during this work

it was usually /dev/cu.usbserial-10.

Erase the physical target, then write all four files at the offsets recorded

by flasher_args.json:

  0x1000   bootloader/bootloader.bin

  0x8000   partition_table/partition-table.bin

  0x1d000  ota_data_initial.bin

  0x20000  snapclient.bin

Flash parameters:

  chip esp32, mode dio, frequency 80m, size 4MB

Equivalent esptool structure (supply the correct port and build paths):

  esptool --chip esp32 --port /dev/cu.usbserial-XX erase-flash

  esptool --chip esp32 --port /dev/cu.usbserial-XX write-flash \

    --flash-mode dio --flash-freq 80m --flash-size 4MB \

    0x1000  build-louder-esp32-plus-fixed-5.5.1/bootloader/bootloader.bin \

    0x8000  build-louder-esp32-plus-fixed-5.5.1/partition_table/partition-table.bin \

    0x1d000 build-louder-esp32-plus-fixed-5.5.1/ota_data_initial.bin \

    0x20000 build-louder-esp32-plus-fixed-5.5.1/snapclient.bin

Erasing removes Wi-Fi credentials. Provision Wi-Fi afterward using Improv over

USB. The easiest durable route is https://web.esphome.io/ in Chrome/Edge:

connect to the serial device and use Configure Wi-Fi. It works because the

native firmware implements Improv; it does not turn the board into ESPHome.

The network credentials are already documented in README.md and secrets.yaml.

An automated Improv helper was used from /tmp/provision_improv_serial.py, but

/tmp is ephemeral and must not be considered part of the durable setup.

7.2 OTA update

For a board already running this native OTA partition layout, only the new

application image is required:

  curl DEVICE_IP:8032 --data-binary @- \

    < build-louder-esp32-plus-fixed-5.5.1/snapclient.bin

Build separately for each hostname before sending. Do not send an image named

for unit 5 to units 1-4. If the partition layout is uncertain, use full USB

recovery instead.

7.3 Useful boot verification

Serial is 115200 baud. A correct boot should show all of these:

  Device hostname: LouderESP32PlusN

  Left and right channel safety cap set to -14 dB

  Found 192.168.42.50:1704

  Buffer length: 250

  Mute: 0                         (assuming currently unmuted in Snapserver)

  Setting volume: <stored value>

  fLaC sampleformat: 44100:16:2

  created new queue with 9        (observed at 250 ms)

  Priming I2S with silence

Check the physical Wi-Fi MAC printed in the boot log against the inventory

before accepting a flash as complete.

8. Snapserver host and installed services

-----------------------------------------

Host: 192.168.42.50

OS: openSUSE Leap 16.0

Snapserver: v0.35.0, revision f1237347

Binary: /usr/local/bin/snapserver

Snapweb files: /usr/local/share/snapserver/snapweb

Service account: snapserver (UID/GID 1003, home /var/lib/snapserver)

Desktop account being captured: bar (UID 1000)

Credentials and SSH access are recorded in README.md. Do not copy them into

additional files unnecessarily.

Snapserver was installed under /usr/local from a staged v0.35.0 binary and

Snapweb file set; it is not an RPM-managed /usr binary. The temporary staging

directory no longer exists. The current installed files and the configuration

below are therefore the recovery authority.

Services:

  /etc/systemd/system/snapserver.service

  /etc/systemd/system/snapserver-desktop-capture.service

Both are enabled and active. snapserver.service runs as snapserver, creates

/run/snapserver with group-writable mode 0770, and starts:

  /usr/local/bin/snapserver --config /etc/snapserver.conf

The capture service runs as User=bar, Group=snapserver and sets:

  XDG_RUNTIME_DIR=/run/user/1000

  PULSE_SERVER=unix:/run/user/1000/pulse/native

It requires and starts after snapserver.service, restarts every three seconds

on failure, and executes:

  /usr/local/libexec/snapserver-desktop-capture

That helper waits for both the user's Pulse socket and Snapserver's FIFO, then

runs:

  /usr/bin/parec \

    --device=@DEFAULT_MONITOR@ \

    --client-name=Snapserver \

    --stream-name="Snapserver desktop capture" \

    --format=s16le \

    --rate=44100 \

    --channels=2 \

    --latency-msec=20 \

    --process-time-msec=20 \

    --raw \

    > /run/snapserver/desktop.pcm

Using @DEFAULT_MONITOR@ is deliberate: browser/desktop audio played by bar is

captured while all existing physical audio devices remain available normally.

9. Current /etc/snapserver.conf essentials

------------------------------------------

  [server]

  user = snapserver

  group = snapserver

  datadir = /var/lib/snapserver

  mdns_enabled = true

  [http]

  enabled = true

  bind_to_address = 0.0.0.0

  port = 1780

  publish_http = true

  doc_root = /usr/local/share/snapserver/snapweb

  [tcp-control]

  enabled = true

  bind_to_address = 0.0.0.0

  port = 1705

  publish = true

  [tcp-streaming]

  enabled = true

  bind_to_address = 0.0.0.0

  port = 1704

  publish = true

  [stream]

  source = pipe:///run/snapserver/desktop.pcm?name=Desktop&mode=create&sampleformat=44100:16:2&codec=flac&chunk_ms=40

  default_source = Desktop

  sampleformat = 44100:16:2

  codec = flac

  chunk_ms = 40

  buffer = 250

  [logging]

  sink = system

  filter = *:info

Important server-side timing corrections were the use of small, regular 20 ms

parec capture/process blocks and explicit 40 ms FLAC chunks. These prevent

large/bursty writes into the FIFO. The stream and capture sample formats must

remain identical.

Configuration backups currently exist as /etc/snapserver.conf.before-* and

/etc/snapserver.conf.codex-before-*. Inspect them before using one; several are

experimental and are not all known-good final configurations.

Useful checks on the server:

  systemctl is-enabled snapserver.service snapserver-desktop-capture.service

  systemctl is-active snapserver.service snapserver-desktop-capture.service

  systemctl status snapserver.service snapserver-desktop-capture.service

  journalctl -u snapserver.service -u snapserver-desktop-capture.service

  ps -eo user,group,pid,args | grep -E '[s]napserver|[p]arec'

10. Control interfaces and Home Assistant

-----------------------------------------

Snapcast ports:

  1704  client audio stream

  1705  TCP control API

  1780  Snapweb HTTP UI and JSON-RPC endpoint

Central Snapweb:

  http://192.168.42.50:1780/

Client web interfaces:

  http://192.168.42.205/  (unit 1)

  http://192.168.42.171/  (unit 2)

  http://192.168.42.177/  (unit 3)

  http://192.168.42.23/   (unit 4)

  http://192.168.42.202/  (unit 5)

The native client pages expose controls including volume, DSP/EQ, DAC settings,

diagnostics, and restart. The central Snapserver is still the preferred place

for ordinary volume control because it contains and persists all five clients.

Snapweb uses Snapserver's Client.SetVolume JSON-RPC method. Read the complete

state without changing anything with:

  curl -fsS -H 'Content-Type: application/json' \

    -d '{"id":1,"jsonrpc":"2.0","method":"Server.GetStatus"}' \

    http://192.168.42.50:1780/jsonrpc

Home Assistant already has a built-in Snapcast integration; no custom webpage

scraper is needed. Add the Snapcast integration with:

  host: 192.168.42.50

  port: 1705

It creates one media_player entity per Snapclient with volume, mute, source,

and grouping controls. The integration talks to the central control API and is

bidirectional/local-push. The permanent -14 dB hardware safety cap remains in

force regardless of Home Assistant's volume setting.

11. Failed approaches and diagnostic conclusions

-------------------------------------------------

11.1 ESPHome Snapclient firmware

Device-specific experimental wrappers remain under:

  /Users/stitch/louder/esp32-audio-dock/firmware/esphome/

    7-louder-esp32-plus/

They include esphome-web-8d3698.yaml and louderesp32plus2.yaml through

louderesp32plus5.yaml. These are historical/diagnostic artifacts, not the

deployed firmware.

The ESPHome clients stuttered even after all five had unique identities, strong

Wi-Fi, healthy memory, 44.1 kHz FLAC, and a 2000 ms buffer. Power supplies and

speakers were swapped between good and bad units without moving the fault.

Later all ESPHome units stuttered, including unit 2. ESPHome loop-time samples

were roughly 74-88 ms, while its playback code used sample insertion and a

2 ms hard-resynchronization threshold. The client implementation/timing was the

leading cause, not amplifier load, power supply, speaker impedance, Wi-Fi,

Snapserver bandwidth, or Home Assistant.

Do not spend another session retesting power supplies and speakers unless new

evidence appears.

11.2 Buffer experiments

The native clients worked without the ESPHome stutter at the 250 ms setting.

A requested 100 ms server buffer was tested with both 40 ms and 20 ms FLAC

chunk settings. Unit 5 had excellent RSSI (about -38 to -43 dBm), but at 100 ms

the firmware created only a three-entry PCM queue and immediately entered a

continuous RESYNCING HARD loop with the queue empty. Reducing chunk_ms to 20 did

not enlarge that queue; decoded FLAC chunks remained about 26.1 ms.

The server was restored to buffer=250 and chunk_ms=40. Unit 5 then received

"Buffer length: 250" and created a nine-entry queue. Do not return to 100 ms

without first changing and understanding the native client's queue/synchronizer

behavior. If lower latency is revisited, test 200 ms and then 150 ms on one

physical canary at safe volume, with a USB serial trace, before fleet rollout.

11.3 Loud-noise incident and safety rule

During the earlier five-client ESPHome investigation, unit 3 emitted a painful

high-pitched sound. It may have been a one-off clock/I2S fault, but it is the

reason for keeping all of the following defenses:

* initialize the DAC muted;

* preserve upstream I2S silence priming;

* retain the permanent -14 dB left/right DSP cap;

* test timing/codec changes on one board first;

* keep Snapserver/client volume low during first playback after a flash;

* never infer that a UI slider at 10% is safe if the DAC is not initialized.

12. Fast next-session checklist

-------------------------------

1. Read README.md and this file before changing anything.

2. Check both git diffs; the critical firmware changes are uncommitted.

3. Verify the physical target by USB serial MAC before flashing.

4. Verify CONFIG_SNAPCLIENT_NAME matches that MAC/unit.

5. Build with ESP-IDF 5.5.1.

6. Prefer OTA only when the board already has the native OTA partition layout.

7. Confirm -14 dB cap, hostname, server IP, buffer, codec, volume, and queue in

   the boot log.

8. Check all five clients in Snapweb after any server/client restart.

9. Do not leave an experimental low-buffer configuration active if the serial

   log shows repeated hard resynchronization.

10. Keep server-side sample format and parec format identical.

This handoff describes the verified state at the timestamp above. DHCP IPs and

user-adjusted volume percentages are live state and may legitimately differ in

a later session.