ESP32 BT&BLE 双模 蓝 牙共存说明 版本 1.0 版权 2018
关于本 手册 本 文档为 ESP32 BT&BLE 双模蓝 牙共存说明 发布说明 日期版本发布说明 V1.0 首次发布 文档变更更通知 用户可通过乐鑫官 网订阅技术 文档变更更的电 子邮件通知 证书下载 用户可通过乐鑫官 网下载产品证书
目录 1. BT&BLE 共存结构图... 1 2. 流程说明... 2 2.1. 初始化流程... 2 2.2. 广播说明... 3 2.3. 连接流程... 3... 4 3.1. 初始化... 4 3.1.1. 初始化流程... 4 3.1.2. 初始化并使能 controller... 4 3.1.3. 初始化并使能 host... 4 3.1.4. 在 DEV_B 中初始化 BT SPP acceptor 和 GATT server... 5 3.1.5. 在 DEV_A 中初始化 BT SPP initiator 和 GATT client... 7 3.2. 连接... 10 3.3. 数据发送与接收... 11 3.4. 性能说明... 11
1. BT&BLE 共存结构图 1. BT&BLE 共存结构图 图 1-1. BT&BLE 共存系统结构图 Espressif 1/12
2. 流程说明 2. 流程说明 2.1. 初始化流程 DEV_A 上电后将初始化 BT SPP initiator 和 BLE GATT client 功能 初始化完成后, 开始查找经典蓝 牙 (SPP) 设备, 找到设备后进 行行连接 SPP 连接完成后开始搜索 BLE 广播, 搜索到设备后进 行行连接 DEV_A 配置如下图 2-1 所示 图 2-1. DEV_A 配置界 面 DEV_B 上电后将初始化 BT SPP acceptor 和 BLE GATT server 功能 初始化完成后, 经典 蓝 牙开始 inquire scan 和 BLE 广播, 等待被连接 DEV_B 配置如下图 2-2 所示 图 2-2. DEV_B 配置界 面 注意 : 在 menuconfig 中必须选中 Classic Bluetooth SPP 和 GATTC/GATTS 选项 Espressif 2/12
2. 流程说明 2.2. 广播说明 经典蓝 牙 inquiry scan 时的设备名称 (EIR 中 ) 与 BLE 广播时的设备名称可以是同 一个名称, 也可以是不不同名称 若需区分两者的设备名称,BLE 可以使 用 esp_ble_gap_config_adv_data_raw() 函数, 广播特定的蓝 牙设备名称 2.3. 连接流程 DEV_B 初始化完成后会 自动进 入 BT inquiry scan 状态, 并且进 行行 BLE 广播,DEV_A 的 BT SPP initiator 搜索到对应设备后进 行行连接, 然后再开始搜索 BLE 设备, 搜索到设备后进 行行连接 DEV_A 和 DEV_B 之间的通信, 请 见图 2-3 图 2-3. DEV_A 和 DEV_B 测试流程图 Espressif 3/12
3.1. 初始化 3.1.1. 初始化流程 esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); ESP_ERROR_CHECK( ret ); 3.1.2. 初始化并使能 controller esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); ret = esp_bt_controller_init(&bt_cfg); ESP_LOGE(BT_BLE_COEX_TAG, "%s initialize controller failed\n", func ); ret = esp_bt_controller_enable(esp_bt_mode_btdm); ESP_LOGE(BT_BLE_COEX_TAG, "%s enable controller failed\n", func ); 注意 : 这 里里如需使 用双模蓝 牙, 则必须将 controller 初始化为 ESP_BT_MODE_BTDM, 且在 menuconfig 中 选择相应的选项 3.1.3. 初始化并使能 host ret = esp_bluedroid_init(); ESP_LOGE(BT_BLE_COEX_TAG, "%s init bluetooth failed\n", func ); ret = esp_bluedroid_enable(); ESP_LOGE(BT_BLE_COEX_TAG, "%s enable bluetooth failed\n", func ); Espressif 4/12
3.1.4. 在 DEV_B 中初始化 BT SPP acceptor 和 GATT server Controller 和 host 初始化完成后, 则开始初始化 BT SPP 和 BLE GATT server: bt_spp_init() ble_gatts_init() 具体代码如下 : void bt_spp_init(void) { // 注册 SPP 的 callback esp_err_t ret = esp_spp_register_callback(esp_spp_cb); ESP_LOGE(BT_SPP_TAG, "%s spp register failed\n", func ); ret = esp_spp_init(esp_spp_mode); ESP_LOGE(BT_SPP_TAG, "%s spp init failed\n", func ); static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { switch (event) { case ESP_SPP_INIT_EVT: //SPP callback 注册成功后, 将返回 ESP_SPP_INIT_EVT 事件, 在此事件中设置蓝牙名称, 设置经典蓝牙 Scan 模式 ESP_LOGI(BT_SPP_TAG, "ESP_SPP_INIT_EVT\n"); esp_bt_dev_set_device_name(bt_device_name); esp_bt_gap_set_scan_mode(esp_bt_scan_mode_connectable_discoverable); esp_spp_start_srv(sec_mask,role_slave, 0, SPP_SERVER_NAME); case ESP_SPP_DISCOVERY_COMP_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT\n"); //SPP 连接成功后, 将返回 ESP_SPP_OPEN_EVT 事件 case ESP_SPP_OPEN_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_OPEN_EVT\n"); //SPP 断开后, 将返回 ESP_SPP_CLOSE_EVT 事件 case ESP_SPP_CLOSE_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_CLOSE_EVT\n"); case ESP_SPP_START_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_START_EVT\n"); case ESP_SPP_CL_INIT_EVT: Espressif 5/12
ESP_LOGI(BT_SPP_TAG, "ESP_SPP_CL_INIT_EVT\n"); case ESP_SPP_DATA_IND_EVT: #if (SPP_SHOW_MODE == SPP_SHOW_DATA) ESP_LOGI(BT_SPP_TAG, "ESP_SPP_DATA_IND_EVT len=%d handle=%d\n", param->data_ind.len, param->data_ind.handle); esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); #else gettimeofday(&time_new, NULL); data_num += param->data_ind.len; if (time_new.tv_sec - time_old.tv_sec >= 3) { print_speed(); #endif case ESP_SPP_CONG_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_CONG_EVT\n"); case ESP_SPP_WRITE_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_WRITE_EVT\n"); case ESP_SPP_SRV_OPEN_EVT: ESP_LOGI(BT_SPP_TAG, "ESP_SPP_SRV_OPEN_EVT\n"); gettimeofday(&time_old, NULL); default: static void ble_gatts_init(void) { esp_err_t ret = esp_ble_gatts_register_callback(gatts_event_handler); if (ret){ ESP_LOGE(BT_BLE_COEX_TAG, "gatts register error, error code = %x", ret); ret = esp_ble_gap_register_callback(gap_event_handler); if (ret){ ESP_LOGE(BT_BLE_COEX_TAG, "gap register error, error code = %x", ret); ret = esp_ble_gatts_app_register(profile_a_app_id); if (ret){ ESP_LOGE(BT_BLE_COEX_TAG, "gatts app register error, error code = %x", ret); ret = esp_ble_gatts_app_register(profile_b_app_id); if (ret){ ESP_LOGE(BT_BLE_COEX_TAG, "gatts app register error, error code = %x", ret); Espressif 6/12
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500); if (local_mtu_ret){ ESP_LOGE(BT_BLE_COEX_TAG, "set local MTU failed, error code = %x", local_mtu_ret); xtaskcreate(notify_task, "notify_task", 2048, NULL, configmax_priorities - 6, NULL); gatts_semaphore = xsemaphorecreatemutex(); if (gatts_semaphore) { 注意 : GATTS 的相关 API 不不在这 里里 一 一说明, 具体可参考 ESP_IDF gatt_server demo 中的 GATT_SERVER_EXAMPLE_WALKTHROUGH.md 3.1.5. 在 DEV_A 中初始化 BT SPP initiator 和 GATT client Controller 和 host 初始化完成后, 开始初始化 BT SPP initiator 和 BLE GATT client: bt_spp_init(); ble_gattc_init() 具体代码如下 : void bt_spp_init(void) { esp_err_t ret = esp_bt_gap_register_callback(esp_bt_gap_cb); ESP_LOGE(SPP_TAG, "%s gap register failed\n", func ); // 注册 SPP callback, 注册成功后对应的 callback 中会有 ESP_SPP_INIT_EVT 事件回调 ret = esp_spp_register_callback(esp_spp_cb); ESP_LOGE(SPP_TAG, "%s spp register failed\n", func ); ret = esp_spp_init(esp_spp_mode); ESP_LOGE(SPP_TAG, "%s spp init failed\n", func ); static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { switch(event){ // 搜索到的扫描结果会在 ESP_BT_GAP_DISC_RES_EVT 中, 查找符合条件的设备, 然后连接 Espressif 7/12
case ESP_BT_GAP_DISC_RES_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_RES_EVT"); esp_log_buffer_hex(spp_tag, param->disc_res.bda, ESP_BD_ADDR_LEN); for (int i = 0; i < param->disc_res.num_prop; i++){ if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR && get_name_from_eir(param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)){ if (strlen(remote_spp_name) == peer_bdname_len && strncmp(peer_bdname, remote_spp_name, peer_bdname_len) == 0) { memcpy(peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN); esp_spp_start_discovery(peer_bd_addr); esp_bt_gap_cancel_discovery(); case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_STATE_CHANGED_EVT"); case ESP_BT_GAP_RMT_SRVCS_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_RMT_SRVCS_EVT"); case ESP_BT_GAP_RMT_SRVC_REC_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_RMT_SRVC_REC_EVT"); default: static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { switch (event) { case ESP_SPP_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); esp_bt_dev_set_device_name(excample_device_name); esp_bt_gap_set_scan_mode(esp_bt_scan_mode_connectable_discoverable); esp_bt_gap_start_discovery(inq_mode, inq_len, inq_num_rsps); case ESP_SPP_DISCOVERY_COMP_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d scn_num=%d",param- >disc_comp.status, param->disc_comp.scn_num); if (param->disc_comp.status == ESP_SPP_SUCCESS) { esp_spp_connect(sec_mask, role_master, param->disc_comp.scn[0], peer_bd_addr); case ESP_SPP_OPEN_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT"); //BLE 开始扫描 uint32_t duration = 30; Espressif 8/12
esp_ble_gap_start_scanning(duration); esp_spp_write(param->srv_open.handle, SPP_DATA_LEN, spp_data); gettimeofday(&time_old, NULL); case ESP_SPP_CLOSE_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT"); case ESP_SPP_START_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT"); case ESP_SPP_CL_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT"); case ESP_SPP_DATA_IND_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT"); case ESP_SPP_CONG_EVT: #if (SPP_SHOW_MODE == SPP_SHOW_DATA) ESP_LOGI(SPP_TAG, "ESP_SPP_CONG_EVT cong=%d", param->cong.cong); #endif if (param->cong.cong == 0) { esp_spp_write(param->cong.handle, SPP_DATA_LEN, spp_data); case ESP_SPP_WRITE_EVT: #if (SPP_SHOW_MODE == SPP_SHOW_DATA) ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT len=%d cong=%d", param->write.len, param- >write.cong); esp_log_buffer_hex("",spp_data,spp_data_len); #else gettimeofday(&time_new, NULL); data_num += param->write.len; if (time_new.tv_sec - time_old.tv_sec >= 3) { print_speed(); #endif if (param->write.cong == 0) { esp_spp_write(param->write.handle, SPP_DATA_LEN, spp_data); case ESP_SPP_SRV_OPEN_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT"); default: Espressif 9/12
{ // 注册 callback 功能至 GAP esp_err_t ret = esp_ble_gap_register_callback(esp_gap_cb); if (ret){ ESP_LOGE(GATTC_TAG, "%s gap register failed, error code = %x\n", func, ret); // 注册 callback 功能至 GATTC ret = esp_ble_gattc_register_callback(esp_gattc_cb); if(ret){ ESP_LOGE(GATTC_TAG, "%s gattc register failed, error code = %x\n", func, ret); ret = esp_ble_gattc_app_register(profile_a_app_id); if (ret){ ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x\n", func, ret); esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500); if (local_mtu_ret){ ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret); xtaskcreate(gattc_notify_task, "gattc_task", 2048, NULL, 10, NULL); 注意 : GATTC 代码相关 API 说明可以参考 ESP_IDF gatt_client demo 中的 gatt_client_example_walkthrough.md 3.2. 连接 DEV_A 在完成初始化后, 开始搜索经典蓝 牙设备, 搜索到设备后会在 esp_bt_gap_cb 函数中回调 ESP_BT_GAP_DISC_RES_EVT, 符合条件后调 用 esp_spp_start_discovery(peer_bd_addr) 连接设备 设备连接成功后会在 esp_spp_cb 函数中回调 ESP_SPP_OPEN_EVT, 在此事件中开始发送 SPP 数据, 计算 SPP 速率, 并开始搜索 BLE 设备 BLE 搜索到 广播后会回调 esp_gap_cb 函数中的 ESP_GAP_SEARCH_INQ_RES_EVT 事件, 找到符合条件的设备后使 用 esp_ble_gattc_open() 连接, 连接成功后会回调 gattc_profile_event_handler 中的 ESP_GATTC_CONNECT_EVT 事件 BLE 连接成功后会注册对端设备 GATT notification, 为后续的数据发送做准备 Espressif 10/12
3.3. 数据发送与接收 DEV_A SPP 连接以后使 用 esp_spp_write() 函数发送 SPP 数据并计算速率 DEV_A BLE 连接成功后, 注册对端设备的 GATT notification, 并开始监听, 接收 GATT notification 数据后会回调 gattc_profile_event_handler 中的 ESP_GATTC_NOTIFY_EVT 事件, 统计数据 长度并计算速率 DEV_B 的 SPP 被连接以后, 等待对端设备发送 SPP 数据 DEV_B BLE 被连接后, DEV_A 会使能 DEV_B 的 GATT notification DEV_B 在 GATTS init 时初始化了了 notify_task, 当 notify_task 检测到 GATT notification 被使能后, 调 用 esp_ble_gatts_send_indicate() 发送 GATT notification 数据 3.4. 性能说明 BT SPP 和 BLE 连接成功以后会 自动发送数据, 计算吞吐率 单独运 行行 BT SPP 的速率为 230 KB/s 左右, 单独运 行行 BLE 的速率为 40 KB/s 左右 ( 优化后有 90 KB/s) 目前, 在 BLE 中仅计算了了 GATT notification 的速率, 使 用当前的参数速率 BT SPP 120 KB/s BLE GATT notification 30 KB/s 左右,BT SPP 和 BLE GATT notification 的速率是可调的 具体可以通过调节 BLE 的连接参数, 发送 GATT notification 的 长度和频率调整 SPP 和 GATT 的吞吐量量 log, 请 见图 3-1 图 3-1. SPP 和 GATT 的吞吐量量 log Espressif 11/12
免责申明和版权公告本 文中的信息, 包括供参考的 URL 地址, 如有变更更, 恕不不另 行行通知 文档 按现状 提供, 不不负任何担保责任, 包括对适销性 适 用于特定 用途或 非侵权性的任何担保, 和任何提案 规格或样品在他处提到的任何担保 本 文档不不负任何责任, 包括使 用本 文档内信息产 生的侵犯任何专利利权 行行为的责任 本 文档在此未以禁 止反 言或其他 方式授予任何知识产权使 用许可, 不不管是明示许可还是暗示许可 Wi-Fi 联盟成员标志归 Wi-Fi 联盟所有 蓝 牙标志是 Bluetooth SIG 的注册商标 乐鑫 IoT 团队 www.espressif.com 文中提到的所有商标名称 商标和注册商标均属其各 自所有者的财产, 特此声 明 版权归 2018 乐鑫所有 保留留所有权利利