IoT 实现记录

Vanling 自有 ESP32 固件(apps/iot)的实现判断记录。AI 产出,未经人类审阅,见上级说明。

对应的系统说明在开发文档 / IoT 固件。

发现速查

下面这张表是回答「某个结论是什么、凭多少数字」的地方。绝大多数问题到这一层就结束,不必读全文。 依据标注沿用各记录正文的约定:结论 + 仍然生效的数字 + 被否方案及原因 + 未决项。

摄像头(GC2145 + ST7789)

发现结论数字
视野怎么算输出宽 ÷ 阵列宽,与寄存器取值无关。ratio 5 整除,320×240 就是满视野320/1600 = 1/5
bins 与帧率同一个开关的两面。本仓库写 0x000x00 4.5 fps / 691 KB/s;0x01 10 fps / 1558 KB/s
ratio 档位ratio 5 是唯一读满整个阵列的档;1 不出帧,3/4 欠采样ratio 3 = 3444 KB/s,ratio 1 = 0 帧
标量模式 0xfd帧率翻倍但视野更窄,本驱动不写(留上电表 0x00)但读回5 → 10 fps
环形描述符链已撤回,保留线性链。外设不走完第一圈就停ring 28 KB/s vs 线性 645 KB/s
线性链丢字节ring 必须整帧大小,代价是重建降到每秒 4 次丢 1 字节后每行错开一行(移动条带)
PSRAM ring永远写乱。esp-hal 无法表达 internal SRAM only,只能停在官方拒绝的配置上150 KB ring
帧率上限来自数据量,不是 AEC 曝光。想更快只能缩窗口或换芯片5.2 fps;exposure 486 lines vs 读出 ~1200
像素时钟固定 24 MHz。改 20 MHz 时 PLL 不锁,输出结构对、全是噪声且寄存器读回一切正常—
反复进出 Camera 页链每次重建且建起来就堵。render 侧 8% → 29–36%启动直入 11 repaints/窗口;反复进出 25–29
进页空窗约 192 ms 无法缩短,用过渡帧 CAMERA WAIT 缓解5.2 fps ⇒ ~192 ms
面板读回面板没有 MISO,寄存器读回全是 0x00,只能目视验证—
帧缓冲持有者FrameOwner 三变体,第四种组合不可表示(曾用两个标志导致永久卡死)153,600 B

音频(ES7210 + ZTS6216)

发现结论数字
采集平面可否节流不可。环只有 sample() 排空,被批到 30 s 就是环撞满 + I2S 停机ring 60 ms 撞满
Off 的实现必须在消费端主动 park 链,不能只是不 poll需求生效后 1 ms 停链
静音判定必须读作「没变化」,否则整条 diff 链空转—
平面边际代价随别的任务醒着而变,不是常数12.52%(独自跑)→ 15.73%(有旁侧)
节流的效果不省设备总量,只是把执行器从一处搬到另一处94.8% → 94.0%
poll 侧缩放按实际节奏缩放,两端都与实测对上+0.6% / −4.1%(缩放前高 40%)
量化噪声会喂出 65 dBA 的假底噪,dBA 表需对原始表换算65 dBA
SPL 标定0 dBFS = 102 dB SPL 是这一颗麦克风的值(397.8 mV/Pa → 满量程 2.51 Pa)101.98 dB

运动(QMI8658A)

发现结论数字
tap engine不可用(据称),敲击判定改在 core 侧;QMI8658_CAPABILITIES 不声明 TAP—
敲击门限用原始幅值,不减重力估计静止摆动 8–22 mG;门限 150 mG / 峰值 250 mG
aLPF 滤波器实测无关,关掉。敲击是宽带瞬态,滤波只会削峰静止噪声与敲击读数完全一致
No-Motion是电平不是事件,只发布 0→1 上升沿,否则静止时按帧率持续报 Still—
摇晃判据比较矢量残差并数过零次数,不是门限之上的时间2.0 g;W3C 的 2.5 g/60 Hz 单轴幅值不适用

输入与触摸

