更新说明

CHANGELOG

Changelog

本文件记录各发布版本的关键变更。格式参考 Keep a Changelog, 版本号遵循语义化版本(SemVer)。

[1.9.8p] - 未发布

2026-09-25 稳定补丁收尾

新增与兼容

保持旧分区和 NVS 布局,页面随应用槽共同升级/回退。

验证说明

[1.9.8] - 2026-09-15

本版本主题:AirPlay RTSP 生命周期加固(阶段1–5)+ HomePod 混组早响根因修复, 并以真机三机台架(COM59/60/65)完成 Apple Music 与多房间投送验证。候选版: 正式对外发布仍待 HomePod 真机声场对齐证据(见"已知问题")。

修复(客户可感知)

v1.9.4 移植的 latencyMin(realtime type 96 延后 11025 samples=250ms 对齐 iPhone 原生多房间)在 handle_setup 中"先写后清"——解析 streams[] 时写入 playout_latency_samples,随后启动接收器触发 audio_timing_reset() 又清零,且 全仓库仅此一处写入,补偿整场会话恒为 0,本机相对 HomePod 早响。修复:latencyMin 改到 start_audio_receiver_or_fail(所有 reset)之后再写入;新增 receiver 会话态 session_playout_latency_samples,seek_flush(切歌/seek)、reset_timing (SETPEERS/PTP 主变更)在 reset 后重 apply,stop_until(结束/换手)清除——同流 暂停/恢复/seek 全程保留补偿。作用域不变:buffered(type 103)/AirPlay 1 恒 0, KabuSync 零触碰。真机实证 SETUP: applied playout_latency=11025 after start (单机 20/20 投送全生效)。守卫:tests/test_airplay_latencymin.py 21 项 (新增 handle_setup 顺序守卫 + 会话保留 5 项)。

接收任务 buff_audio(TCP 收包 + ChaCha20-Poly1305 解密 + AAC 解码,CPU 重)原用 xTaskCreate 未绑核,多房间(type 103)播放时被调度到 core 0 与 httpd 抢核、并饿死 优先级 3 的 log_ws 广播 → 播放期间 /api 超时、/ws/logs 冻结(音频在 core 1 不受影响,故表现为"音乐照放、网页/接口失灵")。修复:buff_audio 主建与 PSRAM fallback 两路均 pin 到 AUDIO_TASK_CORE(=core 1),与 realtime worker 同核,把解码 CPU 移出 core 0。真机三机 A/B:修复前播放中三台 /api 连 20s 全超时;修复后 .103=102ms、.105=208ms 播放中秒回,音频零回归(decode_err=0、underrun=0、buf~900、 ptp 锁定)。守卫:test_audio_task_alloc_resilience.py::test_buffered_task_pinned_to_audio_core。

加固(AirPlay RTSP 生命周期,阶段1–5)

socket 生命周期 mutex + 会话绑定取消 + 分阶段清理确认(停止未确认不发布管线释放); 加密/普通 I/O 分段取消、1s 发送超时、部分帧毒化即终止连接;airplay_ready 严格 合取发布(worker+监听+输出+广播全就绪);普通域常驻栈启动准备提前、四态幂等。

session_id、stream_generation、handoff/cleanup_timeout/worker_defer 等)+ 结构化 失败日志(单一可计数记录不重复统计);CONFIG_KABU_FAULT_INJECTION 门控注入点。

验证(真机三机台架,三台 build 一致)

控制任务 8192B 栈申请失败(Failed to create client task),已由 v1.9.7 M3-b 常驻 worker 根治、本版延续。真机实证:单台 Apple Music(realtime ALAC type 96) 与三机多房间(buffered type 103 / AAC-LC ct=4)均正常出声、decode_err=0、 underrun=0;三台 worker_create_count=1、boot_count=1、cleanup_timeout=0, 且 internal_largest_free_block 低至 6656–7680(< 8192,即 1.9.6P 致命门槛)仍 稳定起播——常驻 worker 结构性消除该失败模式。

失败签名(task 创建失败/ballast/decode_err/underrun/crash)全 0、零重启。

已知问题

rssi 弱至 -45dBm 的设备,多房间播放中 /api 请求-响应 >8s 超时、httpd 反复 ECONNRESET,但其音频完全健康(decode_err=0、underrun=0、buf~900、rssi 回升 -24、/ws/logs 单向推送正常)。同固件的另两台播放中 /api 秒回(102/208ms), 故非系统性 core 饿死(已被 buff_audio 绑核修复消除);疑为该台 WiFi 链路在三路 buffered 拥塞下的往返抖动——深缓冲音频与单向日志推送耐丢包,同步 HTTP 往返不耐。 待单独复确认(更好摆位/单机隔离/更长超时),不阻塞本修复,也不影响播放。

混组下实测 v1.9.8 已与 HomePod 同步,印证 latencyMin 修复(realtime/type 96 早响) 在真机 HomePod 场景生效。仍保留的边界(证据优先,不越级宣称):多房间常走 buffered(type 103,latencyMin 恒 0),buffered 混组下 KABU 与 HomePod 的绝对 对齐、以及稳定性报告 §9.5 的脉冲同录(扣声传播 2.9ms/m、固定中位偏差 ≤5ms)声学 定量测量尚未完成;故记为"真机听感确认同步",非"≤5ms 声学验收通过"。

[1.9.7p] - 2026-09-14

本版本主题:v1.9.7 硬件复测两个发布阻塞项的根因修复(复测报告 output/hardware_audit_20260914_053411,修复验证 output/hardware_audit_20260914_063024/repair_validation.md 与 output/source_matrix_*),并包含 v1.9.7 后的 EQ 低音修复。 全部结论有实机数据支撑,验收门槛未放宽、失败原样保留;三机矩阵 (COM59/60/65)复验通过。

修复(客户可感知,EQ)

预测峰(全频段同时满幅的估计)全额扣回,实测旧 温暖 预设净低频 -1.0 dB、中频 -3.0 dB(滑条总提升 +9.5 dB 被抵消殆尽,拉满 15 段 更是 -9.8 dB 响度暴跌)。现自动前级下限钳位在 -3 dB(用户手动前级 不受影响),超出部分由输出端 float 域二阶软限幅(knee -0.5 dBFS,与 AQ-4 int16 限幅器同形状)平滑吸收,杜绝硬削波平顶。软限幅仅在前级 地板实际生效(存在未补偿增益,即合成峰 > 2.5 dB)时激活:EQ 关闭、纯衰减、 合成峰≤2.5 dB 的轻度提升等场景与改动前位精确一致,旁路透明性不变。

低频重心从 20/31.5 Hz(小喇叭物理上放不出)移至 50–125 Hz,净低频 -1.0 → +1.25 dB;新增 重低音(净低频 +2.87 dB)与 小音量响度(等响曲线思路:低频 +1.4 dB、高频近似持平)预设; 前端响应曲线同步 preamp floor 逻辑,显示与固件一致。

可观测性(EQ)

/api/eq runtime 同步暴露;台架核验判据:播放热素材时计数应少量增长且 saturations 保持 0。

验证(EQ)

旧 warm 净低频 < 0 锚点、新预设净低频阈值、软限幅 C1/单调/零直通形状), 架构测试补 preamp floor + 软限幅落点断言。

修复(发布阻塞项)

仍持有 initial_expected_players 席位,起播要等 4 s dead-peer retire 再拖到 8 s deadline(实测 claim-to-ready 8-9.9 s、startup_miss=1、历史成员对照 3/3 FAIL)。现在 fast-start probe 收到完整 v2 非 Sendspin 响应即撤销 slot 资格并清 peer cache(revoke_non_sendspin_peer);mDNS 残留记录复活 slot 不再盲目重确认(仅 fresh slot 获得资格),真实 WS 连接(ws_on_connected) 才恢复资格——设备切回 Sendspin 即重新入组。三主机矩阵历史成员对照 3/3 PASS,claim-to-ready 9876→3345/3362/3500 ms,串口证据链完整 (ignoring cached non-Sendspin peer mode=1 → slot revoked)。

被读向 64 KB 而只消耗 512 B,memmove 每次搬移 ~63.5 KB 尾部——320 kbps 每秒 ~112 次循环(128 kbps 仅 ~48 次),memmove 独占 stream_task 56.3% 时间,供帧被压到 97.4-98.4%(包率 24.09-24.73 < 24.75 门槛,三机 9/9 FAIL)。 input 稳态收窄到 decode_feed×4(2 KB)后 memmove→1.3%、source_rate→99.96%、 包率→24.99 PASS、source_waits→0;128/320 × 双/三机四格矩阵 8/8 PASS, 320 kbps 的 encoder/socket profile 回到与 128 kbps 一致的健康形态。 FLAC 路径不受影响(decode_feed 即整缓冲);未放宽门槛、未动看门狗让出、 未加大缓冲——消除的是无效拷贝本身。

