You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

17 KiB

通话翻译 硬件协议与数据结构对接文档

适用产品: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 参考实现:

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。如与固件实测不一致,请以双方联调确认为准。