diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index fb0c34f81b..fbdb8178bd 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -1087,9 +1087,13 @@ menu "BT DEBUG LOG LEVEL" endmenu #BT DEBUG LOG LEVEL config BT_ACL_CONNECTIONS - int "BT/BLE MAX ACL CONNECTIONS(1~9)" + int "BT/BLE MAX ACL CONNECTIONS" depends on BT_BLUEDROID_ENABLED - range 1 9 + range 1 2 if IDF_TARGET_ESP32C2 + range 1 9 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 + range 1 15 if IDF_TARGET_ESP32H2 + range 1 50 + default 2 if IDF_TARGET_ESP32C2 default 4 help Maximum BT/BLE connection count. The ESP32-C3/S3 chip supports a maximum of 10 instances, diff --git a/components/bt/host/bluedroid/api/esp_gap_ble_api.c b/components/bt/host/bluedroid/api/esp_gap_ble_api.c index 5cd266eaa8..42724d2245 100644 --- a/components/bt/host/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/host/bluedroid/api/esp_gap_ble_api.c @@ -1759,6 +1759,42 @@ esp_err_t esp_ble_gap_set_csa_support(uint8_t csa_select) return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } + +#if CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION +esp_err_t esp_ble_gap_set_common_factor(uint32_t common_factor) +{ + esp_ble_vendor_cmd_params_t vs_cmd; + uint8_t cmd_param[5]; + + cmd_param[0] = common_factor & 0xFF; + cmd_param[1] = (common_factor >> 8) & 0xFF; + cmd_param[2] = (common_factor >> 16) & 0xFF; + cmd_param[3] = (common_factor >> 24) & 0xFF; + cmd_param[4] = 0x01; + vs_cmd.opcode = 0xFD0F; + vs_cmd.param_len = 5; + vs_cmd.p_param_buf = cmd_param; + + return esp_ble_gap_vendor_command_send(&vs_cmd); +} + +esp_err_t esp_ble_gap_set_sch_len(uint8_t role, uint32_t len) +{ + esp_ble_vendor_cmd_params_t vs_cmd; + uint8_t cmd_param[5]; + + cmd_param[0] = role; + cmd_param[1] = len & 0xFF; + cmd_param[2] = (len >> 8) & 0xFF; + cmd_param[3] = (len >> 16) & 0xFF; + cmd_param[4] = (len >> 24) & 0xFF; + vs_cmd.opcode = 0xFD10; + vs_cmd.param_len = 5; + vs_cmd.p_param_buf = cmd_param; + + return esp_ble_gap_vendor_command_send(&vs_cmd); +} +#endif // CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION #endif // (BLE_VENDOR_HCI_EN == TRUE) #if (BLE_FEAT_POWER_CONTROL_EN == TRUE) diff --git a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h index a75ae93999..409d76c843 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -246,6 +246,8 @@ typedef enum { ESP_GAP_BLE_SUBRATE_CHANGE_EVT, /*!< when Connection Subrate Update procedure has completed and some parameters of the specified connection have changed, the event comes */ ESP_GAP_BLE_SET_HOST_FEATURE_CMPL_EVT, /*!< When host feature set complete, the event comes */ ESP_GAP_BLE_READ_CHANNEL_MAP_COMPLETE_EVT, /*!< When BLE channel map result is received, the event comes */ + ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT, /*!< When set the common factor complete, the event comes */ + ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT, /*!< When set the scheduling length complete, the event comes */ ESP_GAP_BLE_EVT_MAX, /*!< when maximum advertising event complete, the event comes */ } esp_gap_ble_cb_event_t; @@ -1748,6 +1750,18 @@ typedef union { uint8_t param_len; /*!< The length of the event parameter buffer (for internal use only) */ uint8_t *param_buf; /*!< The pointer of the event parameter buffer (for internal use only) */ } vendor_hci_evt; /*!< Event parameter of ESP_GAP_BLE_VENDOR_HCI_EVT */ + /** + * @brief ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT + */ + struct ble_set_common_factor_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate common factor set operation success status */ + } set_common_factor_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT */ + /** + * @brief ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT + */ + struct ble_set_sch_len_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate scheduling length set operation success status */ + } set_sch_len_cmpl; /*!< Event parameter of ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT */ #endif // #if (BLE_VENDOR_HCI_EN == TRUE) #if (BLE_FEAT_POWER_CONTROL_EN == TRUE) /** @@ -3090,6 +3104,46 @@ esp_err_t esp_ble_gap_set_csa_support(uint8_t csa_select); */ esp_err_t esp_ble_gap_set_vendor_event_mask(esp_ble_vendor_evt_mask_t event_mask); +/** + * @brief This function is used to set a common connection interval factor for multiple central-role connections. + * When multiple BLE connections in the central role exist, it is recommended that + * each connection interval be configured to either the same value or an integer + * multiple of the others. And use this function to set the common factor of all + * connection intervalsin the controller. The controller will then arrange the scheduling + * of each connection based on this factor to minimize or avoid connection conflicts. + * + * @note - This function is used in multi-connection scenarios. + * - This function takes effect only when the connection role is central. + * - This function only needs to be called once and before establishing the connection. + * + * @param[in] common_factor: The common connection interval factor (in units of 625us) + * used for scheduling across all central-role connections. + * + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_set_common_factor(uint32_t common_factor); + +/** + * @brief This function is used to Set the scheduling protection time for specific LE role. + * It can be used to configures the minimum protection time to be reserved for a + * connection's TX/RX operations, ensuring that a complete transmission and + * reception cycle is not interrupted. It helps prevent disconnect in scenarios + * with multiple connections competing for time slots. + * + * @note - This function is used in multi-connection scenarios. + * - This function must be called before establishing the connection. + * + * @param[in] role: 0: Central 1: Peripheral + * @param[in] len: The protection time length of the corresponding role (in units of us) + * + * @return + * - ESP_OK : success + * - other : failed + */ +esp_err_t esp_ble_gap_set_sch_len(uint8_t role, uint32_t len); + /** * @brief This function is used to read the current and maximum transmit power levels of the local Controller. * diff --git a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c index 07067748b1..2817365583 100644 --- a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -1441,10 +1441,23 @@ static void btc_ble_vendor_hci_cmd_complete_callback(tBTA_VSC_CMPL *p_param) msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_GAP_BLE; msg.act = ESP_GAP_BLE_VENDOR_CMD_COMPLETE_EVT; + if (!param_invalid) { - param.vendor_cmd_cmpl.opcode = p_param->opcode; - param.vendor_cmd_cmpl.param_len = p_param->param_len; - param.vendor_cmd_cmpl.p_param_buf = p_param->p_param_buf; + switch (p_param->opcode) { + case 0xFD0F: + msg.act = ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT; + param.set_common_factor_cmpl.status = p_param->p_param_buf[0]; + break; + case 0xFD10: + msg.act = ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT; + param.set_sch_len_cmpl.status = p_param->p_param_buf[0]; + break; + default: + param.vendor_cmd_cmpl.opcode = p_param->opcode; + param.vendor_cmd_cmpl.param_len = p_param->param_len; + param.vendor_cmd_cmpl.p_param_buf = p_param->p_param_buf; + break; + } } else { if (p_param) { param.vendor_cmd_cmpl.opcode = p_param->opcode; @@ -1455,7 +1468,11 @@ static void btc_ble_vendor_hci_cmd_complete_callback(tBTA_VSC_CMPL *p_param) param.vendor_cmd_cmpl.p_param_buf = NULL; } - ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), btc_gap_ble_cb_deep_copy, btc_gap_ble_cb_deep_free); + if (msg.act == ESP_GAP_BLE_VENDOR_CMD_COMPLETE_EVT) { + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), btc_gap_ble_cb_deep_copy, btc_gap_ble_cb_deep_free); + } else { + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), NULL, NULL); + } if (ret != BT_STATUS_SUCCESS) { BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__); diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 2051adf8c7..6b19c01198 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -1057,8 +1057,12 @@ /* The number of security records for peer devices. 15 AS Default*/ #ifndef BTM_SEC_MAX_DEVICE_RECORDS +#if (UC_BT_SMP_MAX_BONDS < UC_BT_ACL_CONNECTIONS) +#define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_ACL_CONNECTIONS +#else #define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_SMP_MAX_BONDS #endif +#endif #if BTA_SDP_INCLUDED #define BTM_SDP_SEC_SERVICE_RECORDS 1 diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c index 0644ab7058..5565169a78 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -78,7 +78,7 @@ static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x0 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static UINT32 gatt_tcb_id; +static UINT8 gatt_tcb_id[GATT_MAX_PHY_CHANNEL / 8 + 1]; /******************************************************************************* ** @@ -1005,7 +1005,7 @@ UINT8 gatt_find_i_tcb_free(void) UINT8 i = 0, j = GATT_INDEX_INVALID; for (i = 0; i < GATT_MAX_PHY_CHANNEL; i ++) { - if (!((1 << i) & gatt_tcb_id)) { + if (!(gatt_tcb_id[i >> 3] & (1 << (i & 0x7)))) { j = i; break; } @@ -1030,9 +1030,9 @@ tGATT_TCB *gatt_tcb_alloc(UINT8 tcb_idx) /* Add tcb block to list in gatt_cb */ list_append(gatt_cb.p_tcb_list, p_tcb); /* Update tcb id */ - gatt_tcb_id |= 1 << tcb_idx; - } else if(p_tcb) { - osi_free(p_tcb); + gatt_tcb_id[tcb_idx >> 3] |= (1 << (tcb_idx & 0x7)); + } else if (p_tcb) { + osi_free(p_tcb); p_tcb = NULL; } return p_tcb; @@ -1051,7 +1051,7 @@ void gatt_tcb_free( tGATT_TCB *p_tcb) { UINT8 tcb_idx = p_tcb->tcb_idx; if (list_remove(gatt_cb.p_tcb_list, p_tcb)) { - gatt_tcb_id &= ~(1 << tcb_idx); + gatt_tcb_id[tcb_idx >> 3] &= ~(1 << (tcb_idx & 0x7)); } } /******************************************************************************* @@ -1078,9 +1078,9 @@ tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport) } if (i != GATT_INDEX_INVALID) { p_tcb = gatt_tcb_alloc(i); - if (!p_tcb) { - return NULL; - } + if (!p_tcb) { + return NULL; + } if (allocated) { memset(p_tcb, 0, sizeof(tGATT_TCB)); p_tcb->pending_enc_clcb = fixed_queue_new(QUEUE_SIZE_MAX); diff --git a/components/bt/host/bluedroid/stack/include/stack/gatt_api.h b/components/bt/host/bluedroid/stack/include/stack/gatt_api.h index 6c63db6238..002cb16e33 100644 --- a/components/bt/host/bluedroid/stack/include/stack/gatt_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/gatt_api.h @@ -151,7 +151,11 @@ typedef UINT16 tGATT_DISCONN_REASON; #define GATT_INVALID_CONN_ID 0xFFFF #ifndef GATT_CL_MAX_LCB -#define GATT_CL_MAX_LCB 12 // 22 +#if (GATT_MAX_PHY_CHANNEL > 12) +#define GATT_CL_MAX_LCB GATT_MAX_PHY_CHANNEL +#else +#define GATT_CL_MAX_LCB 12 +#endif #endif #ifndef GATT_MAX_SCCB diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_link.c b/components/bt/host/bluedroid/stack/l2cap/l2c_link.c index 1af7e8b3d2..fbad1cde15 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_link.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_link.c @@ -1077,32 +1077,34 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) L2CAP_TRACE_ERROR("l2cab is_cong_cback_context"); return; } - + list_node_t* start_p_node = NULL; /* If we are in a scenario where there are not enough buffers for each link to ** have at least 1, then do a round-robin for all the LCBs */ if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) ) { list_node_t *p_node = NULL; - tL2C_LCB *p_lcb_cur = NULL; + tL2C_LCB *p_lcb_cur = NULL; if (p_lcb == NULL) { p_node = list_begin(l2cb.p_lcb_pool); - p_lcb = list_node(p_node); + p_lcb = list_node(p_node); } else if (!single_write) { - for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { - p_lcb_cur = list_node(p_node); - if (p_lcb_cur == p_lcb) { - p_node = list_next(p_node); - if (p_node) { - p_lcb = list_node(p_node); - } - break; - } - } + for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) { + p_lcb_cur = list_node(p_node); + if (p_lcb_cur == p_lcb) { + p_node = list_next(p_node); + start_p_node = p_node; + break; + } + } } /* Loop through, starting at the next */ - for ( ; p_node; p_node = list_next(p_node)) { - p_lcb = list_node(p_node); + do { + /* Check for wraparound */ + if (p_node == list_end(l2cb.p_lcb_pool)) { + p_node = list_begin(l2cb.p_lcb_pool); + } + p_lcb = list_node(p_node); #if (BLE_INCLUDED == TRUE) L2CAP_TRACE_DEBUG("window = %d,robin_unacked = %d,robin_quota=%d",l2cb.controller_le_xmit_window,l2cb.ble_round_robin_unacked,l2cb.ble_round_robin_quota); #endif ///BLE_INCLUDED == TRUE @@ -1118,20 +1120,22 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) #else )) #endif ///BLE_INCLUDED == TRUE + { break; - - + } /* Check for wraparound */ - if (p_node == list_end(l2cb.p_lcb_pool)) { + if (p_node == list_end(l2cb.p_lcb_pool)) { p_node = list_begin(l2cb.p_lcb_pool); - p_lcb = list_node(p_node); - } + p_lcb = list_node(p_node); + } + L2CAP_TRACE_DEBUG("in_use=%d,segment_being_sent=%d,link_state=%d,link_xmit_quota=%d",p_lcb->in_use,p_lcb->partial_segment_being_sent,p_lcb->link_state,p_lcb->link_xmit_quota); if ( (!p_lcb->in_use) || (p_lcb->partial_segment_being_sent) || (p_lcb->link_state != LST_CONNECTED) || (p_lcb->link_xmit_quota != 0) || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) { + p_node = list_next(p_node); continue; } @@ -1148,7 +1152,8 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL) { l2c_link_send_to_lower (p_lcb, p_buf); } - } + p_node = list_next(p_node); + } while (p_node != start_p_node); /* If we finished without using up our quota, no need for a safety check */ if ( (l2cb.controller_xmit_window > 0) diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/CMakeLists.txt new file mode 100644 index 0000000000..95bdb31e84 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_multi_conn_cent) diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/README.md b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/README.md new file mode 100644 index 0000000000..8e86afbdbe --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/README.md @@ -0,0 +1,76 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | + +# BLE Multiple Connection Central Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +**This example relies on the BLE controller. please use the chip modules listed under Supported Targets.** + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using: + +```bash +idf.py set-target +``` + +### Hardware Required + +* At least two development board with ESP32-C6/ESP32-H2 SoC (e.g., ESP32-C6-DevKitC, ESP32-H2-DevKitC, etc.) +* USB cable for Power supply and programming + +See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +This is the console output on successful connection: + +``` +I (395) BLE_INIT: ble controller commit:[d2d70d4] +I (405) BLE_INIT: Bluetooth MAC: 60:55:f9:f6:97:de +I (405) phy_init: phy_version 331,5b89037,Mar 3 2025,16:01:12 +I (465) phy: libbtbb version: ec2ecba, Mar 3 2025, 16:01:27 +I (475) PEER_MANAGER: peer manager init complete +I (485) MULTI_CONN_CENT: Common factor set, status 0 +I (485) MULTI_CONN_CENT: Random address set, status 0, addr e6:dd:4b:da:b8:06 +I (485) MULTI_CONN_CENT: GATT client register, status 0, app_id 0, gattc_if 3 +I (495) MULTI_CONN_CENT: Scanning params set, status 0 +I (495) MULTI_CONN_CENT: Random address set, status 0, addr ff:ed:64:bb:b0:f2 +I (505) MULTI_CONN_CENT: Scanning start, status 0 +I (505) MULTI_CONN_CENT: GATT server register, status 0, app_id 0, gatts_if 4 +I (515) MULTI_CONN_CENT: The number handle = 3 +I (515) MULTI_CONN_CENT: Advertising data set, status 0 +I (535) MULTI_CONN_CENT: Create connection, remote da:20:da:dd:c8:9f +I (535) MULTI_CONN_CENT: Scanning stop +I (535) MULTI_CONN_CENT: Scheduling length set, status 0 +I (1275) MULTI_CONN_CENT: Connected, conn_id 0, remote da:20:da:dd:c8:9f, total 1 +I (1275) PEER_MANAGER: peer added to list +I (1275) MULTI_CONN_CENT: Open, conn_id 0, status 0 +I (1275) MULTI_CONN_CENT: Random address set, status 0, addr f1:91:5d:0b:22:3a +I (1285) MULTI_CONN_CENT: Scanning start, status 0 +I (1325) MULTI_CONN_CENT: Create connection, remote e5:4d:be:54:de:1a +I (1335) MULTI_CONN_CENT: Scanning stop +I (1335) MULTI_CONN_CENT: Scheduling length set, status 0 +I (2015) MULTI_CONN_CENT: Connected, conn_id 1, remote e5:4d:be:54:de:1a, total 2 +I (2015) PEER_MANAGER: peer added to list +I (2015) MULTI_CONN_CENT: Open, conn_id 1, status 0 +I (2015) MULTI_CONN_CENT: Random address set, status 0, addr e8:8e:72:3f:6e:29 +I (2025) MULTI_CONN_CENT: Scanning start, status 0 +I (2075) MULTI_CONN_CENT: Create connection, remote eb:06:b7:d0:d4:48 +I (2075) MULTI_CONN_CENT: Scanning stop +I (2075) MULTI_CONN_CENT: Scheduling length set, status 0 +I (2755) MULTI_CONN_CENT: Connected, conn_id 2, remote eb:06:b7:d0:d4:48, total 3 +``` + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/CMakeLists.txt new file mode 100644 index 0000000000..2dbbb22115 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "peer_manager.c" "ble_multiconn_cent_demo.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c new file mode 100644 index 0000000000..49bf214bcf --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c @@ -0,0 +1,727 @@ +/* + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include "ble_multiconn_cent_demo.h" +#include "esp_log.h" + +#define DEMO_TAG "MULTI_CONN_CENT" + +#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED +#define BLE50_SUPPORTED (CONFIG_BT_BLE_50_FEATURES_SUPPORTED) +#else +#define BLE50_SUPPORTED (0) +#endif + +#define EXT_SCAN_DURATION 0 +#define EXT_SCAN_PERIOD 0 +static const char remote_target_name[] = "esp-multi-conn"; + +#define GATTC_PROFILE_NUM 1 +#define GATTC_PROFILE_APP_ID 0 +#define GATTS_PROFILE_NUM 1 +#define GATTS_PROFILE_APP_ID 0 + +#define BLE_PEER_MAX_NUM MAX_CONN_NUM - 1 +#define BLE_PREF_EVT_LEN_MS (5) +#define BLE_PREF_CONN_ITVL_MS ((BLE_PEER_MAX_NUM) * BLE_PREF_EVT_LEN_MS) +#define BLE_PREF_CE_LEN (BLE_PREF_EVT_LEN_MS * 1000 / 625) + +static uint8_t multi_conn_num = 0; +static uint16_t prph_conn_id = 0xFFFF; // ID of connection with peripheral role +static int advertising_state = DISABLED; +static int scan_state = DISABLED; +static SemaphoreHandle_t restart_scan_sem = NULL; + +#define MULTICONN_PROFILE_NUM 1 +#define MULTICONN_PROFILE_APP_ID 0 +#define ADV_HANDLE_INST 0 +#define ADV_HANDLE_NUM 1 +#define ADV_DURATION 0 +#define ADV_MAX_EVTS 0 +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 +#define SVC_INST_ID 0 +#define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) + +static uint8_t char_value[GATTS_DEMO_CHAR_VAL_LEN_MAX] = {0}; +static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; +static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; +static const uint8_t char_prop_write = ESP_GATT_CHAR_PROP_BIT_WRITE; +static const uint8_t remote_svc_uuid[] = {0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59}; +static const uint8_t gatt_svr_chr_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33}; + +static uint8_t adv_data_raw[] = { + 0x02, ESP_BLE_AD_TYPE_FLAG, 0x06, + 0x12, ESP_BLE_AD_TYPE_NAME_CMPL, 'e', 's', 'p', '-', 'b', 'l', 'e', '-', 'r', 'o', 'l', 'e', '-', 'c', 'o', 'e', 'x', +}; + +static esp_gattc_char_elem_t char_elem_result; +static esp_bd_addr_t new_rand_addr; +static esp_bd_addr_t adv_rand_addr; + +static esp_bt_uuid_t remote_svc_uuid_t = { + .len = ESP_UUID_LEN_128, + .uuid = { + .uuid128 = {0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59}, + }, +}; + +static esp_bt_uuid_t char_uuid_t = { + .len = ESP_UUID_LEN_128, + .uuid = { + .uuid128 = {0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33}, + }, +}; + +#if (BLE50_SUPPORTED == 0) +static esp_ble_scan_params_t legacy_scan_params = { + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_type = BLE_SCAN_TYPE_PASSIVE, + .scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE, + .scan_interval = 800, + .scan_window = 320, + +}; +#else +static esp_ble_ext_scan_params_t ext_scan_params = { + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE, + .cfg_mask = ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK, + .uncoded_cfg = {BLE_SCAN_TYPE_PASSIVE, 800, 320}, +}; +#endif + +const static esp_ble_conn_params_t phy_1m_conn_params = { + .scan_interval = 480, + .scan_window = 160, + .interval_min = BLE_PREF_CONN_ITVL_MS * 1000 / 1250, + .interval_max = BLE_PREF_CONN_ITVL_MS * 1000 / 1250, + .latency = 0, + .supervision_timeout = 600, + .min_ce_len = BLE_PREF_CE_LEN, + .max_ce_len = BLE_PREF_CE_LEN, +}; + +#if (BLE50_SUPPORTED == 1) +const static esp_ble_conn_params_t phy_2m_conn_params = { + .scan_interval = 480, + .scan_window = 160, + .interval_min = BLE_PREF_CONN_ITVL_MS * 1000 / 1250, + .interval_max = BLE_PREF_CONN_ITVL_MS * 1000 / 1250, + .latency = 0, + .supervision_timeout = 600, + .min_ce_len = BLE_PREF_CE_LEN, + .max_ce_len = BLE_PREF_CE_LEN, +}; + +const static esp_ble_conn_params_t phy_coded_conn_params = { + .scan_interval = 480, + .scan_window = 160, + .interval_min = BLE_PREF_CONN_ITVL_MS * 1000 / 1250, + .interval_max = BLE_PREF_CONN_ITVL_MS * 1000 / 1250, + .latency = 0, + .supervision_timeout = 600, + .min_ce_len = BLE_PREF_CE_LEN, + .max_ce_len = BLE_PREF_CE_LEN, +}; +#endif + +struct gattc_profile_inst +{ + esp_gattc_cb_t gattc_cb; + uint16_t gattc_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_start_handle; + uint16_t service_end_handle; + uint16_t notify_char_handle; + esp_bd_addr_t remote_bda; +}; + +struct gatts_profile_inst +{ + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_handle; + esp_gatt_srvc_id_t service_id; + uint16_t char_handle; + esp_bt_uuid_t char_uuid; + esp_gatt_perm_t perm; + esp_gatt_char_prop_t property; + uint16_t descr_handle; + esp_bt_uuid_t descr_uuid; +}; + +#if (BLE50_SUPPORTED == 0) +esp_ble_adv_params_t legacy_adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x20, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + +}; +#else +esp_ble_gap_ext_adv_params_t ext_adv_params = { + .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE, + .interval_min = 0x20, + .interval_max = 0x20, + .channel_map = ADV_CHNL_ALL, + .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + .primary_phy = ESP_BLE_GAP_PHY_1M, + .max_skip = 0, + .secondary_phy = ESP_BLE_GAP_PHY_1M, + .sid = 0, + .scan_req_notif = false, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE, +}; +#endif + +static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +static void esp_gatts_cb(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +static struct gattc_profile_inst gattc_profile_tab[GATTC_PROFILE_NUM] = { + [GATTC_PROFILE_APP_ID] = { + .gattc_cb = gattc_profile_event_handler, + .gattc_if = ESP_GATT_IF_NONE, + }, +}; + +static struct gatts_profile_inst gatts_profile_tab[GATTS_PROFILE_NUM] = { + [GATTS_PROFILE_APP_ID] = { + .gatts_cb = gatts_profile_event_handler, + .gatts_if = ESP_GATT_IF_NONE, + }}; + +/* Full Database Description - Used to add attributes into the database */ +static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] = +{ + // Service Declaration + [IDX_SVC] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(remote_svc_uuid), (uint8_t *)&remote_svc_uuid}}, + + // Characteristic Declaration + [IDX_CHAR_A] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_write}}, + // Characteristic Value + [IDX_CHAR_VAL_A] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_128, (uint8_t *)gatt_svr_chr_uuid, ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)&char_value}}, +}; +static uint16_t profile_handle_table[HRS_IDX_NB]; + +#if (BLE50_SUPPORTED == 1) +static esp_ble_gap_ext_adv_t ext_adv[1] = { + [0] = {ADV_HANDLE_INST, ADV_DURATION, ADV_MAX_EVTS}, +}; +#endif + +static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + switch (event) { + case ESP_GATTC_REG_EVT: + ESP_LOGI(DEMO_TAG, "GATT client register, status %d, app_id %d, gattc_if %d", param->reg.status, param->reg.app_id, gattc_if); +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_set_ext_scan_params(&ext_scan_params); +#else + esp_ble_gap_set_scan_params(&legacy_scan_params); +#endif + break; + case ESP_GATTC_CONNECT_EVT: + if (param->connect.link_role == 0) { + ESP_LOGI(DEMO_TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR", total %u", param->connect.conn_id, + ESP_BD_ADDR_HEX(param->connect.remote_bda), ++multi_conn_num); + Peer new_peer; + new_peer.conn_id = param->connect.conn_id; + new_peer.conn_handle = param->connect.conn_handle; + new_peer.gattc_if = gattc_profile_tab->gattc_if; + new_peer.char_handle = 0xFFFF; + memcpy(new_peer.peer_addr, param->connect.remote_bda, sizeof(esp_bd_addr_t)); + peer_add(&new_peer); + if (multi_conn_num < BLE_PEER_MAX_NUM && scan_state == DISABLED) { + scan_state = PENDING; + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)new_rand_addr); + } else { + ESP_LOGI(DEMO_TAG, "BLE peer max number reached"); +#if (BLE50_SUPPORTED == 0) + advertising_state = PENDING; + esp_ble_gap_addr_create_static(adv_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)adv_rand_addr); +#endif + } + } + break; + case ESP_GATTC_DISCONNECT_EVT: + ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x, total %u", + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, + (multi_conn_num ? --multi_conn_num : multi_conn_num)); + if (param->disconnect.conn_id != prph_conn_id) { + Peer *peer = find_peer(param->disconnect.conn_id); + if (peer) { + peer_remove(peer->conn_id); + } + xSemaphoreGive(restart_scan_sem); + } else { + prph_conn_id = 0xFFFF; + } + break; + case ESP_GATTC_OPEN_EVT: + ESP_LOGI(DEMO_TAG, "Open, conn_id %d, status %d", param->open.conn_id, param->open.status); + break; + case ESP_GATTC_CLOSE_EVT: + break; + case ESP_GATTC_DIS_SRVC_CMPL_EVT: + ESP_LOGI(DEMO_TAG, "Service discover complete, conn_id %d", param->dis_srvc_cmpl.conn_id); + esp_ble_gattc_search_service(gattc_if, param->dis_srvc_cmpl.conn_id, &remote_svc_uuid_t); + break; + case ESP_GATTC_SEARCH_RES_EVT: + ESP_LOGI(DEMO_TAG, "Service search result, conn_id %d", param->search_res.conn_id); + if (param->search_res.srvc_id.uuid.len == ESP_UUID_LEN_128 && !memcmp(param->search_res.srvc_id.uuid.uuid.uuid128, remote_svc_uuid_t.uuid.uuid128, ESP_UUID_LEN_128)) { + uint16_t count = 1; + esp_gatt_status_t ret_status = esp_ble_gattc_get_char_by_uuid(gattc_if, + param->search_res.conn_id, + param->search_res.start_handle, + param->search_res.end_handle, + char_uuid_t, + &char_elem_result, + &count); + if (ret_status != ESP_GATT_OK) { + ESP_LOGE(DEMO_TAG, "esp_ble_gattc_get_char_by_uuid error, conn_id = %d, status = %d", param->search_res.conn_id, ret_status); + break; + } + Peer *target_peer = find_peer(param->search_res.conn_id); + if (target_peer) { + target_peer->char_handle = char_elem_result.char_handle; + } else { + ESP_LOGE(DEMO_TAG, "can't find peer"); + break; + } + } + break; + case ESP_GATTC_SEARCH_CMPL_EVT: + ESP_LOGI(DEMO_TAG, "Service search complete, status %d, conn_id %d", param->search_cmpl.status, param->search_cmpl.conn_id); + break; + case ESP_GATTC_WRITE_CHAR_EVT: + ESP_LOGI(DEMO_TAG, "Characteristic write, conn_id %u, status %d", param->write.conn_id, param->write.status); + break; + case ESP_GATTC_NOTIFY_EVT: + if (param->notify.is_notify){ + ESP_LOGI(DEMO_TAG, "Notification received, conn_id %u", param->notify.conn_id); + }else{ + ESP_LOGI(DEMO_TAG, "Indication received, conn_id %u", param->notify.conn_id); + } + ESP_LOG_BUFFER_HEX(DEMO_TAG, param->notify.value, param->notify.value_len); + break; + default: + break; + } +} + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + switch (event) { + case ESP_GATTS_REG_EVT: + ESP_LOGI(DEMO_TAG, "GATT server register, status %d, app_id %d, gatts_if %d", param->reg.status, param->reg.app_id, gatts_if); +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_ext_adv_set_params(ADV_HANDLE_INST, &ext_adv_params); +#else + esp_ble_gap_config_adv_data_raw(adv_data_raw, sizeof(adv_data_raw)); +#endif + esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID); + break; + case ESP_GATTS_WRITE_EVT: + ESP_LOGI(DEMO_TAG, "Characteristic write received, conn_id %u, value", param->write.conn_id); + ESP_LOG_BUFFER_HEX(DEMO_TAG, param->write.value, param->write.len); + traverse_send_peer(param->write.len, param->write.value); + break; + case ESP_GATTS_CONNECT_EVT: + if (param->connect.link_role == 1) { + ESP_LOGI(DEMO_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR", total %u", + param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda), ++multi_conn_num); + prph_conn_id = param->connect.conn_id; + advertising_state = DISABLED; +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_ext_adv_stop(1, &ext_adv[0].instance); +#else + esp_ble_gap_stop_advertising(); +#endif + } + break; + case ESP_GATTS_OPEN_EVT: + break; + case ESP_GATTS_DISCONNECT_EVT: + if (param->disconnect.conn_id == prph_conn_id) { + ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x, total %u", + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, + (multi_conn_num ? --multi_conn_num : multi_conn_num)); + advertising_state = PENDING; +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_addr_create_static(adv_rand_addr); + esp_ble_gap_ext_adv_set_rand_addr(ADV_HANDLE_INST, adv_rand_addr); +#else + esp_ble_gap_addr_create_static(adv_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)adv_rand_addr); +#endif + } + break; + case ESP_GATTS_CREAT_ATTR_TAB_EVT: + ESP_LOGI(DEMO_TAG, "The number handle = %x", param->add_attr_tab.num_handle); + if (param->create.status == ESP_GATT_OK) { + if (param->add_attr_tab.num_handle == HRS_IDX_NB) { + memcpy(profile_handle_table, param->add_attr_tab.handles, + sizeof(profile_handle_table)); + esp_ble_gatts_start_service(profile_handle_table[IDX_SVC]); + } else { + ESP_LOGE(DEMO_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", + param->add_attr_tab.num_handle, HRS_IDX_NB); + } + } else { + ESP_LOGE(DEMO_TAG, " Create attribute table failed, status %x", param->create.status); + } + break; + default: + break; + } +} + +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + if (event == ESP_GATTC_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gattc_profile_tab[param->reg.app_id].gattc_if = gattc_if; + } else { + ESP_LOGE(DEMO_TAG, "reg app failed, app_id %d", param->reg.app_id); + return; + } + } + do { + int idx; + for (idx = 0; idx < GATTC_PROFILE_NUM; idx++) { + if (gattc_if == ESP_GATT_IF_NONE || gattc_if == gattc_profile_tab[idx].gattc_if) { + if (gattc_profile_tab[idx].gattc_cb) { + gattc_profile_tab[idx].gattc_cb(event, gattc_if, param); + } + } + } + } while (0); +} + +static void esp_gatts_cb(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gatts_profile_tab[0].gatts_if = gatts_if; + } else { + ESP_LOGI(DEMO_TAG, "Reg app failed, app_id %04x, status %d", + param->reg.app_id, + param->reg.status); + return; + } + } + + do { + int idx; + for (idx = 0; idx < MULTICONN_PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == gatts_profile_tab[idx].gatts_if) { + if (gatts_profile_tab[idx].gatts_cb) { + gatts_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { + case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: + ESP_LOGI(DEMO_TAG, "Random address set, status %d, addr "ESP_BD_ADDR_STR"", + param->set_rand_addr_cmpl.status, ESP_BD_ADDR_HEX(new_rand_addr)); + if (advertising_state == PENDING && prph_conn_id == 0xFFFF) { +#if (BLE50_SUPPORTED == 0) + advertising_state = ENABLED; + esp_ble_gap_start_advertising(&legacy_adv_params); +#endif + } + if (scan_state == PENDING && multi_conn_num < BLE_PEER_MAX_NUM) { + scan_state = ENABLED; +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD); +#else + esp_ble_gap_start_scanning(0); +#endif + } + break; +#if (BLE50_SUPPORTED == 1) + case ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended scanning params set, status %d", param->set_ext_scan_params.status); + scan_state = PENDING; + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)new_rand_addr); + break; + case ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended scanning start, status %d", param->ext_scan_start.status); + break; + case ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended scanning stop"); + break; + case ESP_GAP_BLE_EXT_ADV_REPORT_EVT: { + uint8_t *adv_name = NULL; + uint8_t adv_name_len = 0; + adv_name = esp_ble_resolve_adv_data_by_type(param->ext_adv_report.params.adv_data, + param->ext_adv_report.params.adv_data_len, + ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); + // ESP_LOGI(DEMO_TAG, "Scan result, device "ESP_BD_ADDR_STR", name len %u", ESP_BD_ADDR_HEX(param->ext_adv_report.params.addr), adv_name_len); + // ESP_LOG_BUFFER_CHAR(DEMO_TAG, adv_name, adv_name_len); + if (strlen(remote_target_name) == adv_name_len && strncmp((char *)adv_name, remote_target_name, adv_name_len) == 0) + { + esp_ble_gap_stop_ext_scan(); + scan_state = DISABLED; + + #if CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION + /* + * The scheduling time for each connection is the common factor divided by the number of connections. + * Note that the time allocated to each connection should be greater than the duration of + * one TX/RX packet exchange to maintain the connection without disconnection. + */ + esp_ble_gap_set_sch_len(0, BLE_PREF_EVT_LEN_MS * 1000); + #endif + + // create gattc virtual connection + esp_ble_gatt_creat_conn_params_t creat_conn_params = {0}; + memcpy(&creat_conn_params.remote_bda, param->ext_adv_report.params.addr, ESP_BD_ADDR_LEN); + creat_conn_params.remote_addr_type = param->ext_adv_report.params.addr_type; + creat_conn_params.own_addr_type = BLE_ADDR_TYPE_RANDOM; + creat_conn_params.is_direct = true; + creat_conn_params.is_aux = true; + creat_conn_params.phy_mask = ESP_BLE_PHY_1M_PREF_MASK | ESP_BLE_PHY_2M_PREF_MASK | ESP_BLE_PHY_CODED_PREF_MASK; + creat_conn_params.phy_1m_conn_params = &phy_1m_conn_params; + creat_conn_params.phy_2m_conn_params = &phy_2m_conn_params; + creat_conn_params.phy_coded_conn_params = &phy_coded_conn_params; + esp_ble_gattc_enh_open(gattc_profile_tab->gattc_if, &creat_conn_params); + ESP_LOGI(DEMO_TAG, "Create connection, remote "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(param->ext_adv_report.params.addr)); + } + break; + } +#else + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Scanning params set, status %d", param->scan_param_cmpl.status); + scan_state = PENDING; + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)new_rand_addr); + break; + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Scanning start, status %d", param->scan_start_cmpl.status); + break; + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Scanning stop"); + break; + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; + if (scan_result->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { + uint8_t *adv_name = NULL; + uint8_t adv_name_len = 0; + adv_name = esp_ble_resolve_adv_data_by_type(scan_result->scan_rst.ble_adv, + scan_result->scan_rst.adv_data_len + scan_result->scan_rst.scan_rsp_len, + ESP_BLE_AD_TYPE_NAME_CMPL, + &adv_name_len); + // ESP_LOGI(DEMO_TAG, "Scan result, device "ESP_BD_ADDR_STR", name len %u", ESP_BD_ADDR_HEX(scan_result->scan_rst.bda), adv_name_len); + // ESP_LOG_BUFFER_CHAR(DEMO_TAG, adv_name, adv_name_len); + if (strlen(remote_target_name) == adv_name_len && strncmp((char *)adv_name, remote_target_name, adv_name_len) == 0) { + esp_ble_gap_stop_scanning(); + scan_state = DISABLED; + + #if CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION + /* + * The scheduling time for each connection is the common factor divided by the number of connections. + * Note that the time allocated to each connection should be greater than the duration of + * one TX/RX packet exchange to maintain the connection without disconnection. + */ + esp_ble_gap_set_sch_len(0, BLE_PREF_EVT_LEN_MS * 1000); + #endif + + esp_ble_gatt_creat_conn_params_t creat_conn_params = {0}; + memcpy(&creat_conn_params.remote_bda, param->scan_rst.bda, ESP_BD_ADDR_LEN); + creat_conn_params.remote_addr_type = param->scan_rst.ble_addr_type; + creat_conn_params.own_addr_type = BLE_ADDR_TYPE_RANDOM; + creat_conn_params.is_direct = true; + creat_conn_params.is_aux = false; + creat_conn_params.phy_mask = ESP_BLE_PHY_1M_PREF_MASK; + creat_conn_params.phy_1m_conn_params = &phy_1m_conn_params; + creat_conn_params.phy_2m_conn_params = NULL; + creat_conn_params.phy_coded_conn_params = NULL; + esp_ble_gattc_enh_open(gattc_profile_tab->gattc_if, &creat_conn_params); + ESP_LOGI(DEMO_TAG, "Create connection, remote "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(param->scan_rst.bda)); + } + } + break; + } +#endif +#if (BLE50_SUPPORTED == 1) + case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended adv random address set, status %d, addr "ESP_BD_ADDR_STR"", + param->ext_adv_set_rand_addr.status, ESP_BD_ADDR_HEX(adv_rand_addr)); + advertising_state = ENABLED; + esp_ble_gap_ext_adv_start(ADV_HANDLE_NUM, &ext_adv[0]); + break; + case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended adv start, status %d", param->ext_adv_start.status); + break; + case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended adv param set, status %d", param->ext_adv_set_params.status); + esp_ble_gap_config_ext_adv_data_raw(ADV_HANDLE_INST, sizeof(adv_data_raw), &adv_data_raw[0]); + break; + case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended adv data set, status %d", param->ext_adv_data_set.status); + advertising_state = PENDING; + esp_ble_gap_addr_create_static(adv_rand_addr); + esp_ble_gap_ext_adv_set_rand_addr(ADV_HANDLE_INST, adv_rand_addr); + break; + case ESP_GAP_BLE_ADV_TERMINATED_EVT: + ESP_LOGI(DEMO_TAG, "Extended adv terminate, status = %d", param->adv_terminate.status); + if (param->adv_terminate.status == 0x00) { + ESP_LOGI(DEMO_TAG, "ADV successfully ended with a connection being created"); + } + break; +#else + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Advertising start, status %d", param->adv_start_cmpl.status); + break; + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Advertising data set, status %d", param->adv_data_raw_cmpl.status); + break; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Advertising stop, status %d", param->adv_stop_cmpl.status); + if (scan_state == PENDING) { + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)new_rand_addr); + } + break; +#endif + case ESP_GAP_BLE_SET_COMMON_FACTOR_CMPL_EVT: + ESP_LOGI(DEMO_TAG, "Common factor set, status %d", param->set_common_factor_cmpl.status); + break; + case ESP_GAP_BLE_SET_SCH_LEN_CMPL_EVT: + ESP_LOGI(DEMO_TAG, "Scheduling length set, status %d", param->set_sch_len_cmpl.status); + break; + default: + break; + } +} +void app_main(void) +{ + // Initialize NVS. + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + // register the callback function to the gap module + ret = esp_ble_gap_register_callback(esp_gap_cb); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s gap register error, error code = %x", __func__, ret); + return; + } + + // register the callback function to the gattc module + ret = esp_ble_gattc_register_callback(esp_gattc_cb); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s gattc register error, error code = %x", __func__, ret); + return; + } + + // register the callback function to the gatts module + ret = esp_ble_gatts_register_callback(esp_gatts_cb); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s gatts register error, error code = %x", __func__, ret); + } + + restart_scan_sem = xSemaphoreCreateBinary(); + peer_manager_init(); + + #if CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION + /* + * In this example, all connection intervals are the same, + * so the common factor is equal to each connection’s interval. + */ + uint32_t common_factor = (BLE_PREF_CONN_ITVL_MS * 1000) / 625; + esp_ble_gap_set_common_factor(common_factor); + #endif + + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)new_rand_addr); + + ret = esp_ble_gattc_app_register(GATTC_PROFILE_APP_ID); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s gattc app register error, error code = %x", __func__, ret); + } + + ret = esp_ble_gatts_app_register(GATTS_PROFILE_APP_ID); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s gatts app register error, error code = %x", __func__, ret); + } + + ret = esp_ble_gatt_set_local_mtu(200); + if (ret) { + ESP_LOGE(DEMO_TAG, "set local MTU failed, error code = %x", ret); + } + + while (1) { + if (xSemaphoreTake(restart_scan_sem, portMAX_DELAY) == pdTRUE) { + if (multi_conn_num < BLE_PEER_MAX_NUM && scan_state == DISABLED) { + scan_state = PENDING; + #if (BLE50_SUPPORTED == 0) + if (advertising_state != DISABLED) { + advertising_state = DISABLED; + esp_ble_gap_stop_advertising(); + break; + } + #endif + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr((uint8_t *)new_rand_addr); + } + } + } + + return; +} diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h new file mode 100644 index 0000000000..3394dd3375 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#ifndef BLE_MULTICONN_CENT_DEMO_H +#define BLE_MULTICONN_CENT_DEMO_H +#include +#include +#include +#include +#include +#include "nvs.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_gap_ble_api.h" +#include "esp_gattc_api.h" +#include "esp_gatt_defs.h" +#include "esp_bt_main.h" +#include "esp_gatt_common_api.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "esp_gatts_api.h" +#include "esp_bt_defs.h" +#include "esp_random.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#define MAX_CONN_NUM (CONFIG_BT_ACL_CONNECTIONS) + +/* Attributes State Machine */ +enum +{ + IDX_SVC, + IDX_CHAR_A, + IDX_CHAR_VAL_A, + HRS_IDX_NB, +}; + +enum +{ + DISABLED, + PENDING, + ENABLED, +}; + +typedef struct +{ + esp_gatt_if_t gattc_if; + uint16_t conn_id; + uint16_t conn_handle; + uint16_t char_handle; + esp_bd_addr_t peer_addr; + +} Peer; + +void peer_manager_init(void); + +void traverse_send_peer(uint16_t len, uint8_t *value); + +esp_err_t peer_add(Peer *peer); + +esp_err_t peer_remove(uint16_t conn_id); + +Peer *find_peer(uint16_t conn_id); + +#endif diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c new file mode 100644 index 0000000000..6b22e1144f --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "ble_multiconn_cent_demo.h" +#define PEER_MANAGER_TAG "PEER_MANAGER" + +static Peer remote_peer_lst[MAX_CONN_NUM]; + +void peer_manager_init(void) +{ + for (int i = 0; i < MAX_CONN_NUM; i++) { + remote_peer_lst[i].conn_id = 0xFFFF; + remote_peer_lst[i].conn_handle = 0xFFFF; + } + + ESP_LOGI(PEER_MANAGER_TAG, "peer manager init complete"); +} + +esp_err_t peer_add(Peer *peer) +{ + for (int i = 0; i < MAX_CONN_NUM; i++) { + if (remote_peer_lst[i].conn_id == 0xFFFF) { + remote_peer_lst[i].char_handle = 0xFFFF; + remote_peer_lst[i].conn_id = peer->conn_id; + remote_peer_lst[i].conn_handle = peer->conn_handle; + remote_peer_lst[i].gattc_if = peer->gattc_if; + memcpy(&remote_peer_lst[i].peer_addr, &peer->peer_addr, sizeof(esp_bd_addr_t)); + ESP_LOGI(PEER_MANAGER_TAG, "peer added to list"); + return ESP_OK; + } + } + + ESP_LOGE(PEER_MANAGER_TAG, "peer list is full"); + return ESP_FAIL; +} + +esp_err_t peer_remove(uint16_t conn_id) +{ + for (int i = 0; i < MAX_CONN_NUM; i++) { + if (remote_peer_lst[i].conn_id == conn_id) { + remote_peer_lst[i].conn_id = 0xFFFF; + return ESP_OK; + } + } + + ESP_LOGE(PEER_MANAGER_TAG, "peer not found in list"); + return ESP_FAIL; +} + +Peer *find_peer(uint16_t conn_id) +{ + for (int i = 0; i < MAX_CONN_NUM; i++) { + if (remote_peer_lst[i].conn_id == conn_id) { + return &remote_peer_lst[i]; + } + } + + ESP_LOGE(PEER_MANAGER_TAG, "peer not found in list, conn_id %d", conn_id); + return NULL; +} + +void traverse_send_peer(uint16_t len, uint8_t *value) +{ + for (int i = 0; i < MAX_CONN_NUM; i++) { + if (remote_peer_lst[i].conn_id != 0xFFFF && remote_peer_lst[i].char_handle != 0xFFFF) { + esp_err_t ret = esp_ble_gattc_write_char(remote_peer_lst[i].gattc_if, remote_peer_lst[i].conn_id, + remote_peer_lst[i].char_handle, len, value,ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + if (ret != ESP_OK) { + ESP_LOGE(PEER_MANAGER_TAG, "traver peer write char failed!"); + } + } + } +} diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults new file mode 100644 index 0000000000..d6755c993c --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# Bluedroid config +CONFIG_BT_ENABLED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y +# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set +CONFIG_BT_MULTI_CONNECTION_ENBALE=y diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32 new file mode 100644 index 0000000000..ef6a897abc --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32 @@ -0,0 +1,6 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32" +CONFIG_BT_ACL_CONNECTIONS=9 +CONFIG_BTDM_CTRL_BLE_MAX_CONN=9 diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c2 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..a1386531a6 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c2 @@ -0,0 +1,10 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c2" +CONFIG_BT_ENABLED=y +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2304 +CONFIG_BT_ACL_CONNECTIONS=2 +# Controller config +CONFIG_BT_LE_MAX_CONNECTIONS=2 +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c3 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..4e0fcca763 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c3 @@ -0,0 +1,7 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c3" +CONFIG_BT_ACL_CONNECTIONS=8 +CONFIG_BT_CTRL_BLE_MAX_ACT=10 +CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE=y diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c5 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c5 new file mode 100644 index 0000000000..04b988e123 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c5 @@ -0,0 +1,12 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c5" +# Bluedroid config +CONFIG_BT_ACL_CONNECTIONS=50 +CONFIG_BT_ALARM_MAX_NUM=150 +# Controller config +CONFIG_BT_LE_MSYS_1_BLOCK_COUNT=100 +CONFIG_BT_LE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_LE_MAX_CONNECTIONS=50 +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c6 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c6 new file mode 100644 index 0000000000..6f85eff191 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32c6 @@ -0,0 +1,12 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c6" +# Bluedroid config +CONFIG_BT_ACL_CONNECTIONS=50 +CONFIG_BT_ALARM_MAX_NUM=150 +# Controller config +CONFIG_BT_LE_MSYS_1_BLOCK_COUNT=100 +CONFIG_BT_LE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_LE_MAX_CONNECTIONS=50 +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32h2 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32h2 new file mode 100644 index 0000000000..c63f282b0e --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32h2 @@ -0,0 +1,6 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32h2" +CONFIG_BT_ACL_CONNECTIONS=15 +CONFIG_BT_LE_MAX_CONNECTIONS=15 diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32s3 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..11c13540c5 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults.esp32s3 @@ -0,0 +1,7 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_BT_ACL_CONNECTIONS=8 +CONFIG_BT_CTRL_BLE_MAX_ACT=10 +CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE=y diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/CMakeLists.txt new file mode 100644 index 0000000000..223e642003 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_multi_conn_prph) diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/README.md b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/README.md new file mode 100644 index 0000000000..01cf4ff01c --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/README.md @@ -0,0 +1,73 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | + +# BLE Multiple Connection Peripheral Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +**This example relies on the BLE controller. Please use the chip modules listed under Supported Targets.** + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using: + +```bash +idf.py set-target +``` + +### Hardware Required + +* At least two development board with ESP32-C6/ESP32-H2 SoC (e.g., ESP32-C6-DevKitC, ESP32-H2-DevKitC, etc.) +* USB cable for Power supply and programming + +See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +This is the console output on successful connection: + +``` +I (402) BLE_INIT: ble controller commit:[d2d70d4] +I (402) BLE_INIT: Bluetooth MAC: 60:55:f9:f6:97:92 +I (402) phy_init: phy_version 331,5b89037,Mar 3 2025,16:01:12 +I (462) phy: libbtbb version: ec2ecba, Mar 3 2025, 16:01:27 +I (462) MULTI_CONN_PRPH: app_main init bluetooth +I (482) MULTI_CONN_PRPH: GATT server register, status 0, app_id 0, gatts_if 3 +I (482) MULTI_CONN_PRPH: The number handle = 3 +I (482) MULTI_CONN_PRPH: Advertising data set, status 0 +I (482) MULTI_CONN_PRPH: Random address set, status 0, addr da:20:da:dd:c8:9f +I (492) MULTI_CONN_PRPH: Advertising start, status 0 +I (8212) MULTI_CONN_PRPH: Connected, conn_id 0, remote ff:ed:64:bb:b0:f2, total 1 +I (8212) MULTI_CONN_PRPH: Advertising stop, status 0 +I (8212) MULTI_CONN_PRPH: Random address set, status 0, addr e5:4d:be:54:de:1a +I (8222) MULTI_CONN_PRPH: Advertising start, status 0 +I (8952) MULTI_CONN_PRPH: Connected, conn_id 1, remote f1:91:5d:0b:22:3a, total 2 +I (8952) MULTI_CONN_PRPH: Advertising stop, status 0 +I (8952) MULTI_CONN_PRPH: Random address set, status 0, addr eb:06:b7:d0:d4:48 +I (8962) MULTI_CONN_PRPH: Advertising start, status 0 +I (9692) MULTI_CONN_PRPH: Connected, conn_id 2, remote e8:8e:72:3f:6e:29, total 3 +I (9692) MULTI_CONN_PRPH: Advertising stop, status 0 +I (9692) MULTI_CONN_PRPH: Random address set, status 0, addr cc:fe:e6:7a:44:f2 +I (9702) MULTI_CONN_PRPH: Advertising start, status 0 +I (10432) MULTI_CONN_PRPH: Connected, conn_id 3, remote e3:49:fc:15:62:ca, total 4 +I (10432) MULTI_CONN_PRPH: Advertising stop, status 0 +I (10432) MULTI_CONN_PRPH: Random address set, status 0, addr f3:5d:a8:3a:d8:59 +I (10442) MULTI_CONN_PRPH: Advertising start, status 0 +I (11172) MULTI_CONN_PRPH: Connected, conn_id 4, remote e2:64:1a:3a:59:63, total 5 +I (11172) MULTI_CONN_PRPH: Advertising stop, status 0 +I (11172) MULTI_CONN_PRPH: Random address set, status 0, addr e1:8e:fa:0d:b5:04 +I (11182) MULTI_CONN_PRPH: Advertising start, status 0 +I (11912) MULTI_CONN_PRPH: Connected, conn_id 5, remote e6:5b:c1:9b:02:b1, total 6 +``` + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/CMakeLists.txt new file mode 100644 index 0000000000..9a0502d1a4 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "ble_multiconn_prph_demo.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/Kconfig.projbuild b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/Kconfig.projbuild new file mode 100644 index 0000000000..8ea2e3f1b6 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/Kconfig.projbuild @@ -0,0 +1,11 @@ +menu "Example Configuration" + + config EXAMPLE_RESTART_ADV_AFTER_CONNECTED + bool + default y + prompt "Restart advertisement when connected" + help + To simulate multiple connections with only one device, + restart the advertisement once a connection has been established. + +endmenu diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c new file mode 100644 index 0000000000..43e6a34a21 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c @@ -0,0 +1,401 @@ +/* + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_bt.h" +// #include "bt_target.h" + +#include "esp_gap_ble_api.h" +#include "esp_gatts_api.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "ble_multiconn_prph_demo.h" +#include "esp_random.h" + +#define DEMO_TAG "MULTI_CONN_PRPH" + +#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED +#define BLE50_SUPPORTED (CONFIG_BT_BLE_50_FEATURES_SUPPORTED) +#else +#define BLE50_SUPPORTED (0) +#endif + +/* Service */ +#define SVC_INST_ID 0 +#define MULTICONN_PROFILE_NUM 1 +#define MULTICONN_PROFILE_APP_IDX 0 +#define EXT_ADV_HANDLE 0 +#define NUM_EXT_ADV_SET 1 +#define EXT_ADV_DURATION 0 +#define EXT_ADV_MAX_EVENTS 0 +#define BLE_MAX_CONN (CONFIG_BT_ACL_CONNECTIONS) +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 +#define CHAR_DECLARATION_SIZE (sizeof(uint8_t)) + +static uint8_t char_value[GATTS_DEMO_CHAR_VAL_LEN_MAX] = {0x00}; +static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE; +static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE; +static const uint8_t char_prop_write = ESP_GATT_CHAR_PROP_BIT_WRITE; +static const uint8_t gatt_svr_svc_uuid[] = {0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, + 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59}; + +static const uint8_t gatt_svr_chr_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33}; +static uint16_t profile_handle_table[HRS_IDX_NB]; + +static uint8_t adv_data_raw[] = { + 0x02, ESP_BLE_AD_TYPE_FLAG, 0x06, + 0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xab, 0xcd, + 0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0x18, 0x11, + 0x0f, ESP_BLE_AD_TYPE_NAME_CMPL, 'e', 's', 'p', '-','m', 'u', 'l', 't', 'i', '-', 'c', 'o', 'n', 'n', +}; + +static bool is_advertising = false; + +#if (BLE50_SUPPORTED) +static esp_ble_gap_ext_adv_t ext_adv[1] = { + [0] = {EXT_ADV_HANDLE, EXT_ADV_DURATION, EXT_ADV_MAX_EVENTS}, +}; + +esp_ble_gap_ext_adv_params_t ext_adv_params = { + .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE, + .interval_min = 0x20, + .interval_max = 0x20, + .channel_map = ADV_CHNL_ALL, + .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + .primary_phy = ESP_BLE_GAP_PHY_1M, + .max_skip = 0, + .secondary_phy = ESP_BLE_GAP_PHY_1M, + .sid = 0, + .scan_req_notif = false, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE, +}; +#else +static esp_ble_adv_params_t legacy_adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x40, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + +}; +#endif + +struct gatts_profile_inst +{ + esp_gatts_cb_t gatts_cb; + uint16_t gatts_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_handle; + esp_gatt_srvc_id_t service_id; + uint16_t char_handle; + esp_bt_uuid_t char_uuid; + esp_gatt_perm_t perm; + esp_gatt_char_prop_t property; + uint16_t descr_handle; + esp_bt_uuid_t descr_uuid; +}; + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst multi_conn_profile_tab[MULTICONN_PROFILE_NUM] = { + [MULTICONN_PROFILE_APP_IDX] = { + .gatts_cb = gatts_profile_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static uint8_t prph_conn_num = 0; +static SemaphoreHandle_t restart_adv_sem = NULL; +static esp_bd_addr_t new_rand_addr; + +static void ble_prph_set_new_adv(void) +{ + if (prph_conn_num >= BLE_MAX_CONN) { + ESP_LOGI(DEMO_TAG, "Max connection reached"); +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_ext_adv_stop(1, &ext_adv[0].instance); +#else + esp_ble_gap_stop_advertising(); +#endif + return; + } + + if (!is_advertising) { + is_advertising = true; + esp_ble_gap_addr_create_static(new_rand_addr); +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_ext_adv_set_rand_addr(EXT_ADV_HANDLE, new_rand_addr); +#else + esp_ble_gap_set_rand_addr(new_rand_addr); +#endif + } +} + +static void ble_prph_restart_adv(void) +{ +#if CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED + if (!xSemaphoreGive(restart_adv_sem)) { + ESP_LOGE(DEMO_TAG, "Failed to give semaphore"); + } +#else + ble_prph_set_new_adv(); +#endif // CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED +} + +/* Full Database Description - Used to add attributes into the database */ +static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] = +{ + // Service Declaration + [IDX_SVC] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(gatt_svr_svc_uuid), (uint8_t *)&gatt_svr_svc_uuid}}, + // Characteristic Declaration + [IDX_CHAR_A] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_write}}, + // Characteristic Value + [IDX_CHAR_VAL_A] = + {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_128, (uint8_t *)gatt_svr_chr_uuid, ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)&char_value}}, +}; + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { +#if (BLE50_SUPPORTED) + case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended adv random address set, status %d, "ESP_BD_ADDR_STR"", + param->ext_adv_set_rand_addr.status, ESP_BD_ADDR_HEX(new_rand_addr)); + esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); + break; + case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended advertising params set, status %d", param->ext_adv_set_params.status); + esp_ble_gap_config_ext_adv_data_raw(EXT_ADV_HANDLE, sizeof(adv_data_raw), &adv_data_raw[0]); + break; + case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended advertising data set, status %d", param->ext_adv_data_set.status); + ble_prph_restart_adv(); + break; + case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Extended advertising start, status %d", param->ext_adv_start.status); + is_advertising = true; + break; + case ESP_GAP_BLE_ADV_TERMINATED_EVT: + ESP_LOGI(DEMO_TAG, "Extended advertising terminated, status = %d", param->adv_terminate.status); + if (param->adv_terminate.status == 0x00) { + ESP_LOGI(DEMO_TAG, "Advertising successfully ended with a connection being created"); + is_advertising = false; + } + break; +#else + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Advertising data set, status %d", param->adv_data_raw_cmpl.status); + esp_ble_gap_addr_create_static(new_rand_addr); + esp_ble_gap_set_rand_addr(new_rand_addr); + break; + case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: + ESP_LOGI(DEMO_TAG, "Random address set, status %d, addr "ESP_BD_ADDR_STR"", + param->set_rand_addr_cmpl.status, ESP_BD_ADDR_HEX(new_rand_addr)); + esp_ble_gap_start_advertising(&legacy_adv_params); + break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Advertising start, status %d", param->adv_start_cmpl.status); + is_advertising = true; + break; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + ESP_LOGI(DEMO_TAG, "Advertising stop, status %d", param->adv_stop_cmpl.status); + is_advertising = false; + break; +#endif + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + ESP_LOGI(DEMO_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d", + param->update_conn_params.status, + param->update_conn_params.conn_int, + param->update_conn_params.latency, + param->update_conn_params.timeout); + break; + default: + break; + } +} + +static void gatts_profile_event_handler(esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + switch (event) + { + case ESP_GATTS_REG_EVT: + ESP_LOGI(DEMO_TAG, "GATT server register, status %d, app_id %d, gatts_if %d", param->reg.status, param->reg.app_id, gatts_if); + esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID); + break; + case ESP_GATTS_READ_EVT: + ESP_LOGI(DEMO_TAG, "Characteristic read, conn_id %u", param->read.conn_id); + break; + case ESP_GATTS_WRITE_EVT: + ESP_LOGI(DEMO_TAG, "Characteristic write, conn_id %u, value", param->write.conn_id); + ESP_LOG_BUFFER_HEX(DEMO_TAG, param->write.value, param->write.len); + break; + case ESP_GATTS_CONNECT_EVT: + ESP_LOGI(DEMO_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR", total %u", + param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda), ++prph_conn_num); + is_advertising = false; +#if CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED + ble_prph_restart_adv(); +#endif + break; + case ESP_GATTS_DISCONNECT_EVT: + ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x, total %d", + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, (prph_conn_num ? --prph_conn_num : prph_conn_num)); + /* start advertising again when disconnected */ + ble_prph_restart_adv(); + break; + case ESP_GATTS_OPEN_EVT: + break; + case ESP_GATTS_CANCEL_OPEN_EVT: + break; + case ESP_GATTS_CLOSE_EVT: + break; + case ESP_GATTS_LISTEN_EVT: + break; + case ESP_GATTS_CONGEST_EVT: + break; + case ESP_GATTS_CREAT_ATTR_TAB_EVT: + { + ESP_LOGI(DEMO_TAG, "The number handle = %x", param->add_attr_tab.num_handle); + if (param->create.status == ESP_GATT_OK) { + if (param->add_attr_tab.num_handle == HRS_IDX_NB) { + memcpy(profile_handle_table, param->add_attr_tab.handles, + sizeof(profile_handle_table)); + esp_ble_gatts_start_service(profile_handle_table[IDX_SVC]); +#if (BLE50_SUPPORTED == 1) + esp_ble_gap_ext_adv_set_params(EXT_ADV_HANDLE, &ext_adv_params); +#else + esp_ble_gap_config_adv_data_raw(adv_data_raw, sizeof(adv_data_raw)); +#endif + } else { + ESP_LOGE(DEMO_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", + param->add_attr_tab.num_handle, HRS_IDX_NB); + } + } else { + ESP_LOGE(DEMO_TAG, " Create attribute table failed, error code = %x", param->create.status); + } + break; + } + + default: + break; + } +} + +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t *param) +{ + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + multi_conn_profile_tab[MULTICONN_PROFILE_APP_IDX].gatts_if = gatts_if; + } else { + ESP_LOGI(DEMO_TAG, "Reg app failed, app_id %04x, status %d", + param->reg.app_id, + param->reg.status); + return; + } + } + + do { + int idx; + for (idx = 0; idx < MULTICONN_PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == multi_conn_profile_tab[idx].gatts_if) { + if (multi_conn_profile_tab[idx].gatts_cb) { + multi_conn_profile_tab[idx].gatts_cb(event, gatts_if, param); + } + } + } + } while (0); +} + +void app_main(void) +{ + esp_err_t ret; + restart_adv_sem = xSemaphoreCreateBinary(); + + // Initialize NVS. + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s init controller failed: %s", __func__, esp_err_to_name(ret)); + return; + } + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + ESP_LOGI(DEMO_TAG, "%s init bluetooth", __func__); + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret)); + return; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(DEMO_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret)); + return; + } + + ret = esp_ble_gatts_register_callback(gatts_event_handler); + if (ret) { + ESP_LOGE(DEMO_TAG, "gatts register error, error code = %x", ret); + return; + } + + ret = esp_ble_gap_register_callback(gap_event_handler); + if (ret) { + ESP_LOGE(DEMO_TAG, "gap register error, error code = %x", ret); + return; + } + + ret = esp_ble_gatts_app_register(MULTICONN_PROFILE_APP_IDX); + if (ret) { + ESP_LOGE(DEMO_TAG, "gatts app register error, error code = %x", ret); + return; + } + +#if CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED + while (true) { + int delay_ms = 0; + if (xSemaphoreTake(restart_adv_sem, portMAX_DELAY)) { + delay_ms = (esp_random() % 300) + 100; + vTaskDelay(pdMS_TO_TICKS(delay_ms)); + ble_prph_set_new_adv(); + } else { + ESP_LOGE(DEMO_TAG, "Failed to take restart_adv_sem"); + } + } +#endif // CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED + +} diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.h b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.h new file mode 100644 index 0000000000..399fa64c04 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + + +#include +#include +#include + + +/* Attributes State Machine */ +enum +{ + IDX_SVC, + IDX_CHAR_A, + IDX_CHAR_VAL_A, + + + HRS_IDX_NB, +}; diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults new file mode 100644 index 0000000000..319fcc0996 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults @@ -0,0 +1,10 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# Bluedroid config +CONFIG_BT_ENABLED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y +# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set +CONFIG_BT_ACL_CONNECTIONS=50 +CONFIG_BT_ALARM_MAX_NUM=150 +CONFIG_BT_MULTI_CONNECTION_ENBALE=y diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32 new file mode 100644 index 0000000000..ef6a897abc --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32 @@ -0,0 +1,6 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32" +CONFIG_BT_ACL_CONNECTIONS=9 +CONFIG_BTDM_CTRL_BLE_MAX_CONN=9 diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c2 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..0a3d436aa0 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c2 @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c2" +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2304 +CONFIG_BT_ACL_CONNECTIONS=2 +CONFIG_BT_LE_MAX_CONNECTIONS=2 +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c3 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..4e0fcca763 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c3 @@ -0,0 +1,7 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c3" +CONFIG_BT_ACL_CONNECTIONS=8 +CONFIG_BT_CTRL_BLE_MAX_ACT=10 +CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE=y diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c5 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c5 new file mode 100644 index 0000000000..04b988e123 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c5 @@ -0,0 +1,12 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c5" +# Bluedroid config +CONFIG_BT_ACL_CONNECTIONS=50 +CONFIG_BT_ALARM_MAX_NUM=150 +# Controller config +CONFIG_BT_LE_MSYS_1_BLOCK_COUNT=100 +CONFIG_BT_LE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_LE_MAX_CONNECTIONS=50 +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c6 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c6 new file mode 100644 index 0000000000..6f85eff191 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32c6 @@ -0,0 +1,12 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c6" +# Bluedroid config +CONFIG_BT_ACL_CONNECTIONS=50 +CONFIG_BT_ALARM_MAX_NUM=150 +# Controller config +CONFIG_BT_LE_MSYS_1_BLOCK_COUNT=100 +CONFIG_BT_LE_HCI_EVT_BUF_SIZE=70 +CONFIG_BT_LE_MAX_CONNECTIONS=50 +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32h2 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32h2 new file mode 100644 index 0000000000..c63f282b0e --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32h2 @@ -0,0 +1,6 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32h2" +CONFIG_BT_ACL_CONNECTIONS=15 +CONFIG_BT_LE_MAX_CONNECTIONS=15 diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32s3 b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..11c13540c5 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults.esp32s3 @@ -0,0 +1,7 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_BT_ACL_CONNECTIONS=8 +CONFIG_BT_CTRL_BLE_MAX_ACT=10 +CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE=y