再拆 leader overlay;sendspin_player_start 非 idle 时先完成 stop/idle 握手, 仍非 idle 才计数放弃——不再带着未排空 ring 与重置的全局量继续运行。

standby 仲裁或起播 barrier(confirmed_sendspin 位);截断 v2 响应不再降级 当 v1 接受(leader 两路 probe + kabu_presence 计数前丢弃);peer cache 加 自旋锁(discovery/HTTPD/persist 三任务并发,NVS 读写保持在临界区外)。

成员 sigma 卡 584-600 µs(贴 CONV_SIGMA_US 600 门槛),冷启动 claim 连续 3 次(31/79/127 s)ready=1/miss=1 直到重启才恢复。根因为 v1.9.6 既有 代码:innovation gate 拒样时 p00 冻结、gate 永不自我放宽,纯冷启动路径 无逃生门;且 gate 地板 500 µs 与 11.5 ms RTT 链路的诚实样本误差界 (rtt/2,ms 级)不匹配。修复为双逃生门(同步质量门槛不动): gate = max(3σ, 500 µs, rtt_accepted_ema/2)(封顶 25 ms)+ 全路径连续 30 次拒样 cold_restart。冷启动 A/B 三轮 3/3 PASS(claim-to-ready 2949/2949/6549 ms vs 修复前 6449-8148 ms + 127 s 卡死),稳态 sigma 259-334 µs(余量翻倍)。

可观测性

(starve/wait/low-water)+ memmove 计时,经 /api/sendspin/leader 顶层暴露; 计数器仅 stream writer 任务写、HTTPD 读,全 __atomic 无锁,音频路径零临界区。

串口采集器活过设备重启(S3 原生 USB 重枚举后静默 3 s 自动重连续写)。

第三方设备 airplay 旁观),阶段计数差分分析。

已知问题

非本版引入)**:6h 压测中 Failed to create client task 触发 20 次, 其中 13 次被 M3-b idle 重试自愈(用户无感),7 次成为场景失败(3 次 投送 did-not-decode + 4 次 A5 改名强应力),签名与 v1.9.6Plus Known Issue 一字不差(internal_largest 7680 < 8192 压舱石、packets_received=0、 ptp 未锁、不崩溃、下轮自愈)。同口径 6h 频率:v1.9.6Plus 24 次 → v1.9.7 2 次 → 本版纯投送 3 次(本轮台架 .103 碎片基线偏紧,7680 B 反复出现);碎片最低 2816 B(v1.9.6Plus 记录 1984 B);M3 不变量保持: ballast unavailable 0(时代 372)、PSRAM fallback 0(时代 102)。 彻底归零需 2 常驻槽 +8192 B(超内部预算门槛,属 M2/M4 后续专项)。 用户视角:长时间多模式混用后 AirPlay 单投小概率“连上无声”,重启 该台即恢复。

AirPlay 模式也应答 probe,混装场景历史成员问题不可修(v2 自 v1.9.6 KabuSync Stage 0/1 已存在,实际风险面极小)。

空闲块下降非持续泄漏(暖态稳定),发布判据以暖态趋势为准。

验证

revive 语义、peer cache 锁、截断丢弃、角色转换、源侧 profile、ring 饿证据、 校时双逃生门);test_sendspin_fast_convergence 同步质量门槛守卫不变。

失败为工作区该目录处于删除状态的既有问题,与本版本无关)。

128/320 × 双/三机四格矩阵 ×2 cycle 全 PASS(075219 轮,P1-2 证实)、 冷启动 A/B ×3(timefilter 证实)。

output/v197_fullstack_20260914_143025/report.md):KabuSync 30/30 (underruns=0)、DLNA 30/30、模式轮转 30/30、长稳循环 31/31;零崩溃、 boot_count=1 全程、结构异常(重启/分脑/黑洞/幽灵组长)=无; decode/decrypt/malformed=0,68.9 万包 98.9% 解码,buffer_underruns 7 (0.001%,pyatv RAOP 压力路径量级,与 v1.9.6Plus 5 次/v1.9.7 1 次同 数量级);7 次场景失败全为上述 AirPlay 碎片病征。基线轮(修复前 08b457109,19 轮)对照:KabuSync 校时卡死 1 次 → 修复后 31 轮零失败。

[1.9.7] - 2026-09-10

本版本主题:内部 RAM / PSRAM 碎片治理——长期可靠性。根治 v1.9.6Plus 头号 已知问题“多小时混用后 AirPlay 偶发起播失败、需重启才恢复”。方案分 M0–M3 里程碑落地,全部经台架实机 + 6h 长测验证(详见 docs/kabusync_memory_governance.md)。

修复(客户可感知)

(8192B)申请失败(Failed to create client task)→ 会话起不来、需重启。 改为 RTSP 主槽常驻 worker(client_worker0:开机一次性分配内部栈、连接间 park/unpark、永不按连接重申请,创建失败时 idle tick 重试)。6h 确认长测 起播失败 24→2(-92%)、Client ballast unavailable 372→0;残余 2 次 为罕见换手瞬间(客户端自动重连)。

(audio_stream_realtime.c),消除碎片化下 102 次/6h 的 PSRAM 栈回退 (回退拖慢接收、增加高负载丢帧风险);6h 长测回退 102→0、退出超时 0。

sendq/freeq/txq 静态队列(存储移 PSRAM),组长侧内部 RAM 占用下降;6h 组播 underruns=0。

WithCaps 任务停车+回收(杜绝 TCB/栈泄漏)。

新增(可观测性,M0)

free/largest/水位 + 分配失败回调环),现场可诊断碎片化。

生产零 footprint),确定性验证退化路径。

预算与红线

radio 模式零额外占用;构建静态 RAM 33.2%(与治理前持平)。

验证

ip_play_2dev_soak --quick 全过;两台零崩溃、6.7h 无重启、KabuSync 组播 underruns=0。证据:docs/kabusync_memory_governance.md §11、 output/ip_play_2dev_20260910_063602/。

发版验证矩阵(最终发布构建;COM31=.76 组长 / COM32=.75 成员 / COM45=.101)

COM31/COM32 试烧均 RESULT:PASS。

DLNA、KabuSync 组播(underruns=0)、三模式轮转+语音、长稳循环全过;稳态计数 malformed/decrypt/decode/underrun=0;M3 不变量全 0。

双槽 ota_0↔ota_1、播放门控、回滚保护(新镜像正常启动未回滚)、版本号变化核验。

复刻 v1.9.2 缺失态)→ 重启 → Prompt self-heal: 13 restored, 0 failed → /api/fs/list 18/18 复原。

(centered p95=max=380us«1000、ppm 84.5≤150、underrun/overrun/hard_resync/ discontinuity 全 0);组长 .76 为 DLNA 源=时间线参考(无从动 cursor,该门限 N/A、 其余全过)。

29/29 underruns=0、模式轮转 28/28、长稳循环 29/29;两台零崩溃、结构异常 (重启/分脑/黑洞/幽灵组长)=无;61.86 万包接收 / 99.04% 解码, decode_errors=decrypt_errors=malformed_packets=0、buffer_underruns 全程仅 1; M3 不变量保持:PSRAM 回退 0、ballast 0、Failed to create client task 24→1(换手 slot1 遇碎片 largest=2176B、184ms 自愈)、worker0 deferred 2→created on retry 2(補强 A 生效)。4 个失败场景全为环境/压测产物:3 次组长 /api/system/info 轮询超时(AirPlay 打满 WiFi 的争用;该轮播放实际 0% 丢包、无重启) + 1 次 WiFi 抖动突发(late=resync=32、underrun=0)。证据: output/ip_play_2dev_20260910_231626/report.md。

已知限制

客户端自动重连);彻底归零需 2 常驻槽 +8192B,超当前 .67 内部预算门槛,暂缓。

[1.9.6Plus] - 2026-09-08

本版本主题:同步修复验证发布版。内容 = v1.9.6 + 三项修复(KabuSync P1 校时、AirPlay 音量曲线回退、ASRC 停靠偏移自适应回拉),全部经台架实机 验证后随工厂镜像 output/kabu_factory_v1.9.6Plus.bin 发布。

发版验证矩阵(COM26/27/28 三台,build f6e370c8a)

