1 changed files with 382 additions and 0 deletions
@ -0,0 +1,382 @@ |
|||
# 通话翻译 硬件协议与数据结构对接文档 |
|||
|
|||
> 适用产品:Voitrans(Deep Voice)AI 翻译耳机 |
|||
> 通信方式:BLE(GATT) |
|||
> 音频编码:Opus(16 kHz / 单声道 / 无文件头裸流) |
|||
> 本文档以 App 端实现(`local_plugins/ble_service`)为准,供硬件/固件侧对接。 |
|||
|
|||
--- |
|||
|
|||
## 0. 名词约定 |
|||
|
|||
| 名词 | 含义 | |
|||
|---|---| |
|||
| App / 手机 | 主控端(BLE Central),负责 ASR、翻译、TTS、下行翻译音频编码 | |
|||
| 设备 / 耳机 | 从机(BLE Peripheral),采集通话双方音频、播放翻译音频 | |
|||
| 本端 / 近端 | 佩戴耳机的用户(mic 采集) | |
|||
| 对端 / 远端 | 通话另一方(耳机 DAC / 通话下行声音) | |
|||
| 上行 | 设备 → App(原始通话音频) | |
|||
| 下行 | App → 设备(翻译后的音频) | |
|||
|
|||
通话翻译整体链路: |
|||
|
|||
``` |
|||
上行 opus(双声道) ASR+翻译+TTS 下行 opus(分声道) |
|||
[耳机 mic/dac] ───────────────► [App 解码/分离] ─────────────► [App 编码] ───────────────► [耳机 左/右声道播放] |
|||
近端 = 左声道 左声道(0)=本端译文 |
|||
远端 = 右声道 右声道(1)=对端译文 |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 1. BLE GATT 服务与特征 |
|||
|
|||
### 1.1 命令与控制服务(主服务) |
|||
|
|||
| 项 | UUID | 属性 | 用途 | |
|||
|---|---|---|---| |
|||
| 主服务 | `0000ABC0-0000-1111-2222-123456789ABC` | — | 命令控制 | |
|||
| 写特征 | `0000ABC1-0000-1111-2222-123456789ABC` | Write | App 下发指令(0xAA 帧) | |
|||
| 通知特征 | `0000ABC2-0000-1111-2222-123456789ABC` | Notify | 设备上报响应/主动上报(0xBB / 0xCC 帧) | |
|||
|
|||
### 1.2 通话音频服务(通话翻译核心) |
|||
|
|||
| 项 | UUID | 属性 | 用途 | |
|||
|---|---|---|---| |
|||
| 通话音频服务 | `0000ABC0-0001-1111-2222-123456789ABC` | — | 通话翻译音频 | |
|||
| 通话写音频特征 | `0000ABC1-0001-1111-2222-123456789ABC` | Write (No Response) | **下行**:App → 设备,翻译后 opus | |
|||
| 通话收音频特征 | `0000ABC2-0001-1111-2222-123456789ABC` | Notify | **上行**:设备 → App,通话原始 opus | |
|||
|
|||
### 1.3 普通音频服务(AI 单次对话 / 音乐等,非通话翻译) |
|||
|
|||
| 项 | UUID | 属性 | 用途 | |
|||
|---|---|---|---| |
|||
| 音频服务 | `0000AE00-0000-1000-8000-00805F9B34FB` | — | 普通音频上行 | |
|||
| 接收音频特征 | `0000AE02-0000-1000-8000-00805F9B34FB` | Notify | 上行 opus(AI 对话等) | |
|||
|
|||
### 1.4 唤醒词 OTA 服务(升级唤醒词资源,非通话翻译) |
|||
|
|||
| 项 | UUID | |
|||
|---|---| |
|||
| 服务 | `0000ABC0-0002-1111-2222-123456789ABC` | |
|||
| 写 | `0000ABC1-0002-1111-2222-123456789ABC` | |
|||
| 通知 | `0000ABC2-0002-1111-2222-123456789ABC` | |
|||
|
|||
> 客户端启用通知使用标准 CCCD 描述符 `00002902-0000-1000-8000-00805F9B34FB`。 |
|||
> App 端建议协商 MTU(下行单包最大约 165B,MTU 需 ≥ ~180,见 §7)。 |
|||
|
|||
### 1.5 广播识别信息(参考) |
|||
|
|||
厂商自定义广播字段常量: |
|||
|
|||
| 字段 | 值 | |
|||
|---|---| |
|||
| 厂商 ID(Manufacturer ID) | `0xABEF` | |
|||
| 客户 ID | `0xA001` | |
|||
| 产品 ID | `0xA001` | |
|||
| 蓝牙状态-未连接 | `0x00` | |
|||
| 蓝牙状态-已连接 | `0x0C` | |
|||
|
|||
> App 端当前扫描/匹配主要依据设备名,广播厂商字段为约定信息。 |
|||
|
|||
--- |
|||
|
|||
## 2. 命令控制协议(0xABC1 / 0xABC2 通道) |
|||
|
|||
所有控制指令走**命令控制服务**的写/通知特征,采用统一帧格式。 |
|||
|
|||
### 2.1 帧格式 |
|||
|
|||
| 方向 | 帧头 | 结构 | |
|||
|---|---|---| |
|||
| App → 设备(请求) | `0xAA` | `[0xAA] [CMD] [LEN] [DATA(LEN 字节)] [CRC]` | |
|||
| 设备 → App(响应) | `0xBB` | `[0xBB] [CMD] [LEN] [DATA(LEN 字节)] [CRC]` | |
|||
| 设备 → App(主动上报) | `0xCC` | `[0xCC] [CMD/类型] [LEN] [DATA(LEN 字节)] [CRC]` | |
|||
|
|||
- **帧头**:1 字节,区分帧类型(`0xAA`/`0xBB`/`0xCC`)。 |
|||
- **CMD**:1 字节,指令码,见 §3。 |
|||
- **LEN**:1 字节,`DATA` 的字节数(不含帧头/CMD/LEN/CRC)。无数据时 `LEN=0x00`。 |
|||
- **DATA**:`LEN` 字节负载。 |
|||
- **CRC**:1 字节,CRC-8(多项式 0x31),**校验范围 = 帧头 + CMD + LEN + DATA(即除 CRC 自身外的全部字节)**。 |
|||
- 整帧总长 = `LEN + 4`。 |
|||
|
|||
> 说明:设备响应(0xBB)App 侧会做 CRC 校验;主动上报(0xCC)当前 App 侧未强制校验 CRC,但**仍要求带上正确的 CRC 字节并保证 `总长 = LEN + 4`**,否则会被丢弃(长度不匹配直接返回)。 |
|||
|
|||
### 2.2 CRC-8 算法(务必与本实现一致) |
|||
|
|||
多项式 `0x31`、初值 `0x00`、MSB 优先、不反转、不异或输出。C 参考实现: |
|||
|
|||
```c |
|||
uint8_t crc8(const uint8_t *data, int len) { |
|||
uint8_t crc = 0x00; |
|||
for (int i = 0; i < len; i++) { |
|||
crc ^= data[i]; |
|||
for (int j = 0; j < 8; j++) { |
|||
if (crc & 0x80) crc = (uint8_t)((crc << 1) ^ 0x31); |
|||
else crc = (uint8_t)(crc << 1); |
|||
} |
|||
} |
|||
return crc; |
|||
} |
|||
``` |
|||
|
|||
### 2.3 帧示例(含实算 CRC) |
|||
|
|||
| 含义 | 完整字节 | |
|||
|---|---| |
|||
| 获取版本号(请求) | `AA 01 00 86` | |
|||
| 获取电量(请求) | `AA 04 00 F1` | |
|||
| 打开解码(0xA1,立体声,请求) | `AA 05 02 A1 03 A3` | |
|||
| **开启通话翻译(0xA2,立体声,请求)** | `AA 05 02 A2 03 8E` | |
|||
| 打开通话录音解码(0xA3,立体声,请求) | `AA 05 02 A3 03 7A` | |
|||
| 关闭编解码(请求) | `AA 05 02 00 03 A8` | |
|||
| 查询语音唤醒状态(请求) | `AA 14 00 9F` | |
|||
| 设置语音唤醒开启(请求) | `AA 15 01 E1 55` | |
|||
|
|||
--- |
|||
|
|||
## 3. 指令码(CMD)总表 |
|||
|
|||
| CMD | 名称 | 方向 | 说明 | |
|||
|---|---|---|---| |
|||
| `0x01` | 获取版本号 | 请求/响应 | DATA 为版本号字节(丢弃开头连续 0xFF,其余按十进制点分) | |
|||
| `0x02` | 获取产品 ID | 请求/响应 | | |
|||
| `0x03` | 获取颜色 ID | 请求/响应 | DATA[0]=颜色 ID | |
|||
| `0x04` | 获取电量 | 请求/响应/上报 | 见 §6.1 | |
|||
| `0x05` | **编解码控制** | 请求/响应/上报 | **通话翻译核心指令**,见 §4、§5 | |
|||
| `0x06` | 音量增大 | 请求 | 无 DATA | |
|||
| `0x07` | 音量减小 | 请求 | 无 DATA | |
|||
| `0x08` | 获取唤醒词版本 | 请求/响应 | 响应含左右耳各 4 字节版本 | |
|||
| `0x09` | 开始 AI 单次对话 | 上报(0xCC) | 设备侧触发,App 打开编码器 | |
|||
| `0x10` | 结束 AI 单次对话 | 上报(0xCC) | 设备侧触发,App 关闭编码器 | |
|||
| `0x11` | 唤醒(语音唤醒信号) | 上报(0xCC) | 设备上报唤醒 | |
|||
| `0x14` | 查询语音唤醒状态 | 请求/响应 | 响应 DATA[0]:`0xE1`=开 / `0xE0`=关 | |
|||
| `0x15` | 设置语音唤醒状态 | 请求/响应 | 请求 DATA[0]:`0xE1`=开 / `0xE0`=关 | |
|||
| `0x16` | **上报:开启通话翻译** | 上报(0xCC) | 设备侧发起开启通话翻译,见 §5.2 | |
|||
| `0x17` | **上报:关闭通话翻译** | 上报(0xCC) | 设备侧发起关闭通话翻译,见 §5.2 | |
|||
|
|||
--- |
|||
|
|||
## 4. 编解码控制指令 0x05(通话翻译核心) |
|||
|
|||
### 4.1 请求格式 |
|||
|
|||
``` |
|||
AA 05 02 [control] [channel] [CRC] |
|||
``` |
|||
|
|||
`DATA = [control(1B), channel(1B)]`,`LEN=0x02`。 |
|||
|
|||
**control(编解码控制值)** |
|||
|
|||
| 值 | 名称 | 含义 | |
|||
|---|---|---| |
|||
| `0x00` | 关闭编解码 | 关闭全部编解码(结束通话翻译/录音/播放) | |
|||
| `0xA1` | 打开解码 | 播放远端/音乐声音(上行单声道 opus 40B) | |
|||
| `0xA2` | **开启通话翻译** | mic + dac 声音上行,并接收 App 下行翻译重编码音频(上行双声道 opus 80B) | |
|||
| `0xA3` | 通话录音 | mic + dac 声音上行录制(上行双声道 opus 80B) | |
|||
| `0xB1` | 打开编码 | 打开编码器(备用) | |
|||
| `0xF0` | 左声道通话休眠 | 见 §5.3(一般由设备主动上报) | |
|||
| `0xF1` | 右声道通话休眠 | 见 §5.3 | |
|||
| `0xF2` | 左声道通话恢复 | 见 §5.3 | |
|||
| `0xF3` | 右声道通话恢复 | 见 §5.3 | |
|||
|
|||
**channel(声道模式)** |
|||
|
|||
| 值 | 含义 | |
|||
|---|---| |
|||
| `0x01` | 左声道 | |
|||
| `0x02` | 右声道 | |
|||
| `0x03` | 立体声(通话翻译默认) | |
|||
|
|||
### 4.2 响应格式 |
|||
|
|||
设备回:`BB 05 02 [control] [channel] [CRC]`(原样回显 control 与 channel)。 |
|||
若执行失败,可回 `LEN` 与实际不符或 DATA[0]=`0x00`,App 会按失败处理。 |
|||
|
|||
### 4.3 各 control 值对应的上行音频参数 |
|||
|
|||
App 收到对应 control 后,按下表启动 Opus 解码器(**必须与固件上行编码参数一致**): |
|||
|
|||
| control | 上行声道数 | 采样率 | 帧长(packetSize) | 备注 | |
|||
|---|---|---|---|---| |
|||
| `0xA1` 打开解码 | 1(单声道) | 16000 | 40 | 远端/音乐 | |
|||
| `0xA2` 开启通话翻译 | 2(双声道) | 16000 | 80 | 左=mic(近端),右=dac(远端) | |
|||
| `0xA3` 通话录音 | 2(双声道) | 16000 | 80 | 同上 | |
|||
|
|||
--- |
|||
|
|||
## 5. 通话翻译业务协议 |
|||
|
|||
### 5.1 开启/关闭方式 |
|||
|
|||
通话翻译可由**两侧任意一方发起**: |
|||
|
|||
- **App 主动开启**:App 下发 `0x05 / 0xA2`(`AA 05 02 A2 03 8E`)→ 设备开始上行双声道通话音频,并准备接收下行翻译音频。 |
|||
- **设备主动发起**:设备上报 `0x16`(开启)/ `0x17`(关闭),App 收到后进入/退出通话翻译流程。 |
|||
|
|||
关闭时下发 `0x05 / 0x00`(`AA 05 02 00 03 A8`)关闭编解码。 |
|||
|
|||
### 5.2 设备主动上报开启/关闭(0x16 / 0x17) |
|||
|
|||
帧格式(0xCC 主动上报): |
|||
|
|||
``` |
|||
CC 16 [LEN] [DATA...] [CRC] // 开启通话翻译 |
|||
CC 17 [LEN] [DATA...] [CRC] // 关闭通话翻译 |
|||
``` |
|||
|
|||
- App 侧解析:若 `DATA` 长度 ≥ 4,取 `DATA[3]` 作为 status(约定 `0xFF`);否则 status 视为无效。 |
|||
- 建议固件按约定 `LEN=0x01, status=0xFF` 或与固件协商一致的固定格式发送;关键在于 `总长 = LEN + 4` 必须成立。 |
|||
|
|||
### 5.3 声道休眠 / 恢复(流控,0x05 的 0xF0~0xF3) |
|||
|
|||
用于通话过程中,设备侧临时暂停某一声道的下行音频(例如该声道当前无需播放),App **不会丢包**,休眠期间该声道下行包转入积压缓存,收到恢复指令后按序补发。 |
|||
|
|||
由**设备主动上报**(0xCC,CMD=0x05,DATA[0]=控制值): |
|||
|
|||
``` |
|||
CC 05 [LEN] [F0] ... [CRC] // 左声道休眠:App 暂停下发左声道(channel=0) |
|||
CC 05 [LEN] [F1] ... [CRC] // 右声道休眠:App 暂停下发右声道(channel=1) |
|||
CC 05 [LEN] [F2] ... [CRC] // 左声道恢复:App 恢复并按序补发左声道积压 |
|||
CC 05 [LEN] [F3] ... [CRC] // 右声道恢复:App 恢复并按序补发右声道积压 |
|||
``` |
|||
|
|||
| 上报值 | 含义 | App 行为 | 配对 | |
|||
|---|---|---|---| |
|||
| `0xF0` | 左声道通话休眠 | 暂停下发左声道(0),仅发右声道 | 与 `0xF2` 配对 | |
|||
| `0xF1` | 右声道通话休眠 | 暂停下发右声道(1),仅发左声道 | 与 `0xF3` 配对 | |
|||
| `0xF2` | 左声道通话恢复 | 恢复左声道下发,补发休眠期间积压包 | 与 `0xF0` 配对 | |
|||
| `0xF3` | 右声道通话恢复 | 恢复右声道下发,补发休眠期间积压包 | 与 `0xF1` 配对 | |
|||
|
|||
> 休眠/恢复为幂等操作;重复上报不叠加。 |
|||
|
|||
--- |
|||
|
|||
## 6. 部分响应数据结构 |
|||
|
|||
### 6.1 电量信息(0x04) |
|||
|
|||
`DATA` 至少 3 字节:`[左耳, 右耳, 充电仓]`,每字节: |
|||
|
|||
- bit0~bit6:电量百分比(`value & 0x7F`) |
|||
- bit7:是否充电(`value & 0x80 != 0`) |
|||
|
|||
主动上报同格式,帧头 `0xCC`,CMD=`0x04`。 |
|||
|
|||
### 6.2 唤醒词版本(0x08) |
|||
|
|||
响应 `DATA` 为 8 字节:`[左耳 4B][右耳 4B]`,每 4 字节点分十进制即版本号。 |
|||
|
|||
### 6.3 语音唤醒(0x14 / 0x15) |
|||
|
|||
- `0x14` 响应:`DATA[0]` = `0xE1`(开) / `0xE0`(关)。 |
|||
- `0x15` 请求:`DATA[0]` = `0xE1`(开) / `0xE0`(关);响应:`DATA[0]`=命令状态,`DATA[1]`=`0x01` 表示设置成功。 |
|||
|
|||
--- |
|||
|
|||
## 7. 音频数据结构(0xABC1-0001 / 0xABC2-0001 通道) |
|||
|
|||
音频不走 0xAA/0xBB/0xCC 帧,直接在**通话音频服务**的特征上传输原始负载。 |
|||
|
|||
### 7.1 上行:设备 → App(通话收音频特征 `0000ABC2-0001-...`) |
|||
|
|||
- **纯 Opus 裸流**(无 0xAA/CRC 帧头、无文件头、无自定义包头),直接把 opus 码流分片放入 Notify 负载即可。 |
|||
- 通话翻译(`0xA2`/`0xA3`):**双声道(stereo)交织** Opus,采样率 **16 kHz**,帧长(packetSize)**80**。 |
|||
- 左声道 = 近端(mic 采集,本端说话人) |
|||
- 右声道 = 远端(dac / 通话对端) |
|||
- 普通解码(`0xA1`):单声道 Opus,16 kHz,帧长 40。 |
|||
- App 侧把收到的负载按到达顺序送入解码器,因此**分片可任意长度**,但需保证 opus 帧边界正确、顺序不乱。 |
|||
|
|||
### 7.2 下行:App → 设备(通话写音频特征 `0000ABC1-0001-...`) |
|||
|
|||
App 将本端/对端译文分别用**独立** Opus 编码器编码(16 kHz / 单声道 / 每帧约 40B),左右各累积 N 帧后合并为 1 个下行包发送。 |
|||
|
|||
**下行包结构:** |
|||
|
|||
``` |
|||
+---------+---------+---------------------------------------------+ |
|||
| 序号 | 声道 | N 个 opus 帧顺次拼接 | |
|||
| 4 字节 | 1 字节 | 每帧约 40 字节(16kHz/mono/无头) | |
|||
| uint32 | 0/1 | | |
|||
| 小端 | | | |
|||
+---------+---------+---------------------------------------------+ |
|||
[0..3] [4] [5 .. 5 + N*framelen) |
|||
``` |
|||
|
|||
| 字段 | 偏移 | 长度 | 说明 | |
|||
|---|---|---|---| |
|||
| 序号 seq | 0 | 4 | **小端序 uint32**(低字节在前)。每个声道各自独立自增,从 0 开始 | |
|||
| 声道 channel | 4 | 1 | `0x00`=左声道(本端译文) / `0x01`=右声道(对端译文) | |
|||
| opus 数据 | 5 | N×帧长 | N 帧 opus 顺次拼接,每帧 16kHz/单声道 | |
|||
|
|||
- **N(合包帧数)**:默认 `4`,可运行时调整(范围 1~20)。故单包总长 = `5 + N × 帧长`(帧长约 40B,默认约 165B)。 |
|||
- 写类型:**Write Without Response**(不等 ACK,保吞吐)。 |
|||
- 发送节拍:App 按固定间隔逐包下发(默认约 60ms/包,可调);左右声道公平轮转。 |
|||
- **不丢包保证**:底层写拥塞时 App 会退避重试并按声道队头重发,固件侧只需按 `seq` 归位/去重与顺序播放。 |
|||
|
|||
> ⚠️ **字节序特别说明**:下行包序号在实现中为**小端序(little-endian)**(`buildBundlePacket` 写入与发送线程解析均为小端)。代码注释里出现的“大端”描述为笔误,**请以小端为准**。 |
|||
|
|||
--- |
|||
|
|||
## 8. 通话翻译完整时序(示例:App 发起) |
|||
|
|||
``` |
|||
App 设备(耳机) |
|||
│ (已连接, 已订阅 0xABC2 / 0xABC2-0001 通知, 已协商 MTU) |
|||
│ |
|||
│ ── 0x05/0xA2 开启通话翻译 ──► AA 05 02 A2 03 8E |
|||
│ ◄── 0xBB 响应回显 ── BB 05 02 A2 03 .. |
|||
│ |
|||
│ 开始采集通话音频 |
|||
│ ◄════ 上行双声道 opus(16k/80) ════ (通话收音频特征 0xABC2-0001, 裸流) |
|||
│ |
|||
│ [App: 解码→分离近/远端→ASR→翻译→TTS→独立编码] |
|||
│ |
|||
│ ════ 下行 opus 合包 ════► (通话写音频特征 0xABC1-0001) |
|||
│ [seq(4B小端)][ch 0/1][N×opus] 耳机按 ch 播放到对应声道 |
|||
│ |
|||
│ ◄── (可选) 0xCC 05 F0/F1 休眠 ── 暂停对应声道下发 |
|||
│ ◄── (可选) 0xCC 05 F2/F3 恢复 ── 恢复并按序补发 |
|||
│ |
|||
│ ── 0x05/0x00 关闭编解码 ──► AA 05 02 00 03 A8 |
|||
│ 或 ◄── 0xCC 0x17 设备请求关闭 ── |
|||
│ 停止采集/播放 |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 9. 关键参数汇总(固件对接必对齐项) |
|||
|
|||
| 项 | 值 | |
|||
|---|---| |
|||
| Opus 采样率 | 16000 Hz | |
|||
| Opus 声道(上行通话翻译) | 2(stereo,左=近端 右=远端) | |
|||
| Opus 帧长(上行通话翻译 0xA2/0xA3) | packetSize = 80 | |
|||
| Opus 帧长(上行单声道 0xA1) | packetSize = 40 | |
|||
| Opus 帧长(下行译文) | 单声道,每帧约 40B | |
|||
| Opus 文件头 | 无(裸流) | |
|||
| 下行合包序号字节序 | **小端 uint32** | |
|||
| 下行声道标识 | 0=左(本端译文) / 1=右(对端译文) | |
|||
| 下行默认合包帧数 N | 4(可调 1~20) | |
|||
| 下行写类型 | Write Without Response | |
|||
| 控制帧 CRC | CRC-8,多项式 0x31,初值 0x00,MSB 优先,校验含帧头 | |
|||
| 控制帧总长 | LEN + 4 | |
|||
| 响应超时(App 侧) | 1000 ms | |
|||
|
|||
--- |
|||
|
|||
## 10. 对接注意事项 / 已知实现细节 |
|||
|
|||
1. **CRC 覆盖范围包含帧头**(`0xAA/0xBB/0xCC`),且 `CRC` 前的所有字节参与计算。 |
|||
2. **主动上报(0xCC)CRC**:App 侧当前未强制校验,但**长度必须满足 `总长 = LEN + 4`**,否则整帧被丢弃。请固件正确填 `LEN` 并补 CRC 字节。 |
|||
3. **下行序号字节序为小端**(代码注释“大端”为笔误,见 §7.2)。 |
|||
4. **下行合包帧数**:默认 `4`(部分注释写“5/205B”为旧值,请以运行参数为准,可由 App 通过调试接口下发调整)。 |
|||
5. **上行为纯 opus 裸流**,无任何自定义包头/序号;分片长度自由,但 opus 帧边界与顺序必须正确。 |
|||
6. **上行通话翻译为双声道交织**(channel=2),近端/远端由左右声道区分;固件需保证左=mic、右=dac 的固定映射。 |
|||
7. 通话翻译期间设备可用 `0xF0~0xF3` 做单声道流控,App 保证不丢包、按 seq 顺序补发;固件按声道 seq 播放即可。 |
|||
8. 建议连接后协商较大 MTU(≥ ~180 字节)以容纳单个下行包,减少分片。 |
|||
|
|||
--- |
|||
|
|||
*本文档依据 App 端 `local_plugins/ble_service`(Android 实现)整理:`BleConst.kt`、`BleCommandSender.kt`、`OpusAudioManager.kt`、`BleService.kt`。如与固件实测不一致,请以双方联调确认为准。* |
|||
Loading…
Reference in new issue