- 0.1.5/0.1.6:刷机模式(flash_mode,最多 2 路,60 分钟可加钟)——窗口内顶高按 4450 mV 绝对值判、不断 VBUS - 0.1.7/0.1.8:反灌断 VBUS 阈值放宽到 -300 mA(adb reboot bootloader 过渡实测 -208 mA) - 0.1.9:WiFi 最多存 4 组,连不上轮换;NVS v1 自动迁移并双写(OTA 回滚仍能上网) - 0.1.10:热插拔进来的刀片重新初始化 PCA9555 + INA226(否则 VBUS 常开、电流恒读 0) - 0.1.11:i2cbus 嵌套检测改看本任务持锁(原全局标志让 key 随机回 busy、vbus 随机回 io) - 0.1.12:硬重启先断 VBUS,开机校验通过 60 s 后再接回(否则假重启 / 误判没开机 / 进关机充电) - 0.1.13:这一路被安全判据断电时一并退出刷机模式(事件 21 arg 254) 26 ctest 全绿;新增用例均先在旧代码上确认变红。板 B 已 OTA 到 0.1.13-c5 并实测。 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
26 KiB
26 KiB
交接
更新:2026-09-28 23:20 固件 0.1.13-c5(断电自动退出刷机模式);板 B 经 OTA 推上;rack 已部署
0.1.13:这一路断了电 ⇒ 刷机模式一并退出(2026-09-28)
- 起因:第 2 槽刀片带电拔走后,刷机模式还挂了约 48 分钟、占着「最多 2 路」的名额。
check_present/ SR-5 / 刀片不应答 / mux 失联全断都是 t_safety 直接关 eFuse,不经过power命令,而只有power off会顺带退刷机模式。 - 修:
cmd_chgmode_tick()(执行器任务,chgmode 的唯一写者)每拍看一遍:刷机模式开着 + eFuse 关着 + 执行器不忙 ⇒ 退出,记事件 21 arg = 254CHG_EV_DROPPED。执行器忙(硬重启 HR_OFF 那几秒 eFuse 本来就关)不算。 - ⚠️ 254 带 0x80 位:rack
api.py事件命名里「断电自动退出」必须排在「加钟」前面判(已改 + 测试)。 - 测试:
test_cmd+1(硬重启中不退 / 关电后下一拍退、只记一次、名额让出);反向验证:去掉判断 ⇒ 红。26 ctest 全绿;rack 48 例全绿并已部署。 - ⏳ 真板未专门验证(需要带电拔一次开着刷机模式的刀片)。
0.1.11 / 0.1.12:命令随机 busy / io;硬重启带着 VBUS(2026-09-28 刷机时发现)
- 总线锁误判(0.1.11):
i2cbus_xact()拿锁前查全局s_in_xact防嵌套 —— 别的任务在事务里时它也是 true。 执行器撞上巡检那 ~2.3 ms(每 20 ms,≈12%)就被判成「嵌套」立刻回 STATE ⇒key随机回 busy、vbus随机回 io (反过来巡检撞上执行器也会白跑一圈)。改成hal_lock_held(HAL_LOCK_I2C)(本任务持锁才算嵌套)。 真板:第 3 路连开关 VBUS 40 次,0 失败。测试test_i2cbus+1(另一任务持锁 ⇒ TIMEOUT 而不是 STATE;旧代码红)。 - 硬重启先断 VBUS(0.1.11)+ 延后接回(0.1.12):VBUS 通着做
power_cycle的三个后果现场全见过 —— 手机靠 USB 撑着没断电(假重启);直供电流 ≈0 ⇒ 开机校验判「没开起来」再按两下;断电那刻带着 USB ⇒ 进关机充电,一拔 USB 就关机。 现在t_actuator_hard_reset()先断 VBUS,开机校验通过后再等ACT_HR_VBUS_BACK_MS= 60 s 才接回 (0.1.11 是校验一过就接回 ⇒ 手机还在 bootloader、关充电补丁没生效 ⇒ 13 s 顶高 4394 mV、SW-8 断 VBUS)。 等待期间执行器空闲;vbus/power命令、再次硬重启、EDL、abort、这一路被关电 ⇒ 作废。没开起来 / 出错 ⇒ 不接回。 真板:第 3 路 VBUS 通 → 硬重启:hr-off 时 vb=false、14 s 校验通过、72 s 接回,无顶高事件。 测试test_t_actuator+3(先断后接回、没开机不接回 / abort 不接回 / 本来没通不打开、等待期间被取消的三种)。26 ctest 全绿。 没修的:第 2 槽刀片被拔走时刷机模式不退出⇒ 0.1.13 已修。
0.1.10:热插拔进来的刀片重新初始化(2026-09-28)
- 现象(板 B,新主板 + 没刷补丁):刀片带电插进槽 ⇒ C67(VBUS)= 5 V,换 U5 也一样;
vb:false。 没刷补丁的手机被充到 4.4 V ⇒ SR-5 过压关路(反复 7 次)。插好再给底板上电就正常。 - 根因:
slot_init()/ina226_init()只在开机 bringup 做。热插拔时 PCA9555 上电复位不干净(SDA/SCL 经上拉寄生供电),VBUS_EN被配成输出高、影子却是 off ⇒ SW-8 不武装;INA226 CALIB = 0 ⇒ 电流恒读 0(第 2 路那段数据正是整 0)。 - 修:
t_safety认PRSNT#下降沿,在位连续T_SAFETY_INSERT_N= 5 圈(100 ms)后在本路锁内slot_init_locked()+ina226_init(); 失败隔 5 圈重试;拔走作废;开机在位的不重做。statussafety.inits= 成功次数。 - 测试 +2(
test_t_safety:乱寄存器插入 ⇒ 第 5 圈恢复、VBUS 断、键全松、CALIB 写回、只做一次;NAK 重试 + 插一半拔走不碰空槽); 反向验证:不置 need_init ⇒ 两例红。26 ctest 全绿。 - ✅ 真板验证(用户现场):带电插刀片后 C67 = 0 V、
safety.inits0 → 1。
0.1.9:多组 WiFi(2026-09-28,用户要求)
- 起因:板 B 只存一组 WiFi(CANBOX-5G),热点一关就离线;而改配置只收 USB。
netcfg_t.wifi[NETCFG_WIFI_N=4]。wifi命令 = 同名改口令、否则追加(回"n":组数;满了state);{"cmd":"wifi","ssid":"X","del":true}删(没有这组回range)。仍然只收 USB。net回应:ssid/pass_set= 正在连的那一组(net_status_t.wifi_idx),新增"ssids":[…]整张表。- 轮换(mlink_esp.c):没连上 ⇒ 换下一组;连上过又掉 ⇒ 先重试同一组;退避按「轮完一圈」算(
attempt / n)。 改配置时正连着的那一组没被删、口令没变、broker 没变 ⇒ WiFi / MQTT 都不断(加备用热点不掉线)。 - NVS:新键
net2(ver 2);读不到就读旧键net(ver 1)迁移。写时两个键都写,net放表里第一组 —— OTA 回滚到 ≤0.1.8 仍连得上网。 - 测试 +3(
test_net10 → 13 例:加 / 改同名、删 + 满表、NVS 空位压实);26 ctest 全绿。 - 真板:烧录后
ssids:["CANBOX-5G"](迁移成功)→ 加AAAAAABBBCCC(用户给的名字,周围扫不到)→ 加AAABBBCCC(本机扫到的实际热点,同口令)⇒ 连上 10.5.5.58、MQTT up、服务器显示在线。 - ⚠️ USB esptool 烧录 = 非计划重启 ⇒ 五路全断(第 1 路手机掉电)。第一次连接报
ClearCommError,COM5 重新枚举后重试即成。
0.1.7 / 0.1.8:反灌断 VBUS 阈值 −50 → −150 → −300 mA(2026-09-28,用户要求)
- 起因:刷机时重启过渡的瞬间反灌误断 VBUS,fastboot 断连。0.1.6 下实测 −59 mA;0.1.7(−150 mA)下
adb reboot bootloader实测 −208.5 mA 仍断。 T_SAFETY_REV_UA= −300000;判据本身(一圈就断、不做确认窗口、不碰 eFuse、刷机模式不豁免)不变。- 测试:注入 −60 → −310 mA,「小电流不算反灌」−40 → −250 mA;26 ctest 全绿。0.1.8 下
adb reboot bootloader过渡没再断 USB。
0.1.6:刷机模式默认 30 → 60 分钟 + 加钟(2026-09-28,用户要求)
CHG_MODE_DEFAULT_MIN= 60。- 加钟:
{"cmd":"flash_mode","rail":1,"add":30}(1~120,与 on / min 互斥)——chgmode_extend()只给开着的窗口加 (没开 / 宽限期里回state:那是重新开,不是加钟),加完剩余截断到 120 分钟;回"left":秒。 事件 21 arg =CHG_EV_EXTEND(0x80)| 分钟。rack:「更多 → 刷机模式加钟 30 分钟」(只在开着时出现)。 - 测试 +2(
test_cmd加钟全路径、test_t_safety_rules截断与宽限期拒绝),rack 48 例。真板:没开加钟被拒 / 开 3600 / 加钟后 5399 / 关。
0.1.5:刷机模式(2026-09-28,用户定「做,最多同时 2 路」)
- 起因:fastboot / bootloader 阶段内核补丁还没起来,手机充电器把直供节点顶到浮充 ~4.4 V(板 B 实测 4395 mV), SW-8 立刻断 VBUS ⇒ fastboot 刷到一半断连。历史 5 次顶高全是只断 VBUS、0 次关路。
- 依据:每路 eFuse TPS25940A 自带反向阻断(V(IN)−V(OUT) < −9.3 mV 断开),顶高时能量进不了 Buck / 12V 母线; 板 B 五次顶高实测电流 0 ~ −0.5 mA。
- 新模块
chgmode.{h,c}:每路一个截止时刻。只有执行器任务写(命令 +cmd_chgmode_tick()到期清理), 巡检只读;tick 必须清标志,否则 32 位毫秒时钟 ~24.8 天后过期窗口会「复活」。 - 判据(
t_safety.c judge_locked):窗口内「顶高断 VBUS」与 SR-5 过压都按绝对值CHG_MODE_MAX_MV= 4450 判; 窗口后 1 s 宽限期里 SW-8 回到按设定值(立刻断 VBUS),SR-5 仍按绝对值(节点往回落的几 ms 不许误关路)。 反灌(< −50 mA)与欠压判据不变。 - 命令 14
flash_mode:{"rail":1,"on":true,"min":60}(默认 60、1~120 分钟;0.1.6 由 30 改 60),回"left":秒; 最多同时 2 路(第 3 路回state);关着电不许开(state);power关电时一并退出。status 每路多"chg":秒。 - 事件 21
LOG_EV_CHG_MODE:arg = 分钟(开)/ 0(手动关)/ 255(到期)。 - rack:白名单 + 事件名 + 单板页「更多 → 刷机模式(1 小时)」(带确认),状态标签显示剩余分钟;模拟板同步。
- 测试:
test_t_safety_rules+10(绝对上限、反灌 / 欠压不变、按路、到期断 VBUS 不关路、宽限期后恢复、2 路上限、时钟回绕),test_cmd+3;26 ctest 全绿;rack 46 例。反向验证:去掉窗口内豁免 → 4 例红;tick 不清标志 → 回绕用例红。 - ✅ 台架实测(2026-09-28,板 B 第 1 路,0.1.6-c5),原始数据
10.5.5.13:/root/phonerail-src/bench-flashmode-20260928.tsv: · 开模式 + VBUS 通 →adb reboot bootloader,6 s 进 fastboot,fastboot 下确实在充电:节点 38644395 mV 锯齿 (充到浮充即停、手机耗下来再充,周期 34 s),VBUS 全程没断、fastboot 一直在线(getvar正常); · 该路电流 −1.0 ~ +1.5 mA(eFuse 反向阻断干净),12V 母线 12.02912.031 V 不动,输入电流 160 → 230 mA(+0.8 W,5V 负担很小); · FLT 在反向阻断期间一直为 true(与 RCB 同进同退,节点跌到 3864 仍为 true = 要 +100 mV 才重新导通), 固件对 FLT 只上报不动作 ⇒ 无事件。是 TPS25940A 的 RCB 指示,不是故障。rack 已改:chg>0 时这一路显示蓝色「刷机模式 · 充电中」、FLT 标签变中性「反向阻断」(模式外 FLT 仍标红); · 对照:充电中关模式 → 当圈 PUSHED_UP 断 VBUS(读数 4034 mV),不关路(宽限期),Buck 立刻接手 3917 mV / 240 mA;FLT 同时消失; · 重开模式 + VBUS → 2 s 回 fastboot。12.031)。 反灌判据保留是对的;实际影响只是「重启过渡时 USB 可能被断,进系统后再开 VBUS」。fastboot getvar unlocked: yes(BL 确已解锁); · ⚠️fastboot reboot回 MIUI 的开机过程中出现 −59 mA 反灌 ⇒ SW-8 断 VBUS(事件 290)。机理:开机大负载时 Buck 在连续导通, 节点只比设定高几 mV、没到 eFuse 的 −9.3 mV 阻断门限,充电器灌进来的电被 Buck 反向吸收。12V 母线没动(12.029
0.1.4:整片刀片不应答 ⇒ 关这一路(2026-09-28,用户拍板)
t_safety每路锁内:eFuse 开着、本路 INA226 和 PCA9555 同时 NAK,连续T_SAFETY_BLADE_MUTE_N= 10 圈(200 ms) ⇒ abort 序列 + 关 eFuse,记新事件 20LOG_EV_BLADE_MUTE。只关 eFuse、不去写那片 PCA9555(刚 NAK 过,再写只白等 IDF 超时)。- §10.10 前两行(只有一颗不答应 ⇒ 盲跑 / 不关输出)不变,
test_a_blind_rail_keeps_its_power原样;需求 §10.10 新增一行 + 表后第 3 条。 - 起因:板 B 第 2 槽
PRSNT2#卡低(见 0.1.3),刀片挪走后空槽 eFuse 开了约 4 h,原有判据一条都没响。 - 宿主机 26 ctest 全绿(
test_t_safety_rules39 → 43 例,test_evlog事件号 20)。rack 已同步事件 20(标红)。
0.1.3:上游电脑在不在进遥测(2026-09-28)
UP_VBUS_DET(IO6,R721/R722 分压,R722 下拉)启动时配成输入;status 新增"host":true|false|null(读不到 null)。 只进遥测、不参与判据。rack 单板页显示「上游:已连电脑 / 未连电脑」。- 起因:面板「损耗」在 ~0.5 W /
1.6 W 两档间跳,与刀片、手机无关;高档时 NTC1(U31 旁)高 610 °C ⇒ USB2517I 工作 (+ AMS1117 线性降压)约 1.1 W。⚠️host是 VBUS 在不在,不是 Hub 是否挂起:电脑休眠时多半仍是 true。 - ⚠️ 热重启后 DAC 设定点回默认(实测 set 4010 → 3900 mV,输出 4.04 → 3.93 V)—— 供电保住了,设定没保住。
- 板 B 第 2 槽
PRSNT2#卡低(09-28 02:09 第一次插刀片起,拔走后仍报在位,跨多次重启):硬件问题,待量 PRSNT2#(R607 / U20.9)对地、 看插座 B31 弹片。副作用:每圈i2c_err+2;02:13~06:25 空槽 eFuse 开着约 4 h(见 0.1.4 的判据)。
0.1.2:硬重启按完电源键之后看电流,没开就重按(2026-09-28)
- 起因:板 B 第 1 路 7 次 power_cycle 只开起来 1 次,失败时电流 1~2 mA、屏不亮;PCA9555 按键输出确实置位(命令都 ok), 镊子短接 J603 能开 ⇒ R2 电源键是主板弹片压刀片铜盘(决策 14),接触时好时坏。这是机械问题,固件只做兜底。
- 做法(
t_actuator,新状态hr-verify):按满 5 s → 松键 → 松键后 3~5 s 看电流,连续 5 个样本(0.5 s)≥ 100 mA 算开了; 否则重按,一共最多 3 次;3 次都不行结果no-boot,电继续开着。读不到电流不重按(对正在开机的手机再长按会进关机菜单)。 为什么 3 s 后才看:「按 2 s」那种 PMIC 醒了、SoC 没接住的情况,松手后 415 mA 撑约 3 s 才掉(宿主机测试把判定延迟改回 1 s 会被它骗)。 - 电流与 PG 一样从巡检快照喂进来(
t_actuator_set_boot_check(),app_main 接);不接就是 0.1.1 的行为,老测试一行没改。 - 新事件 19
LOG_EV_BOOT_CHECK「开机重按」:arg 低 4 位 = 按了几次,bit7 = 最终没开起来;一次就开的不记。 rack 已同步(名称「按了 N 次,开起来了 / 没开起来」,只有没开起来标红),已部署。 - 手动
key命令不校验、不重按(人的意图)。宿主机 26 ctest 全绿(test_t_actuator30 → 37 例),rack 43 例全绿。 - ⚠️ 实机只验到「第一下就开」(接触已恢复),重按路径靠宿主机测试。频繁出现事件 19 = 那一台弹片要处理。
c4:两处修复(2026-09-28,第一片刀片 + 小米8 上板时发现)
- 硬重启后手机不开机:电池端上电的小米8 停在关机态(只漏 ~2 mA)。现在
power_cycle等到 PG 后 长按电源键 5 s(新状态hr-boot,ACT_HR_BOOT_KEY_MS);NO_PG 时不按。ACT_KEY_MS_ON2000 → 5000: 实测按 2 s 时 PMIC 起来了(415 mA),一松手就掉回 1 mA。需求 SW-5 已同步。rack 的二级恢复(power_cycle)因此真的能拉起手机了。 上板:power_cycle → 4.1 s 上电 → hr-boot 5 s → res ok,手机进入开机电流曲线。 - 热点一断整板失联(死锁):执行器「持
s_mtx→esp_mqtt_client_enqueue等api_lock」, esp-mqtt 任务「持api_lock分发 DISCONNECTED →on_mqtt的ST_EDIT等s_mtx」。 现象:USB 命令无应答、WiFi 不重连、遥测停,只有 t_safety / t_thermal 的日志还在 —— 按到一半的键也会松不开。 修:执行器只往s_outbox(深度 4)拷一份,t_link里outbox_drain()交给 esp-mqtt;持s_mtx时不调 esp-mqtt。 ⚠️ 死锁状态下 OTA 进不去,只能 USB 烧(esptool--before usb_reset,第一次常报 ClearCommError,再跑一次就过)。 - c5:带载欠压误关。小米8 插屏幕后开机峰值 ~2.1 A,轨 1 读回 3785 mV(设定 3900,−115 mV),SR-5 的 −100 mV 直接关路。
用户拍板:欠压一侧放宽到 −250 mV(
T_SAFETY_UNDER_MV),过压仍 +100 mV;需求 SR-5 / SW-2 / 验收表同步。 压降 ≈ 55 mΩ × 2 A 的来源(Buck 负载调整 / eFuse / 采样电阻 / 走线)还没拆开查。
一句话现状
OTA 换固件 / 超时回滚现在走 warm_restart()(main/warm_esp.c):目标固件认接管 ⇒ 手机不断电;
崩溃 / 看门狗 / 上电 / 兜底回滚仍全关(SR-6)。26 个 ctest、448 个用例全绿。板 B 跑 0.1.2-c5(经 OTA 推上去的)。
定稿写在需求 §10.9「重启不断电」。C2 OTA 的说明在下面。
0.1.1:I²C 不应答被报成 STATE(真板独有,sim 测不出)
- 根因:IDF 5.4 的
i2c_master_transmit/receive/transmit_receive在从机 NACK / SCL 超时时返回ESP_ERR_INVALID_STATE, HAL 照翻成HAL_ERR_STATE(= 没 init)。于是:I2C_LOST 事件的错误码是 5;命令层from_hal又把 STATE 当成「执行器忙」⇒ PCA9555 不应答时power off回busy(eFuse 其实已经关了、意图也清了),上位机会以为没执行。 - 修:
hal_i2c_esp.c传输函数用map_xfer_err(INVALID_STATE → NAK);cmd_exec.c新增from_bus, I²C / GPIO 路径(set_voltage / vbus / power)失败一律io,from_hal只留给执行器。 - 测试:sim 加
sim_i2c_inject_err_ch()(指定注入的错误码);test_cmd加一段 STATE 注入 ⇒ 必须是 io 不是 busy (把关电那行改回 from_hal 时这段确实红,回应就是台架上看到的{"err":"busy"})。 - 上板(台架镜像,CH1 强制在位):
power off回io、CH1 关掉;I2C_LOST 的错误码 5 → 2。
重启不断电(c3)
- 三道闸:RTC 标记(软件复位 + 魔数 / 反码,一次性)· 目标镜像能力标记(第 288 字节
PRCAPS1,老固件不认就不锁)· 起来后读 EN 真电平核对。 rail_en_init_keep(adopt)五根一律放开 hold:崩溃时上一条命的锁可能还在。pca9555_init_keep():方向寄存器是我们写的才留 VBUS;键 / EDL 清零。- 实测:台架镜像(空槽强制 CH1 在位,没提交)开 CH1 → OTA 带重启 → 事件
18/7 mv=1(新固件读到 IO9 仍为高、接管)。 ⚠️ 没测到的:示波器上看 EN 在重启期间有没有毛刺(warm_take读到高只能说明「起来时还是高」); 「锁着 + 非计划复位」这条路径只有宿主机测试(真板上造不出来)。插上刀片 + 手机后要看一次手机有没有掉电重启。 - 第一次从 c2 升到 c3 仍会断电一次:锁 EN 的是正在跑的固件,c2 不会锁。
下一步(没人定,候选)
- 示波器验证:插一片刀片,CH 开着,OTA 带重启,抓 EN / 输出电压(
scope_*工具)。 - 在手机上看一眼 rack 新界面(390 px 左右):无头 Chrome 最窄只到 ~500 px,窄屏只验到那里。
- 下面「其它挂着的事」里 A15 / A16 留下的几条(I²C 总线挂死恢复、PD 降载、风扇阈值实测)照旧。
rack 侧(服务器前后端,详见 rack/README.md)
- 固件页(顶栏「固件」):上传
build/phonerail.bin→ 选板推送;上传时核镜像头(非本工程 / 非 app 镜像 / >3 MB 直接拒), 显示每个镜像「重启不断电:支持 / 不支持」,勾「立刻重启」时按它给不同提示。通用/cmd不放行ota(URL / 哈希只能由固件库生成)。 - 下载目录自动清理(每小时):库里登记的不删;
ota_push.py直传的放满 7 天删;半截.tmp1 小时删。 - 界面改版:仪器前面板风格 + 暗色模式(跟随系统);总览每个槽带最近两分钟电流小曲线(平线 = 疑似卡死)。
- 底板温度曲线(单板页):两路 NTC + 风扇占空比,10 分钟 ~ 7 天;后端
board_tele_min分钟聚合留 90 天, 接口GET /api/boards/{id}/history。NTC1 贴 Hub 稳压 U31,NTC2 贴输入降压 U37。
踩到的坑(c3)
.rodata_custom_desc没人引用会被--gc-sections丢掉 ⇒main/CMakeLists.txt加-u g_phonerail_caps。PROJECT_VER在 CMake 配置时读 ⇒ 改phonerail.h不会重配,镜像头版本会是旧的 ⇒ 顶层加了CMAKE_CONFIGURE_DEPENDS。- 撤掉台架补丁后要重新编译再推:
build/phonerail.bin还是台架版(这次推错了一次,已换回)。 - Git Bash heredoc 里的 Python 写
\0又变成真 NUL 了(这次在 rack/firmware.py)—— 含反斜杠的一律写 .py 文件。
怎么推固件
python tools/ota_push.py b81f3fc9c8bc # 推 build/phonerail.bin:只下载 + 校验 + 切分区,下次复位生效
python tools/ota_push.py b81f3fc9c8bc --reboot # 校验过就立刻重启(板上 ≥c3 且目标镜像认接管 ⇒ 手机不断电,否则五路全断一次)
python tools/ota_push.py b81f3fc9c8bc --status
它把镜像传到服务器 /root/phonerail/fw/<sha 前 16 位>.bin(nginx 7765,只放行这种文件名,不过门户;
不进固件库,7 天后 rack 后端自动删 —— 要长期留着就从界面上传),
再在服务器上用后端账号经 1884 发命令、盯遥测。
rack 管理界面「固件」页也能推(2026-09-27 加,上传 → 选板 → 可选立刻重启并二次确认;见 rack/README.md「推固件」)。
C2 改了什么
partitions.csv重排(16 MB):nvs 0x9000 没动;ota_0 / ota_1 各 3 MB;otadata 0x610000;evlog 挪到 0x612000(历史事件清空了一次)。sdkconfig.defaults+sdkconfig:flash 16 MB(之前误写 8 MB);CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y(bootloader 配置,只有 USB 整片烧才生效)。main/ota.{h,c}新(宿主机可测):URL / 哈希校验、判定(busy / 待验证拒 / 同镜像不重下)、健康判据。main/ota_esp.{h,c}新:t_ota任务(esp_http_client + mbedtls sha256 +OTA_WITH_SEQUENTIAL_WRITES,每块让一个 tick)、 核 project_name、切分区;t_link里判健康 / 到点重启(先等 2 s 让回应发出去、事件落盘);esp_timer 150 s 兜底回滚。cmd新命令ota=13(cmd_net.c的do_ota);status 多ota一块;evlog新事件LOG_EV_OTA=18。- 顶层
CMakeLists.txt:PROJECT_VER取PHONERAIL_FW_VERSION(否则镜像头版本是 git describe,回滚后ota.bad报79c9f8b-dirty)。 test/test_ota.c新(13 例);test_cmd/test_evlog的冻结数字表各加一行。tools/ota_push.py新;rack/deploy/nginx-phonerail-fw.conf新(已部署);rack/backend/prrack/protocol.py加事件 18 / 命令 13(已部署)。
上板实测(板 B,五槽空)
正常:c2 → c2-t1 经 MQTT --reboot:下载 ≈8 s,推送到自证健康 19 s;事件 18/1 → 18/2 → 18/6 → BOOT → 18/4
哈希错:err "hash"、got 996064、固件不变;下载 20 s 里 t_safety skipped 0
回滚:服务器删掉板子 broker 账号 → USB 发同镜像 reboot:true → 起在 c2-rb(pend true)→ 120 075 ms 记 18/5 → 回到 c2-t1、bad 报版本
(账号已原样恢复,备份已删)
收尾:c2-t1 → 正式 0.1.0-c2 经 MQTT --reboot 成功
踩到的坑(只写会再犯的)
- 路由器不是整机 DMZ:新开的 7765 公网 TCP 都连不上,用户手动加了转发才通(本机测公网要
curl --noproxy '*', 否则 7890 代理回的 502 会被当成服务器的)。 - 本机 PowerShell
Set-Content -Encoding ascii改phonerail.h把中文注释写成??(已git checkout还原)—— 改它一律用 sed。 - Git Bash heredoc 里的 Python 写
'\0'会变成真 NUL 字节进源码;含反斜杠的替换写成 .py 文件再跑。 - 宿主机
bash host/test.sh用-j 4在 cygwin 上偶发「link: Subprocess aborted」,cmake --build build -j 1就过。
其它挂着的事
- 风扇还没下单购买(2026-09-27):板 B 两路报
rpm 0+ 停转是预期的。到货插上后看status.fan.rpm;在转却仍是 0 再查 Q9/Q10 与 PCNT。 - 风扇闭环四个阈值 + 占空比下限 + PPR 都是「待上板实测」(t_thermal.h),手机上架跑满载之后再定。
- A15 留下的:I²C 总线挂死恢复(HAL 层);PD 上限的降载动作(§8.3);CH224A 读到 100 W 与早上记的 20V/3A 不一致,待核。
本次新增/修改的文件
main/ntc.{h,c}新(宿主机可测)— mV → Ω → 查 Murata NCP18XH103F03RB 的 R-T 表(−40~125 °C、5 °C 一格、格内线性插值); 贴 +3V3 / 贴地 / 表外一律「读不到」(⇒ t_thermal 满速)main/thermal_esp.{h,c}新(只进 IDF)— ADC1 oneshot + 曲线拟合校准(8 次平均,引脚反查通道);两个 PCNT 单元数 TACH 上升沿(累计计数、1 µs 毛刺滤波)main/app_main.c—task_thermal任务体(每 10 s 打一行温度 / mV / 占空比 / 转速);thermal_esp_init()+t_thermal_init()放在上电恢复之前;cmd_env.thermal = &s_thermmain/CMakeLists.txtREQUIRES 加esp_adc esp_driver_pcnt;host/CMakeLists.txt加ntc.ctest/test_ntc.c新(4 例:表点、⭐方向「越热电压越低」、⭐开路/短路不是温度、比例式);版本0.1.0-a16
上板实测(板 B,五槽空,风扇状态未知)
status: "temp":[38249,34027] 分压端 1266 / 1384 mV ← 两路 NTC 读数合理(NTC1 在电感区更热)
"fan":{"duty":300,"en":true,"rpm":[0,0],"stall":[true,true]}
events: [31, 14318, 0, 11(FAN_STALL), 0] [32, …, 11, 1] ← 启动宽限 5 s + 一个窗口之后各报一次,不刷屏
踩到的坑(只写会再犯的)
- 比例式 NTC 的电源误差估算:+3V3 偏 1 % 在 25 °C 附近是 ~0.6 °C(中点处电阻误差是电源误差的 2 倍、NTC 每度 ~4 %),不是 0.3 °C。
- PowerShell 5.1 的
Set-Content -Encoding utf8会写 BOM —— 改phonerail.h这种文件用 sed,别用它。 python在 Git Bash 里是 Windows Store 占位程序,会挂住;一律用/c/Python314/python。