# 腾讯小微蓝牙设备协议
# 概要
蓝牙设备(如蓝牙耳机、蓝牙音箱)可以通过蓝牙与“腾讯小微APP”或“QQ音乐App”建立连接,接入小微语音助手的服务。
接入小微语音助手后,设备即可具有语音对话能力,可以通过说话来使用音乐点播、故事播放、新闻收听、天气和百科查询等服务。
一些接入基本情况如下:
- 支持蓝牙协议版本4.2及以上。
- iOS系统支持10.0及以上,连接方式支持IAP,BLE。
- Android系统支持6.0及以上,连接方式支持SPP。
- 录音采样参数:16000采样率,单声道,16bit采样位深。
- 音频编码方式支持Opus,SBC,mSBC。
- 支持OTA升级(可选)。
# 发现与连接
# IAP
仅iOS支持。
protocol string: com.tencent.xiaowei
# SPP
仅Android支持。
UUID: 85dbf2f9-73e3-43f5-a129-971b91c72f1e
# BLE
仅iOS支持。
设备通过Advertising Data和Scan Response来向App传递设备的信息。
优先通过Advertising Data来传递,如数据包大小超过限制,可放到Scan Response中。
广播数据中应包含:
Service UUID:0709
MANUFACTURER SPECIFIC DATA:
Offset | Length | Type | Description |
---|---|---|---|
0 | 2 | uint16_t | compnay_id, 由厂商自行在蓝牙组织申请 |
2 | 4 | uint32_t | product_id,由厂商在腾讯小微硬件管理平台申请 |
6 | 6 | uint8_t[] | 蓝牙MAC地址 |
12 | 8 | uint8_t[] | 特征值,连接App过程中由App分配,没有时用0填充 |
建立BLE连接后,设备向App发送数据的通道为 dfd4416e-e40c-47f7-8248-eb8be3dc47f9
。
设备接收来自App的数据的通道为 9884d812-61fe-4a24-94d3-b2c11a851fac
。
设备需要在与App建立连接后停止广播。
# 连接流程
- App通过BLE,SPP,IAP等方式查找可用设备。
- 发现设备后,App发送103命令给设备来获取product_id,mac地址以及特征值。
如果App已经通过BLE的广播包获取到了相应信息,App会跳过该步骤。 - App检查特征值,判断是否可以自动连接。
- 如不能自动连接,App弹窗询问用户是否连接设备。用户确认后继续流程。
- App发送105命令,给设备分配特征值,设备设备回复配置参数。
- App发送106命令,给设备发送phone id,设备根据校验方式回复校验信息。
- 连接完成,App播放已连接的提示。
- 连接完成后,App会定时发送心跳(112_heartBeat)给设备。如心跳失败,App将断开与耳机的连接。
过程如下图所示
# 录音流程
# 长按
- 用户按下按键时,设备发送101命令,收到回复后开始录音。
- 设备持续通过255命令发送编码后的音频数据。如多次没有收到App的回复时,设备要停止录音并主动发送102命令尝试告知App停止录音。
- 用户松开按键时,设备发送108命令,在收到回复后需要继续录音和发送音频数据。
- 设备收到App发来的102命令时,结束录音。
过程如下图所示
# 短按
- 用户触发录音,设备发送101命令,收到回复后开始录音。
- 设备持续通过255命令发送编码后的音频数据。如多次没有收到App的回复时,设备要停止录音并主动发送102命令尝试告知App停止录音。
- 设备收到App发来的102时,结束录音。
过程如下图所示
# 对话控制
在iOS平台上,如果有第三方的App正在播放音乐,此时小微App需要通过设备来发送AVRCP指令来控制第三方App的音乐播放。
以长按为例,过程如下图所示
# 采样和编码
# 采样
录音采样参数:16000采样率,单声道,16bit采样位深。
# Opus
iOS和Android均支持。
采用固定码率,压缩后的每一帧大小为40个字节。
支持16倍和8倍的压缩,对应的frame_duration分别为20ms, 10ms。
码率(压缩前) | 压缩比例 | 包大小 | frame_duration | 码率(压缩后) |
---|---|---|---|---|
256kbps | 16 | 40Byte | 20ms | 16kbps |
256kbps | 8 | 40Byte | 10ms | 32kbps |
# SBC
iOS和Android均支持。
SBC参数:
sbc->frequency = SBC_FREQ_16000;
sbc->blocks = SBC_BLK_16;
sbc->subbands = SBC_SB_8;
sbc->mode = SBC_MODE_MONO;
sbc->allocation = SBC_AM_LOUDNESS;
sbc->bitpool = 12; // 256 bytes input 32 bytes output
sbc->endian = SBC_LE;
mSBC参数:
sbc->frequency = SBC_FREQ_16000;
sbc->blocks = MSBC_BLOCKS;
sbc->subbands = SBC_SB_8;
sbc->mode = SBC_MODE_MONO;
sbc->allocation = SBC_AM_LOUDNESS;
sbc->bitpool = 26; // 240 bytes input 57 bytes output
sbc->endian = SBC_LE;
# 指令协议
App及设备均可主动发送request指令。
App或设备收到request指令后,回复相应的response指令。
如超过0.5秒没有收到相应的response,耳机会重新发送command,最多重试2次
命令处理成功时,response指令的code为0,其他错误码参考Response Code。
数据序列化时采用小端字节序。
# 包结构
request:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | command_id |
1 | 1 | uint8_t | command_type,1: request,2: response |
2 | 1 | uint8_t | sequence number, 0~255递增循环,response取request值 |
3 | 2 | uint16_t | payload长度,无数据时为0 |
5 | N | uint8_t[] | payload,指令相关数据, 无数据时为空 |
response:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | command_id |
1 | 1 | uint8_t | command_type,1: request,2: response |
2 | 1 | uint8_t | sequence number, 0~255递增循环,response取request值 |
3 | 1 | uint8_t | command result, 0: success |
4 | 2 | uint16_t | payload长度,无数据时为0 |
6 | N | uint8_t[] | payload,指令相关数据, 无数据时为空 |
其中payload数据由各指令自己定义。
# 指令列表
# 101_wakeUp
用户触发录音,发送该命令给App,并开始录音。
request payload: NULL
response payload: NULL
# 102_silence
App检测到静音时,发送该命令给设备,设备结束录音。
或设备在录音时多次没有收到App回复时,发送此命令结束录音。
request payload: NULL
respone payload: NULL
# 103_getInfo
建立连接过程中,由App发送给设备,设备返回product_id以及MAC地址。
request payload:NULL
response payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 4 | uint32_t | product_id |
4 | 6 | uint8_t[] | Bluetooth mac address |
10 | 8 | uint8_t[] | 特征值,连接App过程中由App分配,没有时用0填充 |
18 | 1 | uint8_t[] | connect_state, 最近一次或当前连接的情况: 0 表示未连接其他App或者语音助手。 1 表示已连接QQ音乐App。 2 表示已连接小微App。 3 表示已连接其他语音助手(如有)。 |
# 105_getConfig
建立连接过程中,由App发送给设备,设备返回参数配置信息。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | App期望码率,单位KBps,目前为32 |
1 | 1 | uint8_t | 手机操作系统. 1:iOS 2:Android |
2 | 8 | uint8_t[] | 特征值,8字节,其中包含了App的信息。 参考代码: xw_headphone_fvalue_get_host_app |
response payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | 连接类型,1:BLE, 2:SPP, 3:IAP |
1 | 1 | uint8_t | 编码类型,1:Opus, 2: SBC, 3: mSBC |
2 | 1 | uint8_t | 压缩率,16, 8,编码类型为Opus时适用。 |
3 | 1 | uint8_t | 采样率,1:16000 |
4 | 1 | uint8_t | 录音声道,1:单声道 |
5 | 1 | uint8_t | 位深,1:16bit |
6 | 1 | uint8_t | 录音方式,1:长按,2:短按 |
7 | 4 | uint8_t[] | 固件版本号:1.2.3.4对应的数据应为0x01,0x02,0x03,0x04 |
11 | 1 | uint8_t | 设备型号名称长度 |
12 | N | uint8_t[] | 设备型号名称,UTF8编码 |
12+N | 1 | uint8_t | 设备支持的小微协议版本号,当前为2。 参见附录-App对小微协议版本支持情况。 |
13+N | M | uint8_t[] | extra config |
extra config中包含一到多个配置项。目前支持的配置项有:
- 功能键设备:默认(降噪)/小微助手
- 设备sku信息,用于与厂商在硬件管理平台配置的信息关联。
- 设备自定义名称
- 设备序列号
- 更多详细信息参考代码
xw_headphone_extra_config_item
# 106_getSig
建立连接过程中,由App发送phoneId给设备,设备返回校验信息。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | N | uint8_t[] | phoneId,用于生成校验参数 |
response payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | N | uint8_t[] | sig,生成的校验参数 |
校验未启用时,sig返回长度不为0的任意值。
# 107_stopPlay
由设备发送给App,App收到命令后会停止正在播放对话结果,或者暂停正在播放的内容资源(音乐、故事、新闻等)。
request payload: NULL
respone payload: NULL
# 108_requestStopRceord
长按录音流程中,用户松开按键时,设备需要发送此命令。
request payload: NULL
respone payload: NULL
# 109_setKeyFunction
设置按键功能,如设备不支持,可不实现。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | 1:设备默认功能, 2:小微语音助手 |
response payload: NULL
# 110_clearFValue
清空设备的特征值。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | 1:清空, 0:不清空 |
response payload: NULL
# 111_playControl
播放控制及对话结束通知指令,App通过该指令告诉设备,TTS播放结束,以及需要发送的AVRCP指令(pause、resume)。
在Android上,TTS播放完成后会发送该命令告知设备TTS播放结束。
在iOS上,开始录音时,如有第三方App在播放音乐,App会发送该命令告知设备发送AVRCP Pause指令 。对话结束时,发送此命令告知设备TTS播放完成以及是否需要发送AVRCP Resume指令。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | value: 第0个bit(最低位)为1表示是TTS End。 第1个bit为1表示需要设备发送AVRCP Pause事件。 第2个bit为1表示需要设备发送AVRCP Resume事件。 |
response payload: NULL
# 112_heartBeat
App和设备均可主动发送该指令,用于探测连接是否正常。
App会定期发送心跳给设备,用于维持App在后台时不被系统挂起,并保持连接可用。
request payload: NULL
response payload: NULL
# 115_customSkill
1.6.0及以上版本支持。
用户query了耳机的自定义技能时,App通过该命令将技能的意图和槽位信息转发给耳机。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | skill name length |
1 | N | uint8_t [] | skill name |
N + 1 | 1 | uint8_t | intent name length |
N + 2 | M | uint8_t [] | intent name |
M + N + 2 | 1 | uint8_t | slot_count |
slot info | |||
slot info | |||
... |
每一个slot info包含了槽位名称和槽位值
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | slot name length |
1 | N | uint8_t [] | slot name |
N+1 | 1 | uint8_t | slot value length |
N+2 | M | uint8_t [] | slot value |
response payload:
NULL(如果处理成功)
出错则参考错误码处理回包。
# 255_voiceData
设备发送语音数据给App。
request payload:
Offset | Length | Type | Description |
---|---|---|---|
0 | N | uint8_t[] | 压缩后的语音数据 |
response payload: NULL
# 其他
# extra_config
extra config用于在105命令中返回更多的配置信息。
extra config的数据由0-N个config_item拼接而成。
每个config_item的第一个byte为config_key。
根据config_key的不同,config_item接下来数据格式如下:
config_key < 200时, config_item长度为2
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | config_key |
1 | 1 | uint8_t | value |
config_key >= 200时,config_item长度为2+N,N为对应数据的长度
Offset | Length | Type | Description |
---|---|---|---|
0 | 1 | uint8_t | config_key |
1 | 1 | uint8_t | length |
2 | N | uint8_t[] | data |
相关代码请参考:
xw_headphone_extra_config_key
xw_headphone_extra_config_item_list_pack
xw_headphone_extra_config_item_list_unpack
# 打开App
厂商自有App中可以跳转到小微App。
当跳转到小微App时,小微App已连接设备,会打开设备的设置页面。
当手机上没有安装小微App时,需要打开下载页面,提示用户下载安装。
# 判断是否安装
iOS: 通过url shceme xiaoweiapp://
,使用系统接口-[UIApplication canOpenURL:]
来判断是否安装。
Android:通过url shceme xiaoweiapp://
,使用系统接口判断是否安装。
示例代码:
public boolean checkXwappInstalled() {
Intent action = new Intent(Intent.ACTION_VIEW);
action.setData(Uri.parse("xiaoweiapp://"));
List list = getPackageManager().queryIntentActivities(action,
PackageManager.GET_RESOLVED_FILTER);
return list != null && list.size() > 0;
}
# 下载页面
iOS:https://apps.apple.com/cn/app/tencent-xiaowei/id1454208784?mt=8
Android:https://a.app.qq.com/o/simple.jsp?pkgname=com.tencent.xw
# 跳转链接
iOS & Android:xiaoweiapp://headphone/settings
# 附录
# App对小微协议版本支持情况
协议版本 | iOS | Android |
---|---|---|
2 | 1.2.0 | 1.2.0 |
3 | 1.2.2 | 1.2.2 |
4 | 1.4.0 | 1.4.0 |
5 | 1.6.0 | 1.6.0 |
# Response Code
Code | Description |
---|---|
0 | No error |
1 | Invalid command code |
2 | Parameter length out of range |
3 | Parameter length too short |
4 | Command handling failed |
5 | Waiting response time-out |
6 | The data transfer has already been started |
7 | The data transfer length doesn’t match the actually received data length |
41 | Wake up failed, invalid sign |
60 | 设备不支持此功能 |
# 代码下载
协议参考代码下载