固件 C2:OTA 双槽 + 失败回滚(已上板实测正常 / 哈希错 / 回滚三条路径)

- 分区重排(16 MB,nvs 0x9000 不动):ota_0 / ota_1 各 3 MB、otadata 0x610000、evlog 挪到 0x612000;
  flash 大小改正为 16 MB;开 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
- ota.{h,c}(宿主机可测):URL / sha256 校验、判定(下载中 busy、待验证拒、同镜像不重下)、健康判据
- ota_esp.{h,c}:t_ota 任务经 HTTP 下载、边下边算 sha256、核 project_name 后切启动分区;
  新固件 120 s 内连不上 broker 就回滚(t_link 判 + esp_timer 兜底);重启前等回应发出、事件落盘
- 新命令 ota = 13(两条链路都收,reboot 默认 false:复位 = 五台手机断电一次);status 加 ota 一块;
  新事件 LOG_EV_OTA = 18;PROJECT_VER 取 PHONERAIL_FW_VERSION
- test_ota.c 13 例,全工程 442 例全绿;版本 0.1.0-c2
- 服务器:nginx 7765 只放行 /fw/<16 hex>.bin(不过门户,路由器转发已加);tools/ota_push.py 推送;
  rack protocol.py 加事件 18 / 命令 13(已部署)
