Files
blinky/docs/device_communication_protocol_v1.md
skiinder 3971d7c4b2 feat(proto): 添加设备通信协议v1修订版及统一帧格式
- 新增docs/device_communication_protocol_v1.md文档,定义V1修订版协议
- CDC和BLE GATT均改为直接传输业务消息,去掉外层协议封装
- BLE改为使用NUS(Nordic UART Service)替代原有GATT服务
- 统一键盘位图为29字节格式,FunctionKeyEvent改为上报全键盘位图
- 顶层消息增加msg_id和reply_to字段用于请求响应匹配
- Ack和Error合并为统一Response消息类型
- CDC和NUS均增加统一外层帧格式:magic(2) + len(1) + protobuf
- 添加Proto frame常量定义及长度验证逻辑
- 更新proto文件定义,包含DeviceMessage统一信封和ResponseCode枚举

- 重构hid_flowctrl_module.c中的上下文访问方式,统一使用ctx前缀
2026-04-24 10:54:14 +08:00

303 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 下位机通信协议 v1 修订版
本版基于原 `device_communication_protocol_v1.md` 修订,变更点如下:
- `CDC` 去掉外层协议,直接传业务消息
- `BLE GATT` 改为使用 `NUS (Nordic UART Service)`
- `Bitmap` 统一为 29 字节键盘位图格式
- `FunctionKeyEvent` 不再上报单键按下/释放,改为上报全键盘位图,格式与 `Bitmap` 一致
- 顶层消息增加 `msg_id` / `reply_to`
- `Ack``Error` 合并为统一 `Response`
- `CDC``NUS` 均增加统一外层帧:`magic(2) + len(1) + protobuf`
## 1. 总体原则
- 标准键盘输入继续走 `HID / BLE HID`,不变。
- 私有通信继续保留两条通道:
- `USB`:走 `CDC`
- `BLE`:走 `NUS`
- 两条通道统一承载同一套 `protobuf` 业务消息。
- `CDC``NUS` 均使用完全相同的外层帧格式,帧内承载 `protobuf` 业务消息。
- 当前版本不做应用层分片。
说明:
- 顶层 `protobuf` 消息名改为 `DeviceMessage`,表示独立于具体传输通道的统一业务信封。
- 当前版本使用统一外层帧,`CDC``NUS` 都按完整帧进行发送与解析。
## 2. 共享业务消息
统一的 `protobuf` 业务消息为 `DeviceMessage`,内部 `oneof body` 可取以下类型:
| 消息 | 方向 | 说明 |
| --- | --- | --- |
| `HelloReq` | Host -> Device | 握手请求 |
| `HelloRsp` | Device -> Host | 握手响应 |
| `Bitmap` | Host -> Device | 下发功能键位图配置 |
| `FunctionKeyEvent` | Device -> Host | 上报当前全键盘状态位图 |
| `LedState` | Device -> Host | 上报键盘 LED 状态 |
| `TimeSync` | Host -> Device | 下发时间同步 |
| `ThemeRgb` | Host -> Device | 下发主题颜色 |
| `Response` | Device -> Host | 控制命令统一响应 |
顶层公共字段:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `msg_id` | `uint32` | 当前消息 ID`0` 表示未使用 |
| `reply_to` | `uint32` | 当前消息响应的请求 ID`0` 表示不是响应 |
规则:
- Host 发起的请求消息应填写唯一 `msg_id`
- `HelloRsp``Response` 应填写 `reply_to = 原请求.msg_id`
- `FunctionKeyEvent``LedState` 等异步上报消息应设置 `reply_to = 0`
- 当前版本建议每条链路同一时刻仅保留 `1` 条 in-flight 控制请求。
## 3. CDC 协议
CDC 在线路上的真实字节格式如下:
```text
magic(2B) + len(1B) + protobuf(DeviceMessage)
```
约束:
- `magic` 固定为 `0xAA55`,按小端发送,即线路字节序为 `0x55 0xAA`
- `len` 为后续 `protobuf(DeviceMessage)` 的字节长度,范围 `0..64`
- Host -> Device 与 Device -> Host 都发送完整帧。
- 当前版本要求单条业务消息一次完整发送,不做应用层分片与重组。
## 4. NUS 协议
BLE 私有通信改为使用 `NUS (Nordic UART Service)`
NUS 线路上的真实字节格式如下:
```text
magic(2B) + len(1B) + protobuf(DeviceMessage)
```
方向定义:
- Host -> Device`RX` 特征写入完整帧
- Device -> Host通过 `TX Notify` 上报完整帧
## 5. NUS 服务定义
Service UUID
```text
6E400001-B5A3-F393-E0A9-E50E24DCCA9E
```
Characteristic 定义:
| 名称 | UUID | 属性 | 用途 |
| --- | --- | --- | --- |
| `RX` | `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` | `Write`, `Write Without Response` | Host -> Device |
| `TX` | `6E400003-B5A3-F393-E0A9-E50E24DCCA9E` | `Notify` | Device -> Host |
约束:
- 连接后主机必须先订阅 `TX`
- 当前版本不做应用层分片。
- 因为增加了 `magic + len` 外层帧,建议协商 `ATT_MTU >= 64`,保证完整帧可一次发送。
## 6. 外层帧定义
外层帧字段如下:
| 字段 | 大小 | 说明 |
| --- | --- | --- |
| `magic` | `2` 字节 | 固定 `0xAA55`,线路字节序 `55 AA` |
| `len` | `1` 字节 | `protobuf(DeviceMessage)` 长度,最大 `64` |
| `payload` | `len` 字节 | `protobuf(DeviceMessage)` |
## 7. protobuf 字段定义
以下是业务字段语义。
### HelloReq
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `protocol_version` | `uint32` | 当前固定为 `1` |
### HelloRsp
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `protocol_version` | `uint32` | 当前固定为 `1` |
| `vendor_id` | `uint32` | 当前建议 `0x1209` |
| `product_id` | `uint32` | 当前建议 `0x0001` |
| `firmware_major` | `uint32` | 固件主版本 |
| `firmware_minor` | `uint32` | 固件次版本 |
| `capability_flags` | `uint32` | 能力位 |
### Bitmap
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `usage_bitmap` | `bytes` | 固定 29 字节,表示功能键配置位图 |
语义:
- `Bitmap.usage_bitmap` 长度固定为 `29` 字节。
-`0` 字节表示 `0xE0``0xE7` 这 8 个控制键。
-`1` 到第 `28` 字节表示 `0x00``0xDF`
- 位值为 `1` 表示该 usage 被配置为功能键。
- 位值为 `0` 表示该 usage 不是功能键,继续按普通键处理。
位映射规则:
-`0` 字节:
- `bit0 -> 0xE0`
- `bit1 -> 0xE1`
- ...
- `bit7 -> 0xE7`
-`1` 字节:
- `bit0 -> 0x00`
- `bit1 -> 0x01`
- ...
- `bit7 -> 0x07`
-`2` 字节:
- `bit0 -> 0x08`
- ...
- `bit7 -> 0x0F`
- 依此类推。
-`28` 字节:
- `bit0 -> 0xD8`
- ...
- `bit7 -> 0xDF`
### FunctionKeyEvent
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `usage_bitmap` | `bytes` | 固定 29 字节,表示当前全键盘状态位图 |
语义:
- `FunctionKeyEvent` 不再使用 `usage + action` 的单键事件格式。
- `FunctionKeyEvent.usage_bitmap` 长度固定为 `29` 字节。
- 位图编码方式与 `Bitmap.usage_bitmap` 完全一致。
- 位值为 `1` 表示该 usage 当前处于按下状态。
- 位值为 `0` 表示该 usage 当前处于释放状态。
- 该消息表示一次完整键盘状态快照,而不是单个按键变化。
### LedState
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `led_mask` | `uint32` | NumLock/CapsLock 等位掩码 |
### TimeSync
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `version` | `uint32` | 当前固定 `1` |
| `flags` | `uint32` | 标志位 |
| `timezone_min` | `sint32` | 时区偏移,单位分钟 |
| `utc_ms` | `fixed64` | UTC 毫秒时间戳 |
| `accuracy_ms` | `fixed32` | 精度,毫秒 |
### ThemeRgb
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `red` | `uint32` | `0..255` |
| `green` | `uint32` | `0..255` |
| `blue` | `uint32` | `0..255` |
### Response
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `error_code` | `enum ResponseCode` | 响应结果;`OK` 表示成功,其余表示失败原因 |
## 8. 枚举定义
### ResponseCode
| 值 | 含义 |
| ---: | --- |
| `0` | `OK` |
| `1` | `UNKNOWN_TYPE` |
| `2` | `INVALID_LENGTH` |
| `3` | `INVALID_PARAM` |
| `4` | `NOT_READY` |
说明:
-`KeyAction` 枚举已不再使用,因为 `FunctionKeyEvent` 改为完整位图上报。
## 9. CapabilityFlags 定义
`HelloRsp.capability_flags` 先按以下位定义:
| Bit | 含义 |
| ---: | --- |
| `0` | 支持功能位图配置 |
| `1` | 支持时间同步 |
| `2` | 支持主题切换 |
| `3` | 支持 LED 状态上报 |
| `4` | 支持全键盘位图事件上报 |
当前建议最小返回值:
- 若仅先做握手:可先返回 `0`
-`Bitmap` 可用:打开 `bit0`
-`FunctionKeyEvent` 全键盘位图上报可用:打开 `bit4`
## 10. 握手流程
### CDC 握手
1. Host 打开串口
2. Host 发送 `magic + len + protobuf(DeviceMessage{hello_req})`
3. Device 解析外层帧与 `protobuf(DeviceMessage)`
4. Device 取出 `hello_req`
5. Device 返回 `magic + len + protobuf(DeviceMessage{hello_rsp})`
### NUS 握手
1. Host 连接 BLE
2. Host 发现 `NUS Service`
3. Host 订阅 `TX Notify`
4. Host 向 `RX` 写入完整帧
5. Device 解析外层帧与 `protobuf(DeviceMessage)`
6. Device 通过 `TX Notify` 发回完整帧
说明:
- 文中 `protobuf(HelloReq)` / `protobuf(HelloRsp)` 指业务上发送对应的 `DeviceMessage` 消息,其 `oneof body` 分别为 `hello_req` / `hello_rsp`
## 11. 设备行为规则
- 上电默认所有按键都是普通键。
- 普通键的标准输入行为继续走 `HID / BLE HID`,不变。
- 收到 `Bitmap` 后,位图中标记为功能键的按键按功能键逻辑处理。
- `FunctionKeyEvent` 上报的是当前全键盘状态快照,编码格式与 `Bitmap` 一致。
- `LedState` 在 LED 状态变化时上报。
- `TimeSync` 收到后更新时间管理模块。
- `ThemeRgb` 收到后更新主题模块。
- 控制类请求处理完成后统一返回 `Response`
- 不支持的消息类型也应返回 `Response { error_code = UNKNOWN_TYPE }`,不得静默丢弃。
## 12. Response 规则
- `HelloReq` 不返回 `Response`,而是直接返回 `HelloRsp`,且 `reply_to = HelloReq.msg_id`
- `Bitmap``TimeSync``ThemeRgb` 处理完成后应返回 `Response`
- `Response.error_code = OK` 表示成功。
- 不支持的消息类型返回 `Response { error_code = UNKNOWN_TYPE }`
- 长度错误、参数错误、模块未就绪时分别返回 `INVALID_LENGTH``INVALID_PARAM``NOT_READY`
示例:
- 收到 `Bitmap` 成功处理
返回 `Response { error_code = OK }`
- 收到 `ThemeRgb` 参数非法
返回 `Response { error_code = INVALID_PARAM }`