自适应滑移 61 次启动(59 次满速 100 µs/s),音量曲线 8/8 PASS。

滑移恢复 5.1 s vs 原版 ~120 s;修复版基线 -10.6 ms→135 s 归零、再遇 rebase(-7.6 ms)114 s 归零零过冲;原版停靠 -29.6 ms 冻结 2 min 后 20 µs/s 龟爬;机间偏差 100% 来自原版机单方漂移。证据: output/night_sync_debug/morning_ab_report_15min.md。

停靠被 100 µs/s 满速回拉,KabuSync 成员路径零扰动。

COM28 在网三机组播):KabuSync 成组/组播/拆组 29/29 全过 (underruns=0)、DLNA 29/29、模式轮转 28/28、长稳循环 29/29;两台 零崩溃零意外重启;64.5 万包接收 / 99.05% 解码,buffer_underruns 全程 仅 5 次(1 帧级);ASRC 滑移 9 次全满速,最大停靠偏移 63.9 ms 全部 拉回。AirPlay 投送 13 次失败中 12 次为 pyatv/AirPlay1 压力路径抖动 特性(late/resync 27~46,underrun≈0;iPhone/AirPlay2 真实路径已验证 late=0),1 次为既有碎片化问题(见下)。证据: output/ip_play_2dev_20260907_193152/report.md。

P1 校时修复验证状态更新

6 h 压测 KabuSync 场景 29/29 全过、成员 COM27 串口 glide/rebase/ unlocked 全零、/api/sendspin/player clock 诊断对象工作正常。

老版本 OTA 升级语音素材自愈(2026-09-08)

设备 OTA 到本版后缺少新固件新增的语音素材(v1.9.2 缺 hifi/std + 11 段数字 WAV,v1.9.6 缺数字 WAV),表现为 IP 播报无声、部分 模式播报缺失(串口 Prompt asset missing)。

(main/CMakeLists.txt PROMPT_ASSETS 清单);启动时 voice_prompt_ensure_assets() 在 SPIFFS 挂载后、网络/音频/web server 启动前逐个比对大小,缺失或不符即从内嵌副本重写(每 4 KB chunk yield 一次,避开 idle 看门狗告警;O_TRUNC 覆写语义已核 实)。失败仅告警不阻塞启动(heal 处于 OTA 回滚窗口内,无 panic 路径)。

恢复,Prompt self-heal: 3 restored, 0 failed,零看门狗/panic。

+ hifi + std,共 449 KB,与老设备升级后的真实缺口一致): Prompt self-heal: 13 restored, 0 failed,heal 窗口 1535→7254 ms 即 5.7 s(写吞吐 ~86 KB/s,SPIFFS GC 随分区填充介入),零看门狗 /panic,启动继续正常(web server 13.7 s、main stack free 12576 B)。 两轮均 18/18 素材经 HTTP 下载后 SHA-256 与 data/ 逐字节一致 (含新建的 13 个文件,证明缺失新建与截断重写两条路径)。

重启后返回恢复内容 → 证明 heal 早于 web_server_start()。

大小探测约 0.5 s/每次开机(由 116 ms 完成前 4 次探测推得 ~29 ms/次)。若后续要求零稳态开销,可加 NVS 素材集版本号门控。

桥接下不可用(桥接按 codemodel 用 SCons 编译,不执行 CMake custom command,生成的 .S 变成悬空相对路径源)。改用 configure 期 execute_process 预生成 .S + 绝对路径 target_sources, 勿回退到 EMBED_FILES 写法。

首次开机请耐心等待约 1 分钟,勿断电(回滚窗口内断电会自动回滚 旧版,可重试 OTA,不 brick)。守卫: tests/test_voice_prompt_embed_assets.py(清单三向一致、basename 唯一、符号声明、启动顺序、非致命 + yield、总量预算 ≤1.5 MB)。

Known Issues(本版已知限制,v1.9.7 头号专项)

压测中 COM26 internal_largest_free_block 最低跌至 1984 B;第 26 轮 新会话起播失败时快照 7680 B < 8192 B(cc945fa rtsp_client 压舱石 阈值),RTSP 握手未完成(packets_received=0 / ptp 未锁定),设备不崩溃 (boot_count=1),下一次模式轮转重启后自愈(27~29 轮正常)。与 2026-09-07 夜间 soak“新会话偶发不起播”同款签名;KabuSync 成员路径 不受影响(COM27 最低 12288 B)。压舱石未覆盖“重启后快速再碎片化” 路径,治理方案见 v1.9.7 专项。

[1.9.6] - 2026-09-01

本版本主题:家用 Mesh 组网支持。目标:纯 Mesh 路由环境(同 SSID 多节点、 带外网)下除 KabuSync 外所有功能可用。KabuSync 在无线回程下的时序保证另立版本。

KabuSync P1 校时修复(2026-09-05,待用户测试)

消除连续 innovation 拒样造成的漂移重复累计。

至少两个独立有效测量并满足既有收敛门槛后才允许起播。确认保持 50 ms burst,5 次拒样或从首次回复起 2 s 未确认回落冷启动。

分原因拒样及热启动诊断;旧 time_synced 保持会话已收敛语义。

不改变 WS/Noise 顺序、编码、音频缓冲和 ASRC 参数。

字段含义与验证要点见 docs/KabuSync_v1.9.6_P1_handoff.md。

AirPlay 音量曲线回退(2026-09-07 夜间台架验证)

AirPlay 多房间同步修复:ASRC 停靠偏移自适应回拉(2026-09-07 夜间)

Added 新增

AP 的 802.11k/v 能力、漫游开关与运行时状态、漫游事件历史(rssi_low / btm_query / steered / self_roam,内存环形 8 条)、AirPlay/DLNA 广播状态与 SSDP LOCATION,Mesh 环境排障无需串口日志。

same_ssid_bssids / multi_ap(同 SSID 多 BSSID = Mesh/多 AP 组网)。

中英双语建议;data/www/index.html。

关闭漫游编译时为零开销 stub;tests/test_mesh_network_support.py 静态锁定。

IP 变化行为与诊断入口。

单路由器环境无副作用(无第二节点可漫游)。

docs/archive/module_comparison_wroom_vs_clone.md),Known Issues 中 “WiFi OTA 不可靠(wontfix)”条目随之移除;验收设备 = COM15/COM20/COM13 (原厂 WROOM)。

Fixed 修复

漫游/DHCP 换 IP 后控制点失联。新增 IP_EVENT_STA/ETH_GOT_IP 监听:旧地址发 byebye → 重建 LOCATION → 新地址发 alive,失效事件订阅同步清理;同 IP 重获取时 补发 alive 刷新控制点缓存。新增 dlna_renderer_get_location() 供诊断。

IP 静默杀死 L3 邻接后原会话永不回收(无 FIN 时 recv 只会 EAGAIN 循环)。新增 SO_KEEPALIVE(idle=10s/intvl=3s/cnt=3,约 19s 检测);新连接替换机制不变。

长时间会话更替后最大连续内部块可跌至 9216B(< 接收任务 12KB 栈), 此后每次 SETUP 均 500("Failed to create receiver task")直到重启,实测卡死 25+ 分钟(总 free 36KB、无任务泄漏,纯碎片化)。修复:双压舱石(与两个 任务栈同尺寸的 12288B/4096B 内部块,会话间隙持有,建任务前释放形成 精确窗口,任务退出后原位回收)+ PSRAM 栈兜底(接收任务不碰 flash/NVS, 缓存失效协议下安全),覆盖 realtime 与 buffered 两条路径;两层防护均经 真机日志实证。新增 tests/test_audio_task_alloc_resilience.py 5 项锁定。

RTSP :7000 可进入小时级无响应(HTTP :80 同期正常)。根因:排队中超时已死 的连接仍消耗完整槽位替换周期(最长 6s+3s),服务速率低于重试速率后自持 崩溃。修复:accept 后非破坏性探活(select 零超时 + MSG_PEEK)微秒级甄别 并丢弃死连接;拒绝计数 rtsp_stale_refused/rtsp_slot_refused 经 /api/system/info 可观测(5s 限速日志)。核实:本 IDF 无 LWIP_TCP_LISTEN_BACKLOG 符号,accept 队列由 ACCEPTMBOX(6) 承担且队满 RST 快速失败,保持不变。新增 tests/test_rtsp_congestion_guard.py 5 项。 验证(0.5h 强化档,会话间隔 0.5-2s、abrupt 25%,路由器+网桥强信号 RSSI -26~-33):865 动作仅 1 次瞬时断连(秒级自愈,非黑洞型),黑洞型 失败窗口归零,DLNA SOAP 100%,模式切换 5/5,零意外重启,堆稳定。 弱信号环境下的防护路径触发证据留待下次 mesh 环境补测。

