BT

前言

bt主要是控制命令下发和事件上报,其所有控制命令和事件都在“bt_device.h”文件中枚举出来了,其中事件的上报都在“bt_service.c”文件的“app_bt_notify”函数中根据类型调用相应的函数进行处理。
控制可以有两种方式调用:

  1. 调用“void send_bt_control_msg_to_app_thread(bt_control_cmd_t cmd, uint8_t *data, uint16_t data_len)”函数发送到BT线程异步调用。

  2. 直接调用“bt_comm.c"文件中的对应函数。

  1. 其中“bt_comm.c"文件中是函数是我们封装出来的,有的命令可能没有对应的函数,而方法1都是支持的。

  2. 如使用方法1时其返回值在“BT_EVENT_CONTROL_RET”事件中。

  3. "bt_service.c"文件的"app_bt_notify"函数中的事件处函数请不要做修改,以防止在后继升级过程中不好合代码。如想按自己的逻辑处理BT事件,请参见“BT事件自定义处理”。

一键连接

双模蓝牙因为同时支持BT和BLE,需要分别建立BT和BLE的连接以及可能的配对,在更早之前的蓝牙连接中,通常BT和BLE会使用不同地址,这样终端用户会感知到建立两个连接的过程,甚至对于IOS,因为手机APK不能操作BT连接接口,终端用户必须亲自手动建立BT连接。而一键连接就是为了改善用户体验,使得终端用户只需要操作一次即可连接BT和BLE。
蓝牙协议有个Cross-Transport Key Derivation(CTKD)的功能,可以让BT/BLE根据BLE/BT配对生成的Key直接生成自己的Key,使得BT和BLE本来需要的两次配对变成一次。当然连接依旧是独立的两个连接。
这个在IOS和安卓/鸿蒙操作系统上又有不同,IOS系统是在IOS13以后全平台支持该功能,只是需要在APK创建BLE连接时候有特殊调用,这个在后续章节说明;而安卓/鸿蒙系统,部分支持部分不支持,加上安卓开发时BLE不配对场景居多,所以安卓/鸿蒙系统的一键连接是依靠手机APK主动创建BLE和BT来实现。

IOS系统

IOS系统只要IOS13以上,都支持CTKD,要完成一键连接,需要以下流程:

  1. 通过手机APK连接设备BLE,连接前需要设置connectOption:CBConnectPeripheralOptionEnableTransportBridgingKey,下图是设置示例

  2. 触发BLE配对,从手机端或设备端均可,此时手机会弹出配对框,点击确认配对。

  3. 在BLE配对完成后,只要设备端正常处于被连接状态,手机会自动发起BT的连接。

    1. 需要注意,手机发起BT连接之前,需要一直在APK界面等待,如果设置CTKD的APK在触发BLE配对后被放到后台,IOS不会发起BT连接

  4. IOS会自动完成HFP、A2DP、AVRCP及HID的连接。

安卓/鸿蒙系统

