一帧键盘报文 14 字节:语音键鼠从“帮我打字”到字符上屏的每一毫秒

对小智说一句“帮我打:今天天气不错”,电脑键盘自己哒哒哒敲出来——这个效果演示极佳,但拆开全是算术题。这篇把语音键鼠项目(EC600M UART2 → CH9329 → USB HID)的关键数字全摆出来,欢迎对账。

硬件链路与一个容易忽略的事实

EC600M 通过 UART2 接 CH9329(UART 转 USB HID 芯片),CH9329 的 USB 插在电脑上,枚举出标准 HID 键盘 + 鼠标(VID 6666 / PID 1D63,设备管理器 Status=OK)——对电脑来说这就是个普通键鼠,不用装驱动。语音控制的“手”就是这么伸进电脑的。ESP32 旁路做 BLE HID,同一套语音指令还能控手机。

容易忽略的事实:HID 类设备的瓶颈从来不在主控芯片,在物理接口。所以先算账,再设计交互。

串口账:理论上限 68.6 帧/秒,实际限速 10 指令/秒

CH9329 走 UART 9600bps、8N1(8 数据位 + 1 起始 + 1 停止 = 每字节 10 bit)。一帧键盘报文 14 字节:

9600 bit/s ÷ 10 bit/字节 ÷ 14 字节/帧 ≈ 68.6 帧/秒   ← 物理上限
一帧线上时间 = 140 bit ≈ 14.6 ms

明明能发 68.6 帧/秒,我却在设备端做了 100ms 速率限制(≤10 指令/秒),主动砍到六分之一。为什么?实测结论:CH9329 收完报文要内部处理再给应答,单动作(按下+释放)实测 ≤40ms。按物理上限猛发,它处理不过来——丢帧、乱序、丢按键,表现就是“字打着打着少了一个”。

所以宁可限速保完整:100ms 窗口发一个完整动作,实测稳定打字 ≈9 字符/秒。对语音指令完全够用——正常人说话也就每秒几个字。串口参数和帧格式是这类芯片的通用套路,但“物理上限”和“稳定上限”之间的差距,只能实测出来。

端到端延迟账:每一段的毫秒都抠过

环节 耗时 说明
PTT 按下 → 首帧 Opus 音频上传 ≈100~150ms 板端录音启动 + 上行
语音上行速率 Opus 60kbps / 60ms 包 = 450B ≈ 15 帧/s ≈ 7.5KB/s 4G 上行无压力
云端 STT + LLM 决策 取决于网络和模型 不可控,只能兜底
MCP 指令下发 → 设备解析 4G 空口 + WebSocket —
UART 写 CH9329 14.6ms/帧,单动作 ≤40ms 物理层
电脑 HID 响应 ≤10ms 系统级

按键类操作另有一条线:主循环跑 100Hz,按键延迟 ≤10ms。心跳 20s 一发,服务端 84s 判断掉线——留 4.2 倍安全边界,网络抖一下不会误掉线。线程栈分配上,recv/录音线程给 64KB,其余 8KB——QuecPython 线程对应 RTOS 任务,栈溢出是直接 dump,不给侥幸。

一个已知短板,诚实交代

目前 MCP 的 tools/call 是在接收线程里同步执行的。这意味着执行一个长 type_text(几百字的文本要拆成几百帧发几分钟)时,TTS 播报会被堵住——你能听到 AI 说“我在打字”,但声音是卡的。正确解法已经明确:独立工作线程 + 队列,接收线程只解析入队,执行全回工作线程,项目里正在改。发出来是想说:这类“回调/接收线程里干重活”的架构问题,在资源受限设备上特别常见,欢迎有兴趣的同学一起讨论方案。

心得

  1. 先把“一帧多少字节、一帧多少 ms”算清楚,再设计交互。不知道 14.6ms/帧和 68.6 帧/s 上限的人,才会承诺“实时同步鼠标轨迹”这种物理上做不到的需求。
  2. 物理上限 ≠ 稳定上限。中间隔着芯片内部处理时间、你的重试策略、对方的缓冲深度——差距只能实测。
  3. 演示现场丢一个字,比功能少一个更丢分。限速保完整,是这个项目最重要的一个“反性能”决策。

输入

感谢分享这段调试记录与参数核算,过程描述清晰、数据支撑充分,对遇到同类问题的伙伴很有参考价值。

本回答仅供参考。