691 lines
25 KiB
ReStructuredText
Executable File
691 lines
25 KiB
ReStructuredText
Executable File
中控(Central)
|
||
======================================
|
||
|
||
:link_to_translation:`en:[English]`
|
||
|
||
1 功能概述
|
||
-------------------------------------
|
||
|
||
本工程用于手机、拨号盘等主设备场景,主要功能有
|
||
|
||
| 1.作为a2dp source向对端音响传输音乐数据
|
||
| 2.接受对端的控制(播放暂停)
|
||
| 3.ble gatt server/gatt client
|
||
|
||
1.1 软件规格
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
* a2dp:
|
||
* avdtp source
|
||
* avrcp:
|
||
* tg
|
||
* ct
|
||
* ble:
|
||
* gap
|
||
* gatt server
|
||
* gatt client
|
||
* smp legacy pair/secure connection pair
|
||
|
||
1.2 代码路径及编译命令
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
Demo路径:`./projects/bluetooth/central <https://gitlab.bekencorp.com/wifi/armino/-/tree/main/projects/bluetooth/central>`_
|
||
|
||
编译命令:``make bk7258 PROJECT=bluetooth/central``
|
||
|
||
2 cmd命令简介
|
||
-------------------------------------
|
||
|
||
2.1 a2dp source
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player connect <xx:xx:xx:xx:xx:xx> | 连接音响 |
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player disconnect <xx:xx:xx:xx:xx:xx> | 断开连接 |
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player play <xxx.mp3> | 播放mp3 |
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player stop | 停止播放 |
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player pause | 暂停 |
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player resume | 恢复 |
|
||
+--------------------------------------------------+---------------------------+
|
||
| a2dp_player abs_vol <xxx> | 设置音响绝对音量 0 ~ 127 |
|
||
| | (是否有效视对端而定) |
|
||
+--------------------------------------------------+---------------------------+
|
||
|
||
2.2 ble gatt
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo -h | 显示详细命令 |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo <gatts|gattc> init | gatts/gattc初始化 |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo gatts disconnect <xx:xx:xx:xx:xx:xx> | gatts断开连接 |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo gattc connect <xx:xx:xx:xx:xx:xx> <0|1> | 连接gatts,0为public,1为random |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo gattc disconnect <xx:xx:xx:xx:xx:xx> | gattc断开连接 |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo gattc connect_cancel | 取消连接 |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
| ble_gatt_demo update_param <xx:xx:xx:xx:xx:xx> | 更新连接参数 |
|
||
| <interval> <timeout> | |
|
||
+-------------------------------------------------------------+--------------------------------------+
|
||
|
||
|
||
3 a2dp source 测试
|
||
-------------------------------------
|
||
|
||
3.1 测试过程
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.准备一张sd卡,格式化成exfat,将project/bluetooth/central/1_qcs.mp3放入根目录。(必须是16bits的mp3)
|
||
| 2.插入sd卡,开机。
|
||
| 3.令音响进入配对模式
|
||
| 4.输入 ``a2dp_player connect xx:xx:xx:xx:xx:xx``,其中xx为音响地址。等待连接成功
|
||
| 5.如果连接成功,会提示"a2dp connect complete",如果连接失败,会提示"Unsuccessful Connection Complete"之类的log。
|
||
| 6.输入 ``a2dp_player play xxx.mp3``。
|
||
| 7.如果sd卡正常,会提示"f_mount OK",如果音乐文件存在,会提示"mp3 file open successfully"
|
||
| 8.此时可听到播出声音
|
||
| 9.正在播放的情况下,可以stop pause,停止播放的情况下可以play。(尽量连接后立刻play,不要stop,参见本章节兼容性说明)
|
||
| 10.如果音响支持avrcp,可以通过音响控制播放暂停。(存在兼容性问题,参见本章节兼容性说明)
|
||
|
||
|
||
3.2 兼容性说明
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.仅播放歌曲场景,某些音响如果长时间处于stop(本地a2dp_player stop 或对端avdtp suspend)状态,会主动断开连接。log会提示bt_api_event_cb:Disconnected from xx:xx:xx:xx:xx:xx
|
||
| 2.某些音响不会向本地注册avrcp playback notify,会导致两端操作播放暂停时状态不一致的现象。
|
||
| 3.某些音响不会向本地报告avrcp volume change,此时音响调节音量,central无法得知。
|
||
| 4.某些音响不支持设置绝对音量。
|
||
|
||
3.3 其他注意事项
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.如果sdcard有问题,会出现 f_mount failed 或 read data crc error 的提示
|
||
|
||
|
||
4 ble gatt 测试
|
||
-------------------------------------
|
||
|
||
4.1 单板作为client与手机连接测试
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.手机使用nrf connect开启广播
|
||
| 2.开发板a输入 ``ble_gatt_demo gattc init``
|
||
| 3.开发板a输入 ``ble_gatt_demo gattc connect <手机adv地址> 1``
|
||
| 3.连接成功会有log打印 ``BK_BLE_GAP_CONNECT_COMPLETE_EVT``
|
||
| 4.discover完成会有打印 ``BK_GATTC_DIS_SRVC_CMPL_EVT``
|
||
|
||
4.2 双板对测
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.开发板a输入 ``ble_gatt_demo gatts init``
|
||
| 2.开发板b输入 ``ble_gatt_demo gattc init``
|
||
| 3.开发板b输入 ``ble_gatt_demo gattc connect <a板地址> 1``
|
||
| 4.连接成功会有log打印 ``BK_BLE_GAP_CONNECT_COMPLETE_EVT``
|
||
| 5.b板discover完成会有打印 ``BK_GATTC_DIS_SRVC_CMPL_EVT``
|
||
| 6.后续a板会定时给b板notify
|
||
|
||
5 结构图
|
||
-------------------------------------
|
||
|
||
5.1 运行时
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
.. figure:: ../../../../_static/bluetooth_central_runtime_arch.png
|
||
:align: center
|
||
:alt: module architecture Overview
|
||
:figclass: align-center
|
||
|
||
Figure 1. software module architecture
|
||
|
||
5.2 流程图
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
.. figure:: ../../../../_static/bluetooth_central_flow_chart.png
|
||
:align: center
|
||
:alt: module architecture Overview
|
||
:figclass: align-center
|
||
|
||
Figure 2. flow chart
|
||
|
||
|
||
6 重要流程说明
|
||
-------------------------------------
|
||
|
||
6.1 连接音响
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
int bt_a2dp_source_demo_connect(uint8_t *addr)
|
||
{
|
||
...
|
||
|
||
//连接对端
|
||
err = bk_bt_a2dp_source_connect(a2dp_env.peer_addr.addr);
|
||
|
||
if (err)
|
||
{
|
||
a2dp_loge("connect a2dp err %d", err);
|
||
goto error;
|
||
}
|
||
|
||
a2dp_logi("start wait a2dp connect cb");
|
||
//等待连接成功
|
||
err = rtos_get_semaphore(&s_bt_api_event_cb_sema, 12 * 1000);
|
||
|
||
if (err)
|
||
{
|
||
a2dp_loge("get sem for connect err");
|
||
goto error;
|
||
}
|
||
|
||
a2dp_logi("start wait a2dp cap report cb");
|
||
//等待获取a2dp cap
|
||
err = rtos_get_semaphore(&s_bt_api_event_cb_sema, 6 * 1000);
|
||
|
||
if (err)
|
||
{
|
||
a2dp_loge("get sem for cap select err");
|
||
goto error;
|
||
}
|
||
|
||
a2dp_logi("a2dp connect complete");
|
||
|
||
...
|
||
}
|
||
|
||
|
||
6.2 mp3解码
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
static void bt_a2dp_source_decode_task(void *arg)
|
||
{
|
||
...
|
||
|
||
while (*task_ctrl)
|
||
{
|
||
...
|
||
|
||
if (0 != mp3_read_end_ptr - current_mp3_read_ptr - bytesleft)
|
||
{
|
||
//读取文件
|
||
fr = f_read(&mp3file, current_mp3_read_ptr + bytesleft, mp3_read_end_ptr - current_mp3_read_ptr - bytesleft, &num_rd);
|
||
|
||
if (fr != FR_OK)
|
||
{
|
||
a2dp_loge("read %d %s failed!", num_rd, full_path);
|
||
goto error;
|
||
}
|
||
|
||
if (!num_rd)
|
||
{
|
||
//回到文件头
|
||
a2dp_logi("file end, return to begin");
|
||
|
||
os_memmove(mp3_read_start_ptr, current_mp3_read_ptr, bytesleft);
|
||
current_mp3_read_ptr = mp3_read_start_ptr;
|
||
|
||
f_lseek(&mp3file, frame_start_offset);
|
||
continue;
|
||
}
|
||
|
||
...
|
||
}
|
||
|
||
a2dp_logv("bytesleft %d %p", bytesleft, current_mp3_read_ptr);
|
||
|
||
do
|
||
{
|
||
...
|
||
|
||
//判断ring buffer是否满
|
||
while (ring_buffer_particle_len(&s_rb_ctx) > s_decode_trigger_size)
|
||
{
|
||
if (!*task_ctrl)
|
||
{
|
||
goto error;
|
||
}
|
||
|
||
a2dp_logd("ring buffer not read too much, wait %d", ring_buffer_particle_len(&s_rb_ctx));
|
||
rtos_get_semaphore(&s_source_need_decode_sema, BEKEN_WAIT_FOREVER);
|
||
}
|
||
|
||
//解码
|
||
ret = MP3Decode(s_mp3_decoder, ¤t_mp3_read_ptr, &bytesleft, (int16_t *)pcm_write_ptr, 0);
|
||
|
||
//异常处理
|
||
if (ret != ERR_MP3_NONE)
|
||
{
|
||
if (ERR_MP3_INDATA_UNDERFLOW == ret)
|
||
{
|
||
bytesleft = last_success_bytesleft;
|
||
current_mp3_read_ptr = last_success_read_ptr;
|
||
break;
|
||
}
|
||
|
||
goto error;
|
||
}
|
||
|
||
//写ring buffer
|
||
if (ring_buffer_particle_write(&s_rb_ctx, pcm_write_ptr, tmp_mp3_frame_info.outputSamps * tmp_mp3_frame_info.bitsPerSample / 8))
|
||
{
|
||
a2dp_logd("ring_buffer full %d", ring_buffer_particle_len(&s_rb_ctx));
|
||
|
||
...
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
a2dp_logv("write %d, %d", tmp_mp3_frame_info.outputSamps * tmp_mp3_frame_info.bitsPerSample / 8, ring_buffer_particle_len(&s_rb_ctx));
|
||
}
|
||
|
||
pcm_decode_size += tmp_mp3_frame_info.outputSamps * tmp_mp3_frame_info.bitsPerSample / 8;
|
||
}
|
||
while (tmp_mp3_frame_info.outputSamps && *task_ctrl);
|
||
}
|
||
|
||
...
|
||
}
|
||
|
||
6.3 host a2dp 取数回调(pcm)
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
static int32_t a2dp_source_data_cb(uint8_t *buf, int32_t len)
|
||
{
|
||
uint32_t read_len = 0;
|
||
...
|
||
//从ring buffer读取
|
||
if (ring_buffer_particle_len(&s_rb_ctx) < len)
|
||
{
|
||
a2dp_loge("ring buffer not enough data %d < %d ", ring_buffer_particle_len(&s_rb_ctx), len);
|
||
}
|
||
else
|
||
{
|
||
ring_buffer_particle_read(&s_rb_ctx, buf, len, &read_len);
|
||
}
|
||
|
||
//通知mp3继续解码
|
||
if (s_source_need_decode_sema)
|
||
{
|
||
rtos_set_semaphore(&s_source_need_decode_sema);
|
||
}
|
||
|
||
return read_len;
|
||
}
|
||
|
||
|
||
6.3 host a2dp 重采样回调
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
static int32_t a2dp_source_pcm_resample_cb(uint8_t *in_addr, uint32_t *in_len, uint8_t *out_addr, uint32_t *out_len)
|
||
{
|
||
...
|
||
|
||
bt_audio_resample_req_t rsp_req;
|
||
|
||
os_memset(&rsp_req, 0, sizeof(rsp_req));
|
||
|
||
input_len = *in_len;
|
||
output_len = *out_len;
|
||
|
||
rsp_req.in_addr = in_addr;
|
||
rsp_req.out_addr = out_addr;
|
||
rsp_req.in_bytes_ptr = &input_len;
|
||
rsp_req.out_bytes_ptr = &output_len;
|
||
|
||
//发送给cpu1重采样
|
||
ret = media_send_msg_sync(EVENT_BT_PCM_RESAMPLE_REQ, (uint32_t)&rsp_req);
|
||
|
||
if (ret)
|
||
{
|
||
a2dp_loge("EVENT_BT_PCM_RESAMPLE_REQ err %d !!", ret);
|
||
ret = -1;
|
||
}
|
||
|
||
...
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
6.4 host a2dp sbc编码回调
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
static int32_t a2dp_source_pcm_encode_cb(uint8_t type, uint8_t *in_addr, uint32_t *in_len, uint8_t *out_addr, uint32_t *out_len)
|
||
{
|
||
...
|
||
|
||
bt_audio_encode_req_t rsp_req;
|
||
int ret = 0;
|
||
|
||
os_memset(&rsp_req, 0, sizeof(rsp_req));
|
||
|
||
rsp_req.handle = &s_sbc_software_encoder_ctx;
|
||
rsp_req.in_addr = in_addr;
|
||
rsp_req.type = type;
|
||
rsp_req.out_len_ptr = (typeof(rsp_req.out_len_ptr))&encode_len;
|
||
|
||
//发送给cpu1编码
|
||
ret = media_send_msg_sync(EVENT_BT_PCM_ENCODE_REQ, (uint32_t)&rsp_req);
|
||
|
||
if (ret)
|
||
{
|
||
a2dp_loge("EVENT_BT_PCM_ENCODE_REQ err %d !!", ret);
|
||
return -1;
|
||
}
|
||
|
||
...
|
||
return 0;
|
||
}
|
||
|
||
6.5 ble gap初始化
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
int dm_gatt_main(cli_gatt_param_t *param)
|
||
{
|
||
...
|
||
//注册gap回调
|
||
bk_ble_gap_register_callback(dm_ble_gap_private_cb);
|
||
dm_gatt_add_gap_callback(dm_ble_gap_common_cb);
|
||
|
||
//读取本地ir/er
|
||
bluetooth_storage_read_local_key(&s_dm_gap_local_key);
|
||
|
||
//向host设置ir/er
|
||
ret = bk_ble_gap_set_security_param(BK_BLE_SM_SET_ER, (void *)s_dm_gap_local_key.er, sizeof(s_dm_gap_local_key.er));
|
||
...
|
||
ret = bk_ble_gap_set_security_param(BK_BLE_SM_SET_IR, (void *)s_dm_gap_local_key.ir, sizeof(s_dm_gap_local_key.ir));
|
||
|
||
//生成rpa地址
|
||
...
|
||
ret = bk_ble_gap_generate_rpa(NULL);
|
||
...
|
||
|
||
//设置privacy
|
||
ret = bk_ble_gap_config_local_privacy(s_dm_gatt_privacy_enable);
|
||
|
||
//循环添加已配对设备
|
||
for()
|
||
{
|
||
ret = bk_ble_gap_bond_dev_list_operation(BK_GAP_BOND_DEV_LIST_OPERATION_ADD, &bond_dev);
|
||
}
|
||
|
||
//设置iocap
|
||
ret = bk_ble_gap_set_security_param(BK_BLE_SM_IOCAP_MODE, (void *)&s_dm_gatt_iocap, sizeof(s_dm_gatt_iocap));
|
||
|
||
//设置配对请求auth参数
|
||
ret = bk_ble_gap_set_security_param(BK_BLE_SM_AUTHEN_REQ_MODE, (void *)&s_dm_gatt_auth_req, sizeof(s_dm_gatt_auth_req));
|
||
|
||
//设置密钥分发种类
|
||
ret = bk_ble_gap_set_security_param(BK_BLE_SM_SET_INIT_KEY, (void *)&s_dm_gatt_init_key_distr, sizeof(s_dm_gatt_init_key_distr));
|
||
ret = bk_ble_gap_set_security_param(BK_BLE_SM_SET_RSP_KEY, (void *)&s_dm_gatt_rsp_key_distr, sizeof(s_dm_gatt_rsp_key_distr));
|
||
|
||
//设置gatt mtu
|
||
bk_ble_gatt_set_local_mtu(517);
|
||
}
|
||
|
||
|
||
6.6 ble gatts初始化
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
int dm_gatts_main(cli_gatt_param_t *param)
|
||
{
|
||
//初始化gap
|
||
dm_gatt_main(NULL);
|
||
|
||
//注册gatts回调,注册gatts app
|
||
bk_ble_gatts_register_callback(bk_gatts_cb);
|
||
ret = bk_ble_gatts_app_register(0);
|
||
|
||
//设置service
|
||
ret = bk_ble_gatts_create_attr_tab(s_gatts_attr_db_service_1, s_gatts_if, sizeof(s_gatts_attr_db_service_1) / sizeof(s_gatts_attr_db_service_1[0]), 30);
|
||
ret = bk_ble_gatts_create_attr_tab(s_gatts_attr_db_service_2, s_gatts_if, sizeof(s_gatts_attr_db_service_2) / sizeof(s_gatts_attr_db_service_2[0]), 30);
|
||
|
||
//开启service
|
||
bk_ble_gatts_start_service(s_service_attr_handle);
|
||
//注册gap回调(仅gatts关心)
|
||
dm_gatt_add_gap_callback(dm_ble_gap_cb);
|
||
|
||
//设置adv参数
|
||
ret = dm_gatts_set_adv_param(s_dm_gatts_local_addr_is_public);
|
||
|
||
//设置adv random addr(仅rpa开启或自定义addr)
|
||
if (need_set_random_addr)
|
||
{
|
||
ret = bk_ble_gap_set_adv_rand_addr(ADV_HANDLE, current_addr);
|
||
|
||
if (ret)
|
||
{
|
||
gatt_loge("bk_ble_gap_set_adv_rand_addr err %d", ret);
|
||
goto error;
|
||
}
|
||
|
||
ret = rtos_get_semaphore(&s_ble_sema, SYNC_CMD_TIMEOUT_MS);
|
||
|
||
if (ret != kNoErr)
|
||
{
|
||
gatt_loge("wait set adv rand addr err %d", ret);
|
||
goto error;
|
||
}
|
||
}
|
||
|
||
//设置adv data
|
||
ret = bk_ble_gap_set_adv_data((bk_ble_adv_data_t *)&adv_data);
|
||
|
||
//开启adv
|
||
ret = bk_ble_gap_adv_start(1, &ext_adv);
|
||
|
||
...
|
||
}
|
||
|
||
|
||
6.7 ble gattc初始化
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
int dm_gattc_main(cli_gatt_param_t *param)
|
||
{
|
||
...
|
||
//注册gap回调(仅gattc关心)
|
||
dm_gatt_add_gap_callback(dm_ble_gap_cb);
|
||
...
|
||
//注册gattc回调
|
||
bk_ble_gattc_register_callback(bk_gattc_cb);
|
||
...
|
||
//注册gattc app
|
||
ret = bk_ble_gattc_app_register(0);
|
||
...
|
||
//设置connect/scan rand addr(仅rpa开启或自定义addr)
|
||
if (need_set_random_addr)
|
||
{
|
||
ret = bk_ble_gap_set_rand_addr(current_addr);
|
||
|
||
if (ret)
|
||
{
|
||
gatt_loge("bk_ble_gap_set_rand_addr err %d", ret);
|
||
goto error;
|
||
}
|
||
|
||
ret = rtos_get_semaphore(&s_ble_sema, SYNC_CMD_TIMEOUT_MS);
|
||
|
||
if (ret != kNoErr)
|
||
{
|
||
gatt_loge("wait set rand addr err %d", ret);
|
||
goto error;
|
||
}
|
||
}
|
||
|
||
...
|
||
}
|
||
|
||
6.8 ble gattc 连接
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
::
|
||
|
||
int32_t dm_gattc_connect(uint8_t *addr, uint32_t addr_type)
|
||
{
|
||
...
|
||
|
||
//设置连接参数
|
||
bk_gap_create_conn_params_t param = {0};
|
||
bk_bd_addr_t peer_id_addr = {0};
|
||
bk_ble_addr_type_t peer_id_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||
|
||
param.scan_interval = 800;
|
||
param.scan_window = param.scan_interval / 2;
|
||
param.initiator_filter_policy = 0;
|
||
|
||
//决定是否用rpa连接
|
||
if (g_dm_gap_use_rpa && 0 == dm_gatt_find_id_info_by_nominal_info(addr, addr_type, peer_id_addr, &peer_id_addr_type))
|
||
{
|
||
gatt_logi("local use rpa");
|
||
param.local_addr_type = (s_dm_gattc_local_addr_is_public ? BLE_ADDR_TYPE_RPA_PUBLIC : BLE_ADDR_TYPE_RPA_RANDOM);
|
||
os_memcpy(param.peer_addr, addr, sizeof(param.peer_addr));
|
||
param.peer_addr_type = addr_type;
|
||
}
|
||
else
|
||
{
|
||
param.local_addr_type = (s_dm_gattc_local_addr_is_public ? BLE_ADDR_TYPE_PUBLIC : BLE_ADDR_TYPE_RANDOM);
|
||
os_memcpy(param.peer_addr, addr, sizeof(param.peer_addr));
|
||
param.peer_addr_type = addr_type;
|
||
}
|
||
|
||
//设置连接参数
|
||
param.conn_interval_min = 16;
|
||
param.conn_interval_max = 16;
|
||
param.conn_latency = 0;
|
||
param.supervision_timeout = 500;
|
||
param.min_ce = 0;
|
||
param.max_ce = 0;
|
||
|
||
err = bk_ble_gap_connect(¶m);
|
||
|
||
...
|
||
return err;
|
||
}
|
||
|
||
|
||
7 常见问题FAQ
|
||
-------------------------------------
|
||
|
||
7.1 ble连接、断连evt是怎样的顺序?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1. BK_BLE_GAP_CONNECT_COMPLETE_EVT -> dm_gatts.c dm_gattc.c BK_GATTS_CONNECT_EVT|BK_GATTC_CONNECT_EVT -> 各个上层profile的 BK_GATTS_CONNECT_EVT|BK_GATTC_CONNECT_EVT
|
||
| 2. 各个上层profile的 BK_GATTS_DISCONNECT_EVT|BK_GATTC_DISCONNECT_EVT -> dm_gatts.c dm_gattc.c BK_GATTS_DISCONNECT_EVT|BK_GATTC_DISCONNECT_EVT -> BK_BLE_GAP_CONNECT_COMPLETE_EVT
|
||
|
||
|
||
7.2 蓝牙多模(bt/ble)链接的有哪些库?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| libbluetooth_host_dm_dual.a 和 libbluetooth_controller_dual.a
|
||
|
||
|
||
7.3 对于一个已存在的ble链路,有没有handle之类的以便于控制?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| BK_GATTS_CONNECT_EVT和 BK_GATTC_CONNECT_EVT中有conn_id,所有gatts/gattc接口都以此作为handle。这些evt也包含对端addr可供上层作映射关系。
|
||
| BK_BLE_GAP_CONNECT_COMPLETE_EVT也有hci_handle,但不能在gatt直接使用,需要通过转换接口。
|
||
|
||
7.4 如何创建gatts db,如何将其和attribute handle对应起来?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.请参考dm_gatts.c的表结构。
|
||
| 2.表reg成功后会上报BK_GATTS_CREAT_ATTR_TAB_EVT,handles对应表每一行的attribute handle(其中char decl/char value只有后者有效)
|
||
|
||
7.5 bk_ble_gap_register_callback能不能复用,比如多个profile都想收到evt?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 不能,但在dm_gatt.c里有个dm_gatt_add_gap_callback通过bk_ble_gap_register_callback封装了这个功能,更多细节请参考dm_gatts.c dm_gattc.c及现有profile的实现
|
||
|
||
7.6 attribute handle的evt能不能分别给到各个profile?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 请参考dm_gatts_reg_db的实现
|
||
|
||
7.7 各个profile能否收到gatts的evt?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 请参考dm_gatts_reg_db的实现,目前已添加了几个evt,可以根据需要修改
|
||
|
||
7.8 配对相关的evt有哪些流程?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| BK_BLE_GAP_SEC_REQ_EVT 配对请求
|
||
| BK_BLE_GAP_AUTH_CMPL_EVT 配对结果
|
||
| BK_BLE_GAP_BOND_KEY_GENERATE_EVT key生成
|
||
| BK_BLE_GAP_PASSKEY_NOTIF_EVT passkey生成通知
|
||
| BK_BLE_GAP_PASSKEY_REQ_EVT 本地输入passkey req evt
|
||
| BK_BLE_GAP_NC_REQ_EVT 本地number compare请求
|
||
| 大致流程是BK_BLE_GAP_SEC_REQ_EVT -> passkey、number compare(如果有) -> BK_BLE_GAP_BOND_KEY_GENERATE_EVT -> BK_BLE_GAP_AUTH_CMPL_EVT
|
||
|
||
7.9 app应该存储key吗?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 需要,BK_BLE_GAP_BOND_KEY_GENERATE_EVT需要存入flash
|
||
|
||
7.10 绑定(bond)和解绑流程?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.绑定:BK_BLE_GAP_BOND_KEY_GENERATE_EVT后需要存入flash
|
||
| 2.解绑:通过bk_ble_gap_bond_dev_list_operation 删除(有限制)
|
||
|
||
7.11 绑定相关还有哪些注意事项?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.绑定涉及到的存储分app部分和sdk部分
|
||
| 2.配对成功后,sdk上报BK_BLE_GAP_BOND_KEY_GENERATE_EVT,app需要将key存入flash中以备下次开机使用。sdk也会存储这部分key但只是在ram里。
|
||
| 3.配对成功后,如果断连并第二次连接,如果对端请求加密、鉴权,结果也会在BK_BLE_GAP_AUTH_CMPL_EVT体现
|
||
| 4.配对失败(或者加密、鉴权失败),auth_cmpl.success为0,则app需要将对应key从flash删除
|
||
| 5.一旦app决定要删除key,则同时还需要调用bk_ble_gap_bond_dev_list_operation删除sdk存储的key
|
||
| 6.使用bk_ble_gap_bond_dev_list_operation时,要保证当前ble不在广播、扫描状态。
|
||
| 7.使用bk_ble_gap_bond_dev_list_operation删除或清空key时,sdk不会断开涉及的链路
|
||
| 8.开机蓝牙初始化后,开广播、扫描前,需要用bk_ble_gap_bond_dev_list_operation将先前BK_BLE_GAP_BOND_KEY_GENERATE_EVT的key添加给sdk
|
||
|
||
7.12 如何修改ble地址?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 1.如果是public,则通过bk_get_mac获取,可以通过bk_set_base_mac设置(具体请参考对应文档)。bk_ble_gap_ext_adv_params_t.own_addr_type = BLE_ADDR_TYPE_PUBLIC; bk_gap_create_conn_params_t.local_addr_type = BLE_ADDR_TYPE_PUBLIC
|
||
| 2.如果是random,对于发adv:bk_ble_gap_set_adv_rand_addr设置地址且bk_ble_gap_ext_adv_params_t.own_addr_type = BLE_ADDR_TYPE_RANDOM; 对于master或扫描:bk_ble_gap_set_rand_addr设置地址且bk_gap_create_conn_params_t.local_addr_type = BLE_ADDR_TYPE_RANDOM
|
||
|
||
7.13 bt、ble public addr能否不一样?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 目前只能一样
|
||
|
||
7.14 rpa是什么意思?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| RPA(resolvable private address): ble一种防止跟踪的随机地址生成、使用机制,基于random addr,开启后adv、scan、connect的local addr由特殊算法生成,在空口上看是随机的。
|
||
|
||
7.15 ble如何使用rpa?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| dm_gatt.c dm_gatts.c dm_gattc.c里,g_dm_gap_use_rpa = 1; s_dm_gattc_local_addr_is_public = 0; s_dm_gatts_local_addr_is_public = 0;
|
||
|
||
7.16 ble如果配对使用,推荐用哪种地址?
|
||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||
|
||
| 只推荐public或rpa,单纯random addr会在与手机交互的场景里出现兼容性问题。
|