现场排查 SOP
场景:现场设备报故障,手上只有一台设备、一个报错信息,怎么往下查。 需求号:REQ-001(K7.8)| 源码基准:
V0.11.676(git04e72700)| 更新日期:2026-10-15
第 0 步:先分清「谁在报」
这是最容易走错的一步。 同一个现场现象,可能是 EMS 主控自己判定的,也可能是逆变器/BMS 上报后主控转发的,排查方向完全不同。
用故障码的数值直接判断:
| 码值区间 | 谁报的 | 排查重心 |
|---|---|---|
E0001 ~ E0094 | EMS 主控自己 | 主控与各设备的通信链路、主控外设(Flash/RTC/EEPROM) |
E100000 起 / W110000 起 | 逆变器 | 逆变器本体;主控只是转发 |
E200000 起 | 充电桩 | 充电桩本体 |
E60000 / W70000 / P75000 起 | BMS 电池 | 电池本体 + 电池 CAN |
完整 336 条见 故障码总表.md。
⚠️ EMS 的 30 个故障位里有 7 个是死位(在现版本固件里全工程零引用,永远不会被置起):
E0001E0008E0009E0012E0013E0015E0016。 如果现场看到这 7 个码,说明固件版本与本知识库不一致(或看到的是别的系统的码), 不要在现有代码里找它 —— 见EMS故障分册.md的「死位」一节。
第 1 步:拿到完整故障位图(不要只盯一个码)
EMS 的故障是一个 32 位位图(data/data.h 的 EMS_FAULT_U / g_t_ems_info),
多个故障会同时置位。只拿一个码去查,常会漏掉根因(例如「逆变器丢了」会连带触发一串)。
通路 A:Modbus 寄存器(最可靠,推荐)
读两个保持寄存器,拼成 32 位:
| 寄存器 | 含义 |
|---|---|
0x0701 | 故障位图 高 16 位(bit16 ~ bit31) |
0x0702 | 故障位图 低 16 位(bit0 ~ bit15) |
拼法:u32 = (reg_0x0701 << 16) | reg_0x0702
然后用 故障码表.tsv 把置 1 的 bit 反查成故障码(EMS 组 bit 与码一一对应,bit N ↔ E000(N+1),见分册)。
这两个地址来自
midware/interface/modbus/modbus.h的REG_EMS_FAULT_H_HHE_E/_L_HHE_E, 由枚举REG_EMS_INFO_START_HHE_E = 0x06FB起递增推算得出 (0x06FB起第 6/7 项;固件在0x0711之后跳空 24 个寄存器到0x0729,属预留)。 代码里的读取实现在midware/interface/modbus/modbus.c:3060(高字)与:3064(低字)。
通路 B:设备屏幕的历史故障日志
主控会把每次故障发生和恢复都写进 Flash 故障日志,屏幕上能翻历史记录。
读懂屏幕上那行字(hmi/ui/ui_history_error_log.c:139-165 的 disp_error_describe()):
| 屏幕上显示 | 含义 |
|---|---|
E00007 NO. 1 | 故障正在发生(FAULT_OCCURRED) |
NO E00007 NO. 1 | 该故障已恢复(FAULT_CLEAR)—— 注意行首的 NO |
W70001 NO. 2 | 告警正在发生 |
NO W70001 NO. 2 | 告警已恢复 |
两个必须知道的点:
- 屏幕上是 5 位数字:故障码 7 显示成
E00007,不是本库写的E0007。
看到屏幕上 E00007,对应本库的 E0007(去掉补零)。
- 行首有没有
NO决定这条是"当前存在"还是"曾经发生又恢复了"。
排查时只看没有 NO 的行才是当前故障;带 NO 的说明拍下那一刻已经不在故障态了。
⚠️ NO. 后面那个数字不要当成设备号。 实测(data/fault_warn_log/fault_warn_log.c:556/:560):
EMS 自身故障写日志时把设备号硬编码成 1,所以 E00007 NO. 1 里的 1 没有任何设备含义。
只有 BMS 上报的码(E6xxxx/W7xxxx/P75xxx)才会按电池序号变化 ——
那里传的是循环下标 i(fault_warn_log.c:603),多机场景下才真的用来区分是哪一台电池。
⚠️ 界面代码
hmi/ui/(77 个文件)尚未纳入本知识库,所以这里给不出"按哪几个键、在第几级菜单"翻到这页。 这是 K4 批次的缺口。但上面这个显示格式已从源码原文核实(hmi/ui/ui_history_error_log.c:139)。
通路 C:云端 / 手机 APP
主控会把故障日志打包上传服务器:
net/ems.c:5160 判断发送标志(带 10 秒节流 SENCOND_10S),
:6091 通过 handle_message_queue(STATUS_SEND_ERROR_LOG, ...) 投递,单包上限 5 KB。
云端记录更全,但有延迟,不适合现场即时判断。
第 2 步:故障日志里到底存了什么
每条日志记录的结构(data/fault_warn_log/fault_warn_log.h:643):
| 字段 | 含义 |
|---|---|
Time | 故障发生的 RTC 时间 |
u8_device_type | 设备类型:ems / inv / bms |
u8_fault_or_warn | 是故障还是告警(255 表示空槽) |
u8_fault_location | 电池簇号 |
u8_fault_id | 电池 id,或逆变器通讯地址 |
u32_fault_code | 故障码(就是 E/W/P 那个值) |
s32_fault_value | 故障发生那一刻的数值快照 ← 排查时最有用 |
u16_crc_code | Modbus CRC16 校验 |
s32_fault_value 是关键:很多故障是带阈值判定的,知道当时的值就能立刻判断是"真越限"还是"误判"。
存储参数(fault_warn_log.h:32-41):
| 项 | 值 |
|---|---|
| 日志 Flash 数据区 | 0xF66000 ~ 0xFC8FFF(396 KB) |
| 地址结构 3 个 4K 备份区 | 0xF63000 / 0xF64000 / 0xF65000 |
| 内存缓存上限 | 256 条 |
| 屏幕历史查询缓存 | 50 条 |
三个备份区轮流写、带 CRC 校验,掉电时正在写的那条可能损坏,但不会丢整片日志。 所以偶发「日志少一条」通常不是 bug。
第 3 步:USMART 串口调试命令 —— 量产固件里用不了
midware/debug/shell_usmart 提供了一套串口调试命令(读内存、改寄存器、导数据等),
但它受编译开关门控:
// midware/debug/shell_usmart.h:13
#define DEBUG_SUMART 0
DEBUG_SUMART = 0 ⇒ task_usmart 任务在 user/main.c:321 处整段不编译,量产固件里没有任何 USMART 命令可用。
如果你在别人给的排查文档里看到「输入 xxx 命令导出日志」,那是开发固件的做法,现场量产机做不到。
要向研发要一份 DEBUG_SUMART=1 的固件才能用。
第 4 步:按模块往下走
拿到故障码后,去 EMS故障分册.md 查该码的:
- 触发条件 —— 什么判据会置位(哪个标志、哪个阈值、有无消抖)
- 置位函数 ——
文件:行,可直接调代码 - 上游来源 —— 那个标志本身是谁在什么条件下置的(根因往往在这里)
- 排查方向 —— 现场先量什么、先看什么
非 EMS 的码(逆变器/BMS/充电桩)本库只给到「主控怎么映射」,具体含义要看厂商协议文档。
第 5 步:查代码
cd docs/knowledge/代码解析/索引
node 工具/查找.js -F 逆变器通信丢失 # 功能 → 函数 + 行号
node 工具/查找.js -d 故障码 # 全文搜整个知识库
node 工具/查找.js -f bat_heat # 只查函数名
附:本文事实的出处
本文所有结论都来自源码,未经板级验证:
| 结论 | 出处 |
|---|---|
| 30 个故障位、位域名 | data/data.h:27-114 |
| 故障码 ↔ 位 对应表 | data/fault_warn_log/fault_warn_log.c:518-549 |
| E/W/P 码的数值 | data/fault_warn_log/fault_warn_log.h:76-...(枚举) |
| 7 个死位 | 全工程 grep ems_set_fault_bit 与位域名,零引用 |
| Modbus 故障字寄存器 | midware/interface/modbus/modbus.c:3060,3064;modbus.h:651-659 |
| 日志记录结构 | data/fault_warn_log/fault_warn_log.h:643-654 |
| 日志 Flash 布局 | data/fault_warn_log/fault_warn_log.h:32-41 |
DEBUG_SUMART = 0 | midware/debug/shell_usmart.h:13;user/main.c:321 |
| 故障日志上传路径 | net/ems.c:5160(发送标志 + 10 s 节流)、:6091(投递) |
屏幕故障行格式 E%05u / NO 前缀 | hmi/ui/ui_history_error_log.c:139-165(disp_error_describe()) |