Gebruiker:Stitch/Snap
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.