Verified 验证

江南/梁静茹循环,含杀进程断流/计划重启/模式热切换)**:

AirPlay 永久 SETUP 500(.68 卡死 25+ 分钟,262 次连续 500); 46 次突然断流全部正确恢复(RTSP keepalive 修复实证);9 次重启全部 ~9.3s 恢复;31 次模式热切换全成功;DLNA SOAP p99=625ms。

持续性 SETUP-500 归零(仅剩 3 次孤立且秒级自愈);ballast 循环全程 在位(int_largest 稳定 12288);堆零泄漏;新发现 P1(未修复,已归档 Known Issues):弱信号机(.70,RSSI -61~-72)在高频会话更替下 RTSP :7000 可进入小时级无响应(HTTP :80 同期正常,重启后彻底恢复)。

802.11k/v,漫游默认开启生效。

增补(同版本分支后续提交,version.txt 未升,归入 1.9.6)

主题:上游 v0.2.0 对照移植 A1/A2/A4/A5、中文设备名 AirPlay 兼容修复。 归因:A1/A2/A4/A5 对照上游 rbouteiller/airplay-esp32 v0.2.0(PR #96/#115/#127) 的协议行为与工程参数自写实现(上游 Non-Commercial,策略见 docs/upstream_tracking_v0.3.md 第五节)。
Added 新增

实时 ALAC 突发收包邮箱扩容(每活动 UDP PCB +192B)。

SETRATEANCHORTIME 控制命令的 Nagle + 延迟 ACK 等待。

SETUP 起播即广播);修复按键/睡眠本地静音后经 SETUP 恢复仍无声 (playback_control 仅在 PLAYING 清本地静音)。

投送)/DLNA/KabuSync 成组/模式轮转/A5 中文名专项/长稳循环轮转,全程串口 + 健康 + 堆监控,per-session 计数器采集,report.md/json 汇总;素材用江南/ 梁静茹两首原始曲循环重投(不拼接,避免拼接点污染音频计数器)。

Fixed 修复

KABU-<MAC>,instance 名保留 Unicode。此前直接把用户设备名传入 mdns_hostname_set,中文名致 <名>.local 不可解析(AirPlay 列表可见但 连不上)。对照上游 PR #127。

时改用 0x60 UTF-16BE Unicode-string(新增 bplist_write_string / bplist_utf8_to_utf16be / bplist_is_ascii)。原用 0x50 ASCII-string 直接 memcpy 中文 UTF-8 字节,违反 bplist 规范,致严格解析器(pyatv/plistlib, 可能含 iOS)UnicodeDecodeError(0xe5) 崩溃、AirPlay /info 握手失败。此为 A5「中文名可连」目标中 hostname 之外的另一半(7h 压测 A5 专项挖出)。

成员离开组后(如切 AirPlay,8928 关闭)仍会应答 fast-start presence probe(UDP 8930 任意模式常驻),导致每次起播都被 roll-call 重新准入,虚增 initial_expected_players;而播放期 30s stale 阈值在 8s 起播窗口内永不触发,join window 还被 !available 成员 +3s 扩展,ready set 实测 ~9.2s 才冻结,两机组 超时失败。修复:起播窗口内(仅 LEADER_START_PREPARING)对 “claim 快照在册且本次 start 从未建过 TCP”的 slot 一次性快速退役 (LEADER_DEAD_PEER_STARTUP_MS=4s,present_at_start/ ever_connected_this_start 双豁免 + one-shot),mDNS/late-join 回组兑底不变。三重安全边界:晚于 claim 才被 mDNS 发现的成员豁免、 曾建连(含 claim 时 warm 会话)的成员豁免、已连接 slot 由既有 guard 先行 continue;播放期 30s/standby/hot-start/join window churn fix/FINAL_ROLLCALL(PARKED) 全部零改动。新增 tests/test_leader_dead_peer_startup.py 9 项静态守卫;验证:三机→ COM37 切 AirPlay(不清 peer cache)→两机 FAIL→修复后 PASS, ready frozen ≤8s(详见 output/channel_21/)。

(新 WIFI_CMD_ENSURE_AP)——任何状态下长按都确保配网 AP 开启; 已开启时重置 10 分钟无客户端超时窗口,AP 不再被长按误关; 关闭路径仅剩既有超时机制。AP+STA 共存不断 STA 连接。

dlna_renderer_pause() 全组暂停;成员经新增 Sendspin 上行控制信令 client/command/pause(加密会话,同 client/time 传输;leader 端经 回调钩子 sendspin_leader_set_member_pause_cb() 转发到 DLNA 层, 不引入反向依赖)请求全组暂停。被按键设备随后逐位语音播报本机 IPv4(新 voice_prompt_play_ip() + 11 段中文 TTS 数字 WAV, scripts/gen_digit_prompts.ps1 生成,44.1k/16bit/mono,尾部静音裁剪 后每段 ~0.5s,整次播报 ~6s)。播报会终结暂停会话(与 DLNA 模式 toggle_hq 先例同模式),恢复交给手机 App 的 DLNA 播放键(产品决策); 重复单击幂等地再次播报。新增 tests/test_button_ip_announce.py 10 项静态守卫。

注册数达 ~82,80 槽位时尾部 6 个注册失败(含 /ws/logs WebSocket 与 speedtest 路由,日志 "no slots"/HANDLERS_FULL);守卫测试 test_uri_handler_budget_covers_the_digit_routes 锁定预算 ≥96。

Verified 验证

scenario 186 通过 / 2 偶发(98.9%),无结构异常、无 fatal、无堆耗尽。

KabuSync 组播放全程 2/2 成员流播、underruns=0。

冷启动无预热的 gates 抖动,error=null 连接本身成功)。

层,非固件缺陷。

[1.9.5] - 2026-08-31

Added 新增(2026-09-01 发布配套)

Home Assistant→DLNA 两条链路在 v1.9.5 固件完成全流程真机实测 (注册/播放/暂停/恢复/音量/状态同步),沉淀离线图文手册 docs/ma_ha_user_guide.html + docs/ma_ha_guide_images/(22 图,含 故障排查表与验证清单);当日 pytest 全量回归 626 passed / 2 skipped。

复用 factory_flash 流水线与出厂自检)、scripts/release_coldboot_probe.py (冷启动 OOM 压测循环)、scripts/dlna_release_acceptance.py + tests/test_dlna_release_acceptance.py(DLNA 验收脚本与静态守卫)。

tools/render_youtube_banner_journal.py(产物 assets/ 体积较大且可 重新渲染,已加入 .gitignore 不入库)。

CHANGELOG/CLAUDE/脚本等 11 处引用同步更新,docs 根目录 47→23 份。

Fixed 修复(8h 压测成员回组缺陷,见 docs/stress8h_analysis_20260831.md)

sendspin_leader.c):成员重启后组长以固定 250ms 节奏重拨(30s retire 窗口内 ~120 次)形成连接风暴,把回组拖到分钟级(8h 压测两次回组 >120s 失败)。失败定义收紧为"应用会话达到 SESSION_ACTIVE 之前的失败",覆盖 TCP 连接失败、HTTP Upgrade 失败、101 后立即断开三类,统一按 250→500→ 1000→2000→4000→8000ms 饱和退避(共享 backoff_wait_and_advance(), 250ms 分片并响应 stop)。

client/hello、会话切到 SESSION_ACTIVE 后才能复位退避到 250ms——TCP/HTTP 层成功不等于成员可用(重启中的接收机会应答 TCP 而应用未就绪);健康会话 断开仍固定 250ms 快速重连,下一次失败才重新进入指数退避。

重试,无法消除"101 后立即 read error"的固定节奏循环。

均值 18.6s(门槛 <20s)、单次最大 27.3s(门槛 <30s)、退避序列日志解析 零违规、零结构异常。

退避序列门槛);EXPECTED_VERSION 默认升至 1.9.5。

[1.9.4] - 2026-08-31

Added 新增

AirPlay2 实时流(type 96)按发送端 SETUP 协商的 latencyMin (默认 11025 samples = 250ms@44.1k)延后播放。此前基线忽略该值, 在 iPhone 原生 AirPlay2 多房间(本设备 + HomePod/Apple TV 混组) 时早响 250ms——人耳可闻声场错位;修复后与群组对齐。