- 板 B:推送到自证健康 19 s;错哈希不切分区;删掉 broker 账号后 120 s 回滚,bad 报出被回滚的版本

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-09-27 14:03:13 +08:00
共同撰写人 Claude Opus 5.5
父节点 c37c08aa43
当前提交 ae1aad3b7c
共修改 29 个文件,包含 1561 行新增和 28 行删除
+19 -6
查看文件
@@ -1,18 +1,31 @@
# PhoneRail 的分区表。默认的 partitions_singleapp.csv 里没有给事件日志留地方,
# 而 §10.7 要求「记录路径不依赖上位机在线」—— 那就得有一块自己的 flash。
# PhoneRail 的分区表(C2 重排:双槽 OTA,16 MB flash)。
#
# ⚠️ **nvs 的位置与大小一格都不许动**(0x9000 / 0x6000):里面是 WiFi / MQTT 配置
# 和 `rail_want`(每路上电意图)。挪了 = 板子失联 + 重启后一路都不开。
#
# ota_0 / ota_1:各 3 MB。C2 时 app 是 0xE9920(≈0.9 MB),留 3 倍余量;
# 改大小要跟着改 `ota.h` 的 OTA_SLOT_BYTES(ota_esp_init 会核一遍,不一致打错误日志)。
# 没有 factory:回滚的对象是「上一个好的 OTA 槽」,factory 只是第三份永远不更新的旧版。
# otadata:两个扇区,记「从哪个槽启动 + 那个槽的验证状态」。bootloader 的回滚靠它。
# 0x10000 之前放不下(nvs + phy_init 刚好顶到 0x10000,app 又必须 64 KB 对齐),所以放在两个槽后面。
#
# evlog:**裸的 data 分区**,不是 nvs、也不是 spiffs(理由见 hal_store_esp.c 开头)。
# 大小 0x11000 = 17 个扇区。为什么是 17 而不是 §10.7 里写的「4096 条 ≈ 64 KB」:
# 每个扇区要拿一个 16 B 的槽当扇区头(epoch),于是一扇区只装得下 255 条,
# 16 扇区 = 4080 条,**差 16 条不够 4096**。17 扇区 = 4335 条,
# `test_capacity_meets_the_requirement()` 卡的就是这个 ≥4096。
# 8 MB flash 上多这 4 KB 没有任何代价。
# C2 把它从 0x110000 挪到了 0x612000:evlog 按 label 找分区、格式不变,**只是历史事件清空一次**。
#
# ⚠️ 改了这里要跟着改 `sim_hal.h` 的 SIM_STORE_SECTORS —— 两边不一致时
# ⚠️ 改了 evlog 的大小要跟着改 `sim_hal.h` 的 SIM_STORE_SECTORS —— 两边不一致时
# 宿主机测试仍然全绿,板子上却是另一个容量。测试里有一条断言把它们钉在一起。
#
# ⚠️ 换了分区表之后**第一次必须经 USB 整片烧**(bootloader + 分区表 + otadata + app),
# 之后才能走 OTA。
#
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x100000,
evlog, data, undefined, 0x110000, 0x11000,
ota_0, app, ota_0, 0x10000, 0x300000,
ota_1, app, ota_1, 0x310000, 0x300000,
otadata, data, ota, 0x610000, 0x2000,
evlog, data, undefined, 0x612000, 0x11000,
1 # PhoneRail 的分区表。默认的 partitions_singleapp.csv 里没有给事件日志留地方, # PhoneRail 的分区表(C2 重排:双槽 OTA,16 MB flash)。
2 # 而 §10.7 要求「记录路径不依赖上位机在线」—— 那就得有一块自己的 flash。 #
3 # ⚠️ **nvs 的位置与大小一格都不许动**(0x9000 / 0x6000):里面是 WiFi / MQTT 配置
4 # 和 `rail_want`(每路上电意图)。挪了 = 板子失联 + 重启后一路都不开。
5 #
6 # ota_0 / ota_1:各 3 MB。C2 时 app 是 0xE9920(≈0.9 MB),留 3 倍余量;
7 # 改大小要跟着改 `ota.h` 的 OTA_SLOT_BYTES(ota_esp_init 会核一遍,不一致打错误日志)。
8 # 没有 factory:回滚的对象是「上一个好的 OTA 槽」,factory 只是第三份永远不更新的旧版。
9 # otadata:两个扇区,记「从哪个槽启动 + 那个槽的验证状态」。bootloader 的回滚靠它。
10 # 0x10000 之前放不下(nvs + phy_init 刚好顶到 0x10000,app 又必须 64 KB 对齐),所以放在两个槽后面。
11 # #
12 # evlog:**裸的 data 分区**,不是 nvs、也不是 spiffs(理由见 hal_store_esp.c 开头)。 # evlog:**裸的 data 分区**,不是 nvs、也不是 spiffs(理由见 hal_store_esp.c 开头)。
13 # 大小 0x11000 = 17 个扇区。为什么是 17 而不是 §10.7 里写的「4096 条 ≈ 64 KB」: # 大小 0x11000 = 17 个扇区。为什么是 17 而不是 §10.7 里写的「4096 条 ≈ 64 KB」:
14 # 每个扇区要拿一个 16 B 的槽当扇区头(epoch),于是一扇区只装得下 255 条, # 每个扇区要拿一个 16 B 的槽当扇区头(epoch),于是一扇区只装得下 255 条,
15 # 16 扇区 = 4080 条,**差 16 条不够 4096**。17 扇区 = 4335 条, # 16 扇区 = 4080 条,**差 16 条不够 4096**。17 扇区 = 4335 条,
16 # `test_capacity_meets_the_requirement()` 卡的就是这个 ≥4096。 # `test_capacity_meets_the_requirement()` 卡的就是这个 ≥4096。
17 # 8 MB flash 上多这 4 KB 没有任何代价。 # C2 把它从 0x110000 挪到了 0x612000:evlog 按 label 找分区、格式不变,**只是历史事件清空一次**。
18 # #
19 # ⚠️ 改了这里要跟着改 `sim_hal.h` 的 SIM_STORE_SECTORS —— 两边不一致时 # ⚠️ 改了 evlog 的大小要跟着改 `sim_hal.h` 的 SIM_STORE_SECTORS —— 两边不一致时
20 # 宿主机测试仍然全绿,板子上却是另一个容量。测试里有一条断言把它们钉在一起。 # 宿主机测试仍然全绿,板子上却是另一个容量。测试里有一条断言把它们钉在一起。
21 # #
22 # ⚠️ 换了分区表之后**第一次必须经 USB 整片烧**(bootloader + 分区表 + otadata + app),
23 # 之后才能走 OTA。
24 #
25 # Name # Name
26 nvs nvs
27 phy_init phy_init
28 factory ota_0
29 evlog ota_1
30 otadata
31 evlog