Skip to main content
Glama
README.md
# SerialHub

[English](./README.en.md) | 简体中文

串口(MCU)与网络连接(Web终端/AI)之间的双向桥接器。

**📚 文档**: [MCP 使用指南](./MCP.md) | [项目架构](./AGENTS.md) | [集成测试](./tests/integration/README.md)

## 项目简介

SerialHub 把 MCU 串口同时桥接给人和 AI:

- **人**:浏览器里的 xterm.js Web 终端,实时查看与输入
- **AI**:原生 MCP 服务器(Streamable HTTP + stdio),提供 7 个工具——`serial_list` / `serial_connect` / `serial_write` / `serial_read` / `serial_clear` / `serial_disconnect` / `serial_status`
- 双通道共享**同一串口连接与数据缓冲**,人和 AI 看到的是同一串字节
- **联邦模式**:Windows 主实例 + WSL 从实例,双侧串口以 `side:port` 形式聚合(如 `windows:COM3`、`wsl:/dev/ttyUSB0`),主实例失联后从实例自动晋升
- 单个 Go 二进制(前端内嵌),支持 Windows(系统托盘)/ Linux / macOS

## 系统架构

```
┌─────────────┐
│     MCU     │
└──────┬──────┘
       │ 串口 (COM9, 115200, 8N1)
       ▼
┌─────────────────────────────────────────┐
│              SerialHub                  │
│                                         │
│  ┌─────────────┐    ┌───────────────┐  │
│  │  串口管理器  │    │  数据桥接     │  │
│  │   Serial    │◄──►│  (事件总线)    │  │
│  │   Manager   │    │  DataBridge   │  │
│  └─────────────┘    └───────┬───────┘  │
│                             │          │
│              ┌──────────────┼────────┐ │
│              ▼              ▼        ▼ │
│       ┌───────────┐  ┌──────────┐ ... │
│       │   Web     │  │   MCP    │     │
│       │  终端     │  │  服务    │     │
│       │ (端口5000)│  │ (HTTP)   │     │
│       └───────────┘  └──────────┘     │
└─────────────────────────────────────────┘
       │                    │
       ▼                    ▼
┌─────────────┐     ┌─────────────┐
│   浏览器     │     │  AI 工具     │
│ (人工操作)   │     │ (OpenCode,  │
│             │     │  iFlow CLI) │
└─────────────┘     └─────────────┘
```

## 技术栈

| 组件 | 技术 |
|------|------|
| 语言 | Go 1.26+ |
| 串口通信 | go.bug.st/serial |
| AI 接口 | MCP (Model Context Protocol) / go-sdk |
| Web 终端 | WebSocket / xterm.js |
| CLI | spf13/cobra |
| 配置 | spf13/viper |
| 系统托盘 | getlantern/systray |
| 日志 | sirupsen/logrus |

## 功能特性

- **双路转发**:串口数据同时转发到 Web 终端和 AI 接口
- **双向通信**:Web 终端或 AI 发送的命令均可传输到 MCU
- **Web 终端**:基于 WebSocket 的浏览器终端,支持 xterm.js;启动后自动在浏览器打开(WSL 下也能弹出 Windows 浏览器)
- **MCP 协议**:通过标准 HTTP JSON-RPC(MCP Streamable HTTP 传输、非流式 JSON 响应)提供 AI 工具集成;也支持 `--stdio` 本地拉起(OpenCode local 模式,自动代理到已运行实例)
- **联邦模式**:Windows 与 WSL 可同时运行,后启动的自动接入,`serial_list` 聚合双侧串口;主实例失联时从实例自动晋升
- **可配置**:所有端口、波特率、超时参数均可通过 TOML 或命令行配置
- **可观测**:所有数据流均可记录和追踪
- **错误恢复**:网络/串口故障时优雅处理,不影响其他功能

## 安装