发现结论数字
页面循环硬件上正确。[DISPLAY] 的 2 秒窗口看不出页序,据此判断过一次「页面在乱跳」是错的46 次跃迁,偏离 0、重复 0
四路轮询只有 capture 可关;交互路径按设计不可关稳定形态 button 0 / touch 1 / motion 3 / capture 16–20 ms
交互地板与需求无关的理论下限,不是调优空间10.87%

回声消除

发现结论数字
有没有公开的验证录音**没有。**四份都试过:两份乐鑫录音、Microsoft AEC-Challenge(按设计如此——消费级音频栈可能在发送侧自带 AEC/NS)、ITU-T G.168(附件 D 有显式冲激响应表,但誊录无法验证)相关 0.137 / 0.743 / ≤0.137;Wiener 上界 −4.97…−0.42 dB(40 个 GUID 最好值仍为负)
ERLE 是那份数据集的指标吗**不是。**论文原文「we can not use ERLE and PESQ」ERLE 与主观质量 PCC 0.31
ITU-T G.168 的标准路径**方向正确但誊录不可验证。**8 条 m_i(k) 显式冲激响应表,无需任何音频文件抽取丢首列;读序有歧义;两种读法都违反标准自身「幅度响应 ≤ 0 dB」不变式,超出 1–11 dB
标准自己怎么说G.168 Test 12 注记:真实声学测试向量「for further study」标准机构也确认它们不存在
验收基准改为同一条路径上的离线最优(当场解最小二乘)。算出来的,不是誊来的与最优差距 0.1–1.9 dB(4 条路径)
15 dB 那道闸是对路径说的,不是对算法说的。尾长于滤波器时离线最优本身只有 14.4 dB任何 128 抽线性滤波器都到不了 15
生成的路径必须有直达反射否则没有起点,延迟搜索锁不住,报的是噪声峰无直达反射:12 抽路径报 15、4 抽报 22,ERLE 0.4 dB;补上后延迟全部精确命中,61 dB
冻结拦得住持续损伤,拦不住起始相干度计量器 ~512 样本时间常数,滤波器已跑完那几百个样本冻结覆盖 98%;路径外能量 涨 3.7e6 倍;冻结后不再增长。未修复
CPU:先量时钟当时整块板跑在 80 MHz,不是 240。CpuClock 的默认臂就是 _80MHz,Config::default() 从来不设最高频率esp_hal::clock::cpu_clock() = 80,000,000 Hz
测量曾用错过时钟第一版按 240 MHz 换算出的「219 cycles/MAC」是错的。参照循环揭穿了它——一条平凡整数循环本该更快,却慢 193 倍参照 FIR 12 cycles/MAC @80 MHz
分块自适应:过了**每 4 个样本更新一次系数。**稳态 ERLE 逐位不变,收敛变慢L=16 9,680 µs ≤ 10 ms;L=128 70,945 → 34,911 µs
频域 MDF(最佳实践)写完 512 点定点 FFT 先测它再谈架构,量完就删了(产品路径零引用)。变换一项就超预算 1.8 倍,分区和滤波一分钱没花3,046 µs/次 = 预算的 182%;RAM 3,064 B → 6 KB(省不了 RAM)
第二核:数字逐位相同FROM_CPU_INTR1 是同一颗核的第二个中断源,不是第二核。产品全在 core 0,core 1 空着两核 20 多项测量全部逐位相同 → 瓶颈是两个核共享的
IRAM:也不是蝶形放进 .rwtext(确认在 0x4037a4c4,非 flash)3,046 → 3,235 µs,反而慢 6%。取指不是瓶颈
四条循环 IPC 全在 0.1–0.33取指否了、双核否了,剩下的只能是内存系统本身。最干净的一段代码(FFT 蝶形)IPC 最低蝶形 0.09;参照 FIR 0.33
延迟搜索能单独撑爆 30 ms 的界一次搜索约 32 ms,每 256 ms 一次,摊到每 tick 3.0 ms(占 L=16 的 31%)。栅格抽稀已否宽带参考下相关峰只有一个样本宽——延迟 1 时相邻的 lag 0 和 4 都相关不到(146 vs 1000)
时钟 240 MHz,控制台没坏esp-hal 那条「换 PLL 会影响 USB Serial/JTAG」是 [据称]。实测 240 MHz 跑通、控制台全程正常L=16 3,248 µs;L=128 11,639 µs;最坏 tick 21.6 ms,在 30 ms 界内
cycles 一个都没变时钟纯粹是除数:cycles/蝶形 112 → 112,参照 FIR 12 → 12@240 MHz 时间全部 ÷3
数据缓存走不通ESPHome/ESP-SR 的 AEC 要 DATA_CACHE_64KB + LINE_64B,没有就拒绝启动。esp-hal 无 API、esp-metadata 无 eFuse 字段[据称] 本板有没有,未读出
IPC 谜题不是缓存按读写排,不按工作集:只读 12 / 31.6,读+写 105.8 / 119。工作集最小的不是最快的write-back 缓存 + 每抽头回写同一位置
但翻出一个大得多的杠杆整块板跑在上限的三分之一,现有的 17–25 ms 最坏 tick 同样被压在这里。esp-hal 注释称换 PLL 会影响 USB Serial/JTAG(本仓库控制台正是它)@240 MHz:AEC L=32 → 7.6 ms(塞得下);最坏 tick → 5.7–8.3 ms
这个 target 的 FPU 是关的同形状 f32 循环 >120 s vs i32 孪生 140 ms,二进制带全套 libgcc 软浮点助手浮点走软件模拟,「换浮点」不是捷径
代价3,040 B RAM(板上)state_bytes::<128>() = size_of::<Canceller<128>>();旧函数只数滤波器,低报 3.4×

