← 返回经历树 水烛 · 程千和 · 经历详情
机器人 · 电控

WebUSB 远程 USB

USB/IP 的消费级落地 · 全国大学生嵌入式芯片与系统设计竞赛东部赛区三等奖(2026)
时间 2026.7获奖 东部赛区三等奖团队 白水队 · 3 人选题 无线协议与网络应用 · 网络中继与网关

背景

嵌入式现场运维常遇到这样的问题:客户设备往往只暴露一个 USB 口,远程调试需要携带笔记本电脑到现场;平板和手机无法运行 J-Link 调试软件,也无法读取 U 盘。现有的 USB/IP 命令行或商业 USB 重定向方案需要安装驱动、开启测试模式并输入命令,普通用户难以使用。

WebUSB 将 USB/IP 做成面向普通用户的产品:现场只需一块 ESP32-S3 开发板和一部手机,USB 设备插入后即可出现在远程 Linux 电脑上,被官方软件识别为本地设备,全程无需命令行。

演示视频

功能演示 · 约 2 分钟 · 2026-07

系统架构

现 场 公 网 远 程 USB 设备 J-Link 调试器 · U 盘 ESP32-S3 固件 ESP-IDF 5.2 · FreeRTOS 多任务 USB Host 枚举 · USB/IP 服务端 NimBLE 配网 · Wi-Fi 隧道客户端 虚拟 ACK · 8 KB 流式分块回包 LCD 状态屏 · 长按复位 中转服务器 Relay Node.js + ws Token 撮合 · 三态 Session 字节透明转发,不解析 USB/IP 桌面 Agent Flutter Desktop · Riverpod AgentBridge 生命周期编排 LocalUsbipProxy · PDU 重组 二维码 · 断线重连 · 30 s 心跳 Linux 版完成 · Windows 骨架 内核 vhci_hcd 虚拟 USB 主机控制器 像本地直插一样 自动 mount · 官方软件识别 J-Link 手机 App Flutter · 扫码取 Token → BLE 下发 Control / Bulk WS 拨出 WS 拨出 USB/IP PDU 封装为 0x10 隧道帧,两端都在 NAT 后 TCP 127.0.0.1:3240 attach → mount BLE:Wi-Fi + Relay + Token 扫二维码 同一 Token 撮合 · PEER_READY 四 层 协 议 L1 BLE 配网包 UTF-8 JSON · GATT 0xFF02 L2 隧道帧 1 字节类型 + 载荷 · 7 类 L3 USB/IP PDU 内核线格式 · 大端序 48 B 头 L4 本地 usbip usbip attach · vhci_hcd 实线 = 数据面(USB 请求往返) 虚线 = 配对面(只在首次 / 重新配对时走)
系统架构总览 · 按竞赛技术报告「系统总体设计」以水烛风格重绘

子系统

ESP32-S3 固件ESP-IDF 5.2 + FreeRTOS:USB Host 枚举、USB/IP 服务端、NimBLE 自定义 GATT 配网、Wi-Fi STA、WebSocket 隧道客户端;全局"软状态机"由事件队列驱动,镜像到 0.96 寸 LCD 状态屏
中转 RelayNode.js + ws:只做 Token 配对撮合(三态 Session)与字节透明转发,不解析 USB/IP 内容;两端都主动拨出,绕开 NAT 与端口映射
手机 AppFlutter + Riverpod:扫远程电脑上的二维码取 Token,经 BLE 把 {Wi-Fi 凭据, Relay 地址, Token} 写入 ESP32;BLE 层抽象成接口,真机与 Mock 两套实现
桌面 AgentFlutter Desktop:生成二维码、维持隧道;本地代理监听 127.0.0.1:3240 桥接内核 vhci 的 TCP 流与 WebSocket;attach 后自动 mount 并打开文件管理器。Linux 版完整可用,Windows 版目前只有骨架
内核 vhci_hcdLinux 虚拟 USB 主机控制器,把网络传来的 URB 还原成本地 USB 请求

