蓝牙和TWS的连接原理
📎 原始文档:https://www.kdocs.cn/l/csR4lgOlWnsj 蓝牙和TWS的连接原理
文档简介
本文简单介绍蓝牙连接和TWS连接的原理,简单说明SDK相关命令和接口的使用方法,提供常见问题分析处理思路和解决办法。
一、蓝牙常用的几种状态
网络资料参考:蓝牙的状态详解
- Page连接 主动对其他蓝牙设备发起连接;
- Page Scan连接扫描 可被其他蓝牙设备连接;
当其他蓝牙设备处于Page Scan状态时,才能被发起page 的蓝牙设备连接。 - Inquiry搜索 搜索周围的蓝牙设备;
- Inquiry Scan搜索扫描 可被其他蓝牙设备搜素到;
当其他蓝牙设备处于Inquiry Scan状态时,才能被发起Inquiry 的蓝牙设备搜索到。 - Connection连接状态 已经处于连接状态;
蓝牙设备A通过page或者Inquiry获取到目标蓝牙设备B后,会经过一系列的连接过程,连接完成后两个蓝牙设备A和B就处于连接状态了。
二、蓝牙连接
- 首次配对 手机:由手机发起Inquiry,搜索周围的蓝牙设备;
耳机:耳机要处于Inquiry Scan,才能被手机搜索到;
配对完成:手机和耳机都会记录配对信息,配对信息中包含对方的蓝牙地址。 - 耳机回连手机 耳机:由耳机发起Page,page的蓝牙地址是手机的蓝牙地址
手机:手机要处于Page Scan状态,才能被耳机回连上
特殊点:- page用的蓝牙地址可以从配对信息中获取,这样的过程就是耳机回连已经配对过的手机;
- page用的蓝牙地址来源于其他方式,如uart上报,或者代码指定:
- 如果耳机已经跟手机配对过,就正常回连;
- 如果耳机没有跟手机配对过,会经过一个配对过程,如果手机允许配对,就正常配对连接,产生配对信息,否则连接失败。
- 耳机回连手机失败后 耳机处于Inquiry Scan和Page Scan的可发现可连接状态,可被手机发现和连接。
三、TWS连接
- 手机、TWS主机、TWS从机的关系(仅讨论AC697N以后的方案)
TWS(true wireless stereo 真无线蓝牙)不属于标准蓝牙协议,每家方案都有所区别。我们方案有以下三种方案: - 转发:主机和手机连接,同时和从机连接,主机收到手机数据后,转发给从机。
- 监听:监听模式,主从机可以对于手机来说是同一个设备,从机能监听到手机和主机之间的通讯,从而减小主从之间的数据交流。
- 监听+转发:
- 根据使用的场景切换监听或者转发模式
- 监听的前提下,再加入转发机制
//在lib_btctrler_config.c里面配对TWS方式
//0 监听 1 转发 2 监听+转发
const int CONFIG_TWS_WORK_MODE
- TWS配对
- 左右耳使用公共的配对地址
- 左右耳分别在Page <--> Page Scan之间切换
- 在同一个时间段内,当左右耳中,有一个处于Page,另一个处于Page Scan,则可以建立连接
- 连接成功后记录配对信息
- TWS回连
- 左右耳使用特有的配对地址(可能是配对信息中的地址,或者只有左右耳知道,没有第三者知道的某个地址)
- 左右耳分别在Page <--> Page Scan之间切换
- 在同一个时间段内,当左右耳中,有一个处于Page,另一个处于Page Scan,则可以建立连接
- TWS的配对信息
- 另一只耳机的地址(REMOTE地址)
- 通过特定方式生成的公共地址(COMMON地址)
- 其他相关信息
- 蓝牙地址(BT_MAC)
- TWS本地地址(LOCAL地址)
- 在实际应用中,TWS的连接还受左右声道限制,固定了左右声道后,不允许相同声道的对耳进行连接
- 四个蓝牙地址的关系
- 用于TWS配对连接的地址:LOCAL地址、REMOTE地址,丢失、更改LOCAL地址或者REMOTE地址都会导致TWS无法回连;
- 用于跟手机配对连接的地址:BT_MAC、COMMON地址;
- 当TWS左右耳已经配对上或者配对过且有配对记录:使用COMMON地址跟手机配对连接;
- 当TWS左右耳从来都没有配对过,也就是没有配对记录:使用BT_MAC跟手机配对连接。
四、代码中相关的命令和接口
- 蓝牙连接
Page
USER_CTRL_START_CONNEC_VIA_ADDR,TWS主机或单耳使用这个命令page回连手机
//从配对记录中获取手机的蓝牙地址
get_current_poweron_memory_search_index(bt_user_priv_var.auto_connection_addr);
//结束上一次的page流程
user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
//启动当前的page流程
user_send_cmd_prepare(USER_CTRL_START_CONNEC_VIA_ADDR, 6, bt_user_priv_var.auto_connection_addr);
Inquiry
耳机方案不涉及Inquiry。
Inquiry Scan
TWS主机或单耳执行:
打开:USER_CTRL_WRITE_SCAN_ENABLE
关闭:USER_CTRL_WRITE_SCAN_DISABLE
Page Scan
TWS主机或单耳执行:
打开:USER_CTRL_WRITE_CONN_ENABLE
关闭:USER_CTRL_WRITE_CONN_DISABLE
/*可发现(1)可连接(1),只能被手机搜索连接*/
user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
/*打开可发现, 可连接,可被手机和tws搜索连接*/
int tws_api_wait_pair_by_code(u16 code, const char *name, int timeout_ms);
/*不可发现(0)不可连接(0)*/
user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_DISABLE, 0, NULL);
user_send_cmd_prepare(USER_CTRL_WRITE_CONN_DISABLE, 0, NULL);
/*不可发现(0)可连接(1)*/
user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_DISABLE, 0, NULL);
user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
配对
TWS主机或单耳执行:有两种方式
//第一种:
/*可发现(1)可连接(1),只能被手机搜索连接*/
user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
/*===========================================================================*/
//第二种:
/*打开可发现, 可连接,可被手机和tws搜索连接*/
int tws_api_wait_pair_by_code(u16 code, const char *name, int timeout_ms);
回连
TWS主机或单耳执行:
//从配对记录中获取手机的蓝牙地址
get_current_poweron_memory_search_index(bt_user_priv_var.auto_connection_addr);
//结束上一次的page流程
user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
//启动当前的page流程
user_send_cmd_prepare(USER_CTRL_START_CONNEC_VIA_ADDR, 6, bt_user_priv_var.auto_connection_addr);
回连流程:
手机处于page scan时,会根据自己的mac地址计算出32个频点,每个频点在page scan时会停留1.28秒。
耳机处于page状态时,会根据手机的mac地址计算出32个频点,分成A、B两组,每组16个频点。
为了保证手机能够收到耳机的page包,每组频点会page 1.28+1.28=2.56秒,A、B组都page完需要2.56+2.56=5.12秒。
其他命令
TWS主机或单耳执行:
USER_CTRL_DISCONNECTION_HCI:已经处于连接状态时,执行该命令会断开耳机和手机的连接
USER_CTRL_CONNECTION_CANCEL:正在建立连接时,执行该命令会中断连接流程
USER_CTRL_PAGE_CANCEL:发起回连后,执行该命令会中断回连流程
2. TWS连接
配置配对地址
//调用tws_api_set_quick_connect_addr配置, addr为配对地址
void tws_api_set_quick_connect_addr(u8 *addr);
//或使用配对码的方式间接配置配对地址,code为配对码,底层会将code转换为配对地址
int tws_api_search_sibling_by_code(u16 code, int timeout_ms);
TWS配对
TWS配对的两种方式
//第一种,自动配对
//设置配对地址为跟配对码相关的地址,配对码相同的耳机可以进行TWS配对
tws_api_set_quick_connect_addr(tws_set_auto_pair_code());
tws_api_create_connection(timeout);//timeout单位为ms,经过timeout时间后结束配对流程
/*==========================================================================*/
//第二种,按键配对
//耳机开机先进入等待配对的状态,配对地址为跟配对码相关的地址
tws_api_wait_pair_by_code(bt_get_tws_device_indicate(NULL), bt_get_local_name(), 0);
//按键触发后发起配对,配对地址为跟配对码相关的地址,有其他耳机处于等待配对状态且双方配对码一致时,可以进行配对
tws_api_search_sibling_by_code(bt_get_tws_device_indicate(NULL), timeout);//timeout单位为ms,经过timeout时间后结束配对流程
TWS回连
//设置配对地址,addr为跟配对记录相关的地址,公版SDK中使用左耳local地址或者左右local地址相加,也可以使用没有第三者知道的某个地址
tws_api_set_quick_connect_addr(addr);
tws_api_create_connection(timeout);//timeout单位为ms,经过timeout时间后结束配对流程
其他接口
tws_api_cancle_wait_pair:取消可发现, 可连接,不可被tws搜索到
tws_api_cancle_create_connection:取消tws配对连接流程
tws_api_wait_connection:等待tws连接
五、连接产生的事件
- 蓝牙连接
Page
//page流程的相关状态会出现在hci事件中
static int bt_hci_event_handler(struct bt_event *bt)
{
switch (bt->event) {
case HCI_EVENT_CONNECTION_COMPLETE:
switch (bt->value) {
case ERROR_CODE_SUCCESS://page成功,或者被其他设备page成功
case ERROR_CODE_PAGE_TIMEOUT://page流程超时
case CUSTOM_BB_AUTO_CANCEL_PAGE://使用USER_CTRL_PAGE_CANCEL命令主动取消
}
}
}
HCI层的其他连接事件
查看apps\earphone\include\earphone.h中的注释。
常用事件:
///这个事件上来目前是做了一个连接确认的操作,跟手机配对的时候会有这个事件
#define HCI_EVENT_USER_CONFIRMATION_REQUEST 0x33
///<连接过程中linkkey丢失,手机删除了linkkey,回连就会出现一次,SDK会默认在这种情况下清掉耳机上的配对记录
#define ERROR_CODE_PIN_OR_KEY_MISSING 0x06
///<连接超时,一般拿远就会断开有这个消息
#define ERROR_CODE_CONNECTION_TIMEOUT 0x08
///<需要回连的设备资源不够。有些手机连了一个设备之后就会用这个拒绝。或者其它的资源原因,苹果手机在通知栏点蓝牙按钮断开之后(这时候手机蓝牙已经是不可连接的状态),耳机再回连大概率会出现这个事件
#define ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES 0x0D
///<经常用的连接断开消息。就认为断开就行,目前发现手机端主动断开才会有这个事件
#define ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION 0x13
协议栈的连接事件
连接是否完整和完成,依赖于协议栈的事件。
查看include_lib\btstack\avctp_user.h中STATUS_FOR_USER的注释。
常用事件:
BT_STATUS_FIRST_CONNECTED, /*连接成功*/
BT_STATUS_SECOND_CONNECTED, /*连接成功*/
...
BT_STATUS_FIRST_DISCONNECT, /*断开连接*/
BT_STATUS_SECOND_DISCONNECT, /*断开连接*/
BT_STATUS_PHONE_INCOME, /*来电*/
BT_STATUS_PHONE_NUMBER, /*来电话号码*/
BT_STATUS_PHONE_MANUFACTURER, /*获取手机的厂商*/
BT_STATUS_PHONE_OUT, /*打出电话*/
BT_STATUS_PHONE_ACTIVE, /*接通电话*/
BT_STATUS_PHONE_HANGUP, /*挂断电话*/
...
BT_STATUS_CALL_VOL_CHANGE, /*通话过程中设置音量会产生这个状态变化*/
BT_STATUS_SCO_STATUS_CHANGE, /*当esco/sco连接或者断开时会产生这个状态变化*/
...
BT_STATUS_VOICE_RECOGNITION, /*手机报告当前手机语音助手的状态*/
...
BT_STATUS_A2DP_MEDIA_START, /*手机开始发送音频数据过来*/
BT_STATUS_A2DP_MEDIA_STOP, /*手机停止发送音频数据*/
- TWS连接 include_lib\btctrler\classic\tws_event.h
enum {
TWS_EVENT_SEARCH_TIMEOUT = 1, //调用tws_api_search_sibling_by_code,timeout后会有此事件
TWS_EVENT_CONNECTED, //对耳连接成功,配对成功和连接成功都是这个事件
TWS_EVENT_CONNECTION_TIMEOUT, //调用tws_api_search_sibling_by_code搜索到,但是连接超时,或者调用tws_api_create_connection连接超时,会有这个事件
TWS_EVENT_CONNECTION_DETACH, //对耳连接成功后,由于某些原因断开,会有这个事件,该事件会带一个参数表示断开的原因(见enum tws_detach_reason)
TWS_EVENT_REMOVE_PAIRS, //调用tws_api_remove_pairs函数断开产生的事件
TWS_EVENT_PHONE_LINK_DETACH, //tws和手机断开会有这个事件
TWS_EVENT_SYNC_FUN_CMD, //调用tws_api_sync_call_by_uuid做对耳同步执行,且uuid是'T'时,会有这个事件,该事件会带一个参数表示同步的事件
TWS_EVENT_SYNC_FUN_TRANID, //没有用到
TWS_EVENT_CONNECT_TEST, //没有用到
TWS_EVENT_ROLE_SWITCH, //主从切换成功事件
TWS_EVENT_LOCAL_MEDIA_START, //没有用到
TWS_EVENT_LOCAL_MEDIA_STOP, //没有用到
TWS_EVENT_ESCO_ADD_CONNECT, //对耳连上ESCO通话链路事件
TWS_EVENT_SETUP_MONITOR_LINK,//没有用到
TWS_EVENT_MONITOR_START, //对耳进入监听状态的事件
TWS_EVENT_DATA_TRANS_OPEN, //没有用到
TWS_EVENT_DATA_TRANS_START, //没有用到
TWS_EVENT_DATA_TRANS_STOP, //没有用到
TWS_EVENT_DATA_TRANS_CLOSE, //没有用到
TWS_EVENT_MODE_CHANGE, //sniff without phone,没连手机时tws进入低功耗的事件
TWS_EVENT_ROLE_SWITCH_START, //对耳开始进行主从切换的事件
TWS_EVENT_TONE_TEST = 0xff, //没有用到
};
enum tws_detach_reason { //调用tws_api_detach函数断开TWS的传参,可以根据需要选择
TWS_DETACH_BY_SUPER_TIMEOUT = 8, //对耳已经连接上,但是连接中断导致长时间通信不上而断开
TWS_DETACH_BY_LOCAL, //本地主动断开
TWS_DETACH_BY_REMOTE, //remote主动断开
TWS_DETACH_BY_POWEROFF, //因为要执行关机而断开,最常用的是这个
TWS_DETACH_BY_REMOVE_PAIRS, //调用tws_api_remove_pairs函数断开,但目前已经不常用
TWS_DETACH_BY_TESTBOX_CON, //暂时没有用到
TWS_DETACH_BY_REMOVE_NO_RECONN,//暂时没有用到
};
六、TWS主从切换
手机、TWS主机、TWS从机的关系(仅讨论AC697N以后的方案)
TWS连接手机时,实际上只有主机和手机进行通信,主机会将一些重要的连接信息同步给从机。
主从切换就是让从机切换成主机,接替原来主机的工作去跟手机通信。
产生主从切换的情况一般有以下几种:
- 关机主从切换,主机关机,从机切换为主机:这种情况,是在对耳还没断开的情况下,先进行主从切换,切换完成后TWS断开然后才关机。
- 信号主从切换/超距主从切换,主机信号差,从机信号好,从机切换为主机:这种情况,需要在有业务通信的情况下才会产生主从切换,比如播歌或者通话过程中(通话信号主从切换仅限可视化SDK),同时还要求主从机之间、耳机和手机之间的RF正常。
- 电量主从切换,主机电量低,从机电量高,从机切换为主机:当耳机不处于第二点情况时,才会产生电量主从切换,也就是信号主从切换优先于电量主从切换。 注意:所有的主从切换场景都要求TWS主从之间能正常通信。突然切断TWS通信的场景无法进行主从切换(例如耳机直接复位或关机、断电、放屏蔽箱等)
七、TWS数据透传和同步指令
interface\btctrler\classic\tws_api.h
具体使用方法可参考SDK已有代码
/*
* 通过uuid,主从同步调用相同函数
*/
int tws_api_sync_call_by_uuid(int uuid, int priv, int delay_ms);
/*
* tws 数据发送函数, 要求 len <= 512
*/
int tws_api_send_data_to_sibling(void *data, u16 len, u32 func_id);//支持主从互发数据
int tws_api_send_data_to_slave(void *data, int len, u32 func_id);//只支持主机发给从机
demo:
1、anc同步提示音
2、主从间透传数据:主从间同步电量
常见问题处理
- 蓝牙连接
开机不回连手机
根据上面蓝牙连接章节的内容,我们知道耳机回连手机,需要耳机处于page状态,而手机处于page scan状态,耳机page 的地址必须是手机的蓝牙地址,并且回连时间要大于5.12秒。
基于上述,分析思路如下:
1. 先确保手机的蓝牙处于打开状态,并且没有在回连其他蓝牙设备 2. 通过打印log检查耳机是否处于page的回连状态: 1. 有没有执行到USER_CTRL_START_CONNEC_VIA_ADDR命令 2. USER_CTRL_START_CONNEC_VIA_ADDR带的蓝牙地址是否正确(手机蓝牙地址) 3. 打印中有没有出现大写字母C的打印(蓝牙底层的打印) 3. 检查开机回连时间,最好设置10秒以上,否则回连苹果手机会有小概率回连不了
一些常用的连接状态
1. 区分是首次配对还是回连:配对和回连会有不一样的流程,主要体现在HCI层的连接,当出现HCI_EVENT_USER_CONFIRMATION_REQUEST事件时,可认为当前连接是首次配对。
2. 区分断连是否为超距断连:耳机和手机处于连接状态时,由于某些原因导致了连接中断,比如手机和耳机拉远,由于连接中断导致的断连,会附带一个ERROR_CODE_CONNECTION_TIMEOUT的HCI错误事件,在连接成功之后收到此事件可以认为是超距断连。
3. 区分是否手机主动断开了连接:有些应用需要判断是否手机主动断开,来做不同的动作,手机端主动断开连接时会附带一个ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION的HCI错误事件,在连接成功之后收到这个事件可以认为是手机主动断开了连接。
- TWS连接
对耳开机TWS不回连/不配对
根据TWS连接章节的内容,对耳不管是配对还是回连:
1. 左右耳必须使用同一个地址进行配对和回连 2. 同时TWS的左右耳固定方式也会制约TWS的连接 3. 对耳的连接必须保证,同一段时间内一个耳机处于page状态,另一个耳机处于page scan状态 基于上述条件,分析思路如下:
1. 检查TWS配对时是否使用相同配对码(检查配置工具中的配对码),TWS回连时是否使用相同地址(检查传入tws_api_set_quick_connect_addr的地址),对于回连的,也可以直接检查对耳的LOCAL地址、REMOTE地址是否正常(是否被擦除、更改) 2. 检查两个耳机是否都是左声道,或者都是右声道 3. 检查配对或者回连过程中,log有无类似"CPCPCPCPCPPCPCP"这样C和P随机组合的打印 4. 检查是否有其他途径擦除了TWS配对记录,如是否存在按键功能清除TWS配对记录,存在这种按键功能的时候很容易误触,尤其是触摸按键。不建议使用按键进行清除配对记录,可用测试盒串口配对功能替代 5. 是否开启了交叉配对功能,交叉配对功能允许配对后的耳机跟第三者配对,进而导致丢失原配的配对记录。不建议使用交叉配对功能,可用测试盒串口配对功能替代