从 [GitHub Releases](https://github.com/dongly/serialhub/releases) 下载对应平台的压缩包,
解压即用(Windows 包内含 `start.ps1`/`start.bat` 启动脚本)。

详细步骤(PATH 配置、安装验证、WSL USB 串口挂载)见 [QUICKSTART.md](./QUICKSTART.md)。
WSL 用户推荐用 [wsl-usb-manager](https://github.com/nickbeth/wsl-usb-manager) 一键把
USB 串口 attach 进 WSL。

## 构建

```bash
go build -o bin/serialhub.exe ./cmd/serialhub
```

## 命令参考

### `serialhub`(默认:serve 模式)

启动 HTTP + Web 终端服务器:

```bash
serialhub                                    # 默认配置启动
serialhub -p COM8                            # 指定串口
serialhub -p COM8 -b 9600 --parity even      # 完整串口参数
serialhub -m 8080                            # 使用 8080 端口
serialhub --host 0.0.0.0                     # 监听所有网络接口(联邦模式 Windows 主侧需要)
serialhub --stdio                            # stdio 模式(MCP 客户端本地拉起)
serialhub -c config.toml                     # 使用配置文件
serialhub -D                                 # 调试模式
```

完整的选项:

| 选项 | 简写 | 说明 | 默认值 |
|------|------|------|--------|
| `--serial-port <port>` | `-p` | 串口名 | 配置文件或空 |
| `--baud-rate <rate>` | `-b` | 波特率 | 115200 |
| `--data-bits <bits>` | `-d` | 数据位(5/6/7/8) | 8 |
| `--parity <type>` | - | 校验位(none/even/odd) | none |
| `--stop-bits <bits>` | `-s` | 停止位(1/2) | 1 |
| `--mcp-port <port>` | `-m` | MCP HTTP 服务端口 | 5000 |
| `--host <host>` | - | 监听地址 | 127.0.0.1 |
| `--config <path>` | `-c` | 配置文件路径 | - |
| `--debug` | `-D` | 启用调试模式 | false |
| `--stdio` | - | stdio 模式:MCP 客户端本地拉起(发现主实例则透明代理) | false |
| `--minimized` | - | 由脚本启动,跳过自动打开浏览器(跨平台;Windows 下同时隐藏控制台) | false |

### 系统托盘(Windows)

`serialhub` 在 Windows 上默认启动系统托盘图标,启动后自动隐藏控制台窗口。

**托盘图标状态:**
- 灰色 — 未连接串口
- 绿色 — 串口已连接
- 红色 — 连接错误

**右键菜单功能:**

| 菜单项 | 功能 |
|--------|------|
| 串口信息 | 点击可连接/断开串口 |
| 端口信息 | 显示 MCP 端口(不可点击) |
| 显示/隐藏控制台 | 切换控制台窗口 |
| 退出 | 关闭 SerialHub |

**交互方式:**
- 双击托盘图标:切换控制台窗口显示/隐藏
- 右键托盘图标:打开菜单

### 快速开始

**场景:人工 + AI 同时调试**

1. 启动 SerialHub:

```bash
serialhub -p COM9 --host 0.0.0.0 -D
```

2. 人工通过 Web 终端连接监视:

打开浏览器访问 `http://localhost:5000/terminal`

3. AI 工具通过 HTTP MCP 连接:

```json
{
  "mcp": {
    "serialhub": {
      "type": "remote",
      "url": "http://localhost:5000/mcp",
      "enabled": true
    }
  }
}
```

4. 串口数据同时转发到 Web 终端和 AI 接口,两者可独立向串口发送命令。

### 通过 Web 终端访问

SerialHub 内置基于 WebSocket 的终端界面,使用 xterm.js 提供完整的终端体验。

**访问地址**:`http://localhost:5000/terminal`

**功能特性**:
- 实时显示串口输出
- 支持键盘输入发送到串口
- 支持 Ctrl+C、Ctrl+D 等控制字符
- 自动重连

### MCP HTTP API 调用

服务器启动后,可通过 JSON-RPC 调用 MCP 工具:

```bash
# 健康检查
curl http://localhost:5000/health

# 列出可用串口
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"serial_list"},"id":1}'

# 连接串口
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"serial_connect","arguments":{"port":"COM9"}},"id":2}'

# 发送命令(自动追加换行符)
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"serial_write","arguments":{"data":"help"}},"id":3}'

# 读取串口返回数据(阻塞等待,timeout=0 表示无限等待)
curl -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"serial_read","arguments":{"timeout":5000}},"id":4}'
```

## MCP 工具列表

| 工具名 | 描述 | 参数 |
|--------|------|------|
| `serial_list` | 列出系统中所有可用的串口 | - |
| `serial_connect` | 连接到指定串口 | `port`(必填),`baudRate?`(默认 115200) |
| `serial_disconnect` | 断开当前串口连接 | - |
| `serial_write` | 向串口发送数据 | `data`(必填),`addNewline?`(默认 true,自动追加换行符,置 false 时禁用) |
| `serial_read` | 阻塞式读取串口数据,等待数据到达后返回 | `timeout?`(默认 1000ms,0=无限等待),`maxSize?`(默认 4096 字节) |
| `serial_clear` | 清空 read 缓冲区,丢弃尚未读取的数据 | - |
| `serial_status` | 获取串口连接状态 | - |

### AI 工具使用指南

#### 标准工作流程

```
serial_list → 识别目标串口 → serial_connect → serial_write 发送命令 → serial_read 读取响应
```

#### 工具使用时机

| 场景 | 推荐工具 | 说明 |
|------|----------|------|
| 不知道串口名 | `serial_list` | 获取可用串口列表,根据 vendorId/productId 或厂商名识别目标设备 |
| 开始调试前 | `serial_connect` | 必须先连接串口才能进行后续操作 |
| 发送 Shell 命令 | `serial_write` + `serial_read` | 写入后立即读取响应,如 `help`、`version`、`reboot` |
| 发送调试指令 | `serial_write` | 向 MCU 发送控制命令或配置参数 |
| 获取命令输出 | `serial_read` | 读取设备返回的数据,timeout=0 无限等待适合不确定响应时间 |
| 检查连接状态 | `serial_status` | 操作前确认已连接,或操作失败时检查连接是否断开 |
| 切换设备 | `serial_disconnect` → `serial_connect` | 先断开当前连接,再连接新设备 |
| 结束会话 | `serial_disconnect` | 释放串口资源 |

#### 常见操作示例

**1. 首次连接设备**

```
// 步骤 1: 查找可用串口
serial_list()
// 返回: { ports: [{ path: "COM6", vendorId: "0D28", productId: "0202" }, ...] }

// 步骤 2: 根据硬件 ID 识别目标设备,连接
serial_connect({ port: "COM6", baudRate: 115200 })
// 返回: { success: true, port: "COM6", baudRate: 115200 }
```

**2. 发送命令并获取响应**

```
// 发送命令(自动追加换行符)
serial_write({ data: "version" })
// 返回: { success: true, bytesWritten: 8 }

// 读取响应(等待 2 秒)
serial_read({ timeout: 2000 })
// 返回: { data: "MCU v1.2.3\nBuild: 2024-01-15\n", timedOut: false, bytes: 28 }
```

**3. 等待不确定时间的响应**

```
// timeout=0 表示无限等待,直到有数据到达
serial_write({ data: "flash_verify" })  // 耗时操作
serial_read({ timeout: 0 })  // 等待直到设备返回结果
```

**4. 切换到不同设备**

```
serial_disconnect()  // 断开当前连接
serial_list()        // 重新查找串口
serial_connect({ port: "COM7" })  // 连接新设备
```

**5. 检查连接状态**

```
serial_status()
// 已连接: { connected: true, port: "COM6", baudRate: 115200 }
// 未连接: { connected: false }
```

#### 错误处理

| 错误情况 | 原因 | 解决方案 |
|----------|------|----------|
| serial_write 返回 `串口未连接` | 未调用 serial_connect 或连接已断开 | 先调用 serial_connect |
| serial_read 返回 `timedOut: true` | 超时内无数据到达 | 增大 timeout 或检查设备是否正常响应 |
| serial_connect 返回 `success: false` | 串口不存在、权限问题或设备占用 | 检查 serial_list 结果、确认波特率配置 |
| 读取内容不完整 | 输出较长,一次读取未完全获取 | 循环调用 serial_read 直到 timedOut=true |

#### 最佳实践

1. **始终先检查状态**:复杂操作前调用 `serial_status` 确认连接有效
2. **匹配波特率**:`baudRate` 必须与目标设备配置一致,常见值 115200、9600
3. **合理设置 timeout**:常规命令 1-5 秒,耗时操作设为 0(无限等待)
4. **发送后立即读取**:`serial_write` 完成后立即 `serial_read`,避免数据堆积
5. **解析输出时考虑换行**:大多数 Shell 命令响应包含 `\n` 换行符

## 配置说明

配置优先级:**CLI 参数 > 配置文件 > 默认值**

配置文件格式(TOML),支持 `#` 注释:

```toml
# 日志目录,为空则保存到可执行文件目录下的 logs/
# logDir = "D:/Logs"

[serial]
port = ""           # 串口号,为空时不自动连接
baudRate = 115200
dataBits = 8
parity = "none"     # none / even / odd
stopBits = 1

[mcp]
httpPort = 5000
```

| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `serial.port` | `""`(空) | 串口号,为空时不自动连接 |
| `serial.baudRate` | `115200` | 波特率 |
| `serial.dataBits` | `8` | 数据位(5/6/7/8) |
| `serial.parity` | `"none"` | 校验位(none/even/odd) |
| `serial.stopBits` | `1` | 停止位(1/2) |
| `mcp.httpPort` | `5000` | MCP HTTP 服务端口(同时提供 Web 终端) |
| `logDir` | `""` | 日志目录,为空则保存到可执行文件目录下的 `logs/` |
| `debug` | `false` | 调试模式开关 |

## 开发

```bash
# 开发运行
go run ./cmd/serialhub

# 构建
go build -o bin/serialhub.exe ./cmd/serialhub

# 测试
go test ./...

# 静态分析
go vet ./...

# 整理依赖
go mod tidy
```

## 许可证 / License

本项目采用 [Apache License 2.0](./LICENSE)(Copyright 2026 dongly)发布。

This project is licensed under the [Apache License 2.0](./LICENSE).