硬件约束

发现结论数字
电源管理还没有 PM,所以没有时钟源锁是对的(加两把无持有者的锁就是死代码)。但顺序不能反—
light-sleep外设被时钟门控且不产生中断,依赖外设中断的驱动必须持 NO_LIGHT_SLEEPDVP 与 I2S 链都在此列
共用 MCLK两个单元跑在各自时钟域,发送单元驱动引脚、采集被强制从属,方向不可交换with_signal_loopback 名字是唯一误导处
主任务栈随摄像头进产品而变,以链接 ELF 符号为准_stack_start_cpu0 = 83,060 B
esp-hal 栈溢出 watchpoint会误触发,不能当溢出判据—
数据缓存默认 64 KiB ⇒ reclaimed 区为空esp-hal 把 0x3FCF0000 以上「留给堆」,但要从数据缓存那一头扣。默认配置下 #[ram(reclaimed)] 无处可放本仓既有 ELF 里 0x3FCF0000 以上零符号;压到 32 KiB 换回 32 KiB

网络(Wi-Fi)

发现结论数字
本世代有无 TCP/IP 栈没有 lwIP/esp-netif(crates.io 404 + 仓库无目录 + CHANGELOG 零命中)。替代物是 esp-radio(L2)+ embassy-net(smoltcp)esp-radio 自带 embassy_net_driver::Driver 实现,接上就能拿 IP
「连上」的定义拿到地址才算。参考固件的 STA_CONNECTED 处理函数是空的,连接位在 GOT_IP 里置关联 ≠ 连上
三个阶段的预算是三份扫描归射频、关联归 AP 与重试、DHCP 归租约,合并成「连接中」就看不出哪一段慢探针分开计时
连接默认值的坑快速扫描取第一个匹配 ⇒ 强的同名 AP 一次认证失败就被换成弱的参考固件实测:−73 dBm 在范围内,却连上 −79 dBm
DRAM 预算是板上最硬的一条dram_seg 只能再拿 12,344 B(主栈断言 65,536 B 之下只剩这些),装不下驱动缓冲主栈 77,880 B;Wi-Fi 堆 8 KiB + 32 KiB reclaimed
PHY 标定恢复在 S3 上是 todo!()PROVIDE(misc_nvs_restore = __esp_radio_misc_restore) 把它绑到 #[cfg(xtensa)] 的 todo!(),而 blob 对它是未定义引用规避手段:分区表不写 phy_init(代价:每次开机重标定)
分区表espflash 默认 factory 1 MB,而固件加 Wi-Fi 前已 630 KBfactory 6 MB;16 MB 板

