# serial-mcp-server 串口调试指南 通过 **Adancurusul/serial-mcp-server**(Rust,MIT)自主读取/控制 **USART1 串口**(PA9 TX / PA10 RX,115200bps)的打印流(MCU printf)。 ## 项目与安装 - 仓库:https://github.com/Adancurusul/serial-mcp-server - 本机安装:`%LOCALAPPDATA%\serial-mcp-server\serial-mcp-server.exe`(自源码 `cargo build --release` 编译,v0.3.0) - 两种形态: - **MCP stdio server**(`serve` 子命令)—— opencode.json 中 `mcp.serial`,工具带 `serial_` 前缀 - **CLI**(`list-ports` / `open` / `read` / `write` / `close` 等)—— 适合脚本化/一次性抓取 - **串口端口单一配置源:`.opencode/serial_config.json`**(当前本机 `COM6` / 115200)。端口变化只改这个文件 ## 核心能力边界(重要) **⚠️ 该项目没有"读取历史消息/历史缓冲"功能**(对比 qarnet/serial-mcp 有常驻环形缓冲): - MCP `read` 只能读到**调用之后**到达的字节,之前的数据直接丢弃 - 连接打开期间**没有后台 ring buffer**(源码 `src/serial/connection.rs` 只持有流,无历史) - 抓开机日志必须用 **采集窗口模式**(`duration_ms` + `start_trigger=first-byte`):**先启动采集等第一字节,再触发设备输出**,否则错过 ## 采集窗口模式(capture) 这是该项目抓完整连续输出的唯一方式,等价于"有边界的持续读"。 ### CLI 参数 ``` serial-mcp-server read --port COM6 --baud 115200 \ --duration-ms 30000 \ # 采集窗口时长(从触发点起算) --start-trigger first-byte \ # 触发方式:first-byte(默认)/ immediate --initial-timeout-ms 90000 \ # first-byte 模式最多等多久来第一字节(超时空返回) --idle-timeout-ms 8000 \ # 采集开始后连续静默多久提前结束 --max-bytes 65536 \ # 累计字节硬上限 --json ``` ### 结束原因(completion_reason) | 值 | 含义 | |---|---| | `initial_timeout` | 等 first-byte 超时,返回空(不算错误) | | `duration_elapsed` | 从触发点采满 `duration-ms` | | `idle_timeout` | 采集中途静默超过 `idle-timeout-ms` 提前收工 | | `max_bytes` | 累计达 `max-bytes` | ### 返回结构 `read` 对象含:`data`(合并完整字节)、`bytes_read`、`status`(ok/timeout)、`completion_reason`、`waited_ms`(等到首字节耗时)、`elapsed_ms`(采集时长)、`chunks[]`(每块 offset/bytes_read/waited_ms/elapsed_ms)。 ## 抓取 MCU 开机日志(@test 核心场景) **顺序必须:先启动采集(打开 COM 等 first-byte)→ 再烧录触发复位 → 采集自动抓取。** ```powershell # 一键:采集(含 EOF 重试)→ 烧录 → 提取日志 → verify 校验(在仓库根目录执行,路径用仓库相对路径) powershell -ExecutionPolicy Bypass -File "test\run_boot_test.ps1" ` -Port COM6 -BaudRate 115200 -DurationMs 30000 -InitialTimeoutMs 90000 ` -IdleTimeoutMs 8000 -MaxBytes 65536 ` -FlashCmd "MDK-ARM\download.bat" ` -Required "App init OK;Network Init;net init: OK;PHY: 100M FULL" ` -Forbidden "FAIL;ERROR;HardFault" ``` 退出码:0=PASS / 1=FAIL / 2=采集或烧录错误。 ### 底层脚本(`test/serial_capture.ps1`) - **`-FlashCmd` 分支**:一个调用内完成 稳定启动采集 → 烧录 → 等采集结束,避免工具调用间隔导致采集超时 - **EOF 自动重试**:COM 被上一个进程释放后立即重开会报 `UnexpectedEof: serial stream returned zero bytes during capture`(tokio-serial 读 0 字节,第三方项目 capture.rs 视为致命错误)。脚本检测到后退避 1.5s 重开,最多 3 次 - 采集稳定启动判据:进程存活超过 `-StabilityCheckMs`(默认 1.5s) ### 参数经验值(本机固件) | 参数 | 值 | 理由 | |---|---|---| | `idle-timeout-ms` | 8000 | boot 阶段有自然静默间隙(defaultTask 与 netTask 之间约 3~4s),太小会在网络初始化前提前结束 | | `initial-timeout-ms` | 90000 | 容错:烧录编译+下载耗时可能 40s+,采集必须等得住 | | `duration-ms` | 30000 | 覆盖完整开机流程(boot ~5s + net init ~3s + FTP 延迟 7s) | ## 交互式调试(MCP) 用于主动下发命令、读取响应(非开机日志抓取): 1. `serial_list_ports` → 找 `COM6`(读 `.opencode/serial_config.json` 确认) 2. `serial_open`(port=COM6, baud_rate=115200)→ 返回 `connection_id`(UUID) 3. `serial_write`(connection_id, data, encoding=utf8/hex/base64) 4. `serial_read`(connection_id, timeout_ms=1000)→ 单次读,只能读到调用后的字节 5. `serial_close` MCP `read` 也支持采集窗口字段:`duration_ms`、`start_trigger`、`initial_timeout_ms`、`idle_timeout_ms`(传 `duration_ms` 即进入 capture 模式)。 ## 常见陷阱 | 现象 | 原因/处理 | |---|---| | `UnexpectedEof: serial stream returned zero bytes` | COM 刚被释放立即重开(tokio-serial 读 0 字节)。用 `serial_capture.ps1`(含重试)或间隔几秒再开 | | 抓到日志缺少 `Network Init` | `idle-timeout-ms` 太小,在网络初始化前的静默间隙提前结束;调到 8000ms | | 烧录后无数据 | 采集必须在烧录**前**启动;`initial-timeout-ms` 太短导致采集先超时 | | `Error: Connection ID ... not found` | connection_id 是每次 open 生成的 UUID,读/写/关都要用它 | | 端口被占用 | 先 `serial_close` 释放;Keil ST-Link 下载不占 COM,可并存 | ## 工具历史 本项目是 Serial Studio MCP 的替代方案(更轻量、无 GUI 依赖、无导出文件定位问题)。Serial Studio 相关配置、bridge 与文档已全部清理。