关键技术

  • USB/IP over WebSocket 隧道:标准 USB/IP 走裸 TCP、两端需互相可达;这里把 PDU 封装成 WebSocket 二进制帧(0x10 类型)经公网 Relay 透传,一条 WS 消息恰好对应一个隧道帧边界
  • 蓝牙 + 扫码零配置配对:Token 由 Agent 本地生成(UUID v4)写进二维码,手机扫码后连同 Wi-Fi 凭据经 BLE 下发;Relay 只撮合不签发。用户主动断开时 Agent 重新生成 Token,ESP32 必须重新配网——零配置但可控
  • 无 PSRAM 的流式 bulk IN 分块回包:开发板一启用 PSRAM 就 boot-loop,堆非常紧张。大 URB 先回 48 字节 RET_SUBMIT 头,再按 8 KB 分块发送数据,由远端内核从 TCP 字节流重组;分配失败回 -ENOMEM 让内核重试,避免 URB 悬挂 30 秒卡死
  • 双重枚举的状态同步:ESP-IDF 和 Linux vhci 各枚举一次设备,地址、配置、端点 data toggle 会对不上。固件对 SET_ADDRESS / SET_CONFIGURATION 做"虚拟 ACK"并刷新端点表,对 CLEAR_FEATURE(ENDPOINT_HALT) 配合 pipe reset 与 ESP-IDF 补丁复位 data toggle——否则 bulk 传输会静默丢包
  • 本地代理 PDU 重组:TCP 是字节流、WebSocket 要消息边界,代理按 USB/IP 线格式(OP 阶段看版本号,CMD/RET 阶段 48 字节头 + 传输长度)攒够完整 PDU 再发
  • 体验闭环:BLE 配网分"首次 / 配置相同 / 配置变更"三条分支,变更时延迟 1 秒重启以避开 Android GATT 133 错误;App 先订阅上线通知再下发配置,防止丢通知;30 秒心跳保活,掉线自动重连

协议设计

L1 BLE 配网包手机写入 GATT 0xFF02 的 UTF-8 JSON:版本、Wi-Fi 凭据、Relay 端点、Token、公钥指纹
L2 隧道帧WebSocket 二进制帧,1 字节类型 + 载荷,共 7 类:HELLO / HELLO_ACK / PEER_READY / PEER_GONE / PING / USB_CHANGE / USBIP_DATA
L3 USB/IP PDU遵循 Linux 内核线格式、大端序;ESP32 是小端,多字节字段逐字节读写
L4 本地 usbipAgent 调用 usbip attach / detach 与 vhci_hcd 内核模块

测试与结果

  • Relay 14 个单元测试覆盖 Session 状态机、Token 生命周期、透明转发与错误码;Dart 侧静态分析 0 错误
  • 端到端真机:完整配对 + U 盘读写(MD5 与本地一致)、J-Link 远程枚举并被官方软件识别、Wi-Fi 密码错误、转发中拔 U 盘、断网自动重连、重新配对,全部通过
  • 吞吐约 1.5–6 KB/s,受 ESP32-S3 USB Full Speed(12 Mbps)与公网 RTT 限制:适合 J-Link 命令与小文件,不适合大文件

已知限制:BLE 配网与 WebSocket 目前是明文,产品化前要上 wss 与 protocomm Security v2;Windows Agent 待集成 usbip-win2。

团队与仓库

白水队:程千和、石渊博、俞帅波。

仓库:github.com/QFcattail/webusb(仓库许可证仍为赛期"保留所有权利"声明,正式开源许可发布前本站不提供源码包下载)

技能

ESP32-S3ESP-IDFUSB/IPWebSocketBLE GATTFlutterNode.js协议设计产品设计

后续

由 RoboMaster 电控经验延伸出的产品化实践,将竞赛中积累的嵌入式能力应用于面向普通用户的完整产品。

蒲草青青,织就百物 · © 2026 程千和 · 京ICP备2026006842号-2