日志

发现结论数字
默认级别info 只留事件,稳态可以长时间零行,静默不是死机169 行/43.8 s → 35 行/80 s;最后一行在第 6.2 s
一个旋钮IOT_LOG_LEVEL,认不出的名字退回 info 而不是静音;级别只由 log::max_level() 持有开机首行 [IOT] log level … 是唯一凭据
周期遥测帧成本、栈水位、feed 节奏、链路心跳、扫描列表全在 debug;越界的那一次升 warn同源码 debug 构建 40 s 内 102 条 DEBUG
删行买到什么不是 CPU,是读的人的时间:USB CDC 的串口写不进执行器持有账1426 → 1417 ms / 5 s 窗口
标签一个模块一个标签,不是一种外设一个[MIC]→[AUDIO]、[SPEAKER]→[PLAY]、[TOUCH]→[INPUT]、[RADIO]→[WIFI]、[PSRAM]→[HEAP]、[BACKLIGHT]→[DISPLAY]

探针

每个探针是独立 bin,把框架里的一层单独接到真硬件上。改驱动之前先看这里——「哪个问题跑哪个探针」在这一层就该结束。

它们不是产品的另一种形态:产品固件产生不出探针能产生的那些输入。整机五页四平面全是 Continuous,所以产品上永远拿不到 (配置 × 节奏) 矩阵里除默认格以外的任何一格。

探针回答什么报什么数
demand-probe运动平面每格配置 × 节奏,各值多少每格先 settle 再测:总线事务/秒、执行器占用墙钟占比、测量的 poll 数(供前两者互相校验)
camera-probe相机取到帧、面板逐像素写出,画面有没有被替换帧字节数与校验;「画面看着不对」不是测量
audio-probe放音链路独占运行时的稳态交替两个音效的 stall 与缺口
multicore-s3启动第二核能否在主栈底下存活栈顶是否被启动序例覆写(ThreadFlag 就住在那里)
aec-probe线性回声消除跑一个音频 tick 要多少时间,L = 16/32/64/128首/均/最坏 tick、占 20 ms 比例、延迟搜索/滤波/计量器分开计时
clock-probe运行期切 CPU 频率要多少、切完谁还准、切完核 1 怎么样三档同一工作量的耗时比(不看 cpu_clock(),它读配置不读硬件)、切换墙钟、跨切换的 Instant 差、两核同量对比
wifi-probe射频与 IP 栈能不能立起来,这块板的 DRAM 预算够不够扫描/关联/DHCP 三个分开的耗时、扫描到的条数、驱动堆每 2 秒的余量、开机标定代价
moon run iot:probe-demand-s3 && moon run iot:flash-probe-demand-s3

probe-*-s3 只构建,flash-probe-*-s3 才烧录;端口在 moon.yml 的命令里。wifi-probe 两个芯片都有(-s3 / -c6):同一份源码,C6 那条用普通复位而不是 -a watchdog-reset(原因见 @/development/iot/flashing.md)。

wifi-probe 还多一步:它的凭据是构建期输入,所以构建命令上带两个变量

IOT_WIFI_SSID=home IOT_WIFI_PSK=secret moon run iot:probe-wifi-s3
IOT_PORT=/dev/cu.usbmodem1422301 moon run iot:flash-probe-wifi-s3
# C6:同一份源码,普通复位启动
IOT_WIFI_SSID=home IOT_WIFI_PSK=secret moon run iot:probe-wifi-c6
IOT_PORT=/dev/cu.usbmodem14401 moon run iot:flash-probe-wifi-c6