和IOS系统不同,安卓/鸿蒙系统的BT/BLE连接都需要手机APK主动发起,但做法上分成以下两种:

  1. BLE和BT使用相同地址。在BLE连接上以后,再调用安卓/鸿蒙CreateBond接口创建BT连接。

    这种方式需要注意以下几点点:

    1. 部分手机系统在处理BLE/BT相同地址的场景,在配对BT的时候必须要求BLE属性是双模设备才会发起,否则不会发起连接。要让手机认为设备为双模只有以下两种方式,APK需要满足其中至少一种:

      1. 通过BLE扫描到设备的广播包且广播包的flag没有not support edr的bit,flag默认SDK已经设置好。因此APK只需要保证无论是通过扫描发起连接设备还是扫二维码发起连接设备,都需要做BLE的扫描,扫描到以后再发起连接。

      2. 发起BT配对前,先做BT的扫描,扫描到BT设备再发起配对
        建议使用方法a,因为BLE扫描速度通常是比BT扫描要快很多。

    2. 部分手机虽然APK没有发起BLE的配对,但只要发起了BT配对,手机会先配对BLE,才会配对BT。这个行为不会多弹一个确认框,但有些手机在处理配对后BT/BLE地址有问题,当手机开关蓝牙后,BT或BLE的LinkKey会丢失,对应连接在回连时候会重新配对,这个时候有可能弹框有可能不弹框。

  2. BLE和BT使用不同地址,Solution SDK提供了接口“bt_err_t app_bt_cfg_change_bt_addr(bt_change_bd_addr_method_t type, uint8_t *addr)”可以改变BT的地址,这样可以避免做法1里面遇到的限制a/b。但这种方法也有需要注意的地方:

    1. 因为IOS的CTKD必须要地址相同,所以要确保连接IOS的时候把地址切回和BLE相同,和安卓连接的时候再切换为不同地址。Solution SDK已经提供完整方案,可以直接使用或者参考(“BT_USING_CUSTOMIZE_MAC”宏控制,ble连接成功后APP同步手机类型到手表端,是安卓时函数参数为(BT_CH_BD_METHOD_PRESERVE, NULL),是IOS时函数参数为(BT_CH_BD_METHOD_ORIGIN, NULL)。这样手机时安卓时BT的地址就是ble的MAC地址最后一位异或上0x55。

解除配对

终端用户可以在手机端解除配对,也可以在设备端恢复出厂设置或调用解除配对函数。

  1. 如果是手机端解除配对,分为解除配对时BT/BLE已经连接和没有连接:

    1. 如果BT/BLE已经连接,需要发命令通知设备端删除相关信息。如果是IOS设备,需要提醒用户在手机设置里面删除配对信息,否则会出现IOS的BLE回连中第ii点提到的不停被回连。

    2. 如果BT/BLE没有连接,手机APK要确保不再回连BLE并删除手机的BT配对信息,这样设备端在BT回连时才能收到LinkKey Missing的错误停止回连并删除相关信息。

  2. 如果是设备端恢复出厂设置,只要删除相关信息就好。

  3. 调用解除配对函数:

void app_bt_delete_appoint_peer_addr(bt_mac_t *mac);

配对弹窗

要开启设备端BT配对弹窗,需要打开配置“BT_USING_PAIRING_CONFIRMATION”,当打开此配置后BT配对时有对应的事件上报

case BT_EVENT_IO_CAPABILITY_IND:
{
    bt_io_capability_rsp_t res;
    res.io_capability = CAPABILITY_DISPLAY_YES_NO;  //In factory mode, the value is CAPABILITY_NO_INPUT_NO_OUTPUT
    rt_memcpy(res.mac.addr, args->args, sizeof(res.mac.addr));
    res.mitm = false;
    res.bonding = true;
    send_bt_control_msg_to_app_thread(BT_CONTROL_IO_CAPABILITY_RES, (uint8_t *)&res, sizeof(bt_io_capability_rsp_t));
}

// 弹窗事件
case BT_EVENT_USER_CONFIRM_IND:
{
    bt_pair_confirm_t *confirm = (bt_pair_confirm_t *)args->args;
    LOG_D("user confirm: %02x%02x%02x%02x%02x%02x", confirm->mac.addr[0], confirm->mac.addr[1], confirm->mac.addr[2],
            confirm->mac.addr[3], confirm->mac.addr[4], confirm->mac.addr[5]);
    app_send_msg_to_ui_wake(BT_APP, args->event, (uint8_t *)(args->args), sizeof(bt_pair_confirm_t));
}

// BT_EVENT_PAIR_IND 配对应答事件,根据参数判断是成功或失败。

// 当UI端收到此事件时弹出BT配对弹窗,根据用户选择填写对应参数。
send_bt_control_msg_to_app_thread(BT_CONTROL_USER_CONFIRM_RES, (uint8_t *)&res, sizeof(bt_user_confirm_rsp_t));

BT功能配置说明

要关闭BT功能时只需要在HCPU配置“PERI_USING_BT”即可,打开时按功能需求打开对应宏:

名称

描述

PERI_USING_BT

使能外围设备BT并选择sifli

BT_FINSH

使能bt finsh

BT_METRICS

使能BT保存数据

AUDIO_BT_AUDIO

使能BT音频

然后根据需要的功能打开BT的各个功能,如下图所示:



注:其PAN功能依赖于LWIP,如果不能打开请先打开LWIP功能。

HFP

HFP定义了音频网关(AG)和免提组件(HF)两个角色:
音频网关(AG) – 该设备为音频输入/输出的网关 ,典型作为网关的设备为手机。
免提组件(HF) – 该设备作为音频网关的远程音频输入/输出机制,并可提供若干遥控功能,典型作为免提组件的设备为车机、蓝牙耳机
我们的设备在连接手机时可做为HF,连接TWS耳机时可做为AG,下面对两个角色功能单独介绍。

HF

HF相关控制命令如下:

命令

对应函数

描述

BT_CONTROL_MAKE_CALL

app_bt_make_call

拔打电话

BT_CONTROL_PHONE_CONNECT

app_bt_answer_call

接听电话

BT_CONTROL_PHONE_HANDUP

app_bt_hang_up

挂断电话

BT_CONTROL_DIAL_BACK

app_bt_dial_back

回拔

BT_CONTROL_SET_INBAND_RING

app_bt_set_inband_ring

设置inband,已在连接成功时调用

BT_CONTROL_GET_PHONE_NUMBER

获取手机号码,已在连接成功时调用

BT_CONTROL_DTMF_DIAL

app_bt_dtmf_dial

在通话中输入按键信息

BT_CONTROL_GET_SIRI_CAPABILITY

获取手机是否支持SIRI,有兼容性问题,未使用

BT_CONTROL_SIRI_ON

app_bt_open_siri

打开SIRI

BT_CONTROL_SIRI_OFF

app_bt_close_siri

关闭SIRI

BT_CONTROL_3WAY_HOLD

app_bt_control_3way_hold

三方通话操作

BT_CONTROL_SET_WBS_STATUS

app_bt_set_wbs_status

使能/禁止WBS,测试时使用

BT_CONTROL_UPDATE_BATT_BY_HFP

向手机发送电池电量值: 0 ~ 9

BT_CONTROL_AUDIO_TRANSFER

app_bt_audio_transfer

切换音频

HF相关事件如下:

事件

描述

BT_EVENT_CALL_STATUS_IND

通话信息(通话状态、号码、ring都在其中)

BT_EVENT_DIAL_COMPLETE

拔打电话应答事件,即使是成功了4秒内无通话状态事件通话应用也应按失败处理

BT_EVENT_LOCAL_CALL_NUMBER

手机号码

BT_EVENT_CINDS_IND

HFP连接成功时上报(是否有服务、手机电量、GSM信号强度、是否支持漫游)

BT_EVENT_CIND_IND

手机信息发生变化时上报

BT_EVENT_AT_CMD_CFM_STATUS

通话相关AT命令发送结果

BT_EVENT_CALL_lINK_ESTABLISHED

SCO建立

BT_EVENT_CALL_LINK_DOWN

SCO断开

BT_EVENT_SIRI_STATE_NOTIFY

手机端通知的SIRI状态

BT_EVENT_SIRI_ON_COMPLETE

打开SIRI成功

BT_EVENT_SIRI_OFF_COMPLETE

关闭SIRI成功

BT_EVENT_SIRI_CAPABILITY_NOTIFY

SIRI是否支持

HF其他接口如下:

/**
* @brief  get bt active call state
* @return function execution result @see bt_call_state_t
*/
bt_call_state_t app_bt_get_active_call_state(void);

/**
* @brief  get bt call info
* @return function execution result @see bt_call_info_t
*/
bt_call_info_t *app_bt_get_call_info(void);

/**
* @brief  get bt call state by idx
* @param[in] idx  call idx
* @return function execution result @see bt_call_info_t
*/
bt_call_state_t app_bt_get_call_state_by_idx(uint8_t idx);

/**
* @brief  get hfp sco link status
* @retval 0 remote device
* @retval 1 local device
*/
uint8_t app_bt_get_hfp_sco_link(void);

说明:

  1. 当通话状态发生改变时都是通过“BT_EVENT_CALL_STATUS_IND”事件上报的,UI端收到此事件时通过“app_bt_get_call_info”函数得到通话信息,通话应用逻辑客户自行处理。

  2. 很多有通话功能的第三方APP(如:微信、钉钉等)在通话时,也会有通话信息传到手表,目前我们是屏蔽了第三方APP的通话。

    1. 安卓手机是采用特殊号码过滤,不同的安卓手机特殊号码不一样,如有新的特殊号码可添加在"specific_number"数组中。

    2. 苹果手机是通过判断手机号码是否有来电号码相同进行屏蔽的。

    3. 号码中除了包含’+'号之外,有其他字符都会认为是特殊号码,需特别注意号码中不能有空格(有的厂测机器中号码带空格会导致异常)。

  3. 当通话状态发生改变时从HCI看都会有CLCC命令,代码中都会进“bt_call_clcc_hdl"函数进行处理并有相关打印,如通话状态不对可以先查看CLCC中的信息是否正确,其含义如下。


  4. 当有三方通话时,CLCC信息有两条,必须通过“BT_CONTROL_3WAY_HOLD”命令进行控制,对应AT指令中CHLD。

  5. 有些手机的蓝牙不支持三方通话,但手机做了三方通话功能测试起来就有问题,我们可以通过HCI在连接HFP时查看手机是否支持三方通话,另外手机支持三方通话时如果手机传过来的信息和手机实际上的信息对不上我们也处理不了。


  6. 工厂测试时使用的音频盒子或是其他机器可能并不是按标准通话流程进行的,可能会导致在建立sco后被手表主动给断开了,而不能进行测试。为了避免这一现象,需要在进行工厂测试时调用“app_bt_set_audio_switching(SWIITCH_FACTORY_MASK, true);”函数,设置音频处于工厂模式,这时手表端就不会主动去断开esco。

  7. 如客户需要将设备端mic采集数据传到手机端,则需要客户在进入对应应用中调用“app_bt_set_audio_switching(SWIITCH_SIRI_MASK, true);”函数,允许手机建立esco,并通过ble发送命令到手机,手机APP收到命令后调用手机建立esco的接口(虚拟通话)来主动建立esco。这时mic的数据就会通过esco传到手机端,手机端APP对数据进行处理。当退出对应应用时要再调用“app_bt_set_audio_switching(SWIITCH_SIRI_MASK, false);”,禁止手机来建立esco。

AG

AG相关控制命令如下:

命令

描述

BT_CONTROL_AG_PHONE_CALL_STATUS_CHANGED

通话状态发生变化时调用

BT_CONTROL_AG_LOCAL_PHONE_INFO_RES

本机号码

BT_CONTROL_AG_REMOTE_CALL_INFO_RES

当前通话状态

BT_CONTROL_AG_ALL_INDICATOR_INFO_RES

indicator信息

BT_CONTROL_AG_INDICATOR_CHANGED_RES

indicator信息变化

BT_CONTROL_AG_SPK_VOL_CHANGE_REQ

设置扬声器增益

BT_CONTROL_AG_MIC_VOL_CHANGE_REQ

设置麦克风增益

BT_CONTROL_AG_CMD_RESULT_RES

命令回复

BT_CONTROL_AUDIO_TRANSFER_EX

切换音频

AG相关事件如下:

事件

描述

BT_EVENT_AG_ANSWER_CALL_REQ

接听电话

BT_EVENT_AG_HUNGUP_CALL_REQ

挂断电话

BT_EVENT_MAKE_CALL_REQ

拔号

BT_EVENT_DTMF_KEY_REQ

DTMF

BT_EVENT_GET_LOCAL_PHONE_INFO_REQ

获取本机号码请求事件

BT_EVENT_GET_INDICATOR_STATUS_REQ

获取indicator状态请求事件

BT_EVENT_GET_ALL_REMOTE_CALL_INFO_REQ

获取所有通话信息请求事件

A2DP

A2DP相关控制命令如下:

命令

函数

描述

BT_CONTROL_SET_A2DP_BQB_TEST

app_bt_set_a2dp_bqb_test

A2DP BQB测试

BT_CONTROL_RELEASE_SET

app_bt_release_a2dp

关闭a2dp的音频通路,BQB测试时使用

BT_CONTROL_UNREGISTER_AVSINK_SDP

app_bt_unregister_a2dp_snk_sdp

注销sink的sdp(手机查询不到A2DP,但设备可以主动连接手机A2DP)

BT_CONTROL_REGISTER_AVSINK_SDP

app_bt_register_a2dp_snk_sdp

注册sink的sdp

BT_CONTROL_SET_A2DP_SRC_AUDIO_DEVICE

app_bt_set_a2dp_src_audio_device

source时切换音频

A2DP相关事件如下:

事件

描述

BT_EVENT_A2DP_START_IND

有音频开始传输

A2DP切换媒体音频(用于切换媒体音频在手机端还是在手表端)
如要有切换媒体音频在手机端与手表端功能时需要客户在自己的文件中调用“app_bt_enent_register_cb(bt_notify_cb_t cb)”函数注册自己的事件处理函数,然后在BT初始化完成(BT_EVENT_BT_STACK_READY事件)时调用“注销sink的sdp”命令(send_bt_control_msg_to_app_thread(BT_CONTROL_UNREGISTER_AVSINK_SDP, NULL, 0);)。
在进入厂测模式时调用“注册sink的sdp”命令(send_bt_control_msg_to_app_thread(BT_CONTROL_REGISTER_AVSINK_SDP, NULL, 0);)。

/**
* @brief  The a2dp function is connect
* @param[in] profile_mask   Can multiple profile, example : BT_PROFILE_A2DP_MASK | BT_PROFILE_AVRCP_MASK
* @param[in] enable         true:媒体音频在手表端  false:媒体音频在手机端
* @return function execution result @see bt_err_t
*/
bt_err_t app_bt_profile_connect_enable(uint32_t profile_mask, bool enable);

//将媒体音频切换到手表端
app_bt_profile_connect_enable(BT_RPOFILE_A2DP_MASK, true);

//将媒体音频切换到手机端
app_bt_profile_connect_enable(BT_RPOFILE_A2DP_MASK, false);

//如果打开了多连接则此函数会多一个参数,需要传入MAC地址。
//可以获取各个角色最后一次连接的MAC地址。
bt_mac_t app_bt_get_last_slave_addr();
bt_mac_t app_bt_get_last_master_addr();

注:1、A2DP如果连接成功后如没有声音,可以先查看一下音量是为0、a2dp是否打开以及过滤是否是有

AVRCP(音乐信息及控制)

AVRCP相关控制命令如下:

命令

函数

描述

BT_CONTROL_PHONE_PLAY_PREVIOUS

app_bt_phone_play_previous

上一曲

BT_CONTROL_PHONE_PLAY_NEXT

app_bt_phone_play_next

下一曲

BT_CONTROL_PHONE_PLAY

app_bt_phone_play

播放

BT_CONTROL_PHONE_PLAY_SUSPEND

app_bt_phone_play_suspend

暂停

BT_CONTROL_PHONE_PLAY_STOP

app_bt_phone_play_stop

停止

AVRCP相关事件如下:

事件

描述

BT_EVENT_MP3_DETAIL_INFO

MP3详细信息

BT_EVENT_MUSIC_TRACK_CHANGED

TWS控制上一曲、下一曲

BT_EVENT_SONG_PLAY_PROGRESS

歌曲播放进度

BT_EVENT_MUSIC_PLAY_STATUS_CHANGED

播放状态发生变化

注:
1. 很多安卓手机绝对音量需要在手机端开启或者不支持绝对音量。
2. IOS没有歌曲播放进度上报,建议IOS使用AMS。

HID(人机接口)

HID相关控制命令如下:

命令

描述

BT_CONTROL_SET_HID_DEVICE

设置连接手机类型

BT_CONTROL_PHONE_DRAG_UP

上翻页

BT_CONTROL_PHONE_DRAG_DOWN

下翻页

BT_CONTROL_PHONE_ONCE_CLICK

单击

BT_CONTROL_PHONE_DOUBLE_CLICK

双击

BT_CONTROL_PHONE_TAKE_PICTURE

拍照(手机已打开相机此功能才能使用)

BT_CONTROL_PHONE_VOLUME_UP

音量加

BT_CONTROL_PHONE_VOLUME_DOWN

音量减

SPP

SPP 是 Serial Port Profile(串口协议)的缩写,其定义了使用蓝牙进行 RS232(或类似)串行电缆仿真的设备应使用的协议和过程。简单来说就是在蓝牙设备之间建立虚拟的串口进行数据通信,说白了就是以两个设备间发送自定义数据。我们的SPP支持多通道和多连接
注:IOS不支持SPP协议。

SPP相关控制命令如下:

命令

描述

BT_CONTROL_SEND_SPP_DATA

发送数据

BT_CONTROL_SEND_SPP_RSP

收到数据后返回应答(不返回应答收不到下一包数据)

BT_CONTROL_SEND_SPP_DISC_REQ

断开连接

SPP相关事件如下:

事件

描述

BT_EVENT_SPP_CONN_IND

连接成功

BT_EVENT_SPP_DATA_IND

接收数据

BT_EVENT_SPP_DATA_CFM

发送数据成功

BT_EVENT_SPP_DISCONN_IND

断开成功

PBAP

Phone Book Access Profile的简称,电话本访问协议,由于手机的电话本太多同步到手表端数据太多,因此采用在通话过程中通过号码去手机端获取联系人的方式。 注:此功能需要在配对时在手机端给予相应的权限。

PBAP相关控制命令如下:

命令

描述

BT_CONTROL_PBAP_SET_PB

设置获取联系人存储库(连接时已默认设置为手机存储联系人)

BT_CONTROL_PBAP_GET_NAME_BY_NUMBER

通过号码获取联系人名称(有通话时已调用)

PBAP相关事件如下:

事件

描述

BT_EVENT_VCARD_LIST_ITEM_NOTIFY

联系人名称上报(手机端一个号码存为多个名称时会多次上报)

BT_EVENT_VCARD_LIST_CMP

本次获取结束(未使用)

注:PBAP功能只需要打开对应的功能,然后在应用中当有“BT_EVENT_VCARD_LIST_ITEM_NOTIFY”事件上报时就联系人名称显示出来就行。可参考我们的对应代码。

GATT

GATT相关控制命令如下:

命令

描述

BT_CONTROL_BT_GATT_SDP_REG_REQ

注册

BT_CONTROL_BT_GATT_SDP_UNREG_REQ

取消注册

BT_CONTROL_BT_GATT_MTU_CHANGE_REQ

修改MTU

GATT相关事件如下:

事件

描述

BT_EVENT_BT_GATT_REG_RES

注册成功

BT_EVENT_BT_GATT_UNREG_RES

取消注册成功

BT_EVENT_BT_GATT_MTU_RES

MTU更新

注:GATT需要手机来进行连接,设备端不能主动发起连接

通用功能

相关控制命令如下:

命令

函数

描述

BT_CONTROL_SEARCH_EQUIPMENT

app_bt_search_device_start

搜索设备(TWS)

BT_CONTROL_CANCEL_SEARCH

app_bt_search_device_stop

取消搜索

BT_CONTROL_CONNECT_DEVICE

app_bt_connect_start

连接

BT_CONTROL_CONNECT_DEVICE_EX

app_bt_connect_start_ex

连接指定profile

BT_CONTROL_DISCONNECT

app_bt_disconnect

断连所有设备

BT_CONTROL_DISCONNECT_EX

app_bt_disconnect_ex

断连指定profile

BT_CONTROL_DISCONNECT_BY_CONNIDX

app_bt_disconnect_by_conn_idx

断连指定连接ID

BT_CONTROL_SET_LOCAL_NAME

app_bt_set_local_name

设置BT名称

BT_CONTROL_RD_LOCAL_NAME

读取BT名称

BT_CONTROL_RD_LOCAL_RSSI

app_bt_get_bt_rssi

读取RSSI

BT_CONTROL_SWITCH_OFF

app_bt_switch_off

关闭SCAN

BT_CONTROL_SWITCH_ON

app_bt_switch_on

打开SCAN

BT_CONTROL_SET_SCAN

app_bt_set_scan

设置SCAN状态

BT_CONTROL_GET_SCAN

app_bt_get_scan

获取SCAN状态

BT_CONTROL_EXIT_SNIFF

app_bt_exit_sniff

退出sniff状态

BT_CONTROL_SWITCH_TO_SOURCE

app_bt_switch_to_source

切换为source(连接TWS时为此状态)

BT_CONTROL_SWITCH_TO_SINK

app_bt_switch_to_sink

切换为sink(连接手机时为此状态)

BT_CONTROL_GET_BT_MAC

app_bt_get_addr

获取BT的MAC

BT_CONTROL_GET_VOLUME

app_bt_get_volume

获取音量(通过类型区分通话音频、媒体音频)

BT_CONTROL_SET_VOLUME

app_bt_set_volume

设置音量(通过类型区分通话音频、媒体音频)

BT_CONTROL_GET_PEER_DEVICEINFO

app_bt_get_peer_deviceinfo

获取配对设备信息

BT_CONTROL_LOCAL_PLAY_NEXT

app_bt_local_play_next

本地音乐下一曲

BT_CONTROL_LOCAL_PLAY

app_bt_local_play

本地音乐播放

BT_CONTROL_GET_LOCAL_SONG_DETAILS

app_bt_get_local_song_detail_info

获取本地音乐歌曲信息

BT_CONTROL_GET_LOCAL_SONG_BRIEF

app_bt_get_local_song_brief_info

获取本地音乐简言

BT_CONTROL_GET_LOCAL_SONG_TOTAL_NUM

app_bt_get_local_song_total_num

获取本地音乐总数量

BT_CONTROL_SET_LOCAL_SPK_VOL

设置本地音乐音量

BT_CONTROL_SET_MIC_MUTE

app_bt_set_mic_mute

设置麦克风静音状态

BT_CONTROL_GET_MIC_MUTE

app_bt_get_mic_mute

获取麦克风静音状态

BT_CONTROL_CLOSE_DEVICE

sned_bt_control_switch_to_app_thread

关闭BT

BT_CONTROL_OPEN_DEVICE

sned_bt_control_switch_to_app_thread

打开BT

BT_BQB_TEST_MODE

进入BQB测试模式

BT_CONTROL_GET_RMT_VERSION

获取蓝牙版本信息

BT_CONTROL_IO_CAPABILITY_RES

配对类型

BT_CONTROL_USER_CONFIRM_RES

配对弹窗确认

BT_CONTROL_AUDIO_TRANSFER_EX

app_bt_audio_transfer_ex

切换通话音频(AF和AG都可调用)

BT_CONTROL_CHANGE_BD_ADDR

app_bt_change_bd_addr

更改BT地址

BT_CONTROL_CANCEL_PAGE

app_bt_cancel_page

取消连接

BT_CONTROL_CANCEL_PAGE_BY_ADDR

app_bt_cancel_page_by_addr

取消指定地址的连接

BT_CONTROL_LOCAL_PLAY_PROMPT_TONE

app_bt_local_play_prompt_tone

播放本地提示音

BT_CONTROL_PLAY_POWER_ON_RING

app_bt_play_power_on_ring

开启循环播放

BT_CONTROL_PLAY_POWER_OFF_RING

app_bt_play_power_off_ring

关闭开启循环播放

BT_CONTROL_GET_RMT_NAME

app_bt_get_rmt_name

获取对端蓝牙名称

相关事件如下:

事件

描述

BT_EVENT_CALL_LINK_ESTABLISHED

esco建立结果

BT_EVENT_CALL_LINK_DOWN

esco断开

BT_EVENT_INQ

搜索到的设备

BT_EVENT_INQ_FINISHED

搜索完成

BT_EVENT_CONNECT_COMPLETE

profile连接成功

BT_EVENT_PROFILE_DISCONNECT

profile连接失败或断连

BT_EVENT_DISCONNECT

ACL断连

BT_EVENT_KEY_MISSING

对端取消配对(不用处理,我们发布版代码已做了处理)

BT_EVENT_ENCRYPTION

加密

BT_EVENT_VOL_CHANGED

音量变化

BT_EVENT_CONTROL_RET

所有控制事件的结果,通过此事件返回

BT_EVENT_POWER_OFF

BT关闭成功

BT_EVENT_BT_STACK_READY

BT初始化完成

BT_EVENT_BT_ADDR_IND

BT地址上报

BT_EVENT_CLOSE_COMPLETE

关闭完成

BT_EVENT_RD_LOCAL_NAME

BT名称

BT_EVENT_RD_LOCAL_RSSI

RSSI

BT_EVENT_ACL_OPENED_IND

ACL连接成功

BT_EVENT_PAIR_IND

配对事件

BT_EVENT_CHANGE_BD_ADDR

修改BT地址的应答

BT_EVENT_CANCEL_PAGE_IND

取消连接的应答

BT_EVENT_IO_CAPABILITY_IND

配对模式

BT_EVENT_USER_CONFIRM_IND

配对确认

BT_EVENT_KEY_OVERLAID

linkkey被覆盖

BT_EVENT_SCAN_ENB_CFM

SCAN状态上报

BT_EVENT_RMT_NAME_IND

对端名称

其他接口如下:

/**
* @brief get profile connect state
* @param[in] profile  Bluetooth profile
* @return profile connect state @see bt_connect_state_t
* @note Use app_bt_get_profile_state_by_conn_idx after BT_CONNECT_SUPPORT_MULTI_LINK is opened and used
*/
bt_connect_state_t app_bt_get_profile_state(bt_profile_t profile);

/**
* @brief get profile connect state
* @param[in] idx  conn index
* @param[in] profile  Bluetooth profile
* @return profile connect state @see bt_connect_state_t
*/
bt_connect_state_t app_bt_get_profile_state_by_conn_idx(uint8_t conn_idx, bt_profile_t profile);

/**
* @brief get bt role
* @return role  @see bt_role_t
*/
bt_role_t app_bt_get_role(void);

/**
* @brief  get acl state
* @return function execution result @see bt_acl_state_t
* @note Use app_bt_get_acl_state_by_conn_idx after BT_CONNECT_SUPPORT_MULTI_LINK is opened and used
*/
bt_acl_state_t app_bt_get_acl_state(void);

/**
* @brief  get acl state by conn index
* @param[in] idx  conn index
* @return function execution result @see bt_acl_state_t
*/
bt_acl_state_t app_bt_get_acl_state_by_conn_idx(uint8_t conn_idx);

/**
* @brief  get bt media state
* @return function execution result @see bt_media_state_t
* @note Use app_bt_get_media_state_by_conn_idx after BT_CONNECT_SUPPORT_MULTI_LINK is opened and used
*/
bt_media_state_t app_bt_get_media_state(void);

/**
* @brief  get bt media state by conn index
* @param[in] idx  conn index
* @return function execution result @see bt_media_state_t
*/
bt_media_state_t app_bt_get_media_state_by_conn_idx(uint8_t conn_idx);

BT事件自定义处理

请在自己的文件中调用“app_bt_event_register_cb”函数注册一个回调函数对相应的事件处理,在对应的回调函数中自行添加处理逻辑,如对应事件返回值为true则不会执行我们的代码,如返回值为false则我们的代码也会同时执行。

    case BT_EVENT_RMT_VERSION_IND:
    {
        send_msg_to_gui_thread((args->args), sizeof(bt_rmt_version_t), bt_event_process_in_gui_thread_cb, args->event, DONT_NEED_WAKEUP_UI);
    }
    break;

//例如发布版本的代码中"BT_EVENT_RMT_VERSION_IND"事件只是将信息传送到了UI,客户想保存这个信息,可在客户自己的文件中增加如下代码:
//以下代码在客户自己的文件中。
static bt_rmt_version_t rmt_version = {0};
static bool custom_notify_cb(bt_notify_t *args)
{
    uint16_t type = args->event >> 8;
    bool ret = false;
    switch (args->event)
    {
    case BT_EVENT_RMT_VERSION_IND:
    {
        rt_memcpy(&rmt_version, args->args, sizeof(bt_rmt_version_t));  //保存到变量中
        ret = true;     //返回值为true时,不执行原厂的代码,为false时,会执行原厂的代码
    }
    break;
    default:
        break;
    }
    return ret;
}
app_bt_event_register_cb(custom_notify_cb);

如何进行BT的连接&&回连

连接

在BT处于打开状态时,手机可以搜索并配对连接,但做为手表使用时一般采用一键双链(前面有介绍)。

/**
* @brief  send bt control open/close to app thread
* @param[in] cmd  BT_CONTROL_OPEN_DEVICE/BT_CONTROL_CLOSE_DEVICE
* @param[in] save  0:do not save  1:save
*/
void    send_bt_control_switch_to_app_thread(bt_control_cmd_t cmd, uint8_t save);

注:在打开/关闭BT时不要直接调用app_bt_open/app_bt_close函数,而是调用以上函数进行,否则会出现开关BT时不回连且开关状态不同步的情况。如在测试时需要临时关闭BT请将参数save传0以防止在测试完成后下次开机BT被关闭的情况。

BT连接流程

回连

BT允许设备端发起回连,加上HFP/A2DP等profile存在,回连会更加复杂,下面提到次数的在Solution SDK都是允许修改配置。

  1. 拉距或异常断开:这两种场景在设备端都会收到0x08的断连理由,Solution SDK在收到该理由会立刻发起BT连接。如果此时手机不在连接范围,会收到Page timeout(0x04)的连接错误提醒,此时Solution SDK会继续尝试连接,直到连接成功或3次连接都以0x04失败。

  2. 设备端重启或设备端关开蓝牙:这两种场景在Solution SDK都会触发回连上一次连接的设备,直到连接成功或3次连接都以0x04失败。

  3. 如果场景1和2超时后,Solution SDK不会进行回连。但当BLE被手机连接上后,经过一段时间等待且手机没有发起BT连接,则设备会主动回连上一次连接的设备。

  4. 手机主动断开蓝牙,比如关闭手机蓝牙或者在手机设置里面点击断开,这种断开Solution SDK不会发起连接。但可以修改成这种场景也回连,方法为打开“BT_USING_INFINITE_RECONNECT”宏。

  5. 如果手机已经取消配对,手表发起连接后会收到LinkKey Missing的错误。这样手表会停止回连并删除该手机的信息。
    注:回连BT时如最后一次是耳机测不会进行回连,如果又要连接耳机又要连接手机时当最后一次连接的是耳机时会出现手机ble连接时,不会回连手机的BT。如需要ble连接上时都回连上手机则需要不保存耳机的回连地址。

连接/回连性能

BT连接的速度和Page Scan有关系,但因为手机和设备都有可能发起连接,所以根据谁发起的连接看对方的Page Scan参数。手机端不可设置,都是采用11.25ms窗口/1.28s间隔的配置。

  1. 在设备端,SDK提供了默认参数:通常情况下11.25ms的scan 窗口以及1.28s的scan 间隔。如果BLE连接后会修改成22.5ms的窗口以及320ms的间隔,这个只会在BLE连接后的30秒内起作用,timeout或者BT连接上后,都会切换成11.25ms/1.28s。这种配置下,手机基本都可以连接上手表,为了避免环境干扰特别强导致连接失败,手机最好可以在一次连接后,再尝试一次。

  2. 当设备端连接手机时,因为手机不会修改参数,所以有些场景可能需要回连1-2次,最差场景回连3次才能连上。默认连接到连接失败(Page timeout)的时间是15秒,正常情况下在5秒内都可以连上,最差可以到30-40秒。

如何通过BT设置&&同步手机音量

当前音量可通过函数"app_bt_volume_db_get()"获取,在设置音量时可以实时保存也可以不保存,当用划动方式调节音量时建议中间设置值不要保存,只在最后一次保存这样可以加快运行速度和提高FLASH寿命。

/**
* @brief get volume information
* @return volume
*/
bt_volume_t *app_bt_volume_db_get(void);

/**
* @brief save volume information
* @param[in] info  volume information
*/
void app_bt_volume_db_set(bt_volume_set_t *info);

当手机端调节音量时通过"BT_EVENT_VOL_CHANGED"事件上报手机当前设置的音量值。

注:1. 在设置音量时请使用发送命令的方式(BT_CONTROL_SET_VOLUME),这种方式我们有做处理会有对应的保存。
2. 通话音量只有在sco建立时设置才会有效,其他任何时候设置都不会生效。
3. 设置媒体音频返回值为"BT_ERROR_AVRCP_NO_REG(0x10000011)"时,说明手机端未打开或不支持绝对音量时,如手机端找不到打开绝对音量设置,则表明此手机就是不支持绝对音量,不能使用AVRCP调节媒体音量。

工厂测试音频切换

工厂测试时使用的音频盒子或是其他机器可能并不是按标准通话流程进行的,可能会导致在建立sco后被手表主动给断开了,导致不能进行测试。为了避免这一现象,需要在进行工厂测试时调用“app_bt_set_audio_switching(SWIITCH_FACTORY_MASK, true);”函数,设置音频处于工厂模式,这时手表端就不会主动去断开esco。

常见问题说明

  1. 在测试过程中如遇到BT不能回连或是手机不能连接需要首先确认BT是否已打开。

  2. 在单连接时连接耳机需要先判断A2DP是否处于允许连接,如处于禁止连接需要先设置为允许连接。