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:
Island
2025-09-04 12:02:33 +08:00
36 changed files with 1767 additions and 37 deletions

View File

@@ -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,

View File

@@ -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)

View File

@@ -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.
*

View File

@@ -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, &param, 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, &param, 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, &param, sizeof(esp_ble_gap_cb_param_t), NULL, NULL);
}
if (ret != BT_STATUS_SUCCESS) {
BTC_TRACE_ERROR("%s btc_transfer_context failed\n", __func__);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "peer_manager.c" "ble_multiconn_cent_demo.c"
INCLUDE_DIRS ".")

View File

@@ -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 connections 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;
}

View File

@@ -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

View File

@@ -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!");
}
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "ble_multiconn_prph_demo.c"
INCLUDE_DIRS ".")

View File

@@ -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

View File

@@ -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
}

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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