不设这两个变量它照样能跑,只是会说「这个构建没有让我连任何东西」——那是「扫描本身通不通」的答案,而不会假装自己是一次连接测试。两个名字在 scripts/iot-xtensa.sh 的 FORWARDED_ENV 里,宿主测试 build_env.rs 会在「读了却没转发」时失败构建。

五个 bin 每次 check-s3 都编一遍,只编不链。 没有编译它们的探针会烂在原地:每个都自己调一遍 bring-up 或芯片 API,Board、Speaker、芯片层一改就会编译失败,而没人会注意到,直到某天在台架上要用。

探针与产品的分界线会无声地被挪动:两边各自都还能编译,所以编译期抓不到漂移。改动若触及探针调用的框架路径(poll_plan_of、MotionConfig::for_mode、驱动入口),把探针烧一次当作证据,不要只等编译通过。

检查任务

moon 按内容哈希缓存。不写 inputs 时默认把项目下的每个文件都算进去;写了 inputs 是替换而不是叠加。所以经仓库根脚本编译的任务(check-s3 / check-features / probe-*-s3)必须显式点名那个脚本,否则改分派脚本不会让缓存失效。详见检查任务与缓存记录。

发现结论数字
缓存键里有什么任务文本 + 项目级 env: + 每个输入文件一条 sha1;默认输入是整项目check-host 的默认键 121 条
脚本在键外经 scripts/iot-xtensa.sh 编译的任务要显式点名它,否则改脚本不失效追加一行注释:键 0973f854 → 577401ca;还原后命中
check-features 曾经每次真跑改前是 cache: false。现在同树重跑 0.09 s,改源码仍付全价2m20s570ms(八套 S3 子集各 ~15 s)
C6 产品检查改宿主 stable:该目标有预编译 core/alloc,固件本来也是宿主产的1m12s → 1.65 s
8 次容器合成 1 次被否:容器启动实测 0.45 s,最多省 3.5 s,不值得加批量协议3 × 0.45 s(5.4 s 是冷启读数)
仍走分派脚本的守卫check-features 的 C6 子集特意保留:它是 S3-only ESP_HAL_CONFIG_DATA_CACHE_SIZE gating 在 CI 里唯一的见证—

各记录正文

需要推导过程、被否方案全文或复现方式时再下钻。

章节内容
放音实现记录Cp0Disabled 调查、被否方案、feed 节拍归属、ISR 拆分、音效配方
硬件约束记录共享 MCLK、时钟源锁与 PM 的先后顺序、麦克风前端写序与标定、栈容量由来
摄像头实现记录GC2145 视野算术、bins 与标量实测、PSRAM 不可用由来、面板接线、反复进出导致链重建后停滞
音频实现记录静音判定、执行器边际代价实测、采集链只有一处能停、标定走查受需求门控、积压深度、dBA 与原始列、量化噪声底噪
Wi-Fi 实现记录本世代没有 lwIP 只有 esp-radio+embassy-net、「连上」= 拿到地址、连接默认值抄自参考固件的三条、DRAM 预算怎么凑出来的、PHY 标定 todo!() 与分区表的规避手段
运动实现记录敲击判定的被否方案、门限来源、No-Motion 是电平不是事件
输入与触摸实现记录页面循环硬件实测、2 秒窗口看不出页序、四路轮询里只有一路可关
回声消除实现记录乐鑫向量为何测不了 AEC、步长不能按残余缩放、相干度冻结判据、延迟搜索的置信度与限频、ERLE 稳态与全程的差
运行期改 CPU 频率记录esp-hal 的 Frozen Clock Frequencies、越界写寄存器实测比值 1 : 2.00 : 2.95、切频 1–50 µs、第二个核与外围无差异、表跟着芯片走、挡路的既有缺陷
检查任务与缓存记录moon 缓存键的组成与脚本盲点、check-features 开缓存、C6 检查改宿主、被否的容器合并
日志实现记录默认 info 的实测依据、IOT_LOG_LEVEL 单点持有、事件/遥测/越界三档判据、标签统一、post-mortem 缺口

节流的完整推导、实测对照与方法在架构文档,不在这份记录里。