# EchoEar OTA 下载性能优化 日期:2026-10-01。设备:`KP01-020026091601`,型号 `ECHOEAR-S3`。 ## 实现 - 已确认清单型号、版本、有效期、大小、SHA256 和同源 HTTPS,HTTP 200 与 Content-Length 匹配后,使用 `esp_ota_begin(target, state.size)` 按已知镜像大小预擦备用应用分区。SDK 可合并对齐的 64KiB 擦除;运行应用分区另有地址检查,禁止作为目标。 - 写入缓冲使用 `MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT`,8KiB 优先、4KiB 降级,两个分配均失败即保留旧应用并报错。SPI1 的 SDK 非 DRAM 写入分支每次通过 32 字节临时缓冲拷贝,显式内部内存避免落入这条路径。未记录旧固件缓冲的实际位置,不能据此单独证明此前慢速的主因。 - HTTP 接收缓冲由 4KiB 增至 16KiB,写入缓冲保持内部内存。第二轮将 TCP 接收邮箱从 6 增至 24,运行时 Wi-Fi 动态接收缓存从 16 增至 32;TCP 窗口仍为 32KiB,静态接收、发送缓存与 BA 窗口保持原值。8KiB 写入块对齐 SDK 常规写入块大小,保留内部内存余量。 - 下载预算从分配和连接前开始,包含预擦、读取、写入、SHA 校验及 OTA 结束。每次 HTTP 等待按剩余预算裁剪,只有 EAGAIN 在同一连接与偏移上有限重试。SDK HTTP 的超时是每次 transport 等待的上限;生产代码在每次应用读取前后检查总预算,不宣称能在底层一次阻塞读取的中途主动取消。 - 累计并记录预擦、读取、写入、SHA、OTA 结束、最长阶段调用及完整用时;每 30 秒最多一条进度日志。成功摘要写入原有 100 字节持久化消息,经过重启确认再发云端成功回执。后续日志提交或启动选择失败明确覆盖摘要,保留真实失败原因。 - 不改变 OTA 日志结构、型号/签名/摘要/证书校验、应用槽、启动健康确认、A/B 回退及绑定/配网规则。SDK begin 可能先分配句柄再遇到擦除失败,非零句柄会被 abort 回收。 方案参考 [ESP-IDF 5.5.1 官方 OTA 性能建议](https://github.com/espressif/esp-idf/blob/v5.5.1/docs/en/api-reference/system/ota.rst);实现依据本机同版本 SDK 的 `app_update/esp_ota_ops.c`、`spi_flash/esp_flash_api.c`、`hal/spi_flash_hal.c` 和 `esp_http_client/esp_http_client.c`。 ## 验证 完整生产 C 与真实 SHA256 的 22 组故障测试通过 GCC 13.3、AddressSanitizer、UndefinedBehaviorSanitizer 和 float-cast-overflow。覆盖已知大小与非运行槽预擦、预擦错误句柄清理、预擦/连接/校验期限、8KiB/4KiB 降级与尾包、全部分配失败、读写/摘要失败、成功计时跨重启保存及后续失败原因。 `controls.17` 启动包和 `controls.18` OTA 包均通过 ESP-IDF 5.5.1 编译,大小 3,760,896 字节。前一版 16 的 38 项应用/配置来源中,仅 `main/device_ota.c` 和 `main/firmware_version.h` 变化;两新包之间仅版本字符串变化。SDK 配置、分区表、模型、音频和屏幕资源均未修改。 优化逻辑已应用程序方式安装到同一设备的 `ota_0 / 0x10000`:先核对原 16MiB 恢复备份、MAC、有效 OTA 选择、原 15 应用摘要与分区,再只写 17 应用;完整读回 SHA256 为 `43178315a21126608023cc18fba3ca6352e83096d46707cf0bcbc6a3440d9fd1`。分区表和 OTA 数据写前写后相同,原绑定数 1、配置 19/19、音量 56 均保留,云端确认 17 在线。 真实云端 17 → 18 已失败:任务 `2a3ca992-c246-46ab-98ee-dc5606ade2c4` 在接收 1,372,872 / 3,760,896 字节后出现 HTTP 读取错误,耗时约 355 秒,设备保留 17 并继续在线。这次 USB 只采集非敏感日志,18 镜像由设备通过 Wi-Fi/HTTPS 接收,没有通过 USB 写入。断开 USB 后的独立供电验证尚未完成。 设备实测预擦耗时 4.69 秒;前 605,685 字节的写入仅 1.149 秒,读取累计 206.511 秒,说明剩余慢速主要发生在网络读取。匿名候选下载连接累计重传 464 次、拥塞窗口降至 1;接收窗口非零,不能只凭这一连接快照确定丢包位置。源码核对未发现服务端 3 分钟硬期限,暂不修改生产 API 超时。 第二轮针对接收容量调整,增加慢速读取、短读取和 EAGAIN 的纯数字日志。完整生产 C 的 22 组故障测试及原有网络重连、BLE 配网生命周期、超时恢复、配网端点回归均通过。 真实 19 → 20 任务 `aebecc4c-a07a-4fc9-a37a-5afbc4fb2703` 已在约 31.3 秒接收完 3,760,896 字节并通过完整 SHA256。30.036 秒时已收 3,612,672 字节,累计读取 16.637 秒、写入 6.939 秒,预擦 3.065 秒。匿名候选 TCP 流的 ACK 在 10 秒内增加 957,138 字节,队列清零,拥塞窗口 18 → 25,无新增重传;这支持接收端调整的效果,但不能替代任务成功回执。 这轮随后在 SDK 的 `esp_ota_end` 镜像验证阶段触发内部栈断言:`esp_image_verify → bootloader_mmap → esp_mmu_map → esp_cache_freeze_caches_disable_interrupts`,控制任务原有 12KiB PSRAM 栈不满足要求。因此任务失败并保留 19,不能将下载完成称为升级成功。 最终修复将 END、启动分区选择及失败回退的镜像验证放在临时 8KiB `MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT` 任务中。先释放 HTTP/TLS、SHA 和内部写入缓冲,再创建验证任务,完成后由原任务调用 `vTaskDeleteWithCaps` 回收任务栈和 TCB;分配失败仍 abort 活跃句柄。已调用 `esp_ota_end` 的错误返回不再重复 abort。回退任务使用无重启 API,回收后由原任务按原有成功条件重启,保留健康时限、日志和启动选择顺序。编译后的 21/22 应用均为 3,761,200 字节,两包只差版本字符串;精确生产 C 的 24 组 OTA 及 4 套联网/BLE 回归通过三个 sanitizer。主机调度模拟不证明 DRAM/MMU 行为,最终安装路径另经真实硬件验收。 ## 最终实物验收 2026-10-01 09:43:24 北京时间,从已登录家庭端发起真实 21 → 22 云端任务 `18216193-939f-47a4-b49b-96e9002eb05e`。09:44:31 成功回执确认运行 `1.7.0-controls.22 / code 6`,备用 `ota_1 / 0x410000` 镜像经过完整摘要、SDK 安装校验与重启健康确认。镜像大小 3,761,200 字节,SHA256 `c13ae3b2aab18431466d40b5d60b101a8c3345e438be87ef474f85ac8653616f`。 | 实测项目 | 用时 | | --- | --- | | 下载、写入、SHA 和 SDK 安装校验 | 41.003 秒 | | 备用分区预擦 | 6.087 秒 | | 累计网络读取 | 22.656 秒 | | 累计 Flash 写入 | 7.267 秒 | | 累计 SHA 校验 | 1.409 秒 | | 用户发起至重启后的成功回执 | 67.394 秒 | 整包处理吞吐约 91.7KB/s,约为旧失败尝试 6.32KB/s 的 14.5 倍。比较基线是旧版本的未完成传输,不能描述为同一完整镜像的成功 A/B 速度测试;以上是单次实测,网络状况会影响时间。21/22 与 19/20 的接收设置相同。 成功后 MQTT 在线、支持 OTA,配置 19/19、音量 56/56、原绑定数 1 均保留。网页显示当前版本 22 和“升级完成”,成功消息持久化原始计时。USB 当时接着提供供电和非敏感日志,22 镜像由设备通过 Wi-Fi/HTTPS 下载,没有通过 USB 写入。**尚未完成断开 USB 的独立供电验收、受控实物启动失败回退和手机热点切换**;不将软件故障测试冒称为这些实物验收。 最终可上传应用包及已脱敏验收记录在 [固件 22 目录](../artifacts/firmware/1.7.0-controls.22/manifest.json)。发布使用真实 SSH 维护服务身份,四条 service/null 成功审计及完整公网回读均通过,原 11 个生产容器不变,临时 helper 和凭据文件均已清理。 ## 对照与解释 - 旧固件失败尝试:430.039 秒传送 2,719,221 字节,约 6.3KB/s;7 分钟期限内只完成约 72%。这是包含设备反压的端到端数据,未分开测出网络和 Flash 耗时。 - 同路线电脑对照:16 固件 3,758,224 字节,5.812 秒,646.6KB/s,完整 SHA256 匹配。对照缩小排查范围,但不能替代实物优化测速。 - 最终以完整镜像校验、启动确认、云端成功回执及新版本上线为验收条件。速度使用设备实际完成时间;总时间包含连接、预擦和校验,不能只选取某次最快读取作为整包速度。 受限验证资料保存在 `E:/Codex/story/.local-evidence/ota-speed-20261001`;含设备恢复数据的备份不可作为发布固件分发。