# 腾讯小微蓝牙设备协议

# 概要

蓝牙设备(如蓝牙耳机、蓝牙音箱)可以通过蓝牙与“腾讯小微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建立连接后停止广播。

# 连接流程

  1. App通过BLE,SPP,IAP等方式查找可用设备。
  2. 发现设备后,App发送103命令给设备来获取product_id,mac地址以及特征值。
    如果App已经通过BLE的广播包获取到了相应信息,App会跳过该步骤。
  3. App检查特征值,判断是否可以自动连接。
  4. 如不能自动连接,App弹窗询问用户是否连接设备。用户确认后继续流程。
  5. App发送105命令,给设备分配特征值,设备设备回复配置参数。
  6. App发送106命令,给设备发送phone id,设备根据校验方式回复校验信息。
  7. 连接完成,App播放已连接的提示。
  8. 连接完成后,App会定时发送心跳(112_heartBeat)给设备。如心跳失败,App将断开与耳机的连接。

过程如下图所示

蓝牙_发现

# 录音流程

# 长按

  1. 用户按下按键时,设备发送101命令,收到回复后开始录音。
  2. 设备持续通过255命令发送编码后的音频数据。如多次没有收到App的回复时,设备要停止录音并主动发送102命令尝试告知App停止录音。
  3. 用户松开按键时,设备发送108命令,在收到回复后需要继续录音和发送音频数据。
  4. 设备收到App发来的102命令时,结束录音。

过程如下图所示

蓝牙_录音_长按

# 短按

  1. 用户触发录音,设备发送101命令,收到回复后开始录音。
  2. 设备持续通过255命令发送编码后的音频数据。如多次没有收到App的回复时,设备要停止录音并主动发送102命令尝试告知App停止录音。
  3. 设备收到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 设备不支持此功能

# 代码下载

协议参考代码下载