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

io.github.stillsurgePublished by GitHub account stillsurgev0.1.1Updated Oct 30, 2026
Embedded debug MCP: serial, flash verify, fault decode, 45-pattern log analyzer, driver library
Local MCP server for embedded hardware debugging: serial read/write, log fault analysis, pin checks, firmware flashing and register decoding.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Follow the launch instructions in the repository.
让 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 字段必须如实写验证状态——评测员和学生都会实测Source: README.md at commit f277f4c
列出系统可用串口。识别 CH340/CP2102/FT232 等常见 USB 转串口芯片并给驱动建议。 毕设学生"串口列表里找不到板子"时先调这个。
Takes no parameters.
打开串口,返回句柄 handle。port 形如 "COM3" 或 "/dev/ttyUSB0"。 波特率不确定就先用 115200(STM32 HAL 默认)或 9600(老例程/模块默认)。
portrequiredparitybaudratedata_bitsstop_bits关闭串口句柄。调试结束记得关,释放端口。
handlerequired从串口读取数据(非阻塞,三条件提前退出)。 expect:正则,命中立即返回(如 expect="OK|ERROR" 等响应); idle_ms:静默超时(板子说完就走); seconds:总时长上限。返回文本视图 + HEX 视图 + 是否命中。 读到的内容同时累积进会话日志。
expecthandlerequiredidle_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 用)。
datarequiredexpecthandlerequiredwait_snewlinehex_mode自动探测波特率:逐档打开串口采样,按"最像正常日志"评分排序并给推荐。 log_text 提供现成日志样本时走离线评分(不占串口)。设备静默时会提示先复位再扫。 ★USB CDC 口(ESP32-S3 原生 USB、RP2040 等)上波特率不参与数据路径,本工具会 直接短路说明,不给排序——在那种口上"各档评分"只是同一批字节被反复读。 确需强行扫(例如怀疑枚举成 CDC 但实际是 UART 桥)时显式传 candidates, 如 candidates=[115200, 9600]。
portrequiredlog_textsample_scandidates批量执行 AT 指令序列并逐条断言(半自动回归测试)。 steps 形如 [{"cmd": "AT", "expect": "OK", "timeout_s": 2}, {"cmd": "AT+CWJAP?", "expect": "WIFI|ERROR"}](expect 是正则)。 输出每步 PASS/FAIL/TIMEOUT + 回显尾,中途失败不中断,方便一次看全。
portrequiredstepsrequireddefault_baudtotal_timeout_s同时监听两个串口,输出时间线交错的录制日志(RS485 主从对话/两设备联调)。 每行前缀 [A]/[B] 标明来源口,附 HEX 视图防波特率猜错时完全看不懂。最长 120s。
baud_abaud_bport_arequiredport_brequiredseconds取该句柄自打开以来的全部累积日志(喂给 analyze_log 做整体分析用)。
handlerequired列出已落盘的串口会话日志(serial_open 时自动创建,新→旧)。 会话被关闭甚至对话轮次已隔很久之后,仍可用它找回之前的串口现场。
limit读取指定会话日志的内容(文件名来自 sessions_list)。 默认显示尾部 200 行(日志开头常是复位前旧数据)。整段内容可直接喂 analyze_log。
fnamerequiredtail_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_textrequired计算或校验 Modbus RTU 的 CRC16(RS485 毕设必备)。 check=False:对数据部分计算 CRC;check=True:校验完整帧(数据+CRC)。
checkhex_framerequired解析 Modbus RTU 帧:正常响应拆出寄存器值/线圈状态,异常响应(功能码|0x80) 直译异常码 01-06(非法功能/非法地址/非法值/设备故障/忙...)并给排查建议。 从站"该响应但不响应/回了一帧看不懂"时用它。
hex_framerequired查错误码释义表(外置知识库 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)。 写代码分配引脚之前必须先过一遍。
mcurequiredpinsrequiredboard列出引脚知识库已收录的 MCU 型号。pin_check 前可用它确认型号是否支持。
Takes no parameters.
输出指定 MCU 的 pinmap JSON 原文(全引脚复用映射 + caveats + 常见冲突提示)。 pin_check 报错后想看完整数据、或需要给用户展示映射依据时用。
mcurequired列出板级知识库已收录的开发板。选板子前先看有没有现成板级坑清单。
Takes no parameters.
输出指定开发板的板级知识:板载外设及占用引脚、板级坑清单(按翻车率排序)、 供电说明、数据来源。如 "bluepill"(蓝药丸)、"blackpill_f411"(黑药丸)。 给用户做硬件选型/排板级坑之前先查这个。
namerequired列出已收录的嵌入式生态/框架(Zephyr / RT-Thread / PlatformIO / Keil / IAR / Rust defmt)。 拿到不认识的工作目录、或一段陌生的报错时,先判断「这是哪个生态的工程」—— 识别错了,后面每一步排查都会拐到沟里。
Takes no parameters.
输出指定生态的识别特征:工程产物 / 构建配置 / 日志特征 / 常见坑。 name 用 ecosystem_list 给的 key(如 zephyr / keil / rust_defmt)。
namerequired识别一个**工程目录**属于哪个生态(扫标志文件: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。
Takes no parameters.
探测已连接的调试器(DAPLink/ST-Link/J-Link...)和目标芯片。 "烧录失败/找不到目标芯片"时先调这个。需要可选依赖 pyocd。
Takes no parameters.
通过 pyocd 的 RTT 通道读目标板调试日志(⚠ 实验性)。 固件需集成 SEGGER RTT 控制块;比 UART 快且不占串口外设。 cbuffer:控制块编号(多通道时指定,默认读全部);probe_serial:多调试器时指定。 失败自动给排查指引,建议先 probe_list 确认调试器。
cbuffersecondsprobe_serial检测本机已安装的烧录工具(esptool/st-flash/pyocd/openocd)。 烧录前先调这个看有什么可用。
Takes no parameters.
烧录固件到开发板(**不可逆操作,双闸门**)。 第一道: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_pathrequired列出可用的预验证驱动模板(带出厂验证记录)。 用户要做毕设传感器部分时,先看这里有没有现成的,而不是自己写。
Takes no parameters.
输出指定驱动的接线表+完整代码+验证记录。name 来自 driver_list。 这些代码是预验证过的,直接交付用户;需要适配的只有 *_port.h。
namerequired查指定驱动的接线知识库:接线表、头号坑清单(按翻车率排序)、 输出日志解读、答辩加分点。回答"XX传感器怎么接/为什么不工作"前先查这个。
namerequired器件选型清单:按接口(i2c / uart / timer_pwm / gpio_timing)列出可用的 传感器与模块,带地址、微秒时序要求。回答"我要做温湿度采集/测距/姿态, 用哪个现成器件"时先查这里,别凭记忆报型号。bus 可按接口过滤(如 "i2c")。
bus单个器件的选型详情:接口 / 地址 / 移植要求 / 头号坑 / 出厂验证记录 / 接线避坑全文。name 来自 component_list。
namerequired外设 API 用法知识层:每个驱动要占哪些外设(I2C1/TIM1/UART2…)、 初始化顺序有什么硬要求。移植驱动或排查"外设配了但读不到数"时先查这里。
bus单个驱动的外设初始化要求全文(取自其 *_port.h 适配层原注释)。 name 来自 peripheral_api_list。
namerequired把寄存器原始值按位域拆开解释。 fields 形如 [{"name":"HSION","bit":0}, {"name":"PLLSRC","lsb":16,"width":2}]。 手头有寄存器值不知道每个位什么意思时用。
valuerequiredwidthfieldsrequired用 CMSIS-SVD 文件解码寄存器位域(含枚举值提示与描述)。 svd_path 指向厂商 SVD 文件(CubeMX/固件包里有),如: decode_register_svd("STM32F103.svd", "RCC", "CR", 0x00040002)
valuerequiredregisterrequiredsvd_pathrequiredperipheralrequired解码 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,大小由相邻符号地址差推出(可能偏大)。
pathrequiredtop_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 摆出来让你换源。 找不到符号时明确报"未找到",不给猜测的函数名——猜出来的名字比没名字更害人。
pathrequiredaddressesrequired栈回溯:把 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 反查不到符号时,说明是典型「跑飞」,工具会给排查方向。
sppathrequiredframeexc_return环境自检:依赖 / 串口 / 烧录器 / 知识库根 / 版本,一次问清。 拿到陌生机器、或工具突然不好使时先跑这个:它把「这里能干什么、缺什么、 缺了会导致哪些工具不可用」一次列清,省掉一个个试错。 ★ 只报实测——探不到的会明说为什么探不到。
Takes no parameters.
解析 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 的行会单独指出并跳过,不当成数据。
textrequired通用二进制帧解析:按「帧头 + 长度字段」切帧,可选逐帧校验。 单片机自定义协议(上位机抓到的 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_hexrequiredmax_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_pathrequired时序 / 数据率分析:找丢帧(序号跳号)、卡顿(间隔离群)、节拍漂移。 输入二选一: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。
portrequiredsecondsbaudrateout_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)。
busregrequiredaddrrequiredportcountbaudrate写 I2C 从设备的寄存器。data 支持 "0x01" / "1,2" / [1,2]。 verify=True 会在写完后回读比对。★ 默认关:有些寄存器是**只写**的 (命令寄存器、软复位寄存器),回读必然拿到别的东西,默认开着会造成大量假 FAIL。 写操作受隔离层写权限门禁:只读档会被拒绝,并给出放开方式。
busregrequiredaddrrequireddatarequiredportverifybaudrate逐条 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_byterequired1-Wire 搜索 ROM,列出总线上每颗器件的 64 位 ROM 码,并解出家族码对应的器件。 搜不到时的第一条怀疑对象是**数据线缺 4.7k 上拉**(1-Wire 靠上拉给器件充电, 缺了必然一颗都搜不到)——输出里会给完整清单。
busportbaudrate经桥的 RS485 收发器发送字节(桥负责 DE 方向脚的时序)。 ★ 能力边界:它只能确认**桥把字节推出去了**,对端有没有收到它证明不了。 要验证整条链路得让对端回话(再用 serial_read 读),或示波器看 A/B 差分波形。 这句限制会直接打印在输出里,不代用户下结论。
busdatarequiredportde_msnewlinebaudrate90% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
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:// 回环端到端)