serial_list
列出系统可用串口。识别 CH340/CP2102/FT232 等常见 USB 转串口芯片并给驱动建议。 毕设学生"串口列表里找不到板子"时先调这个。
無需輸入參數。

io.github.stillsurge由 GitHub 帳號 stillsurge 發布v0.1.1更新於 Oct 30, 2026
Embedded debug MCP: serial, flash verify, fault decode, 45-pattern log analyzer, driver library
面向嵌入式硬體除錯的本機 MCP 伺服器:序列埠讀寫、日誌故障分析、腳位檢查、韌體燒錄與暫存器解碼。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
參照 儲存庫 中的啟動說明。
让 AI"看到并看懂"你手上的板子:串口读写 + 故障模式库(45 条规则)+ 烧录失败模式直译 + Modbus 帧解析 + 编译报错解析 + 引脚冲突检测(三引擎)+ 板级知识库 + 波特率自动探测 + AT 序列回归 + 双口对录 + 会话持久化 + RTT 通道 + 烧录桥接 + 寄存器位域解码 + 调试器探测 + 预验证驱动模板库(9 款)+ 器件选型库(按接口/地址/时序要求) + 外设 API 用法层(初始化顺序硬要求) + 错误码统一查询(6 张表·带出处) + 报文解析(自定义二进制帧 / NMEA-GPS) + 工程结构与工具链探测 + 会话日志治理与交付报告。共 59 个工具 + 16 个 MCP resources + 6 个 prompts(8 静态 resources + 8 模板 resources)。
和"直接问 AI"的本质区别:AI 不再听你转述"传感器好像不太对",而是亲自读到串口里的真实字节——AT 指令返回了什么、数据是全 0 还是乱码、程序是不是在反复重启、Keil 为什么烧不进去——然后按出厂预置的故障模式库给出诊断和排查步骤。引脚分配前用数据手册级映射表核对(能抓 AI 幻觉接线)。驱动代码来自验证过的模板库(纯函数 PC 可测 + Python 交叉验证),不是 AI 现编的。
这个 MCP 的工具会真的打开串口、写字节、烧 flash。开发机上常年挂着正在使用的设备 (UART 桥、调试器 CDC 口、正在跑的调试会话),一次误调用就能打断它们。所以默认档位是只读:
完整的风险矩阵(每个操作会做什么、需要什么才能执行、哪些操作没纳入闸门及原因)见
SECURITY.md。随时可用 env_status 查当前档位、名单与「因此被禁用了哪些操作」。
同类串口 MCP(qarnet/serial-mcp、mcp-serial、uart-mcp 等)只做「串口通道」;本项目的差异化在知识层:
总线工具需要一块跑 MCUDBG/1 桥固件的 MCU 当执行端:固件源码在
tools/bridge_fw/(NUCLEO-H563ZI 现成,附"编译 → 烧录 → 真机验证 → 自动恢复原固件"一条龙脚本)。 为什么不自动扫串口找桥、为什么 SP-Link bridge 和 FT2232H 走不通,见core.py总线工具区块头注释。 |
知识库只读浏览,AI 可主动引用——每类知识都是「清单 + 明细」两段:清单看有哪些,明细读原文(条目级 source 出处一并可见)。
可带 board 参数交叉检查板载外设占用(如 pin_check("stm32f103c8t6", {...}, board="bluepill")——PC13 被板载 LED 占用会警告)。
内置 30 条:
总线/传感器层(13):MOJIBAKE 乱码→波特率 · AT_ERROR · AT_NO_RESP · BOOT_LOOP · HARDFAULT+CFSR 位级解读 · SENSOR_ZERO 全 0 · SENSOR_FF 全 FF · DS18B20_85 恒 85°C · DHT_TIMEOUT · NEG_INVALID 错误码负值 · I2C_NACK · I2C_SCAN_EMPTY 扫描零设备 · ADC_SATURATED 恒满量程
ESP 生态(5):ESP_PANIC Guru Meditation · ESP_BROWNOUT 掉电 · ESP_WDT 看门狗 · ESP_FLASH_CORRUPT 镜像损坏 · CWJAP_ERR 连 WiFi 错误码 1-4
AT/网络协议(4):AT_CME_ERROR(3GPP 码表)· NET_LINK WiFi 断链 · MQTT_FAIL(PubSubClient rc 码表)· HTTP_ERR 4xx/5xx/curl
RTOS/系统层(4):RTOS_ASSERT 断言 · RTOS_STACK_OVF 栈溢出 · HAL_ASSERT STM32 HAL 参数断言 · UART_FRAMING 帧错误/溢出
开发期报错(3):COMPILE_UNDEF 链接未定义符号 · COMPILE_SYNTAX 编译错误(gcc/Keil/armcc/MSVC)· SD_INIT_FAIL
外置 15 条(knowledge/fault_patterns.json,社区可贡献):
串口报错贴进 analyze_log 一样能分析——编译报错、烧录失败日志直接粘进来就行。
dht11(温湿度)· hcsr04(超声波)· ds18b20(单总线温度,CRC8+补码换算)· mpu6050(六轴,量程换算+静止自检)· esp8266at(AT 响应解析库,IPD 帧+错误码)· ssd1306(OLED,帧缓冲+页寻址)· servo_sg90(舵机,角度→脉宽→定时器 CCR)· stepper_28byj48(步进电机,精确减速比 63.684:1)· hc05(蓝牙 AT 助手,响应分类+命令生成)
每个驱动:纯函数与总线时序分离(PC 可测)· wiring.md(接线表+头号坑清单按翻车率排序+答辩加分点)· meta.json 如实标注验证状态。
serial_open 自动在 logs/ 落盘会话日志(端口/波特率/时间戳元信息 + 全部收发内容),读写时实时追加。调试被中断、换会话、隔几天之后:sessions_list 列出历史会话 → sessions_get 回看现场 → 整段喂 analyze_log。只读文件系统下静默降级,不影响主流程。
Claude Desktop(claude_desktop_config.json)/ Cursor(mcp.json):
默认
readonly档:读/枚举可用,写串口、烧录等硬件操作被拒并说明如何放开。 调试需要真机写入时再加"MCU_DEBUG_MCP_MODE": "write"+MCU_DEBUG_MCP_PORT_DENY锁住在用的口(见上文安全表)。
第 1 步:装包(本机需有 Python 3.10+;没有就让 WorkBuddy 代装,见下方提示词)
第 2 步:配连接器(连接器 → 自定义连接器 → 配置 MCP → 粘贴 → 保存):
若
python不在 PATH,把command换成 python.exe 全路径; 或改用 console script:"command": "mcu-debug-mcp.exe"的全路径 (pip --user 安装在%APPDATA%\Python\Python312\Scripts\,全局安装在 Python 目录的Scripts\)。
第 3 步:Trust 信任(连接器列表点「信任」启用)→ 新开任务说"列出 mcu-debug-mcp 下的所有工具"验证。
Claude Desktop / Cursor / Windsurf 同格式(python -m mcu_debug_mcp.server)。
Cline 安装参考本 README(llms-install 兼容)。
MCP 真机实测抓出的四个真问题(已修,均为"看似合理的假通过"类):
baudrate_autodetect 在 USB CDC 口上原理上无意义,必须先识别并短路(2026-09-21 师兄指出 ESP32 用的是 USB CDC)。
USB CDC 走芯片自带 USB 外设(CDC ACM),波特率参数不参与数据路径,各档收到的字节流完全相同 ⟹ 所谓"各档评分"只是同一批字节被反复读。
实测铁证:COM4 上同一波特率(115200)连测 3 轮全 0 字节,而另一次 9600 拿到 88064 字节——同波特率结果从 88KB 到 0,差异只来自设备当时有没有在输出。
⟹ 该口扫档 = 在测"这次打开时设备有没有在说话",纯随机。原实现给的"9 档同分 0.99"主因也不是采样不足(我上一版归因偏了),而是根本没有波特率差异。
修法:新增 _port_baud_relevance(),按 VID/PID 识别 CDC(303A:1001 Espressif、2E8A:* RP2040 等)与 UART 桥(0403:6010 FT2232H、10C4:EA60 CP210x、1A86:7523 CH340、1366:0105 J-Link 等),CDC 口直接短路并给出「该口不用管波特率 / 读不到别换档 / 真要判必须接独立 UART 桥听 TX 脚」三步指引;serial_open 也附同类提示。显式传 candidates 可强行扫(怀疑枚举成 CDC 实为桥的场合)。同时给 analyze_log 的 MOJIBAKE 诊断加了 USB CDC 例外(原建议「把波特率逐个换」在该口是纯无效动作)。
baudrate_autodetect 在真 UART 上自信地给错档(COM4 实测,判别力门禁的由来):11 个候选里 9 个同分 0.99,工具推荐 460800,真实是 115200;而正确的 115200 反而排最低(0.071)。
根因链:① 打开串口翻转 DTR/RTS ⟹ ESP32 复位 ⟹ 每个档位收到同一段纯 ASCII ROM 启动日志;② if len(buf) > 2048: break 在应用数据到达前退出;③ 同分时稳定排序回退到候选表顺序,而候选表是降序的 ⟹ 永远推荐最高档。
修法:采样上限 2048→8192;新增 _BAUD_MIN_MARGIN = 0.10 判别力门禁——最高分与次高分差距不足门限时,明确输出「★判不出来……别照它改代码」并给手工确认步骤,而不是硬推一个。
at_sequence / serial_read / serial_send 的 expect 是全文子串搜索 ⟹ 假 PASS(COM4 实测):设备跑的是 BQ27421 电量计固件、根本没有 AT 应答,但日志行 I (371) led: init OK R=IO21 G=IO48 B=IO47 里含 "OK" ⟹ 判 [PASS]。
修法:新增 _at_line_match(),默认 整行匹配(行首尾允许空白,re.fullmatch),并回显「命中行: …」便于核对;确需旧行为可在该步骤写 {"loose": true}。修复后同场景返回 [TIMEOUT],而 loose=true 对照组仍 [PASS] —— 两相对照即为铁证。
pin_check 的 io_mux 引擎(ESP32)不校验引脚存在性 ⟹ 拼错引脚名给"通过":{"GPIO999": "GPIO"} 返回「✅ 任意路由(GPIO Matrix)… 结论:全部通过,可以按此接线」。fixed_mux 引擎(STM32)有「该 MCU 没有此引脚」的拦截,两个引擎判定口径不对称,任何手滑拼写(GPI08)或幻觉引脚都会拿到"通过"结论。
修法:io_mux 分支加引脚存在性前置校验,并列出相近的已知引脚,结论改为「必须改」。
同时并存的可用性缺陷(未改代码,需决策)——参数名/参数集与直觉不符:
analyze_log(log_text=) 不是 text;modbus_crc(hex_frame=) 不是 data;pin_check(pins=) 不是 assignments;flash_firmware(file_path=) 不是 firmware;at_sequence(steps=) 且步骤内是 cmd 不是 send、timeout_s 不是 wait_s。
另有一处参数集不一致(已修):core.baudrate_autodetect 有 candidates 但 server.py 的 MCP 工具签名未暴露 ⟹ MCP 客户端无法「强行扫」,文档承诺无法兑现。已补上 candidates 参数。
首次调用按直觉猜参数名的失败率极高(本轮实测猜错 7 次全部报 pydantic 校验错)。建议在 README/工具描述里补「易错参数名对照表」,或在 server.py 加别名兼容。
排故链(Cortex-M 故障链)实测抓出的真问题(已修,均为"看似合理的假答案"类,2026-09-22):
这一批全部是靠真实 gcc 生成的两份 .map(主机 x86 + 目标 ARM)跑出来才暴露的,单测的 happy path 一条都测不出:
symbol_lookup 把 0xdeadbeef 反查成 app_state+0xbeadb817 并打勾 ✓。
根因:GNU ld 的 .map 不记符号大小,推断出的 size 对 .bss 段末符号为 0,于是「偏移 < size 才越界」的检查被短路,任何巨大偏移都被判成「在符号内」。
修法:给每个符号算地址上界(有 size 用 size;无 size 用下一符号地址;段尾用段末),地址越出上界就拒绝给函数名,并说明「落在符号表空隙里 = 野指针」。同一份解析逻辑被抽成 _build_bounds,symbol_lookup 与 stack_unwind 共用——避免只修一处、另一处照旧放行(实测中 stack_unwind 起初就复现了同样的假答案)。
_infer_sizes 跨段推断,算出 384MB 的「符号大小」。
.text 段最后一个符号拿 .data 段首地址当「下一个符号」,0x20000000 − 0x0800039c = 402652260 被当成真实大小印进「占用最大的符号」视图。
修法:推断时做跨段保护(两符号必须同段)+ 段大小上限(推断值不能超过所属段)。
主机程序(x86 mingw PE)的 .map 被当成 MCU 固件照单全收。
输出里混着 __mingw_module_is_dll、.idata/.rsrc/.pdata 等主机特征,用户拿错 map 会得到一堆无关符号还以为是自己的固件。
修法:新增 _map_target_check,命中主机特征(运行时符号 ≥3 条 或 主机专有段 ≥3 个)时明确警告「这不是 MCU 固件的 map」;同时用一份按真实 arm-none-eabi-ld 格式固化的 ARM map 做反向对照,确认正常 MCU map 不误报。
serial_list 对已插着的 FT2232H / J-Link 显示「未收录」。
根因:core._USB_UART_BRIDGE_IDS 认识 0403:6010/1366:0105,但 analyzer.USB_SERIAL_VIDPID 没有这两条——同一 VID:PID 在一处被识别、另一处说不认识。
修法:补齐 analyzer 表(FT2232H/FT232H/CH9102 变体/J-Link CDC/RP2040/ESP32 USB-JTAG 等),并加跨表一致性测试,锁住两张表不再脱节。
decode_fault 把 cortex-m0 报成「没有收录架构」——M0 是已知架构、只是没有 CFSR,这不是"缺个 json"的问题,而是判定路径根本不同(需走压栈帧 PC 反查)。
修法:M0/M0+ 走专门分支,说明为什么用不了并给出替代路径(symbol_lookup + stack_unwind 不受架构限制)。
附带修:BFARVALID=1(位域说"有效")时不再无脑放行——识别哨兵值(0xDEADBEEF、全同字节、0xFFFFFFFF、<0x100)并提示「位域有效 ≠ 值本身合理」;valid=0 且用户没传该地址时,不再断言"读到的值是垃圾值"(根本没读到)。
测试自身抓出的两个静默失效(已修):
@unittest.skipUnless(os.path.isfile(MAP)) 装饰器在类定义时求值,而 setUpModule 之后才生成 map ⟹ map 明明刚生成,14 个测试却全被 skip。改为运行时判断函数 _have_demo()。shutil.which("gcc") 成功 ≠ gcc 能跑:msys2 的 gcc 找不到 cc1/as/ld 时会 rc≠0 + 空输出静默失败。改为始终把 msys2 bin 前置进 PATH,并加 _gcc_works() 真跑一次 --version 确认。ssd1306.h 的 include guard(SSD1306_H)与屏高宏(SSD1306_H = 64)撞名——文件卫士被屏高宏覆盖,任何 #ifdef SSD1306_H 判断恒为真。已把卫士改为 SSD1306_DRIVER_Hmpu6050_read_raw 的 uint8_t b[14] 未初始化,gcc 报 -Wmaybe-uninitialized(桩函数空实现时尤其明显);真机上 I2C 部分读失败会留脏数据。已改 = {0}串口回环实测抓出的测试代码 bug(已修):TestSerialLoop 从未真正运行过,第一次跑就暴露三处——serial_for_url("loop://") 返回的对象已自动 open(显式 .open() 抛 "Port is already open.")、setUpClass 共享端口被各测试互相 close、_get_session 方法名写错(实为 get_session)。重写为每测试独立端口 + addCleanup。
pip install -r requirements.txt → python tests/test_serial_tools.pypython tests/run_c_tests.pypython tests/validate_knowledge.pyserial_list 确认识别芯片 ✅ 已跑(COM4=ESP32-S3 303A:1001,板载固件 bno085_imu V2.4.3-20260920_a023)baudrate_autodetect 扫档 → 推荐档位与真实波特率一致
⚠️ 分两种口:① USB CDC 口(ESP32-S3/C3、RP2040 等)——波特率不参与数据路径,工具会识别并短路,拒绝给排序(这是正确行为,不是失败);② 真 UART / 独立桥(FT2232H、CH340、CP210x、J-Link CDC)——波特率真实生效,扫档有意义。
要验证「能推荐对」,需一台非 USB CDC、且以非 115200 输出的设备接在 UART 桥上。本机 COM4 是 CDC 故只验了短路路径;COM7(FT2232H)无设备接入,扫出全 0 分并被正确提示"设备没在说话"。serial_open + serial_read 读启动日志 → analyze_log 应报 MOJIBAKE ✅ 已跑(COM4 读到 114319 B 真实日志,命中 #EVT type=FAULT fault=0x02000000)serial_send("AT", expect="OK", wait_s=3) 确认 expect 提前返回;at_sequence 跑一组 AT 用例 ✅ 已跑(并因此抓出假 PASS 缺陷,见上)sessions_list → sessions_get → 能完整回看刚才的现场 ✅ 已跑(自动落盘 20260921_115559_COM4_h1.log 132.8 KB,可回看)CWJAP_ERR(码 2=密码错误)⏳ 无 ESP8266 AT 固件设备modbus_parse 应直译异常码;serial_pair_record 对录主从对话 ✅ 已跑(modbus_parse 异常码 0x02 正确直译;serial_pair_record 用 FT2232H COM7/COM11 实跑,空数据降级提示正确)pin_check 应提示需要 remap;PC13 配 GPIO + board=bluepill 应报板载 LED 占用 ✅ 已跑(知识层三引擎全部正确;并因此抓出 io_mux 不校验引脚存在性的缺陷)rtt_read 读日志 ⏳ J-Link 在位(COM6),但本机只有 VS Code ARM 扩展自带的那份 pyocd.exe,当前解释器 import pyocd 失败,所以 rtt_read 还跑不起来。已验的是「模块缺失时如实说明、不崩」的降级路径。pip install pyocd 后可用 NRF52840 板补测(也可直接调那份 exe:pyocd list)。python tools/live_acceptance.py COM21 --allow-write --elf <fw.elf> --map <fw.map> ✅ 已跑(PASS 16 / FAIL 0 / SKIP 1;并抓出 6 个只靠离线测试抓不到的缺陷,见 CHANGELOG 4.2.0)硬件盘点(本机实测):COM4=ESP32-S3 原生 USB(303A:1001,跑 BNO085 IMU 固件)|COM6=JLink CDC UART(1366:0105)|COM7/COM11=FT2232H 双通道(0403:6010)|COM8/COM9=蓝牙幻影口。
注意:NRF52840 板只有 SWD + SEGGER RTT,未引出 UART ⟹ 串口类清单项需用上述其他硬件;RTT 类需装 pyocd。
如果你在 Windows + msys2 下跑 C 测试遇到「gcc 明明在,但编译 rc=1 且没有任何输出」,不是沙箱也不是权限问题:
msys2 的 gcc.exe 单独执行正常(--version 有输出),但真正编译时它要 fork cc1.exe,而 cc1.exe 依赖同目录的运行时 DLL(libgmp-10.dll/libmpfr-6.dll/libisl-*.dll/libmpc-3.dll)。Windows 的 DLL 搜索路径不含 exe 所在目录(除非该目录在 PATH 里),于是 cc1.exe 崩在 STATUS_DLL_NOT_FOUND (0xC0000135)——由于崩溃发生在子进程,父进程拿不到任何 stderr,表现为「静默失败」。
验证方法:gcc -dumpmachine 能输出(驱动本身不 fork cc1),gcc -E foo.c 无输出失败(要 fork cc1)。
修法:把 gcc 的 bin 目录加进 PATH。tests/run_c_tests.py 与 Makefile.logic 都已自动处理,直接用它们即可。
min_count,如 WiFi 断连 ≥3 次才算风暴),无需改代码,重启生效_load_code_table 挂载engine 字段(fixed_mux/af_table/io_mux 三选一),来源标数据手册;不确定的字段留空并在 caveats 说明,不要编python tests/validate_knowledge.py——它抓得出引脚数录错、功能名录错、同功能双占、正则写坏、外设条目缺引脚字段(已实测抓出 3 处 F103 数据错误)<name>.h/.c/<name>_port.h/wiring.md/meta.json,meta.json 的 verified 字段必须如实写验证状态——评测员和学生都会实测來源:README.md,提交 f277f4c
列出系统可用串口。识别 CH340/CP2102/FT232 等常见 USB 转串口芯片并给驱动建议。 毕设学生"串口列表里找不到板子"时先调这个。
無需輸入參數。
打开串口,返回句柄 handle。port 形如 "COM3" 或 "/dev/ttyUSB0"。 波特率不确定就先用 115200(STM32 HAL 默认)或 9600(老例程/模块默认)。
port必填paritybaudratedata_bitsstop_bits关闭串口句柄。调试结束记得关,释放端口。
handle必填从串口读取数据(非阻塞,三条件提前退出)。 expect:正则,命中立即返回(如 expect="OK|ERROR" 等响应); idle_ms:静默超时(板子说完就走); seconds:总时长上限。返回文本视图 + HEX 视图 + 是否命中。 读到的内容同时累积进会话日志。
expecthandle必填idle_msseconds向串口发送数据并等响应。newline=True 自动追加 \r\n(AT 指令必须!)。 expect:正则,如 "OK"——发 AT 后等到 OK/ERROR 立即返回,不傻等; wait_s:等待响应上限秒数(默认 0.5,AT 慢响应建议 3~5)。 hex_mode=True 时 data 按 HEX 解析(RS485/Modbus 用)。
data必填expecthandle必填wait_snewlinehex_mode自动探测波特率:逐档打开串口采样,按"最像正常日志"评分排序并给推荐。 log_text 提供现成日志样本时走离线评分(不占串口)。设备静默时会提示先复位再扫。 ★USB CDC 口(ESP32-S3 原生 USB、RP2040 等)上波特率不参与数据路径,本工具会 直接短路说明,不给排序——在那种口上"各档评分"只是同一批字节被反复读。 确需强行扫(例如怀疑枚举成 CDC 但实际是 UART 桥)时显式传 candidates, 如 candidates=[115200, 9600]。
port必填log_textsample_scandidates批量执行 AT 指令序列并逐条断言(半自动回归测试)。 steps 形如 [{"cmd": "AT", "expect": "OK", "timeout_s": 2}, {"cmd": "AT+CWJAP?", "expect": "WIFI|ERROR"}](expect 是正则)。 输出每步 PASS/FAIL/TIMEOUT + 回显尾,中途失败不中断,方便一次看全。
port必填steps必填default_baudtotal_timeout_s同时监听两个串口,输出时间线交错的录制日志(RS485 主从对话/两设备联调)。 每行前缀 [A]/[B] 标明来源口,附 HEX 视图防波特率猜错时完全看不懂。最长 120s。
baud_abaud_bport_a必填port_b必填seconds取该句柄自打开以来的全部累积日志(喂给 analyze_log 做整体分析用)。
handle必填列出已落盘的串口会话日志(serial_open 时自动创建,新→旧)。 会话被关闭甚至对话轮次已隔很久之后,仍可用它找回之前的串口现场。
limit读取指定会话日志的内容(文件名来自 sessions_list)。 默认显示尾部 200 行(日志开头常是复位前旧数据)。整段内容可直接喂 analyze_log。
fname必填tail_lines会话日志的保留策略与清理入口(日志只增不减时用)。 ★ 默认 dry_run=True:只列出会删哪些文件、能回收多少空间,**什么都不删**。 要真删:dry_run=False + confirm=True,且隔离档位需为 write (删除不可逆——那些日志往往是几天前那次偶发故障的唯一现场)。 keep_days / keep_count 传 0(默认)时用内置策略:保留最近 30 天 / 最新 200 个。 ⓘ 日志会原样记录串口收发内容,可能含 WiFi 密码 / token 等凭据,分享前请检查。
confirmdry_runkeep_dayskeep_count把一次串口会话导出成 **Markdown 交付报告**(故障分析 + 原始日志附后)。 调完一次问题,把「现象/证据/结论/下一步」直接变成可交付文件——毕设报告、 工单、给同事看都行。原始日志原样附在末尾,避免「只有结论没有证据」。 数据来源:给 handle(活动会话)或 fname(已落盘日志,来自 sessions_list); 都不给时若恰好只有一份日志会自动用它。 默认落点 <数据根>/reports/;已存在不覆盖(要覆盖传 overwrite=True + confirm=True)。
fnamehandleconfirmout_pathoverwritetail_lines分析日志,识别 30 类常见故障模式(乱码/AT报错/反复重启/HardFault/传感器全0/全FF/ DS18B20恒85/DHT超时/I2C NACK/网络断链/ESP崩溃/掉电/看门狗/固件损坏/CWJAP错误码/ CME错误码/RTOS断言/栈溢出/链接错误/编译错误/I2C扫描空/UART帧错/HAL断言/ADC饱和/ MQTT失败/HTTP错误/SD卡失败等),每条给出人话诊断和排查步骤。 输入可以是 serial_read 的输出、用户粘贴的串口日志、编译器/链接器报错。
log_text必填计算或校验 Modbus RTU 的 CRC16(RS485 毕设必备)。 check=False:对数据部分计算 CRC;check=True:校验完整帧(数据+CRC)。
checkhex_frame必填解析 Modbus RTU 帧:正常响应拆出寄存器值/线圈状态,异常响应(功能码|0x80) 直译异常码 01-06(非法功能/非法地址/非法值/设备故障/忙...)并给排查建议。 从站"该响应但不响应/回了一帧看不懂"时用它。
hex_frame必填查错误码释义表(外置知识库 knowledge/error_codes/*.json,每条带出处)。 table 留空列出所有可查的表;只给 table 列出该表全部码; table+code 给出该码的释义、排查方向与依据(出处)。 已收录:esp_idf(esp_err_t)/ at_common(AT 通用响应)/ cwjap / cme / modbus_exception / mqtt_rc。 查不到时**明说未收录、不给释义**——不要拿别的码去猜。
codetable检查 MCU 引脚分配是否冲突(内置数据手册级复用映射)。 mcu 如 "stm32f103c8t6";pins 如 {"PA9": "USART1_TX", "PB6": "I2C1_SCL", "PA0": "GPIO"}。 board 可选开发板名(如 "bluepill"):板载外设占用的引脚再挪用会警告。 能抓:该脚没有此功能(AI 幻觉接线)、同一功能双占、SWD 脚挪用、 remap 档不匹配、非 5V 容忍脚;ESP32 类查硬约束(flash 保留脚/仅输入/strapping)。 写代码分配引脚之前必须先过一遍。
mcu必填pins必填board列出引脚知识库已收录的 MCU 型号。pin_check 前可用它确认型号是否支持。
無需輸入參數。
输出指定 MCU 的 pinmap JSON 原文(全引脚复用映射 + caveats + 常见冲突提示)。 pin_check 报错后想看完整数据、或需要给用户展示映射依据时用。
mcu必填列出板级知识库已收录的开发板。选板子前先看有没有现成板级坑清单。
無需輸入參數。
输出指定开发板的板级知识:板载外设及占用引脚、板级坑清单(按翻车率排序)、 供电说明、数据来源。如 "bluepill"(蓝药丸)、"blackpill_f411"(黑药丸)。 给用户做硬件选型/排板级坑之前先查这个。
name必填列出已收录的嵌入式生态/框架(Zephyr / RT-Thread / PlatformIO / Keil / IAR / Rust defmt)。 拿到不认识的工作目录、或一段陌生的报错时,先判断「这是哪个生态的工程」—— 识别错了,后面每一步排查都会拐到沟里。
無需輸入參數。
输出指定生态的识别特征:工程产物 / 构建配置 / 日志特征 / 常见坑。 name 用 ecosystem_list 给的 key(如 zephyr / keil / rust_defmt)。
name必填识别一个**工程目录**属于哪个生态(扫标志文件:platformio.ini / *.uvprojx / prj.conf / SConstruct / *.ewp / Cargo.toml),并给出该生态的构建方式与常见坑。 拿到别人的工程压缩包、或不知道从哪下手时先跑这个。只探一层,不递归。
path探测**本机**嵌入式工具链:编译器(ARM GCC / armclang / RISC-V / Xtensa)、 调试器、构建系统(CMake/Ninja/west/SCons/PlatformIO/Cargo)各给版本。 换机器、或「我这能编过、换台电脑就编不过」时先看这个。烧录器用 flash_tool_detect。
無需輸入參數。
探测已连接的调试器(DAPLink/ST-Link/J-Link...)和目标芯片。 "烧录失败/找不到目标芯片"时先调这个。需要可选依赖 pyocd。
無需輸入參數。
通过 pyocd 的 RTT 通道读目标板调试日志(⚠ 实验性)。 固件需集成 SEGGER RTT 控制块;比 UART 快且不占串口外设。 cbuffer:控制块编号(多通道时指定,默认读全部);probe_serial:多调试器时指定。 失败自动给排查指引,建议先 probe_list 确认调试器。
cbuffersecondsprobe_serial检测本机已安装的烧录工具(esptool/st-flash/pyocd/openocd)。 烧录前先调这个看有什么可用。
無需輸入參數。
烧录固件到开发板(**不可逆操作,双闸门**)。 第一道:dry_run 默认 True,只返回将要执行的完整命令供逐字核对,不写 flash。 第二道:真正执行必须 dry_run=False 且 confirm=True —— 两道都齐才会写。 另受隔离档位管辖:MCU_DEBUG_MCP_MODE 为 readonly(默认)时直接拒绝。 STM32+ST-Link(默认首选,实测最稳): flash_firmware("app.bin", chip="stm32h563") → STM32_Programmer_CLI -c port=SWD mode=normal -w app.bin 0x08000000 -v -rst 接多台同型号调试器时用 sn 指定探针序列号。 ESP 系:flash_firmware("app.bin", chip="esp32", port="COM5") 通用 Cortex-M:flash_firmware("app.elf", tool="pyocd", chip="stm32f103c8") 烧完自动 -rst 复位;**不复位的话 SWD 会把内核 halt 住,板子不会跑**。
snchipporttoolconfirmdry_runfile_path必填列出可用的预验证驱动模板(带出厂验证记录)。 用户要做毕设传感器部分时,先看这里有没有现成的,而不是自己写。
無需輸入參數。
输出指定驱动的接线表+完整代码+验证记录。name 来自 driver_list。 这些代码是预验证过的,直接交付用户;需要适配的只有 *_port.h。
name必填查指定驱动的接线知识库:接线表、头号坑清单(按翻车率排序)、 输出日志解读、答辩加分点。回答"XX传感器怎么接/为什么不工作"前先查这个。
name必填器件选型清单:按接口(i2c / uart / timer_pwm / gpio_timing)列出可用的 传感器与模块,带地址、微秒时序要求。回答"我要做温湿度采集/测距/姿态, 用哪个现成器件"时先查这里,别凭记忆报型号。bus 可按接口过滤(如 "i2c")。
bus单个器件的选型详情:接口 / 地址 / 移植要求 / 头号坑 / 出厂验证记录 / 接线避坑全文。name 来自 component_list。
name必填外设 API 用法知识层:每个驱动要占哪些外设(I2C1/TIM1/UART2…)、 初始化顺序有什么硬要求。移植驱动或排查"外设配了但读不到数"时先查这里。
bus单个驱动的外设初始化要求全文(取自其 *_port.h 适配层原注释)。 name 来自 peripheral_api_list。
name必填把寄存器原始值按位域拆开解释。 fields 形如 [{"name":"HSION","bit":0}, {"name":"PLLSRC","lsb":16,"width":2}]。 手头有寄存器值不知道每个位什么意思时用。
value必填widthfields必填用 CMSIS-SVD 文件解码寄存器位域(含枚举值提示与描述)。 svd_path 指向厂商 SVD 文件(CubeMX/固件包里有),如: decode_register_svd("STM32F103.svd", "RCC", "CR", 0x00040002)
value必填register必填svd_path必填peripheral必填解码 Cortex-M 故障寄存器,给出人话诊断 + 排查方向。 设备跑飞/进 HardFault 时用。传你从 fault handler 读到的原始值(任意组合): decode_fault(cfsr=0x8200, hfsr=0x40000000, bfar=0x40021000) ★ 会自动处理三类陷阱,别跳过它的结论: · BFARVALID/MMARVALID=0 时地址是垃圾值——工具会拒绝解读,不给假地址 · IMPRECISERR 时即使 valid,BFAR 也不可信(总线写延迟)——工具会标注 · FORCED=1 说明真因在 CFSR——工具会提示去读 CFSR 拿到可信地址后,用 symbol_lookup 反查是哪个函数。
archbfarcfsrhfsrmmfar解析 .map 文件:段占用(Flash/RAM)、符号表、可疑大符号(查大数组/大缓冲)。 支持 GNU ld / Keil / IAR 三种格式,自动识别;认不出会明说,不猜。 parse_map("build/app.map") 查"RAM 怎么爆了""哪个大数组吃栈"时用。结果里的符号大小若是「推断」值, 表示该 map 不记 size,大小由相邻符号地址差推出(可能偏大)。
path必填top_n把地址反查成「函数名+偏移」。fault 地址定位代码的关键一步。 symbol_lookup("build/app.elf", ["0x08004a2c"]) ★优先给 .elf 而不是 .map:.elf 的 .symtab 带真实 st_size,能判断 「地址还在不在这个函数内」;.map 只能靠「下一个符号 − 本符号」推断。 ★GNU ld(GCC/STM32CubeIDE/ESP-IDF)默认**不往 .map 里写符号表**, 同一份固件的 .map 可能只有个位数条目(实测 3 个,而 .elf 有 31 个)。 此时反查会命中段符号给出**假函数名**——所以本工具遇到这种稀疏 map 会 明确提示,并把同目录的 .elf 摆出来让你换源。 找不到符号时明确报"未找到",不给猜测的函数名——猜出来的名字比没名字更害人。
path必填addresses必填栈回溯:把 fault 时压栈的 8 个字还原成调用链,定位出错函数。 frame 按硬件压栈顺序:[R0, R1, R2, R3, R12, LR, PC, xPSR] stack_unwind("build/app.elf", frame=[r0,r1,r2,r3,r12,lr,pc,xpsr]) ★同 symbol_lookup:优先给 .elf。.map 的符号表稀疏时 PC/LR 可能反查出 假函数名,工具会就此提示。 exc_return 传 handler 入口时的 LR——它决定栈帧是标准还是 FPU 扩展、 以及异常前用的是 MSP 还是 PSP(RTOS 任务)。传了会给出更准的判断。 ★ PC 反查不到符号时,说明是典型「跑飞」,工具会给排查方向。
sppath必填frameexc_return环境自检:依赖 / 串口 / 烧录器 / 知识库根 / 版本,一次问清。 拿到陌生机器、或工具突然不好使时先跑这个:它把「这里能干什么、缺什么、 缺了会导致哪些工具不可用」一次列清,省掉一个个试错。 ★ 只报实测——探不到的会明说为什么探不到。
無需輸入參數。
解析 NMEA 0183 语句(GPS/北斗模块最常见的串口输出),给人话解读。 支持 GGA/RMC/GSA/GSV/VTG/GLL,逐句输出 UTC 时间、经纬度(度分 + 十进制度)、 定位质量、卫星数、HDOP、速度、日期。把模块吐出来的整段原始数据直接贴进来: nmea_parse("$GNGGA,012345.00,3112.3456,N,12122.3456,E,1,08,0.9,12.3,M,,M,,*47") ★ 每句都验 XOR 校验和,不过的会明说「这句数据不可信」,不会把错帧当对的解。 ★ 非 NMEA 的行会单独指出并跳过,不当成数据。
text必填通用二进制帧解析:按「帧头 + 长度字段」切帧,可选逐帧校验。 单片机自定义协议(上位机抓到的 hex 流)对不上时用。参数都能少填就少填: frame_parse("AA 55 04 01 02 03 04 AA 55 02 05 06") frame_parse("AA 55 04 01 02 03 04", header="AA55", checksum="sum8") frame_parse("01 03 04 00 0A 00 0B 9B F6", checksum="crc16_modbus") header 不给则须给 length_offset(长度字段相对帧首的偏移)。 checksum 可选 "none"/"sum8"/"crc16_modbus"。 ★ 切不出来(找不到帧头 / 长度越界 / 帧被截断)会**逐条报出并附剩余字节**, **绝不静默丢数据**——静默丢帧是这类工具最危险的错。
endianheaderchecksumdata_hex必填max_frameslength_sizelength_offsetlength_includes_header读目标芯片的身份与存储信息(型号 / Device ID / 修订 / Flash 容量 / UID / 读保护)。 mode="read"(默认)走 hotplug:**不复位、不停内核**,板子照跑。 能拿到 型号 / Device ID / Revision / Device CPU / Board / 电压 / 调试器信息, 以及 DBGMCU_IDCODE 里更精确的 DEV_ID 与 REV_ID(会和连接 banner 交叉核对)。 mode="full" 走 normal:多拿 真实 Flash 容量 / 96 位 UID / 选项字节(读保护等)。 代价是**连接本身会复位一次目标并 halt 内核** —— 受隔离层管辖(需要写权限)。 ★ 如果你正在查复位原因,别先调它:这次复位会把 PINRSTF 打进 RCC_RSR,污染现场。 实测依据:system memory(UID @0x08FFF800 / FlashSize @0x08FFF80C)在 hotplug 下 读不到(CLI 报 "failed to read the requested memory content"),必须 normal 档; 而 normal 档连上就复位。所以拆成两档,把选择权交给你,不悄悄替你做主。
snmode读复位原因(RCC_RSR 标志)与内核运行状态,走 hotplug:**不复位、不停内核**。 回答「板子为什么自己重启了」「是不是看门狗喂狗没喂上」这类问题。 RCC_RSR 会给出 PINRSTF(复位引脚)/ BORRSTF(欠压)/ SFTRSTF(软件)/ IWDGRSTF(独立看门狗)/ WWDGRSTF(窗口看门狗)/ LPWRRSTF(低功耗退出)。 ★ 关键理解:这些标志是【累积】的,只有写 RMVF 或整片掉电才清。 所以「好几个位同时为 1」= 「自上次清零以来发生过这几类复位」, **不等于**「最后一次是它们一起发生」。 要定位「这一次」:先 clear_flags=True + confirm=True 清零 → 复现 → 再读。 clear_flags 不可逆(销毁现场),受隔离层管辖且需 confirm=True。 then_reset 只在 clear_flags=True 时有意义:清零并读回验证后再复位一次, 让内核继续跑(默认 True)。 ★ 哪些动作会自己置标志(实测,用来区分「真复位」与「读操作自带的噪声」): hotplug 连接(仅连接)→ 不置任何标志; normal 连接(仅连接)→ PINRSTF|SFTRSTF; hotplug + -rst → PINRSTF|SFTRSTF; hotplug + -hardRst → 仅 PINRSTF。 所以清零后读回还看到 PINRSTF/SFTRSTF 几乎总是命令自己造成的,不是 RMVF 没生效。 ★ 为什么必须 hotplug:STM32CubeProgrammer 以 normal 档连接**动作本身就会复位目标** 并 halt 内核。用 normal 读复位原因,读到的第一个标志就是这次读操作自己造成的 —— 结论必然错。
snconfirmthen_resetclear_flags烧录后校验:把固件文件与芯片上实际内容**逐字节比对**(只读,走 hotplug)。 比 `-v` 有用:`-v` 只能紧跟烧录命令,等于重烧一遍;这个读回来直接比, 还会报出**第一个不一致的偏移**和前后 8 字节上下文 —— 能直接看出来是「整段没写进去」 还是「某几个字节错了」。 file_path 支持 .bin(配 address,默认 0x08000000)/ .hex / .elf。 烧完提示 "verified" 但设备不工作时,先调它排除「根本没烧进去」。 ★ 不要拿 CLI 的 -checksum 和 -fchecksum 互相验:两者口径不同,实测同一份固件 给出不同值。字节比对才是唯一真凭据。
snchunkaddressfile_path必填时序 / 数据率分析:找丢帧(序号跳号)、卡顿(间隔离群)、节拍漂移。 输入二选一:text(现成日志内容)或 path(日志/采集文件)。 最顺的用法:先 stream_capture 采一段,再把它的文件路径喂进来。 时间戳自动识别两种:[+0.0123] 采集前缀、HH:MM:SS.mmm;其他格式用 pattern (第 1 个捕获组是秒)。 seq_pattern 给序号(第 1 个捕获组),例如 TICK 42 这种要写 seq_pattern="TICK\s+(\d+)"; 不给则只认行首那个整数。 ★ 定位说明:时间戳来自 PC 侧(OS 调度 + USB 缓冲),自带毫秒级抖动。上面的数适合 【相对比较】——哪几次明显变慢、有没有跳号、是否整体漂移;不适合当示波器判 「周期是不是精确 1.000 ms」。
pathtextpatternseq_pattern长时录测:持续采集串口一段时间,逐行打上到达时间戳落盘。 产出的文件**直接可以喂给 timing_analysis(path=...) ** —— 两件工具是同一条链路。 seconds 最长采集秒数;max_lines 收够多少行提前结束(0=不限); idle_stop_s 连续多少秒没数据提前结束(0=不用);out_path 默认落在会话日志目录 (已存在同名文件会自动改名,不覆盖旧现场)。 ★ 为什么时间戳必须在采集端打:事后才拿到的日志如果没时间戳,时序信息就永久丢了。 采集时按到达时刻记一行,成本几乎为零,却让后面所有分析成立。 排查「偶发丢数据」「隔一会儿卡一下」时用:让它自己采几分钟,再看 timing_analysis。
port必填secondsbaudrateout_pathmax_linesidle_stop_s桥自检:问那块跑桥固件的板「你是谁、支持哪些总线」——不碰任何总线,纯握手。 排查「总线工具不工作」的第一步:先确认桥活着、认协议,再去怀疑接线。 省得在「固件问题」和「接线问题」之间来回猜。 port 不填则读环境变量 MCU_DEBUG_BRIDGE_PORT。桥固件源码见 tools/bridge_fw/。
portbaudrate扫描 I2C 总线上的 7 位地址,标出每个应答地址上**可能**是什么器件。 这是「板子上到底挂了什么」的第一手答案。low/high 可缩小范围 (默认 0x08–0x77,即 I2C 规范里可自由分配的段;保留段会被跳过)。 ★ 三条边界(别把结论放大): · 「有应答」只说明该地址有人 ACK,不代表这颗器件功能正常; · 同一地址常被多颗器件共用(0x68 上可能是 MPU-6050 也可能是 DS1307), 要定种必须读它的 WHO_AM_I / ID 寄存器(i2c_read_reg); · 挂在 TCA9548A 这类 I2C 多路复用器**后面**的器件默认是断开的, 直接扫主总线只能看到复用器自己(0x70–0x77)。
buslowhighportbaudrate读 I2C 从设备的寄存器(最常见的「这颗 IMU 是不是活的」动作)。 addr 是**7 位**地址(MPU-6050 写 0x68,别写 8 位的 0xD0/0xD1,工具会拦下来); reg 是寄存器地址;count 是要读的字节数(1–256)。 ★ 读回全 0 或全 0xFF 时不要直接判「器件坏了」:工具会分别给出这两种情况 完全不同的成因清单(全 0xFF 往往是器件没接/没上电,而不是寄存器的值是 0xFF)。
busreg必填addr必填portcountbaudrate写 I2C 从设备的寄存器。data 支持 "0x01" / "1,2" / [1,2]。 verify=True 会在写完后回读比对。★ 默认关:有些寄存器是**只写**的 (命令寄存器、软复位寄存器),回读必然拿到别的东西,默认开着会造成大量假 FAIL。 写操作受隔离层写权限门禁:只读档会被拒绝,并给出放开方式。
busreg必填addr必填data必填portverifybaudrate逐条 CS 读 JEDEC ID(0x9F),找出板上挂的 SPI flash,并解出厂商与容量。 ★ SPI **没有地址概念**,所以「扫描」只能是「对每条 CS 发 0x9F」。因此本工具 只对遵守 0x9F 的 SPI NOR/NAND flash 有效;对 SPI 传感器 / ADC / 显示驱动 (各有自己的命令字)扫不出来是**正常**的,不是故障 —— 那种器件请用 spi_read_reg 给具体命令字。
busportbaudrate发一个 SPI 命令字并读回 N 字节(对读了命令字的从设备通用)。 例:读 JEDEC ID → spi_read_reg(cmd_byte=0x9F, count=3)。
csbusportcountbaudratecmd_byte必填1-Wire 搜索 ROM,列出总线上每颗器件的 64 位 ROM 码,并解出家族码对应的器件。 搜不到时的第一条怀疑对象是**数据线缺 4.7k 上拉**(1-Wire 靠上拉给器件充电, 缺了必然一颗都搜不到)——输出里会给完整清单。
busportbaudrate经桥的 RS485 收发器发送字节(桥负责 DE 方向脚的时序)。 ★ 能力边界:它只能确认**桥把字节推出去了**,对端有没有收到它证明不了。 要验证整条链路得让对端回话(再用 serial_read 读),或示波器看 A/B 差分波形。 这句限制会直接打印在输出里,不代用户下结论。
busdata必填portde_msnewlinebaudrate已滿足適用檢查項的 90%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
MCU_DEBUG_MCP_MODE=write # 放开写权限(做真机操作时)MCU_DEBUG_MCP_PORT_DENY=COM6,COM28 # 把在用的口锁死(即使放开了写权限也打不开)MCU_DEBUG_MCP_NO_TOUCH_DTR=1 # 打开端口时不翻转 DTR/RTS,尽量不触发开发板自动复位# 方式一:PyPI / pip(发布形态,src 正规包——不污染 site-packages 顶层)pip install mcu-debug-mcp # 核心(MCP 框架)pip install "mcu-debug-mcp[all]" # + pyserial(串口)+ pyocd(调试器/RTT)
# 方式二:从源码目录安装pip install .[all]
# 方式三:开发模式(改代码即时生效;测试/审计直接从 src/ 导入)pip install -e .[all]
# 装完后用 console script 起服务(等价于 python -m mcu_debug_mcp.server)mcu-debug-mcp# Claude Codepip install "mcu-debug-mcp[all]"claude mcp add mcu-debug-mcp -- python -m mcu_debug_mcp.server{ "mcpServers": { "mcu-debug-mcp": { "command": "python", "args": ["-m", "mcu_debug_mcp.server"], "env": { "MCU_DEBUG_MCP_MODE": "readonly" } } }}pip install "mcu-debug-mcp[all]"{ "mcpServers": { "mcu-debug-mcp": { "command": "python", "args": ["-m", "mcu_debug_mcp.server"] } }}帮我安装 mcu-debug-mcp:执行 pip install "mcu-debug-mcp[all]",装好后把 {"mcpServers": {"mcu-debug-mcp": {"command": "python","args": ["-m", "mcu_debug_mcp.server"]}}} 写入 MCP 配置并告诉我如何 Trust 启用。mcu-debug-mcp/├── src/mcu_debug_mcp/ # ★ 正规包(2026-10-13 C 档重构:wheel 不再污染 site-packages 顶层)│ ├── __init__.py # 版本单一来源(pyproject dynamic attr 从这读)│ ├── server.py # MCP 薄壳(FastMCP 注册 55 工具 + 13 resources + 6 prompts)│ ├── core.py # 全部工具实现(零框架依赖,可独立测试)│ ├── analyzer.py # 故障模式引擎(30 内置规则 + JSON 外置规则加载 + min_count 计数门槛)│ ├── guard.py # 运行时隔离层(默认禁写硬件 + 端口名单 + 不可逆操作二次确认)│ ├── paths.py # 数据根定位($HOME env > ~/.mcu-debug-mcp > 包内置;外置层可合并覆盖)│ ├── knowledge/ # 领域知识库(JSON 外置,社区可贡献;随包分发)│ │ ├── fault_patterns.json # 可外置故障模式规则(正则类,逐条标 source,支持 min_count)│ │ ├── error_codes/ # 错误码直译表(esp_idf/at_common/cwjap/cme/mqtt_rc/modbus_exception)│ │ ├── pinmap/ # MCU 引脚复用映射(数据手册级,三引擎:f103c8t6/f411ceu6/esp32dev)│ │ └── boards/ # 板级知识库(bluepill / blackpill_f411)│ ├── drivers/ # 预验证驱动模板 9 款(LLM 只选型,不现编代码;.h/.c/port/wiring.md/meta.json)│ └── tools/ # live_acceptance.py 真机验收夹具(默认只读)+ bridge_fw 桥固件│ # + verify_stdio.py 端到端 + mutation_check.py 变异验证├── setup.py # 仅保留构建临时目录重定向(打包声明全在 pyproject.toml)├── coverage.json # 能力面清单(26 缺口 + 27 能力轴,审计脚本据此对账)├── SECURITY.md # 安全与隔离模型 + 破坏性操作矩阵 + 漏洞报告通道├── pyproject.toml # src 布局 + include-package-data + console script(v4.5.5)├── MANIFEST.in # sdist 收录清单(漏一行就复发「装出来是空壳」)├── requirements.txt├── examples/ # offline_demo.py:离线可跑的示例├── docs/ # AUDIT-2026-10-13.md 审查报告(含修复记录)等└── tests/ ├── test_analyzer.py # 101 项,✅ 已验证 ├── test_driver_logic_py.py # 49 项,✅ 已验证(C 算法对拍) ├── test_driver_logic.c # C 版同源测试,✅ 107 checks 0 failed(9 款驱动全量) ├── run_c_tests.py # C 测试运行器(无需 make,自动处理 msys2 的 cc1 PATH 坑) ├── Makefile.logic # C 测试 makefile(等价路径) ├── validate_knowledge.py # 知识库核验脚本(CI 可挂),✅ 0 错误 ├── audit_capability.py # 能力面审计:广度/深度阶梯/缺口/文档对账/**隔离与安全** ├── audit_constants.py # 常量一致性审计(含版本单一来源 ↔ README) ├── conftest.py # 测试期强制硬件隔离(锁档位 + 禁端口 + 替换串口构造) ├── isolation.py # 临时解除隔离的唯一入口(给「测笼子内部逻辑」的测试用) ├── test_isolation.py # 隔离层专项 ├── test_package_smoke.py # 打包冒烟(真构建 sdist+wheel 双安装 + 子进程 + 剔除源码树) ├── stub/ # C 测试桩(port_all.h + 各驱动桩头文件) └── test_serial_tools.py # ✅ 全过(含 loop:// 回环端到端)