fix(examples): improve bluedroid BLE example error handling

Check GAP/GATT event status, fix prepare-write buffer lifecycle, and
align example code with updated Bluedroid API behavior.

(cherry picked from commit bfc47ace48)

Co-authored-by: zhiweijian <zhiweijian@espressif.com>
This commit is contained in:
Zhi Wei Jian
2026-06-12 10:54:03 +08:00
committed by zhiweijian
parent ccd011323c
commit 29a6200e40
48 changed files with 1606 additions and 721 deletions
@@ -287,7 +287,7 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]);
break;
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "Extended advertising start, status %d", param->ext_adv_data_set.status);
ESP_LOGI(GATTS_TABLE_TAG, "Extended advertising start, status %d", param->ext_adv_start.status);
break;
case ESP_GAP_BLE_ADV_TERMINATED_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "Extended advertising terminated, status %d", param->adv_terminate.status);
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -163,6 +163,9 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count)
if (addr == NULL || count == 0) {
return 0;
}
if (count > (ESP_GATT_MAX_MTU_SIZE - 3U)) {
return 0;
}
for(int i = 0; i < count; i++) {
sum = sum + addr[i];
@@ -175,6 +178,15 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count)
return (uint8_t)~sum;
}
static void throughput_client_resume_ext_scan(void)
{
connect = false;
esp_err_t err = esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD);
if (err != ESP_OK) {
ESP_LOGE(GATTC_TAG, "start_ext_scan failed: %s", esp_err_to_name(err));
}
}
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)
{
esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
@@ -202,6 +214,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
case ESP_GATTC_OPEN_EVT:
if (param->open.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "Open failed, status %d", p_data->open.status);
throughput_client_resume_ext_scan();
break;
}
ESP_LOGI(GATTC_TAG, "Open successfully, MTU %u", param->open.mtu);
@@ -293,12 +306,13 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
&count);
if (ret_status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0){
} else if (count == 0) {
ESP_LOGE(GATTC_TAG, "decsr not found");
} else {
descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count);
if (!descr_elem_result){
if (descr_elem_result == NULL){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{
} else {
ret_status = esp_ble_gattc_get_descr_by_char_handle( gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
p_data->reg_for_notify.handle,
@@ -307,10 +321,10 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
&count);
if (ret_status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_descr_by_char_handle error");
}
/* Every char has only one descriptor in our 'throughput_server' demo, so we use first 'descr_elem_result' */
if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG){
} else if (count > 0 &&
descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 &&
descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG) {
/* Every char has only one descriptor in our 'throughput_server' demo, so we use first 'descr_elem_result' */
ret_status = esp_ble_gattc_write_char_descr( gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
descr_elem_result[0].handle,
@@ -318,29 +332,37 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
(uint8_t *)&notify_en,
ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE);
if (ret_status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_write_char_descr error");
}
}
if (ret_status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_write_char_descr error");
}
/* free descr_elem_result */
free(descr_elem_result);
descr_elem_result = NULL;
}
}
else{
ESP_LOGE(GATTC_TAG, "decsr not found");
}
}
break;
}
case ESP_GATTC_NOTIFY_EVT: {
#if (CONFIG_GATTS_NOTIFY_THROUGHPUT)
if (p_data->notify.is_notify &&
(p_data->notify.value[p_data->notify.value_len - 1] ==
check_sum(p_data->notify.value, p_data->notify.value_len - 1))){
notify_len += p_data->notify.value_len;
if (p_data->notify.is_notify) {
uint16_t vlen = p_data->notify.value_len;
uint8_t *val = p_data->notify.value;
/* value_len == 0 makes (vlen - 1) wrap; never index or pass to check_sum before validating. */
const uint16_t max_notify_len = (uint16_t)(ESP_GATT_MAX_MTU_SIZE - 3U);
if (val == NULL) {
ESP_LOGW(GATTC_TAG, "notify ignored: null value");
} else if (vlen == 0) {
ESP_LOGW(GATTC_TAG, "notify ignored: zero length");
} else if (vlen < 2) {
ESP_LOGW(GATTC_TAG, "notify ignored: length too short for payload+checksum");
} else if (vlen > max_notify_len) {
ESP_LOGW(GATTC_TAG, "notify ignored: length exceeds bound");
} else if (val[vlen - 1] == check_sum(val, (uint16_t)(vlen - 1U))) {
notify_len += vlen;
} else {
ESP_LOGE(GATTC_TAG, "notify checksum mismatch");
}
} else {
ESP_LOGE(GATTC_TAG, "Indication received, value:");
}
@@ -385,8 +407,14 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
current_time = 0;
notify_len = 0;
#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */
#if (CONFIG_GATTC_WRITE_THROUGHPUT)
/* Unblock throughput_client_task if it is waiting on gattc_semaphore while congested. */
can_send_write = true;
xSemaphoreGive(gattc_semaphore);
#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */
ESP_LOGI(GATTC_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason);
throughput_client_resume_ext_scan();
break;
case ESP_GATTC_CONGEST_EVT:
#if (CONFIG_GATTC_WRITE_THROUGHPUT)
@@ -517,7 +545,6 @@ static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp
#if (CONFIG_GATTC_WRITE_THROUGHPUT)
static void throughput_client_task(void *param)
{
vTaskDelay(2000 / portTICK_PERIOD_MS);
uint8_t sum = check_sum(write_data, sizeof(write_data) - 1);
write_data[GATTC_WRITE_LEN - 1] = sum;
@@ -619,6 +646,17 @@ void app_main(void)
return;
}
#if (CONFIG_GATTC_WRITE_THROUGHPUT)
/* Create the semaphore before registering the GATTC callback so that any
* xSemaphoreGive() invoked from the callback is guaranteed to see a valid handle.
*/
gattc_semaphore = xSemaphoreCreateBinary();
if (gattc_semaphore == NULL) {
ESP_LOGE(GATTC_TAG, "%s: gattc semaphore create failed", __func__);
return;
}
#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */
//register the callback function to the gattc module
ret = esp_ble_gattc_register_callback(esp_gattc_cb);
if(ret){
@@ -636,20 +674,11 @@ void app_main(void)
ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
}
#if (CONFIG_GATTC_WRITE_THROUGHPUT)
// The task is only created on the CPU core that Bluetooth is working on,
// preventing the sending task from using the un-updated Bluetooth state on another CPU.
/* Create the task only after the semaphore exists; never rely on priority or delays. */
xTaskCreatePinnedToCore(&throughput_client_task, "throughput_client_task", 4096, NULL, 10, NULL, BLUETOOTH_TASK_PINNED_TO_CORE);
#endif
#if (CONFIG_GATTS_NOTIFY_THROUGHPUT)
xTaskCreatePinnedToCore(&throughput_cal_task, "throughput_cal_task", 4096, NULL, 9, NULL, BLUETOOTH_TASK_PINNED_TO_CORE);
#endif
#if (CONFIG_GATTC_WRITE_THROUGHPUT)
gattc_semaphore = xSemaphoreCreateBinary();
if (!gattc_semaphore) {
ESP_LOGE(GATTC_TAG, "%s, init fail, the gattc semaphore create fail.", __func__);
return;
}
#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -164,6 +164,18 @@ typedef struct {
static prepare_type_env_t a_prepare_write_env;
static void prepare_write_env_clear(prepare_type_env_t *env)
{
if (env == NULL) {
return;
}
if (env->prepare_buf != NULL) {
free(env->prepare_buf);
env->prepare_buf = NULL;
}
env->prepare_len = 0;
}
extern void esp_ble_switch_phy_coded(bool phy_500k);
void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param);
void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param);
@@ -175,6 +187,9 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count)
if (addr == NULL || count == 0) {
return 0;
}
if (count > (ESP_GATT_MAX_MTU_SIZE - 3U)) {
return 0;
}
for(int i = 0; i < count; i++) {
sum = sum + addr[i];
@@ -228,6 +243,8 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
status = ESP_GATT_INVALID_OFFSET;
} else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) {
status = ESP_GATT_INVALID_ATTR_LEN;
} else if (param->write.len > ESP_GATT_MAX_ATTR_LEN) {
status = ESP_GATT_INVALID_ATTR_LEN;
}
if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) {
@@ -239,13 +256,19 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
}
}
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t));
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)calloc(1, sizeof(esp_gatt_rsp_t));
if (gatt_rsp) {
gatt_rsp->attr_value.len = param->write.len;
gatt_rsp->attr_value.handle = param->write.handle;
gatt_rsp->attr_value.offset = param->write.offset;
gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
if (status == ESP_GATT_OK) {
if (param->write.value == NULL) {
status = ESP_GATT_INVALID_ATTR_LEN;
} else {
gatt_rsp->attr_value.len = param->write.len;
memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
}
}
esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);
if (response_err != ESP_OK) {
@@ -275,11 +298,7 @@ void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble
if (param->exec_write.exec_write_flag != ESP_GATT_PREP_WRITE_EXEC){
ESP_LOGI(GATTS_TAG,"Prepare write cancel");
}
if (prepare_write_env->prepare_buf) {
free(prepare_write_env->prepare_buf);
prepare_write_env->prepare_buf = NULL;
}
prepare_write_env->prepare_len = 0;
prepare_write_env_clear(prepare_write_env);
}
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
@@ -356,9 +375,22 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT)
if (param->write.handle == gl_profile_tab[PROFILE_A_APP_ID].char_handle) {
// The last value byte is the checksum data, should used to check the data is received corrected or not.
if (param->write.value[param->write.len - 1] ==
check_sum(param->write.value, param->write.len - 1)) {
write_len += param->write.len;
uint16_t wlen = param->write.len;
uint8_t *wval = param->write.value;
/* len==0 makes (wlen-1) wrap; cap len before indexing or passing to check_sum. */
const uint16_t max_write_len = (uint16_t)(ESP_GATT_MAX_MTU_SIZE - 3U);
if (wval == NULL) {
ESP_LOGW(GATTS_TAG, "write ignored: null value");
} else if (wlen == 0) {
ESP_LOGW(GATTS_TAG, "write ignored: zero length");
} else if (wlen < 2) {
ESP_LOGW(GATTS_TAG, "write ignored: length too short for payload+checksum");
} else if (wlen > max_write_len) {
ESP_LOGW(GATTS_TAG, "write ignored: length exceeds bound");
} else if (wval[wlen - 1] == check_sum(wval, (uint16_t)(wlen - 1U))) {
write_len += wlen;
} else {
ESP_LOGE(GATTS_TAG, "write checksum mismatch");
}
if (start == false) {
@@ -451,10 +483,12 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
ESP_LOGI(GATTS_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"",
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id;
prepare_write_env_clear(&a_prepare_write_env);
break;
}
case ESP_GATTS_DISCONNECT_EVT:
is_connect = false;
prepare_write_env_clear(&a_prepare_write_env);
ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x",
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]);
@@ -96,6 +96,16 @@ static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
},
};
/** Clear connect-in-progress flag and restart indefinite extended scan (after failed open or disconnect). */
static void central_resume_ext_scan(void)
{
connect = false;
esp_err_t err = esp_ble_gap_start_ext_scan(0, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "start ext scan failed, error = 0x%x", err);
}
}
/**
* @brief GATT client event handler
*/
@@ -112,6 +122,10 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
case ESP_GATTC_REG_EVT:
ESP_LOGI(TAG, "GATT client register, status %d, app_id %d, gattc_if %d",
p_data->reg.status, p_data->reg.app_id, gattc_if);
if (p_data->reg.status != ESP_GATT_OK) {
ESP_LOGE(TAG, "GATT client register failed, status %d", p_data->reg.status);
break;
}
gl_profile_tab[PROFILE_A_APP_ID].gattc_if = gattc_if;
// Set default subrate parameters
esp_ble_default_subrate_param_t default_subrate_params = {
@@ -148,11 +162,19 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
ESP_LOGI(TAG, "Subrate request sent successfully");
}
break;
case ESP_GATTC_OPEN_EVT:
if (p_data->open.status != ESP_GATT_OK) {
ESP_LOGE(TAG, "Open failed, status %d", p_data->open.status);
central_resume_ext_scan();
break;
}
ESP_LOGI(TAG, "GATT open OK, conn_id %d, MTU %u", p_data->open.conn_id, p_data->open.mtu);
break;
case ESP_GATTC_DISCONNECT_EVT:
connect = false;
g_conn_handle = 0xFFFF;
ESP_LOGI(TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason);
central_resume_ext_scan();
break;
default:
break;
@@ -208,10 +230,9 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
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;
if (esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params) != ESP_OK)
{
connect = false;
if (esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params) != ESP_OK) {
ESP_LOGE(TAG, "Failed to open connection");
central_resume_ext_scan();
}
}
}
@@ -37,6 +37,7 @@
#define REMOTE_NOTIFY_UUID 0xFF01
#define EXT_SCAN_DURATION 0
#define EXT_SCAN_PERIOD 0
#define BLE_CONN_HDL_INVALID ((uint16_t)0xFFFF)
///Declare static functions
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
@@ -95,7 +96,7 @@ const esp_ble_conn_params_t phy_coded_conn_params = {
static uint8_t antenna_ids[2] = {0x00, 0x01};
esp_ble_cte_recv_params_params_t cte_recv_params = {
.conn_handle = 0xff,
.conn_handle = BLE_CONN_HDL_INVALID,
.sampling_en = ESP_BLE_CTE_SAMPLING_ENABLE,
.slot_dur = ESP_BLE_CTE_SLOT_DURATION_2US,
.switching_pattern_len = sizeof(antenna_ids),
@@ -103,14 +104,14 @@ esp_ble_cte_recv_params_params_t cte_recv_params = {
};
static esp_ble_cte_req_en_params_t cte_conn_req_en = {
.conn_handle = 0xff,
.conn_handle = BLE_CONN_HDL_INVALID,
.enable = ESP_BLE_CTE_SAMPLING_ENABLE,
.cte_req_interval = 0x05,
.req_cte_len = ESP_BLE_CTE_MAX_REQUESTED_CTE_LENGTH,
.req_cte_Type = ESP_BLE_CTE_TYPE_AOA,
};
uint16_t cur_conn_hdl = 0xff;
uint16_t cur_conn_hdl = BLE_CONN_HDL_INVALID;
#define PROFILE_NUM 1
#define PROFILE_A_APP_ID 0
@@ -210,6 +211,31 @@ static char *esp_auth_req_to_str(esp_ble_auth_req_t auth_req)
return auth_str;
}
/** After a failed open or disconnect: clear connect flag and restart extended scan. */
static void cte_resume_ext_scan(void)
{
connect = false;
esp_err_t err = esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD);
if (err != ESP_OK) {
ESP_LOGE(LOG_TAG, "start ext scan failed, error = 0x%x", err);
}
}
/** Start CTE receive setup only after link is authenticated (see esp_gap_cb AUTH_CMPL). */
static void cte_enable_connection_receive_after_encrypted(void)
{
if (cur_conn_hdl == BLE_CONN_HDL_INVALID) {
ESP_LOGW(LOG_TAG, "Skip CTE receive params: no connection handle");
return;
}
ESP_LOGI(LOG_TAG, "Set CTE connection receive params after encryption, conn_handle %d", cur_conn_hdl);
cte_recv_params.conn_handle = cur_conn_hdl;
esp_err_t cte_ret = esp_ble_cte_set_connection_receive_params(&cte_recv_params);
if (cte_ret != ESP_OK) {
ESP_LOGE(LOG_TAG, "CTE set connection receive params failed, 0x%x", cte_ret);
}
}
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)
{
esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
@@ -227,6 +253,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
case ESP_GATTC_OPEN_EVT:
if (param->open.status != ESP_GATT_OK){
ESP_LOGE(LOG_TAG, "Open failed, status %x", p_data->open.status);
cte_resume_ext_scan();
break;
}
ESP_LOGI(LOG_TAG, "Open successfully, MTU %d", p_data->open.mtu);
@@ -239,19 +266,14 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break;
case ESP_GATTC_CFG_MTU_EVT:
ESP_LOGI(LOG_TAG, "MTU exchange, status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id);
if (!param->cfg_mtu.status) {
ESP_LOGI(LOG_TAG, "Set CTE connection receive params, conn_handle %d", cur_conn_hdl);
cte_recv_params.conn_handle = cur_conn_hdl;
esp_ble_cte_set_connection_receive_params(&cte_recv_params);
}
break;
case ESP_GATTC_DIS_SRVC_CMPL_EVT:
if (param->dis_srvc_cmpl.status != ESP_GATT_OK){
ESP_LOGE(LOG_TAG, "Service discover failed, status %d", param->dis_srvc_cmpl.status);
break;
}
ESP_LOGI(LOG_TAG, "Service discover complete, conn_id %d", param->dis_srvc_cmpl.conn_id);
esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid);
ESP_LOGI(LOG_TAG, "Service discover complete, conn_id %d", p_data->dis_srvc_cmpl.conn_id);
esp_ble_gattc_search_service(gattc_if, p_data->dis_srvc_cmpl.conn_id, &remote_filter_service_uuid);
break;
case ESP_GATTC_SEARCH_RES_EVT: {
ESP_LOGI(LOG_TAG, "Service search result, conn_id %x, is primary service %d", p_data->search_res.conn_id, p_data->search_res.is_primary);
@@ -314,9 +336,9 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
case ESP_GATTC_DISCONNECT_EVT:
ESP_LOGI(LOG_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason);
connect = false;
get_service = false;
esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD);
cur_conn_hdl = BLE_CONN_HDL_INVALID;
cte_resume_ext_scan();
break;
default:
break;
@@ -397,8 +419,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
ESP_LOGI(LOG_TAG, "Pairing failed, reason 0x%x",param->ble_security.auth_cmpl.fail_reason);
} else {
ESP_LOGI(LOG_TAG, "Pairing successfully, auth mode %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode));
// Enable CTE
cte_enable_connection_receive_after_encrypted();
}
break;
}
@@ -429,7 +450,10 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
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(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params);
if (esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params) != ESP_OK) {
ESP_LOGE(LOG_TAG, "Failed to open connection");
cte_resume_ext_scan();
}
}
break;
@@ -453,14 +477,17 @@ static void cte_event_handler(esp_ble_cte_cb_event_t event, esp_ble_cte_cb_param
case ESP_BLE_CTE_SET_CONN_TRANS_PARAMS_CMPL_EVT:
ESP_LOGI(LOG_TAG, "CTE set connection transmit params, status %d", param->conn_trans_params_cmpl.status);
break;
case ESP_BLE_CTE_SET_CONN_RECV_PARAMS_CMPL_EVT:
ESP_LOGI(LOG_TAG, "CTE set connection receive params, status %d", param->conn_recv_params_cmpl.status);
case ESP_BLE_CTE_SET_CONN_RECV_PARAMS_CMPL_EVT: {
uint16_t recv_cmpl_conn_hdl = param->conn_recv_params_cmpl.conn_handle;
ESP_LOGI(LOG_TAG, "CTE set connection receive params, status %d, conn_handle %d",
param->conn_recv_params_cmpl.status, recv_cmpl_conn_hdl);
if (!param->conn_recv_params_cmpl.status) {
cte_conn_req_en.conn_handle = cur_conn_hdl;
ESP_LOGI(LOG_TAG, "Enable CTE request, conn_handle %d", cur_conn_hdl);
cte_conn_req_en.conn_handle = recv_cmpl_conn_hdl;
ESP_LOGI(LOG_TAG, "Enable CTE request, conn_handle %d", recv_cmpl_conn_hdl);
esp_ble_cte_connection_cte_request_enable(&cte_conn_req_en);
}
break;
}
case ESP_BLE_CTE_SET_CONN_REQ_ENABLE_CMPL_EVT:
ESP_LOGI(LOG_TAG, "CTE set connection request enable, status %d", param->conn_req_en_cmpl.status);
break;
@@ -35,6 +35,9 @@
#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40
/* Connection_Handle is 12-bit (0x0000..0x0EFF); 0xFFFF is outside the valid range. */
#define BLE_CONN_HDL_INVALID ((uint16_t)0xFFFF)
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
@@ -68,18 +71,18 @@ esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
static uint8_t antenna_ids[2] = {0x00, 0x01};
static esp_ble_cte_conn_trans_params_t cte_conn_trans_params = {
.conn_handle = 0xff,
.conn_handle = BLE_CONN_HDL_INVALID,
.cte_types = ESP_BLE_CTE_TYPES_ALL,
.switching_pattern_len = sizeof(antenna_ids),
.antenna_ids = &antenna_ids[0],
};
static esp_ble_cte_rsp_en_params_t cte_conn_rsp_en = {
.conn_handle = 0xff,
.conn_handle = BLE_CONN_HDL_INVALID,
.enable = ESP_BLE_CTE_RESPONSE_FOR_CONNECTION_ENABLE,
};
uint16_t cur_conn_hdl = 0xff;
uint16_t cur_conn_hdl = BLE_CONN_HDL_INVALID;
struct gatts_profile_inst {
esp_gatts_cb_t gatts_cb;
@@ -315,10 +318,14 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
ESP_LOGI(LOG_TAG, "Pairing failed, reason 0x%x",param->ble_security.auth_cmpl.fail_reason);
} else {
ESP_LOGI(LOG_TAG, "Pairing successfully, auth_mode %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode));
// Setting CTE connection transmit parameters
cte_conn_trans_params.conn_handle = cur_conn_hdl;
ESP_LOGI(LOG_TAG, "Set CTE connection transmit params, conn_handle %d", cur_conn_hdl);
esp_ble_cte_set_connection_transmit_params(&cte_conn_trans_params);
if (cur_conn_hdl != BLE_CONN_HDL_INVALID) {
// Setting CTE connection transmit parameters
cte_conn_trans_params.conn_handle = cur_conn_hdl;
ESP_LOGI(LOG_TAG, "Set CTE connection transmit params, conn_handle %d", cur_conn_hdl);
esp_ble_cte_set_connection_transmit_params(&cte_conn_trans_params);
} else {
ESP_LOGW(LOG_TAG, "Skip CTE transmit params: no active connection handle");
}
}
break;
}
@@ -385,6 +392,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(LOG_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x",
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
cur_conn_hdl = BLE_CONN_HDL_INVALID;
/* start advertising again when missing the connect */
esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]);
break;
@@ -399,18 +407,18 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
case ESP_GATTS_CONGEST_EVT:
break;
case ESP_GATTS_CREAT_ATTR_TAB_EVT: {
if (param->create.status == ESP_GATT_OK){
if(param->add_attr_tab.num_handle == HRS_IDX_NB) {
if (param->add_attr_tab.status == ESP_GATT_OK) {
if (param->add_attr_tab.num_handle == HRS_IDX_NB) {
ESP_LOGI(LOG_TAG, "Attribute table create successfully, num_handle %x", param->add_attr_tab.num_handle);
memcpy(profile_handle_table, param->add_attr_tab.handles,
sizeof(profile_handle_table));
sizeof(profile_handle_table));
esp_ble_gatts_start_service(profile_handle_table[IDX_SVC]);
}else{
} else {
ESP_LOGE(LOG_TAG, "Attribute table create abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)",
param->add_attr_tab.num_handle, HRS_IDX_NB);
param->add_attr_tab.num_handle, HRS_IDX_NB);
}
}else{
ESP_LOGE(LOG_TAG, "Attribute table create failed, status %x", param->create.status);
} else {
ESP_LOGE(LOG_TAG, "Attribute table create failed, status %x", param->add_attr_tab.status);
}
break;
}
@@ -422,15 +430,17 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
static void cte_event_handler(esp_ble_cte_cb_event_t event, esp_ble_cte_cb_param_t *param)
{
switch (event) {
case ESP_BLE_CTE_SET_CONN_TRANS_PARAMS_CMPL_EVT:
ESP_LOGI(LOG_TAG, "CTE set connection transmit params, status %d", param->conn_trans_params_cmpl.status);
case ESP_BLE_CTE_SET_CONN_TRANS_PARAMS_CMPL_EVT: {
uint16_t trans_cmpl_conn_hdl = param->conn_trans_params_cmpl.conn_handle;
ESP_LOGI(LOG_TAG, "CTE set connection transmit params, status %d, conn_handle %d",
param->conn_trans_params_cmpl.status, trans_cmpl_conn_hdl);
if (!param->conn_trans_params_cmpl.status) {
ESP_LOGI(LOG_TAG, "Setting CTE connection response enable");
// Enable CTE response for connection
cte_conn_rsp_en.conn_handle = cur_conn_hdl;
ESP_LOGI(LOG_TAG, "Setting CTE connection response enable, conn_handle %d", trans_cmpl_conn_hdl);
cte_conn_rsp_en.conn_handle = trans_cmpl_conn_hdl;
esp_ble_cte_connection_cte_response_enable(&cte_conn_rsp_en);
}
break;
}
case ESP_BLE_CTE_SET_CONN_RECV_PARAMS_CMPL_EVT:
ESP_LOGI(LOG_TAG, "CTE set connection receive params, status %d", param->conn_recv_params_cmpl.status);
break;
@@ -223,7 +223,11 @@ static void start_periodic_adv(void)
{
// Create static random address
esp_bd_addr_t rand_addr;
esp_ble_gap_addr_create_static(rand_addr);
esp_err_t addr_ret = esp_ble_gap_addr_create_static(rand_addr);
if (addr_ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gap_addr_create_static failed: %s", esp_err_to_name(addr_ret));
return;
}
ESP_LOG_BUFFER_HEX(TAG, rand_addr, ESP_BD_ADDR_LEN);
@@ -243,7 +243,9 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
param->ext_adv_report.params.adv_data_len,
ESP_BLE_AD_TYPE_NAME_CMPL,
&adv_name_len);
if ((adv_name != NULL) && (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) {
if ((adv_name != NULL) && (adv_name_len > 0) &&
(adv_name_len == strlen(remote_device_name)) &&
(memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) {
// Note: If there are multiple devices with the same device name, the device may sync to an unintended one.
// It is recommended to change the default device name to ensure it is unique.
periodic_sync = true;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -41,16 +41,21 @@
#define FUNC_SEND_WAIT_SEM(func, sem) do {\
esp_err_t __err_rc = (func);\
if (__err_rc != ESP_OK) { \
ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \
ESP_LOGE(LOG_TAG, "%s failed: %s", #func, esp_err_to_name(__err_rc)); \
return; \
} \
xSemaphoreTake((sem), portMAX_DELAY); \
if (last_ble_async_status != ESP_BT_STATUS_SUCCESS) { \
ESP_LOGE(LOG_TAG, "Async completion after %s failed, status 0x%x", #func, last_ble_async_status); \
return; \
} \
xSemaphoreTake(sem, portMAX_DELAY); \
} while(0);
#define EXT_ADV_HANDLE 0
#define NUM_EXT_ADV 1
static SemaphoreHandle_t test_sem = NULL;
static esp_bt_status_t last_ble_async_status = ESP_BT_STATUS_SUCCESS;
uint8_t addr_2m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02};
@@ -113,44 +118,56 @@ static esp_ble_cte_trans_enable_params_t cte_trans_enable = {
static uint8_t periodic_adv_hdl = 0xff;
static void ble_async_complete_signal(esp_bt_status_t status)
{
last_ble_async_status = status;
if (test_sem != NULL) {
xSemaphoreGive(test_sem);
}
}
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->ext_adv_set_rand_addr.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status %d", param->ext_adv_set_rand_addr.status);
break;
case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->ext_adv_set_params.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->ext_adv_set_params.status);
break;
case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->ext_adv_data_set.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status %d", param->ext_adv_data_set.status);
break;
case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->scan_rsp_set.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT, status %d", param->scan_rsp_set.status);
break;
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->ext_adv_start.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, status %d", param->ext_adv_start.status);
break;
case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->ext_adv_stop.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT, status %d", param->ext_adv_stop.status);
break;
case ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT:
periodic_adv_hdl = param->peroid_adv_set_params.instance;
xSemaphoreGive(test_sem);
if (param->peroid_adv_set_params.status == ESP_BT_STATUS_SUCCESS) {
periodic_adv_hdl = param->peroid_adv_set_params.instance;
} else {
ESP_LOGE(LOG_TAG, "periodic adv set params failed, not updating periodic_adv_hdl");
}
ble_async_complete_signal(param->peroid_adv_set_params.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->peroid_adv_set_params.status);
break;
case ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->period_adv_data_set.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT, status %d", param->period_adv_data_set.status);
break;
case ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->period_adv_start.status);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT, status %d", param->period_adv_start.status);
break;
default:
@@ -162,11 +179,11 @@ static void cte_event_handler(esp_ble_cte_cb_event_t event, esp_ble_cte_cb_param
{
switch (event) {
case ESP_BLE_CTE_SET_CONNLESS_TRANS_PARAMS_CMPL_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->set_trans_params_cmpl.status);
ESP_LOGI(LOG_TAG, "ESP_BLE_CTE_SET_CONNLESS_TRANS_PARAMS_CMPL_EVT, status %d", param->set_trans_params_cmpl.status);
break;
case ESP_BLE_CTE_SET_CONNLESS_TRANS_ENABLE_CMPL_EVT:
xSemaphoreGive(test_sem);
ble_async_complete_signal(param->set_trans_enable_cmpl.status);
ESP_LOGI(LOG_TAG, "ESP_BLE_CTE_SET_CONNLESS_TRANS_ENABLE_CMPL_EVT, status %d", param->set_trans_enable_cmpl.status);
break;
case ESP_BLE_CTE_SET_CONNLESS_IQ_SAMPLING_ENABLE_CMPL_EVT:
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -37,15 +37,16 @@
#include "esp_ble_cte_api.h"
#define LOG_TAG "PERIODIC_SYNC"
#define FUNC_SEND_WAIT_SEM(func, sem) do {\
esp_err_t __err_rc = (func);\
if (__err_rc != ESP_OK) { \
ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \
ESP_LOGE(LOG_TAG, "%s failed: %s", #func, esp_err_to_name(__err_rc)); \
return; \
} \
xSemaphoreTake(sem, portMAX_DELAY); \
xSemaphoreTake((sem), portMAX_DELAY); \
} while(0);
#define LOG_TAG "PERIODIC_SYNC"
#define EXT_SCAN_DURATION 0
#define EXT_SCAN_PERIOD 0
@@ -136,13 +137,16 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
param->ext_adv_report.params.adv_data_len,
ESP_BLE_AD_TYPE_NAME_CMPL,
&adv_name_len);
if ((adv_name != NULL) && (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) {
const size_t remote_cap = sizeof(remote_device_name);
size_t remote_len = strnlen(remote_device_name, remote_cap);
if ((adv_name != NULL) && (adv_name_len > 0) &&
(adv_name_len <= remote_cap) &&
(adv_name_len == remote_len) &&
(memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) {
// Note: If there are multiple devices with the same device name, the device may sync to an unintended one.
// It is recommended to change the default device name to ensure it is unique.
periodic_sync = true;
char adv_temp_name[30] = {'0'};
memcpy(adv_temp_name, adv_name, adv_name_len);
ESP_LOGI(LOG_TAG, "Create sync with the peer device %s", adv_temp_name);
ESP_LOGI(LOG_TAG, "Create sync with the peer device %.*s", (int)adv_name_len, (const char *)adv_name);
periodic_adv_sync_params.sid = param->ext_adv_report.params.sid;
periodic_adv_sync_params.addr_type = param->ext_adv_report.params.addr_type;
memcpy(periodic_adv_sync_params.addr, param->ext_adv_report.params.addr, sizeof(esp_bd_addr_t));
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -180,12 +180,12 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
}
break;
case ESP_GATTC_CONNECT_EVT:
ESP_LOGI(TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR"",
p_data->connect.conn_id, ESP_BD_ADDR_HEX(p_data->connect.remote_bda));
ESP_LOGI(TAG, "Connected, conn_id %d, hci_conn_handle %d, remote "ESP_BD_ADDR_STR"",
p_data->connect.conn_id, p_data->connect.conn_handle,
ESP_BD_ADDR_HEX(p_data->connect.remote_bda));
gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id;
memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
conn_handle = p_data->connect.conn_id;
// Initialize power control after connection
conn_handle = p_data->connect.conn_handle;
init_power_control(conn_handle);
break;
case ESP_GATTC_DISCONNECT_EVT:
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -40,9 +40,10 @@
#define FUNC_SEND_WAIT_SEM(func, sem) do {\
esp_err_t __err_rc = (func);\
if (__err_rc != ESP_OK) { \
ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \
ESP_LOGE(LOG_TAG, "%s failed: %s", #func, esp_err_to_name(__err_rc)); \
return; \
} \
xSemaphoreTake(sem, portMAX_DELAY); \
xSemaphoreTake((sem), portMAX_DELAY); \
} while(0);
#define EXT_ADV_HANDLE 0
@@ -95,43 +96,50 @@ static esp_ble_gap_ext_adv_t ext_adv[1] = {
[0] = {EXT_ADV_HANDLE, 0, 0},
};
static void periodic_adv_gap_sem_give(void)
{
if (test_sem != NULL) {
xSemaphoreGive(test_sem);
}
}
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Extended advertising random address set, status %d, instance %d", param->ext_adv_set_rand_addr.status, param->ext_adv_set_rand_addr.instance);
break;
case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Extended advertising params set, status %d, instance %d", param->ext_adv_set_params.status, param->ext_adv_set_params.instance);
break;
case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Extended advertising data set, status %d, instance %d", param->ext_adv_data_set.status, param->ext_adv_data_set.instance);
break;
case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Extended advertising scan response data set, status %d, instance %d", param->scan_rsp_set.status, param->scan_rsp_set.instance);
break;
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "Extended advertising start, status %d, instance numble %d", param->ext_adv_start.status, param->ext_adv_start.instance_num);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Extended advertising start, status %d, instance number %d", param->ext_adv_start.status, param->ext_adv_start.instance_num);
break;
case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "Extended advertising start, status %d, instance numble %d", param->ext_adv_stop.status, param->ext_adv_stop.instance_num);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Extended advertising stop, status %d, instance number %d", param->ext_adv_stop.status, param->ext_adv_stop.instance_num);
break;
case ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Periodic advertising params set, status %d, instance %d", param->peroid_adv_set_params.status, param->peroid_adv_set_params.instance);
break;
case ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Periodic advertising data set, status %d, instance %d", param->period_adv_data_set.status, param->period_adv_data_set.instance);
break;
case ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT:
xSemaphoreGive(test_sem);
periodic_adv_gap_sem_give();
ESP_LOGI(LOG_TAG, "Periodic advertising start, status %d, instance %d", param->period_adv_start.status, param->period_adv_start.instance);
break;
default:
@@ -185,9 +193,18 @@ void app_main(void)
ESP_LOGE(LOG_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
return;
}
test_sem = xSemaphoreCreateBinary();
if (test_sem == NULL) {
ESP_LOGE(LOG_TAG, "Failed to create semaphore");
return;
}
ret = esp_ble_gap_register_callback(gap_event_handler);
if (ret){
ESP_LOGE(LOG_TAG, "gap register error, error code = %x", ret);
vSemaphoreDelete(test_sem);
test_sem = NULL;
return;
}
@@ -195,8 +212,6 @@ void app_main(void)
esp_bd_addr_t rand_addr;
esp_ble_gap_addr_create_static(rand_addr);
test_sem = xSemaphoreCreateBinary();
// 2M phy extend adv, Non-Connectable and Non-Scannable Undirected advertising
ESP_LOG_BUFFER_HEX(LOG_TAG, rand_addr, ESP_BD_ADDR_LEN);
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(EXT_ADV_HANDLE, &ext_adv_params_2M), test_sem);
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -84,10 +84,13 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
break;
case ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "Extended scanning stop, status %d", param->period_adv_stop.status);
ESP_LOGI(LOG_TAG, "Extended scanning stop, status %d", param->ext_scan_stop.status);
break;
case ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT:
ESP_LOGI(LOG_TAG, "Periodic advertising create sync, status %d", param->period_adv_create_sync.status);
if (param->period_adv_create_sync.status != ESP_BT_STATUS_SUCCESS) {
periodic_sync = false;
}
break;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT:
ESP_LOGI(LOG_TAG, "Periodic advertising sync cancel, status %d", param->period_adv_sync_cancel.status);
@@ -97,9 +100,13 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
break;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT:
ESP_LOGI(LOG_TAG, "Periodic advertising sync lost, sync handle %d", param->periodic_adv_sync_lost.sync_handle);
periodic_sync = false;
break;
case ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT:
ESP_LOGI(LOG_TAG, "Periodic advertising sync establish, status %d", param->periodic_adv_sync_estab.status);
if (param->periodic_adv_sync_estab.status != ESP_BT_STATUS_SUCCESS) {
periodic_sync = false;
}
ESP_LOGI(LOG_TAG, "address "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(param->periodic_adv_sync_estab.adv_addr));
ESP_LOGI(LOG_TAG, "sync handle %d sid %d perioic adv interval %d adv phy %d", param->periodic_adv_sync_estab.sync_handle,
param->periodic_adv_sync_estab.sid,
@@ -113,13 +120,16 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
param->ext_adv_report.params.adv_data_len,
ESP_BLE_AD_TYPE_NAME_CMPL,
&adv_name_len);
if ((adv_name != NULL) && (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) {
const size_t remote_cap = sizeof(remote_device_name);
size_t remote_len = strnlen(remote_device_name, remote_cap);
if ((adv_name != NULL) && (adv_name_len > 0) &&
(adv_name_len <= remote_cap) &&
(adv_name_len == remote_len) &&
(memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) {
// Note: If there are multiple devices with the same device name, the device may sync to an unintended one.
// It is recommended to change the default device name to ensure it is unique.
periodic_sync = true;
char adv_temp_name[30] = {'0'};
memcpy(adv_temp_name, adv_name, adv_name_len);
ESP_LOGI(LOG_TAG, "Create sync with the peer device %s", adv_temp_name);
ESP_LOGI(LOG_TAG, "Create sync with the peer device %.*s", (int)adv_name_len, (const char *)adv_name);
periodic_adv_sync_params.sid = param->ext_adv_report.params.sid;
periodic_adv_sync_params.addr_type = param->ext_adv_report.params.addr_type;
memcpy(periodic_adv_sync_params.addr, param->ext_adv_report.params.addr, sizeof(esp_bd_addr_t));