mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
Merge branch 'feat/support_cble50y24_108_v5.5' into 'release/v5.5'
Support multi-connection optimization and examples on Bluedroid (v5.5) See merge request espressif/esp-idf!41200
This commit is contained in:
@@ -1210,9 +1210,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,
|
||||
|
@@ -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)
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -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__);
|
||||
|
@@ -1132,8 +1132,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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
@@ -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 <chip_name>
|
||||
```
|
||||
|
||||
### 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.
|
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "peer_manager.c" "ble_multiconn_cent_demo.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -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;
|
||||
}
|
@@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#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
|
@@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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)
|
@@ -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 <chip_name>
|
||||
```
|
||||
|
||||
### 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.
|
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "ble_multiconn_prph_demo.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -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
|
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#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
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Attributes State Machine */
|
||||
enum
|
||||
{
|
||||
IDX_SVC,
|
||||
IDX_CHAR_A,
|
||||
IDX_CHAR_VAL_A,
|
||||
|
||||
|
||||
HRS_IDX_NB,
|
||||
};
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
Reference in New Issue
Block a user