固件 0.1.19~0.1.21-c5:配网热点(SoftAP + 网页)、风扇 40 ℃ 以下断电
- 0.1.19 配网热点:没配 WiFi / 连续 60 s 连不上时自己开 SoftAP,192.168.4.1 网页里加删 WiFi、改 MQTT; 配置拼成 wifi / mqtt 命令走唯一的命令处理器(新链路号 3 CMD_LINK_AP),MQTT 仍不收配置命令 - 0.1.20 热点口令固定 88888888 - 0.1.21 风扇:板温 >=40 ℃ 上电、<38 ℃ 断电,40~65 ℃ 线性调速;fan 命令加可选 sec(下限到点自动交回) - mqtt 命令加 keep_pass;net 回应加 ap;status 的 fan 加 left - 新增 test_prov(13 例),test_t_thermal +3;29 ctest 全绿 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
+329
-283
@@ -1,283 +1,329 @@
|
||||
# 交接
|
||||
更新:2026-10-06 固件 **0.1.18-c5**(CH224A 档位进遥测);板 B 经 OTA 推上;rack 已部署
|
||||
|
||||
## 0.1.17 / 0.1.18:CH224A 档位实时进遥测(2026-10-06)
|
||||
- 起因:rack 要显示 PD 供电信息,而档位只在 `bringup.c` 启动时读一次;板 B 历史上读到过 5 A → 3.25 A → 3 A。
|
||||
- 新模块 `pdmon.{h,c}`:每秒读 `0x09`(协议状态)+ `0x50`(档位电流),**只读**。先试上次应答的支路(主干 / mux 通道 5)。
|
||||
档位变了(连续两拍一致、非 0、和日志里最后那条不同)记 `LOG_EV_PD_LIMIT`,**arg 2 = 运行中换档**(0 / 1 仍是启动那条)。
|
||||
只报不动:`pd_limit_mw` 仍是启动时定的,降载(§8.3)照旧没人做。
|
||||
- status 新增 `"pd":{"st":136,"ma":3000}`,这一拍没读到是 `null`(满配 1164 / 1536 字节,`test_cmd` 的余量断言改成「至少留 256」)。
|
||||
- ⚠️ **0.1.17 放在热管理任务里读,板 B 实测 `safety.skipped` 每秒涨 1**(持锁那一下正好撞上巡检的下一拍,那一圈五路都没读)。
|
||||
0.1.18 挪进巡检任务(`t_safety_step()` 之后、每 50 圈一次),`skipped` 停在启动时的 1 不再涨。别再挪出去。
|
||||
- 板 B 实测:`st` = 0x88(bit3 = PD 握上;bit7 手册没写,rack 只认已知位)、`ma` = 3000。两次 OTA 都是热重启,五路 en 全程保持。
|
||||
- 测试:新增 `test_pdmon`(6 例),`test_cmd` 加 `pd` 两条断言;**28 ctest 全绿**。rack 侧见 `rack/frontend/src/pages/Board.vue` 的「PD 供电」格。
|
||||
|
||||
## 0.1.15 / 0.1.16:开机长按 3 s 试验失败,所有开机操作统一 5 s(2026-09-30)
|
||||
- 0.1.15 把 `ACT_KEY_MS_ON` 5000 → 3000:板 B 第 2 路 `power_cycle` 三轮全是「按住 330~390 mA、松手掉回 0」⇒ `no-boot`(事件 19);
|
||||
同一台手工 `key pwr 5000` 一次开起来(松手后稳在 ~250 mA)。⇒ 小米8 开机键 2 s、3 s 都不够,**别再往下调**。
|
||||
- 0.1.16 改回 5000;rack 单路「更多 → 开机」原来发的是 `ms:2000`(多半从来就开不了机),改成 5000、文案「长按电源 5 秒」。
|
||||
- 上板:第 3 路 `power_cycle` → hr-boot 5 s → 松键后 ~210 mA 平台 → `res ok`。
|
||||
|
||||
## 0.1.14:Hub 按需启停(2026-09-29,用户定「方案一:GPIO8 按住复位」)
|
||||
- 起因:五路 VBUS 全关时,上行连着电脑 Hub 仍是活动态(第 6 口的 ESP32 USB 让电脑挂不起它),约多耗 1 W。
|
||||
- **推翻 R2 D13 代价 2 / R-11「运行期不许动 `HUB_RST#`」**,理由与退路见 `刀片式方案规划-R2.md` §8.2a、`main/hubctl.h` 文件头。
|
||||
- 新模块 `hubctl.{h,c}`:执行器任务每拍 `hubctl_tick()`(空闲时 ≤100 ms 一拍)。开的理由(按优先级)
|
||||
`mode` 常开 > `vbus` 任一路 VBUS > `flash` 刷机模式 > `act` 执行器忙 > `hold` 保持窗口 > `link` MQTT 断满 60 s;
|
||||
都没有时再给 `idle` 10 分钟(`HUB_IDLE_MS`),从最后一次「需要」算起 —— 启动、`UP_VBUS_DET` 上升沿、USB 上来一条命令都算。
|
||||
关 = GPIO8 配输出拉低;开 = 配成输入靠 R716 拉高,**从不驱高**。
|
||||
- **命令 15 `hub`**(两条链路都收,会动东西 ⇒ 必须带 seq):
|
||||
`{"cmd":"hub","mode":"auto"|"on"}` 改模式(NVS 键 `hub_mode`,默认 auto);
|
||||
`{"cmd":"hub","min":30}` 保持 1~240 分钟;`{"cmd":"hub","min":0}` 取消保持并结束 idle 窗口(没别的理由下一拍就关,量功耗用)。
|
||||
两个键可同时给。回应带 `"hub":{...}`。
|
||||
- status 新增 `"hub":{"on":true,"mode":"auto","why":"vbus","hold":秒,"idle":秒}`(满配 1140 / 1536 字节)。
|
||||
- **事件 22 `LOG_EV_HUB`**(rail 0):arg 0 = 关、1 = 开、0x10|模式 = 改模式;bus_mv = 开的理由(`hub_why_t`,冻结:
|
||||
0 off / 1 mode / 2 vbus / 3 flash / 4 act / 5 hold / 6 link / 7 idle)。
|
||||
- `bringup.c` 的上电脉冲不变(仍只在 POWERON);脉冲后 `hubctl_init()` 把脚放成输入。
|
||||
- 测试:新增 `test_hubctl`(9 例:启动窗口、理由优先级、断网 60 s、电脑插上 / USB 命令刷新、保持与 min=0、
|
||||
时钟回绕不复活、引脚从不写 1 且每次翻转只记一条、模式存盘、NVS 垃圾值回 auto),`test_cmd` +1(hub 命令)、
|
||||
`test_evlog` / 冻结编号同步;**27 ctest 全绿**;IDF 构建通过(0xf5960,余 68%)。
|
||||
- ✅ **板 B 实测(2026-09-29,0.1.14-c5,三路手机开着、VBUS 全关、上行插着电脑)**:Hub 开 / 关交替各 60 s,
|
||||
看「底板损耗 = 输入 − 三路主板合计」(手机电流 ±100 mA 的抖动会淹没输入电流):开 **1.77 W**,关 0.69 / 0.72 / 0.80 W ⇒
|
||||
**Hub 空转 ≈ 1.03 W**(12 V 输入电流 377 → 288 mA)。原始数据 `10.5.5.13:/root/phonerail-src/bench-hub-20260929.jsonl`,
|
||||
脚本 `bench-hub.sh` 同目录。`hub min:0` 下一拍就关、`hub min:3` 立刻开(why = hold),事件 22 各记一条。
|
||||
- ✅ **rack**:白名单放行 `hub`(mode auto/on、min 0~240);事件 22 命名(唤醒理由 / 休眠 / 模式改为…,mV 列置空);
|
||||
单板页「Hub」一行:状态 + 按需 / 常开 + 「唤醒 30 分」+「马上休眠」;模拟板同步。pytest 50 例,已部署。
|
||||
- ⏳ **没验证**:上行电脑不是本机也不是服务器,电脑那一侧 Hub 消失 / 重现、手机 VBUS 开后 adb 1~2 s 回来,都还没亲眼看过。
|
||||
- ⚠️ 现在起板 B 没人用 USB 满 10 分钟 COM 口就消失 —— 要 USB 烧录 / 看日志,先在 rack 点「唤醒 30 分」(或 MQTT 发 `hub min`)。
|
||||
|
||||
## 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 = **254 `CHG_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 圈重试;拔走作废;开机在位的不重做。status `safety.inits` = 成功次数。
|
||||
- 测试 +2(`test_t_safety`:乱寄存器插入 ⇒ 第 5 圈恢复、VBUS 断、键全松、CALIB 写回、只做一次;NAK 重试 + 插一半拔走不碰空槽);
|
||||
反向验证:不置 need_init ⇒ 两例红。26 ctest 全绿。
|
||||
- ✅ 真板验证(用户现场):带电插刀片后 C67 = 0 V、`safety.inits` 0 → 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_net` 10 → 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 下确实在充电**:节点 3864~4395 mV 锯齿
|
||||
(充到浮充即停、手机耗下来再充,周期 3~4 s),**VBUS 全程没断**、fastboot 一直在线(`getvar` 正常);
|
||||
· 该路电流 −1.0 ~ +1.5 mA(eFuse 反向阻断干净),12V 母线 12.029~12.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。`fastboot getvar unlocked: yes`(BL 确已解锁);
|
||||
· ⚠️ **`fastboot reboot` 回 MIUI 的开机过程中出现 −59 mA 反灌 ⇒ SW-8 断 VBUS**(事件 290)。机理:开机大负载时 Buck 在连续导通,
|
||||
节点只比设定高几 mV、没到 eFuse 的 −9.3 mV 阻断门限,充电器灌进来的电被 Buck 反向吸收。12V 母线没动(12.029~12.031)。
|
||||
**反灌判据保留是对的**;实际影响只是「重启过渡时 USB 可能被断,进系统后再开 VBUS」。
|
||||
|
||||
## 0.1.4:整片刀片不应答 ⇒ 关这一路(2026-09-28,用户拍板)
|
||||
- `t_safety` 每路锁内:eFuse 开着、本路 INA226 **和** PCA9555 同时 NAK,连续 `T_SAFETY_BLADE_MUTE_N` = 10 圈(200 ms)
|
||||
⇒ abort 序列 + 关 eFuse,记新事件 **20 `LOG_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_rules` 39 → 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 旁)高 6~10 °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_actuator` 30 → 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_ON` 2000 → **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 天删;半截 `.tmp` 1 小时删。
|
||||
- **界面改版**:仪器前面板风格 + 暗色模式(跟随系统);总览每个槽带最近两分钟电流小曲线(平线 = 疑似卡死)。
|
||||
- **底板温度曲线**(单板页):两路 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_therm`
|
||||
- `main/CMakeLists.txt` REQUIRES 加 `esp_adc esp_driver_pcnt`;`host/CMakeLists.txt` 加 `ntc.c`
|
||||
- `test/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`。
|
||||
# 交接
|
||||
更新:2026-10-06 固件 **0.1.21-c5**(风扇 40 ℃ 以下断电);板 B 已 OTA 推上,新板 b81f3fc85d2c 还是 0.1.20(热重启,旧 WiFi / MQTT 配置仍在,热点 `PhoneRail-c9c8bc` 待机);**热点本身还没实触发验证**;rack 已部署
|
||||
|
||||
## 0.1.21:风扇 40 ℃ 以下断电 + 带时限的下限(2026-10-06,用户要求)
|
||||
- **凉了断电**(`t_thermal.{h,c}`):两路 NTC 取高,≥40 ℃(`T_THERMAL_FAN_ON_MC`)给 `FAN_EN`,<38 ℃(`FAN_OFF_MC`)撤;
|
||||
中间维持原状(2 ℃ 迟滞,板温就在 40 ℃ 上下晃)。40→65 ℃ 仍是 30 %→100 % 的线性曲线。
|
||||
**一律开着的三种情况**:温度读不到、上位机给了下限、启动后读到第一个温度之前那一秒。
|
||||
断电期间不判停转、转速报 0、`duty` 报 0;`stalled` 保持断电前的结论(没插的那只不会每次启停各记一条事件)。重新上电先过 5 s 启动宽限。
|
||||
- **`fan` 命令加可选 `sec`**(0~86400):下限保持这么多秒后自动回 0;不给 = 一直保持(老行为)。回应带 `sec` 时才回 `"sec"`。
|
||||
status 的 `fan` 多一项 `"left"`(下限还剩几秒,0 = 没时限)。
|
||||
- 测试:`test_t_thermal` +3(迟滞启停、启停不误报停转、凉着也得转的两种情况 + 时限到期 / 续期 / 回绕),`test_cmd` 加 `sec` 的断言;
|
||||
原来拿 25 ℃ 当「凉但在转」的用例改用 40 ℃。**29 ctest 全绿**;IDF 0xfe420。
|
||||
- ✅ 板 B(OTA,热重启,五路 en 保持):板温 30 / 32 ℃ ⇒ `en:false, duty:0, rpm:[0,0], stall:[false,false]`;
|
||||
`fan duty 500 sec 5` ⇒ 立刻 `en:true, duty:500, left:3`,6 s 后自己回到 `en:false`。
|
||||
- ⏳ 新板 `b81f3fc85d2c`(风扇真能转的那块)推的时候不在线,**还是 0.1.20** —— 上电后补推,并看一次真风扇在 38~40 ℃ 之间启停。
|
||||
- rack:白名单放行 `fan` 的 `sec`(0 / 不给不下发这个键)、单板页风扇断电显示「停」、下限选项加 30 %;59 pytest,已部署。
|
||||
- AppTask(`D:\AppTask`):转发口白名单加 `fan`;App「机架」页点「风扇」那一格开面板
|
||||
(交回自动 / 不低于 30·50·70·100 % 保持 30 分钟 / 满速一直)。**只编译过、APK 已打出,后端和 App 都还没部署。**
|
||||
- ⚠️ **风扇不转的根因(10-06 新板上现场量出)**:J26.2 只有 1.7 V、Q7 栅极 11.3 V = 比 12 V 低一个二极管压降 ⇒
|
||||
怀疑 **DZ3 贴反 / 封装极性反**(正向导通把 Vgs 钳在 −0.7 V)。用户处理后新板风扇转了(300 ‰ ≈800 rpm,满速 ≈3850 rpm,+120 mA),
|
||||
**具体怎么修的没确认**;板 B 同样不转,待同样处理。第 2 路(J27)转速一直 0,是没插还是没转也没确认。
|
||||
|
||||
## 0.1.20:热点口令固定为 88888888(2026-10-06,用户要求)
|
||||
- `PROV_PASS`(prov.h)编在固件里,所有板子一样;0.1.19 的随机口令、NVS 键 `ap_pass`(板 B 上还留着,没人读了)、
|
||||
`prov_pass_from/ok` 全部删掉。`net` 的 `ap.pass` 照报。代价写在 prov.h「安全」:口令公开,门只剩「没网时才开 + 人在无线范围内」。
|
||||
- 29 ctest 全绿;IDF 0xfe250;板 B OTA 后 `ap.pass` = 88888888。
|
||||
- ⚠️ 推 0.1.20 的下载途中(重启前约 26 s,当时跑的是 0.1.19)**第 1 路 `PRSNT#` 变高**(pr 1→0,eFuse 随之关),之后一直不在位;
|
||||
其余四路没动。是有人拔了刀片还是接触问题,没查清 —— 重启后启动事件 15(刀片不在位)rail 1。
|
||||
|
||||
## 0.1.19:配网热点 SoftAP + 网页(2026-10-06,用户定「连 WiFi 失败后默认回到配网模式」)
|
||||
- 起因:改网络配置只有 USB 一条路;路由器换口令 / 板子搬地方就得拆机插线,Hub 休眠后连 COM 口都没有。
|
||||
- **判据**(`prov.{h,c}`,宿主机可测):没配 WiFi ⇒ 立刻开;连续 60 s 没地址 ⇒ 开;开着不设超时;
|
||||
STA 连上 ⇒ 没人立刻关 / 有人留 60 s。热点上有人且 5 分钟内动过网页 ⇒ STA 重连间隔 ≥20 s(只放慢不停)。
|
||||
- **胶水**(`prov_esp.c`):`STA ↔ APSTA` 切换;esp_http_server(`/` 网页、`/s` 状态、`/scan`、`/c` 提交,其余 302 回 `/`);
|
||||
DNS 劫持任务(手机连上自动弹页)。热点 `PhoneRail-<id 末 6 位>`,WPA2,口令随机 10 位存 NVS 键 `ap_pass`。
|
||||
网页是 `main/prov.html`(EMBED_TXTFILES)。只应答本地地址 = 热点地址的连接(linger 那一分钟里路由器一侧进不来)。
|
||||
- **配置怎么进**:网页 POST 按行的纯文本 → `prov_build_line()` 拼成 `wifi` / `mqtt` 命令 → 命令队列(新链路号 **3 `CMD_LINK_AP`**)→
|
||||
唯一的命令处理器。`wifi` / `mqtt` 现在是「不收 MQTT」(USB 与热点都收)。seq 用随机数。
|
||||
- `mqtt` 新增 `"keep_pass":true`(口令沿用已存的;网页口令留空就发它)。`net` 新增 `"ap":{"on","why","sta","ssid","pass"}`。
|
||||
- mlink_esp.c 顺带两处:`esp_wifi_connect()` 当场被拒(网页正在扫描)⇒ 3 s 后自己重排,免得重连链断掉;
|
||||
没连着时改配置 ⇒ **刚加 / 刚改口令的那组先试**(原来是接着试 `s_idx`)。
|
||||
- sdkconfig:`CONFIG_HTTPD_MAX_REQ_HDR_LEN` 512 → 1024(手机浏览器的请求头超 512 会回 431)。
|
||||
⚠️ `sdkconfig` 不进 git,只改 `sdkconfig.defaults` 对已有的构建目录不生效 —— 本机那份已手改。
|
||||
- 测试:新增 `test_prov`(13 例:开 / 关 / 留 / 放慢、回绕、表单拼命令与拒绝、keep_pass、链路口径、net 的 ap);**29 ctest 全绿**;
|
||||
IDF 构建通过(0xfe490,余 67%)。
|
||||
- ⏳ **真板一项都没验**:热点起不起得来、手机弹不弹页、APSTA 下 STA 重连对网页的影响、扫描、提交后连上并自动关热点、
|
||||
OTA 后旧配置仍在(NVS 布局没动,只多一个键)。OTA 有 120 s 自证回滚兜底(MQTT 连不上就回 0.1.18)。
|
||||
- 没做:~~热点口令不能改~~(0.1.20 改成固定值);rack 界面还没显示 `ap`(要看口令得发一条 `net`);开关热点不记事件日志。
|
||||
|
||||
## 0.1.17 / 0.1.18:CH224A 档位实时进遥测(2026-10-06)
|
||||
- 起因:rack 要显示 PD 供电信息,而档位只在 `bringup.c` 启动时读一次;板 B 历史上读到过 5 A → 3.25 A → 3 A。
|
||||
- 新模块 `pdmon.{h,c}`:每秒读 `0x09`(协议状态)+ `0x50`(档位电流),**只读**。先试上次应答的支路(主干 / mux 通道 5)。
|
||||
档位变了(连续两拍一致、非 0、和日志里最后那条不同)记 `LOG_EV_PD_LIMIT`,**arg 2 = 运行中换档**(0 / 1 仍是启动那条)。
|
||||
只报不动:`pd_limit_mw` 仍是启动时定的,降载(§8.3)照旧没人做。
|
||||
- status 新增 `"pd":{"st":136,"ma":3000}`,这一拍没读到是 `null`(满配 1164 / 1536 字节,`test_cmd` 的余量断言改成「至少留 256」)。
|
||||
- ⚠️ **0.1.17 放在热管理任务里读,板 B 实测 `safety.skipped` 每秒涨 1**(持锁那一下正好撞上巡检的下一拍,那一圈五路都没读)。
|
||||
0.1.18 挪进巡检任务(`t_safety_step()` 之后、每 50 圈一次),`skipped` 停在启动时的 1 不再涨。别再挪出去。
|
||||
- 板 B 实测:`st` = 0x88(bit3 = PD 握上;bit7 手册没写,rack 只认已知位)、`ma` = 3000。两次 OTA 都是热重启,五路 en 全程保持。
|
||||
- 测试:新增 `test_pdmon`(6 例),`test_cmd` 加 `pd` 两条断言;**28 ctest 全绿**。rack 侧见 `rack/frontend/src/pages/Board.vue` 的「PD 供电」格。
|
||||
|
||||
## 0.1.15 / 0.1.16:开机长按 3 s 试验失败,所有开机操作统一 5 s(2026-09-30)
|
||||
- 0.1.15 把 `ACT_KEY_MS_ON` 5000 → 3000:板 B 第 2 路 `power_cycle` 三轮全是「按住 330~390 mA、松手掉回 0」⇒ `no-boot`(事件 19);
|
||||
同一台手工 `key pwr 5000` 一次开起来(松手后稳在 ~250 mA)。⇒ 小米8 开机键 2 s、3 s 都不够,**别再往下调**。
|
||||
- 0.1.16 改回 5000;rack 单路「更多 → 开机」原来发的是 `ms:2000`(多半从来就开不了机),改成 5000、文案「长按电源 5 秒」。
|
||||
- 上板:第 3 路 `power_cycle` → hr-boot 5 s → 松键后 ~210 mA 平台 → `res ok`。
|
||||
|
||||
## 0.1.14:Hub 按需启停(2026-09-29,用户定「方案一:GPIO8 按住复位」)
|
||||
- 起因:五路 VBUS 全关时,上行连着电脑 Hub 仍是活动态(第 6 口的 ESP32 USB 让电脑挂不起它),约多耗 1 W。
|
||||
- **推翻 R2 D13 代价 2 / R-11「运行期不许动 `HUB_RST#`」**,理由与退路见 `刀片式方案规划-R2.md` §8.2a、`main/hubctl.h` 文件头。
|
||||
- 新模块 `hubctl.{h,c}`:执行器任务每拍 `hubctl_tick()`(空闲时 ≤100 ms 一拍)。开的理由(按优先级)
|
||||
`mode` 常开 > `vbus` 任一路 VBUS > `flash` 刷机模式 > `act` 执行器忙 > `hold` 保持窗口 > `link` MQTT 断满 60 s;
|
||||
都没有时再给 `idle` 10 分钟(`HUB_IDLE_MS`),从最后一次「需要」算起 —— 启动、`UP_VBUS_DET` 上升沿、USB 上来一条命令都算。
|
||||
关 = GPIO8 配输出拉低;开 = 配成输入靠 R716 拉高,**从不驱高**。
|
||||
- **命令 15 `hub`**(两条链路都收,会动东西 ⇒ 必须带 seq):
|
||||
`{"cmd":"hub","mode":"auto"|"on"}` 改模式(NVS 键 `hub_mode`,默认 auto);
|
||||
`{"cmd":"hub","min":30}` 保持 1~240 分钟;`{"cmd":"hub","min":0}` 取消保持并结束 idle 窗口(没别的理由下一拍就关,量功耗用)。
|
||||
两个键可同时给。回应带 `"hub":{...}`。
|
||||
- status 新增 `"hub":{"on":true,"mode":"auto","why":"vbus","hold":秒,"idle":秒}`(满配 1140 / 1536 字节)。
|
||||
- **事件 22 `LOG_EV_HUB`**(rail 0):arg 0 = 关、1 = 开、0x10|模式 = 改模式;bus_mv = 开的理由(`hub_why_t`,冻结:
|
||||
0 off / 1 mode / 2 vbus / 3 flash / 4 act / 5 hold / 6 link / 7 idle)。
|
||||
- `bringup.c` 的上电脉冲不变(仍只在 POWERON);脉冲后 `hubctl_init()` 把脚放成输入。
|
||||
- 测试:新增 `test_hubctl`(9 例:启动窗口、理由优先级、断网 60 s、电脑插上 / USB 命令刷新、保持与 min=0、
|
||||
时钟回绕不复活、引脚从不写 1 且每次翻转只记一条、模式存盘、NVS 垃圾值回 auto),`test_cmd` +1(hub 命令)、
|
||||
`test_evlog` / 冻结编号同步;**27 ctest 全绿**;IDF 构建通过(0xf5960,余 68%)。
|
||||
- ✅ **板 B 实测(2026-09-29,0.1.14-c5,三路手机开着、VBUS 全关、上行插着电脑)**:Hub 开 / 关交替各 60 s,
|
||||
看「底板损耗 = 输入 − 三路主板合计」(手机电流 ±100 mA 的抖动会淹没输入电流):开 **1.77 W**,关 0.69 / 0.72 / 0.80 W ⇒
|
||||
**Hub 空转 ≈ 1.03 W**(12 V 输入电流 377 → 288 mA)。原始数据 `10.5.5.13:/root/phonerail-src/bench-hub-20260929.jsonl`,
|
||||
脚本 `bench-hub.sh` 同目录。`hub min:0` 下一拍就关、`hub min:3` 立刻开(why = hold),事件 22 各记一条。
|
||||
- ✅ **rack**:白名单放行 `hub`(mode auto/on、min 0~240);事件 22 命名(唤醒理由 / 休眠 / 模式改为…,mV 列置空);
|
||||
单板页「Hub」一行:状态 + 按需 / 常开 + 「唤醒 30 分」+「马上休眠」;模拟板同步。pytest 50 例,已部署。
|
||||
- ⏳ **没验证**:上行电脑不是本机也不是服务器,电脑那一侧 Hub 消失 / 重现、手机 VBUS 开后 adb 1~2 s 回来,都还没亲眼看过。
|
||||
- ⚠️ 现在起板 B 没人用 USB 满 10 分钟 COM 口就消失 —— 要 USB 烧录 / 看日志,先在 rack 点「唤醒 30 分」(或 MQTT 发 `hub min`)。
|
||||
|
||||
## 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 = **254 `CHG_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 圈重试;拔走作废;开机在位的不重做。status `safety.inits` = 成功次数。
|
||||
- 测试 +2(`test_t_safety`:乱寄存器插入 ⇒ 第 5 圈恢复、VBUS 断、键全松、CALIB 写回、只做一次;NAK 重试 + 插一半拔走不碰空槽);
|
||||
反向验证:不置 need_init ⇒ 两例红。26 ctest 全绿。
|
||||
- ✅ 真板验证(用户现场):带电插刀片后 C67 = 0 V、`safety.inits` 0 → 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_net` 10 → 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 下确实在充电**:节点 3864~4395 mV 锯齿
|
||||
(充到浮充即停、手机耗下来再充,周期 3~4 s),**VBUS 全程没断**、fastboot 一直在线(`getvar` 正常);
|
||||
· 该路电流 −1.0 ~ +1.5 mA(eFuse 反向阻断干净),12V 母线 12.029~12.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。`fastboot getvar unlocked: yes`(BL 确已解锁);
|
||||
· ⚠️ **`fastboot reboot` 回 MIUI 的开机过程中出现 −59 mA 反灌 ⇒ SW-8 断 VBUS**(事件 290)。机理:开机大负载时 Buck 在连续导通,
|
||||
节点只比设定高几 mV、没到 eFuse 的 −9.3 mV 阻断门限,充电器灌进来的电被 Buck 反向吸收。12V 母线没动(12.029~12.031)。
|
||||
**反灌判据保留是对的**;实际影响只是「重启过渡时 USB 可能被断,进系统后再开 VBUS」。
|
||||
|
||||
## 0.1.4:整片刀片不应答 ⇒ 关这一路(2026-09-28,用户拍板)
|
||||
- `t_safety` 每路锁内:eFuse 开着、本路 INA226 **和** PCA9555 同时 NAK,连续 `T_SAFETY_BLADE_MUTE_N` = 10 圈(200 ms)
|
||||
⇒ abort 序列 + 关 eFuse,记新事件 **20 `LOG_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_rules` 39 → 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 旁)高 6~10 °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_actuator` 30 → 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_ON` 2000 → **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 天删;半截 `.tmp` 1 小时删。
|
||||
- **界面改版**:仪器前面板风格 + 暗色模式(跟随系统);总览每个槽带最近两分钟电流小曲线(平线 = 疑似卡死)。
|
||||
- **底板温度曲线**(单板页):两路 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_therm`
|
||||
- `main/CMakeLists.txt` REQUIRES 加 `esp_adc esp_driver_pcnt`;`host/CMakeLists.txt` 加 `ntc.c`
|
||||
- `test/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`。
|
||||
|
||||
在新工单中引用
屏蔽一个用户