compute_early_us 的 PTP/NTP/本地三路径目标时间统一加项; audio_receiver_set_playout_latency_samples() 转发 API; handle_setup 解析 streams[] 的 latencyMin(合理性校验: >0 且 <10s 采样数,异常回落默认值)。

KabuSync 链路零触碰(架构断言守护)。

与同步偏差(KabuSync <1ms / AirPlay2 混播 ≤5ms)正交。

解析/作用域/公式数值);既有同步测试无绝对延迟断言,不受影响。

(与看门狗同谓词),压测 harness 据此断言无源组长 ≤8s 自愈。

Fixed 修复(KabuSync 脑裂 R25/R32 级联,见 docs/v1.9.4_kabusync_split_brain_fix_plan.md)

playback_activity 替代 session_alive`。连接态不再阻塞热备成员的本地 claim(R25 直接根因),同时保留换曲瞬空窗口对活跃组的保护;协议零改动。

释放 grace 从 10s 收到 4s,按绝对 deadline 轮询。消除“先 TIMEOUT 后约 3s 才 reject”的倒挂;调用者永远拿到确定结果(接受或明确拒绝)。

become_dlna_leader() 成功后发现失效立即退回待机,不再留下已广播的无源组长。

幽灵组长 ≤6s 自愈,级联在结构上不可持续。

失败则保留 retired 状态待重试;释放路径 pause-ack 用 4s 预算;每次 enable 最多 revive 1 个 retired slot;选举/稳定等待/拨号循环对排队 claim 让路。

拨号复用同一快照(删除第三/四次独立探测);先加冕 + 发 beacon 再拨号;加冕前对 leader/standby beacon 复检。

暂停释放转待机窗口内节点是 STANDBY_LEADER 且会话仍带上一世代音频, claim 快路径(跳 mDNS、无成员 grace)会在成员仍在渲染旧流时提升新组长, 导致“transport stayed STOPPED”与无源组长 churn;守卫从仅 MEMBER 扩展为 MEMBER || STANDBY_LEADER,同享 4s 有界等待。同轮压测中无源组长看门狗两次 实战自愈(≤6s,未级联),验证了 fix1 防线。

立即换组长”竞态变体与幽灵组长专项断言;新增 scripts/ma_sendspin_acceptance.py (MA WebSocket API 自动化验收)与整晚/白天无人值守编排脚本。

[1.9.3] - 2026-08-27(暂定,待实测验收)

多台设备在 QQ 音乐等 DLNA 投屏列表里全部显示同名 "KABU",无法区分 哪台是哪台;且非 DLNA 模式设备(KabuSync 按设计暴露 DLNA 入口、SSDP 缓存残留)与 DLNA 模式设备混在一起,造成误认。

Added 新增

settings_get_public_name()——用户自定义名优先;从未改名的设备 自动使用 KABU- + Wi-Fi MAC 后 4 字节(如 KABU-A3F21B7C),多台 出厂机在投屏列表中不再重名。

KabuSync 组内昵称、Web 门户与设置页全部改走公共名;kabu_mqtt.c (Home Assistant 实体)与 mode_manager 内部组长标识保持不变。

名字来源与改名生效时机对照,“为何能看到非 DLNA 模式设备”成因。

改为切换标准/高品质输出;高品质模式按源采样率直通输出 (44.1/48/88.2/96 kHz,消除 44.1→48 k 重采样损耗,16bit),切换时 插播英文语音提示(“High quality mode” / “Standard quality mode”), 播完自动重放当前曲目。HQ 状态 NVS 持久化,重启保持;暂停/继续改由 推流端 App、网页控制台与 MQTT 控制(mode_manager.c、 dlna_renderer_native.c、audio_output.c 运行时 I2S 时钟重配、 http_stream_player.c 会话挂钩、voice_prompt 新增提示音、 settings.c dlna_hq 键)。KabuSync 组内不启用直通(共享组时钟)。 回归守卫:tests/test_mode_manager_architecture.py 新增 3 项。

ES parser 无法跳过 FLAC 大体积 metadata 块——内嵌专辑封面 (PICTURE,实测 242 KB)会导致 "Not found frame in max frame size" 随后 no-PCM 超时拒播;且 no-PCM 看门狗按原始网络字节计数,封面 本身就会耗尽 64 KB 配额。新增 FLAC metadata 剥离器 (http_stream_player.c:reader 侧仅剥 PICTURE 块,其余 metadata 原样透传;FLAC 喂入从 512 B/次改为整缓冲,大帧不再被拆碎)。 《杀死那个石家庄人》(16bit/44.1kHz/带 242KB 封面)实机压测通过。

支持自动扫描 tmp/dlna_media/(mp3/flac/wav/m4a/ogg,MIME 按后缀 映射)或命令行直接传任意文件路径;修复非 ASCII 文件名(中文+空格) 未做 URL 编码导致拉流失败的隐性 bug。

ABS_TIME):字节目标由会话自身进度(已收字节/首帧以来时间)推导, 不依赖 Content-Length 或恒定码率;前向 seek 发 HTTP Range 请求, 后向 seek 从头重拉并丢弃前置字节;FLAC 重开时自动前置缓存的 "fLaC"+STREAMINFO 头(否则无头流无法解析)。SCPD 声明 Seek action 与状态变量,GetTransportInfo 的 Actions 上报 Seek;拖动后 GetPositionInfo 的 RelTime 从新基点继续,暂停冻结。压测脚本每 周期验证 seek 位置误差与暂停位置冻结(dlna_renderer_native.c、 http_stream_player.c)。

的 HQ 标志,下一会话生效;物理单击仍是播报+重放的即时切换)。

HQ 直通模式各 1 小时(DLNA_STRESS_HQ=0/1),各 101 周期(切歌、 seek 90s、音量、暂停位置冻结、声道切换、恢复、停止)全部通过, 堆漂移 -120B / 0B,零重启。

docs/archive/hires_24bit_decoder_experiment.md):真机实验确认预编译解码器 对 24bit FLAC 如实报告 bps=24 且输出 3 字节 packed(容器判别 扫描法);打通端到端 24bit 管道——解包 float → float EQ(新增 audio_eq_process_float_in)→ float 音量 → 通道模式 → audio_eq_float_to_s32 → I2S 32bit slot @ 源采样率;直通白名单扩 至 176.4/192 kHz(24/192 实测 retune: 192000 Hz, 32-bit slot、 underrun=0);非 HQ、白名单外、SUB 模式自动降 16bit 走原链。 顺带修复:24bit 大帧(max 27KB)使 Seek 重开在最坏情况下超出 32KB 解码输入缓冲导致静默死锁——HSP_INPUT_BUFFER_SIZE 32KB→64KB; DLNA 拉流 ring 128KB→2MB(PSRAM,约 19s 抗抖动,512KB/128KB 逐级回退);web URI 槽 72→80(新增端点后 /ws/logs 注册被挤掉)。 实测:24/96 与 24/192 FLAC seek/播放全通过,16bit 标准+HQ 回归 通过;DLNA_STRESS_SEEK 环境变量支持短曲目 seek 目标。

时长”码率估计严重偏大,seek offset overshoot 至 EOF 附近导致重开 会话立即结束——改用精确几何 Content-Length × target/duration (reader 记录首包 Content-Length;DLNA 传入 metadata 时长),bitrate 作回退,并留 3s 边距。向后 seek(从头重拉+丢弃)路径的 strip 会吃掉 流自带头、skip 又将其丢弃导致解码器无头 stall——该路径同样前置缓存 的 fLaC+STREAMINFO 头。压测脚本升级为类真控制点:解析本地资产时长、 推送含 duration 的 DIDL 元数据、seek 目标随时长随机、随机上一首/ 下一首与暂停时长。

成员 churn/换组长窗口内 local_sink_task 在 OVERRUN/DISCONTINUITY 路径零延迟 continue 形成 busy-spin,饿死本核 IDLE → task watchdog 连发 → HTTP 看门狗重启设备 → 组塌方与 “Another Sendspin source is active” 连锁拒播(2h 矩阵 9 轮失败)。三处 continue 补 1 tick 让步(audio_group_timeline.c),churn 聚焦回归验证。

Known Issues 已知问题(不在本次修复范围,需专项迭代)

成员时钟收敛可能晚于 leader 的 ready-set 冻结窗口(实测迟到 ~5s), 导致“组起播后成员未全部流播”;个别成员在长时 churn 下软件重启 (reset reason 3)后恢复缓慢。该缺陷在 v1.9.2 固件的 8h/2h 矩阵中 即存在(同类黑洞/卡死),本次 spin 修复消除了 watchdog 重启放大 链路,但收敛时序与重启恢复需专项迭代(ready-set 窗口自适应、 成员重连退避、leader 释放死锁超时)。常规场景(暂停/恢复/切歌/ 换组长/soak/外来抢占)2h 矩阵 20/29 轮通过,失败全部集中在 churn 边界。

改善为“零星深层失败”):① leader ready-set 冻结自适应——窗口到期 时若存在已连接但未收敛成员,一次性延展 3s(sendspin_leader.c, 实测冻结全部变为 all_ready miss=0);② leader 释放死锁超时—— release_pending 超 8s 且 DLNA 会话残留时强制释放,消除 “Another Sendspin source is active” 持续拒播(sendspin_role.c); ③ 成员 warm 时钟二次验证——首次 snap-gate 失败不再立即冷重置, 下一采样再验一次,churn 后重收敛从 ~5s 降到亚秒(sendspin_client.c)。 残留深层失败(leader 重启自愈 rebase 卡死、外来抢占拒绝判定) 在修复前基线同样失败,属控制面多轮迭代缺陷,需专项。

① 外来抢占拒绝扩展——member 守卫从“正在流播”扩为“连着组会话” (新增 sendspin_client_session_alive()),覆盖 stream 重启瞬空窗口, 播放中的外来 DLNA 抢占实测被正确拒绝、组保持不动;② SOURCE_PREROLL 卡死自愈——write_frame==0 超 10s 先重放 DLNA 源 (有界一次),再卡则回退重 claim;配套把 enable 时的 group_start+barrier 初始化抽为 begin_preparing(),并在其中对共享 编码器做新代际 reposition,消除快速 Stop→Play 后旧代际缓存残留 (修复前日志 write=0/encoded>0/exact=0 永久卡死的直接原因); preroll diag 增加 dlna_active/dlna_playing 字段。基线对比: preroll 卡死事件 0 次。遗留:冷启动成组“未全员流播”与外部 SetAVTransportURI 改写 transport 状态后的级联残留(下轮专项)。

结论修正——SetAVTransportURI 不加组会话守卫:它是 DLNA 标准切歌 动作(播放中直接覆盖曲目),对 armed leader 拒绝会破坏切歌功能 (实测 track_switch 全挂);组完整性由 Play claim 守卫单独承担 (成员在组内被 Play 劫持会被拒),外部 set_uri 预设 URI 无害。 ② 冷启动成组——join window 自适应扩展条件从“已连接未收敛”放宽 为“已知槽位未 ready”,消除首启后 window 冻结 miss 与 late-join rebase 链。③ release 顺序保持“先成员 stream/end 后停时间轴” (fast path 变体会破坏 stream/end 送达,已回退)。验收:常规 0.35h 5/5 PASS(成员回组 2.2s,此前 19.6s);churn 0.7h 15/18 零级联(失败不传播,后续轮次正常,对比修复前基线持续性塌方); 单台 DLNA 1h 80 周期 79 过、1 次瞬态切歌 error 下一周期自恢复、 零重启、堆漂移阈值内。pytest 596 通过。

Changed 变更

生效;AirPlay 需重新进入 AirPlay 模式(中英文)。

[1.9.2] - 2026-08-26

三台 KABU 组成 2.1 声场:KabuSync 模式下每台设备在网页自选声道角色 (立体声/左/右/低音炮),DLNA 推给任意一台即三机同步播放各自声道。 声道分离位于每台设备本地渲染末端,组时间轴与协议零改动;AirPlay 与 蓝牙音源强制立体声,稳定性与音质不受影响。

Fixed 修复

重启循环**(web_server.c):v1.9.1 的探针有界化提交有两处缺陷: ① 非阻塞 connect 走 select 成功时返回 1 而非 0,if (rc == 0) 永不 进请求阶段;② 为 connect 设置的 O_NONBLOCK 未清除,recv 在 httpd 尚未应答前就立即 EAGAIN 返回(实测 ~160µs)。tcpip 线程一旦忙到 loopback connect 异步化(DLNA/Sendspin 模式必然如此)探针即 100% 失败,健康设备每 48 秒被看门狗重启一次,三机组播与 DLNA 压测全部 不可用。现修复:select 成功路径归一化 rc=0;请求阶段恢复阻塞 I/O (由 SO_*TIMEO 限界);探针另改 SO_LINGER=0 关闭(RST 直接销毁 PCB,避免 TIME_WAIT 冲刷 TCP PCB 池)。实测三机 DLNA 静置与压测 不再重启。

(sendspin_leader.c):高频会话翻动下,一台设备角色层已降为成员后 其组长 WebSocket 会话任务仍持续重拨成员,与真组长互相置换成员唯一 的服务器槽位,任何一侧都无法建立稳定会话,后续全部场景连环失败。 根因:降权拆除只走异步 TX_PAUSE(命令队列卡住时永不执行);既有 "32 秒无连接即退役"守卫被每秒重连刷新时间戳绕过。现修复:新增 stop_all_leader_transports() 同步停止全部成员 wsclient; standby_stop() 追加同步拆除;discovery_task 禁用分支增加僵尸 回收器(带 claim→enable 过渡守卫)。配套静态守卫 tests/test_leader_zombie_guard.py;修复后同类高频翻动 1h 压测 23/23 场景全过。详见 [docs/archive/v192_stereo_kabusync_heap_acceptance.md] (docs/archive/v192_stereo_kabusync_heap_acceptance.md)

Added 新增

低音炮角色——(L+R)/2 经 120 Hz 二阶 Butterworth 低通(Q39 定点 + 8 bit 分数反馈,规避近 DC 极点的极限环增益损失);LEFT/RIGHT 沿用 既有声道提取;audio_output_set_channel_mode() 直设接口。

(PSRAM 任务不碰 NVS),两种 mode profile 初始化后统一恢复 (mode_manager.c)。

写后读回校验,no-store)。

生效、AirPlay 始终立体声。

刷写→串口取 IP→声道配置持久化闭环→DLNA 三机组播→AirPlay 门控→ 模式切换回归);tests/test_channel_mode_21.py 静态架构守卫 + 定点滤波器参考仿真(DC 增益、极限环增益损失两项门禁)。

AirPlay 稳定性底线

在 SUB 角色且非 AirPlay 音源时运行。

[1.9.1] - 2026-08-25

Music Assistant 实测兼容修复:Sendspin legacy 明文方言自动协商。 MA 2.9.13 捆绑的 aiosendspin 6.0.5 是明文旧版协议(无 client/init、无 Noise),与 v1.9 新规范握手首条消息即不兼容;本版本在固件侧增加 per-IP 探测与明文方言回退,实测 MA 投送出声、暂停、音量全通过; HA DLNA 链路同步复核通过。附完整验收报告与连接操作手册。

Added 新增

IP 连续两次在等待 server/init 阶段断开后,后续连接自动以明文 client/hello 应答并全程明文收发(二进制音频帧直入既有 9 字节头 管线);KabuSync 内部拨号(X-Kabu-Internal)永不走 legacy,组协议 路径零改动。

压测 + heap/任务数/重启门禁);home_assistant_dlna_acceptance.py 版本门禁参数化(--fw-version,默认 1.9.1)。

docs/ma_ha_cast_environment.md(E 盘环境部署指南)、 docs/ma_ha_interop_report.md(互操作验证报告)、 docs/v191_release_acceptance_report.md(发布验收报告)。

验收门禁

表现(非本版本回归),登记为独立定位任务。

[1.9.0] - 2026-08-24

三普通模式无重启热切换 + 三击进入 KabuSync;Music Assistant / Home Assistant / Roon 兼容补强;待机热会话 Noise nonce 顺序问题根治。 AirPlay 2 核心路径与 v1.8 KabuSync 同步行为零劣化。

Added 新增

(stop→播报→start,不再 esp_restart),秒级完成;提交改用 settings_confirm_boot_mode(不残留 boot_pending,断电不误计启动失败)。

内双击返回最近使用的普通模式(last_normal NVS 持久化,无效安全回退 AirPlay);普通模式双击循环不再包含 KabuSync。重启后首次进入 ACTIVE 播报"当前模式名 + 成功提示音"。

Assistant 的 DLNA 渲染器兼容完善;Roon AirPlay(RAOP)播放路径补强 (mDNS / bplist / RTSP)。

home_assistant_dlna_acceptance / kabusync_jiangnan_stress(江南单曲循环 与三机主机轮换压力门禁)四套真机门禁脚本。

Changed 变更

三机实测健康待机主机接管 6.049s、旧窗口贴边;与 8s 硬件验收门禁对齐, 稳定全集仍提前冻结(不是固定等待 8s)。

Fixed 修复

丢弃在队密文,违反"nonce 一旦推进,密文必须完整按序送达"不变量, 会让保温成员在下次认领时解密失败。改为先排空已加密音频、再发送 stream/end 并等待写入确认,失败才退回干净重连;standby 状态先于传输 处理发布,warm 会话不再被误拆。

重连看门狗(健康成员被误判晚入组的根因),ACTIVE 会话不受影响。

不可用启动模式在计数与资源构造前清除并安全回退 AirPlay。

动画、mute GPIO 负移位。

发布门禁(全部通过)

Flash 38.8%);buildfs PASS。

三主机轮换全 PASS(公共首帧 0、group time 一致、startup miss 0, 丢包/跳帧/时间线不连续/sendq drop/writer failure/late chunk/underrun/ flap 全零);每台 5 轮普通模式循环 + KabuSync 往返 27/27 PASS (普通域 uptime 单调零重启,跨域恰有一次可观测重启边沿)。

重刷后全量重验):同步/音频/协议门禁全绿——零 underrun/零 desync/ 零 skip/零 sendq drop/零 flap/零 startup miss,公共起播首帧 0 且 group time 完全一致,覆盖率 ≥99.36%,包速 25/s 锁定,时钟全程收敛; 待机释放(nonce 有序排空路径)执行 6 次零异常。脚本判定的 FAIL 项 均为短窗伪象(脚本门禁按默认 30min/轮设计,10min 窗无法产生 “≥7 曲循环”与校准后资源样本),非固件缺陷。

全程采集):零 underrun/零解密错误/零 panic/零看门狗,累计 uptime 7.2h;堆低水位漂移 ≤1KB(门禁 8KB)、最大块与任务数全程稳定, 无泄漏;热认领 claim→ready 325–527ms(v1.8 基线 312–590ms, 持平)、首次冷组网 3.2–6.4s(v1.8 实测 6.049s,持平);待机释放 nonce 排空路径执行 57 次零异常;压测后单机三模式热切换验收 PASS (heap 跨轮无损失、普通域零重启)。

已知问题(沿用 v1.8)

docs/archive/airplay_join_volume_unification_issue.md,本版不修)。

本版新增已知边界(如实登记)

或轮首认领后 0.4–6s 内、双成员同时),固件按设计毒化连接并干净 重握手,<1s 自愈,零 underrun/零失步、无可闻影响;后续带串口抓 首次突发时序定位。

起始段 reader/floor margin 瞬降至 120ms(门禁 160ms),斜率 +2406 fps 快速回补,零 underrun;登记观察,不修。

连续 6 个循环(约 26min)出现近 100% 射频丢失,全部经重传 100% 救回(迟到副本按序丢弃),零 underrun、音频全程连续、同步不失; 后续两轮同成员组合零复现,判定环境 RF 事件。

[1.8.0] - 2026-08-22

五机 KabuSync 支持:群组上限 3→5(CONFIG_SENDSPIN_GROUP_CAP=5, Kconfig 默认仍为 3)。

通宵五机深度压测(约 4.7h 矩阵 + 复验,全程五路串口)

冷启动 8 轮完全确定性:claim→冻结 312ms、claim→出声 4312ms(与三机一致)。

待机组重建 5/40/5s。

仅弱信号成员出现 RF 迟到块(0.015–0.35%,零 underrun,各轮设备不同, 与 RSSI 相关,环境问题)。

已知容量边界(如实登记)

lwIP/WiFi 开销,全夜功能零异常;TCP_SND_BUF 实测非瓶颈(保持调优值 64KB), WiFi TX 缓冲 128 为同步稳定性调优值不动。后续若需加大群组,优先内部 RAM 专项审计。

信道 6/11 或 5GHz。

上次存储音量,且音源对各设备下发音量可能有 ~1dB 差异。已记录不在本版修复, 见 docs/archive/airplay_join_volume_unification_issue.md。

发布门禁(全部通过)

冒烟 PASS;OTA 实测 1.7.2→1.8.0 版本号变化;AirPlay 三机 10min: 126pps 满速、零 underrun/零硬重同步、PTP 全锁、丢包 100% 重传救回。

锁定率 100%、堆漂移斜率 ≈0(无泄漏);拥塞信道下丢包 578–1953 个/台 中 4 台 100% 重传救回、零 underrun;仅最弱信号机(RSSI -68)出现一次 射频突发(64 underrun/47 重同步,11 个间隙未能重传),其后 50+ 分钟 无复发、自愈。属拥塞 2.4GHz 环境对最弱信号机的影响,非固件缺陷。

[1.7.2] - 2026-08-21

KabuSync 对标 Sonos 快速起播发布:L0 常驻存在层 + L1 待机热会话, 组网从“每次起播重新发现/连接/握手/收敛”变为“舰队常驻热态、认领 直接起播”。快循环成员崩溃根因一并根治。AirPlay2 路径零改动、实测 零劣化。

Added 新增

UDP :8930 被动应答服务(probe 协议 v2:RESP 携带 mode/boot_id/flags, REQ 保持 v1 兼容旧固件)。AirPlay 模式下纯被动、零周期发包(源码钉住); /api/lan/devices 增加 presence 快路径与 boot_id 重启检测;点名结果 自动保鲜组长 NVS peer 缓存。

下设备自动选举待机组长(上次组长优先、NVS 持久化、MAC 平局,无协商 协议),预建 WS+Noise 会话并以成员 1Hz 时钟交换保温时间滤波器;Stop/EOF 不再拆连接(滞留待机);认领走热路径:跳过发现/连接/Noise/收敛,300ms 稳定窗即冻结起播。选举含两次点名稳定校验 + 30s 复核让位,防启动竞态 双组长分脑;见活动组长 beacon 即拆会话退位。

sessions_warm;角色状态新增 standby_leader/standby_member。

组网路径,分阶段 p50/p95,warm/cold 分桶);soak 门禁适配待机角色。

Fixed 修复

handle_server_time 的时钟热重启日志位于自旋锁临界区内,newlib stdio 锁在高 INTLEVEL 下被 FreeRTOS 视为 ISR 上下文,锁竞争即 abort(v1.7.1 “成员 10–20s 补入”已知限制的真实根因,串口+coredump 实锤)。两处日志 移出临界区(置标志位后打印)并源码钉住。

成员单槽位(500ms 周期风暴);retire 现在同步停连。

各设备独立选举同时加冕多个待机组长,影子拨号互相翻搅会话,导致 60min 长稳中成员黑洞 47min。修复三件套:① 成员流播中会话拒绝新连接抢占; ② 选举宽限 30s + 待机组长广播 _kabu-group role=standby beacon,选举遇 在任 beacon 不加冕、同时加冕按 id 确定性让位、退位一律撤 beacon; ③ 维护拨号前 beacon 复核。验证:重启组长 80s 监控零分脑、待机 16s 重建、 快循环回归通过。

STANDBY_MEMBER 角色且重选 tick 持续运行,拥塞 AP 上 mDNS beacon 连丢 >30s 宽限失效后自选待机组长,拨号风暴活跃组、自毁会话成永久黑洞 (三次复现均 .65,t+4.8/11.4/15.0min,连锁看门狗复位)。双守卫: ① 流播成员拒绝任何认领劫持(改源必须先停组);② 流播节点永不参选待机 组长(sendspin_player_stream_active 守卫,实测拦截 45 次/台)。复验: 同 churn 条件旧固件 15min 即黑洞,新固件 60min 全门禁 PASS。

两次 mDNS 查询 + claim 自身再查,最坏 ~900ms 顶破 1s 门禁(近 4 次运行 3 现)。修复:待机组长认领免重复 mDNS 复核(每秒监视已覆盖)+ 双 beacon 监视按 tick 奇偶交替(每 tick 至多一次查询)。实测 ack 1109ms→188ms。

擦除,关缓存窗口把 discovery 任务短临界区拉长 >300ms 触发 Interrupt WDT panic(串口回溯 addr2line 实锤)。启用 CONFIG_SPI_FLASH_AUTO_SUSPEND (SoC 原生支持,擦写期间 CPU 取指不再被整段阻塞)。

Performance 性能(三机实测,素材:江南单曲循环)

(目标 ≤4.5s 达成 9 成);p95=6339ms 含拓扑变更后 ≤30s 过渡窗内的冷 路径轮次(不劣于旧基线 6–9s,窗口内自愈)。

待机会话常驻开销 ≈8KB 内部 RAM(一次性分配)。

验证(发布门禁)

临界区日志 1 + 看门狗栈 1 + 配置钉住 2);pio run -e airplay2 零新增 warning。

成员重启自愈/90min 长稳/模式 churn×4/90min 长稳;发现的 3 个问题均串口 实锤定位并修复,修复后同条件复验全绿(churn 后 60min 长稳全门禁 PASS、 快循环 5/5 PASS、SOAP ack 218–796ms)。

各收 24.1 万包 125.3pps 满速;丢包间隙 227/228/159 全部重传救回(100%); 零 underrun/late/hard_resync、零 PTP 重锁、零 ASRC 异常;堆 ±1KB 稳定; 零重启。计划要求 30min×3 轮,本轮为第 1 轮,其余轮次留待后续长听时补测。

长稳全门禁 PASS;快循环 4/4 轮 PASS(热路径 claim_to_ready=329ms);测后 刷回 cap=3 发布固件冒烟 PASS。

output/airplay_30min_monitor.jsonl。

已知限制

(6–7.5s),窗口内自愈;稳态后恢复秒级。

重选周期),单台重启不受影响(~15s)。

source_waits=0、发送满速,非固件缺陷);建议信道 6/11 或 5GHz。

[1.7.1] - 2026-08-19

KabuSync 体验改善包 + 快速循环挂起专项修复。

修复(可靠性)

发送会阻塞会话任务整个 TCP 重传窗口(~15s),stop 超时后 destroy 拒释活任务, 每轮泄漏一个 fd;反复 stop/play 耗尽 lwIP TCP PCB 池,HTTP 与全部成员连接 同时瘫痪(此前需断电恢复)。组长 socket 现与成员侧一致:SO_SNDTIMEO 2s + keepalive 放宽(idle=5/intvl=2/cnt=3)。回归实测:原挂死场景第 5 轮自恢复, 全程无重启、HTTP 始终存活。

作为残余 lwIP 死锁类故障的自愈兜底。

新增(用户体验)

模式总览 + 一键全部切换 KabuSync/AirPlay。发现走 mDNS 双服务扫描 (_sendspin + _raop,两轮合并去重,任一模式下的设备都能被发现和切回), 成员信息经 esp_http_client 有界轮询,本机最后切换以保证响应送达。 新端点:GET /api/lan/devices、POST /api/lan/mode。

(不再静默待机),群组解散回待机。

不再只调组长本机。

验证

快速循环回归无硬挂起;看门狗无误重启(uptime 持续 >300s)。

已知限制(延续 1.7.0)

(成员侧会话拆除后的重新接受延迟,根因待成员侧串口定位)。

[1.7.0] - 2026-08-19

R3 起播提速发布:多机同步起播从"双峰 7–48s"收敛为"常态全员共同起播 ~6–9s", 并新增组长有界加入窗口兜底。底层同步算法与数据面零改动。

Added 新增

6000ms)。到期按当前已就绪集合冻结(可为 0,组长独播),未到成员走迟到 加入路径数秒内流式补入;冻结日志标注触发原因(all_ready/window/deadline)。

大步长无听感风险,根治"错首样本→样本饥饿"的 48s 尾部);收敛前突发采样 100→50ms;RTT 接收地板 25→50ms;会话边界热重启再对齐(同组长 120s 内重连 且首样本与 drift 外推差 <25ms 时再对齐 offset、保留 drift,亚秒再收敛, 带连续拒收冷复位兜底)。收敛判据(10 样本 + 600us)不变,同步质量不降级。

(SENDSPIN_CTRL_PORT)撞车导致 responder 每次开机 bind 失败、此前从未工作, 移至 8930 并加防回归钉住;probe 随 CONFIG_SENDSPIN_PEER_PROBE 启用。

会话拆除互斥等待改有界 2.5s、keepalive 放宽至 5s+3×2s(丢包 AP 误杀健康会话 的修复);wsclient 连接超时 5s→2s、重试间隔 1s→250ms。

验证(拥塞 AP,COM23/24/25 三机)

已知限制

接受延迟,导致此类场景第二轮起退化为"组长先播、成员约 10–15s 流式补入"; 长时间间隔的常规使用与长播稳态不受影响。

组长硬挂起(无 panic 转储,需断电/串口复位恢复;三机 10 轮后与五机第 2 轮 各复现一次)。连续播放长稳(三机 30min/10min、五机 15min)均零错误,常规 使用路径不受影响。成员侧 accept 阻塞与组长挂起同属会话生命周期问题,列为 下一调查项(需成员/组长两侧串口捕获定位)。

五机 15min 长稳四成员零 underrun/零 disc。

[1.6.1] - 2026-08-19

R3 起播问题修复与诊断化发布。核心成果:起播 0 失败(原 5/6 失败)、 两次崩溃/挂起隐患根因修复、起播分段遥测上线,慢起播残留尾端被精确定位 到成员时钟收敛(待干净 AP 复测分离环境贡献)。

Fixed 修复

NVS 读写会关闭 flash cache,触发 cache_utils.c:127 断言 panic(堆栈回溯 实锤)。修复:缓存加载提前到 sendspin_leader_init(内部 RAM 上下文), 冻结持久化移交专职内部 RAM worker。

压测下组长会硬挂起(纯 v1.6.0 12 轮对照无此问题)。改为 init 期一次性 创建常驻 worker,冻结仅发任务通知,12 轮压测验证无挂起。

Added 新增

available_ms(相对认领时刻),慢起播可直接拆分为发现/连接/收敛三段。

二次点名(final roll-call)均已实现并有架构测试钉住,但分别由 CONFIG_SENDSPIN_PEER_PROBE / CONFIG_SENDSPIN_FINAL_ROLLCALL(均默认关) 门控:前者待挂起风险排查后启用,后者待 barrier-tick 交互根因定位。

多成员组。

R3 验证数据(拥塞 AP,三机,10 轮)

发现+连接+Noise 三段均 <1 s(遥测实锤)——瓶颈不在发现环节。

[1.6.0] - 2026-08-18

用户层品牌改名发布。多房间同步模式对外名称由 Sendspin 改为 KabuSync, 仅调整用户可直接感知的载体;底层协议实现、代码命名与全部对外接口保持不变。

Changed 变更

可见载体:模式切换语音提示(改播 "KabuSync mode")、Android 发送端 App 名称与界面文案(apps/sendspin-android)、用户手册 docs/user_manual.md、本发布说明。

(stop→播报目标模式→持久化→重启);新增一次性 "Switch complete" 提示音 (success.mp3),仅在用户切换触发的启动首次 ACTIVE 时播报,失败回退启动不播。

AirPlay 低频监控 tools/g1_stage0/airplay_30min_monitor.py。

Unchanged 保持不变(兼容性承诺)

WebSocket 路径、MQTT/API 标识、SENDSPIN_GROUP_CAP 等代码标识符均不变。

资产(SPIFFS)有变化,USB 全量烧录或重刷 SPIFFS 生效。

Validated 已验证

跳变,成员堆零泄漏,leader CPU core0 62–64%。

PTP 零重锁,堆零泄漏。

[1.5.0] - 2026-08-17

三机 Sendspin 同步发布。首次实现无需服务器、无需 Apple 源的多台 ESP32 自包含 同步播放,同步精度从行业常见的 <10 ms 提升到 p95 <1 ms。

Added 新增

SENDSPIN_GROUP_CAP 定稿为 3(去实验前缀,range 2-8,>3 需扩展容量验证)。

全重置控制器并让渲染端 re-anchor,无需重启整组即可恢复同步;self_recoveries 计数经 /api/system/info 暴露。

单机循环听感等探针脚本。

Changed 变更

(DLNA / 电台 / AirPlay / Sendspin)统一汇聚到 48k 输出。

取代通用浮点重采样用于 44.1→48k;源路径 core1 占用 96.7%→55.9%, source_waits 归零。Kconfig AUDIO_FAST_RESAMPLE_441_480 默认 y。

限幅 ±200µs,抑制 WiFi 抖动坏样本导致的时钟瞬移。

ASRC 吸收而非填静音。

Performance / Quality 性能与质量

(对比原 AirPlay 基线 <10 ms,提升约 10 倍)。

Validated 已验证(发布门禁)

Known Limitations 已知限制

的亚毫秒同步与音质。干净 AP 复测与可选的静态成员白名单为后续项。

[1.0.0] - 早期基线

AirPlay 渲染基线(本仓库改造前上游版本,对应 1c68f12 v0.1.29 之后)。

使用指南固件下载返回首页