mirror of
https://github.com/espressif/esp-idf.git
synced 2026-01-15 04:08:11 +00:00
Merge branch 'bugfix/fix_ble_security_issue_2025_v3_v5.4' into 'release/v5.4'
fix(ble/bluedroid): Fix type mismatch and length validation in HCI packet parser (v5.4) See merge request espressif/esp-idf!44666
This commit is contained in:
@@ -65,10 +65,15 @@ void osi_mutex_unlock(osi_mutex_t *mutex)
|
||||
xSemaphoreGive(*mutex);
|
||||
}
|
||||
|
||||
/** Delete a semaphore
|
||||
* @param mutex the mutex to delete */
|
||||
/** Delete a mutex
|
||||
* @param mutex the mutex to delete
|
||||
* Note: Safe to call with NULL or uninitialized mutex (IDFGH-16853)
|
||||
*/
|
||||
void osi_mutex_free(osi_mutex_t *mutex)
|
||||
{
|
||||
if (mutex == NULL || *mutex == NULL) {
|
||||
return;
|
||||
}
|
||||
vSemaphoreDelete(*mutex);
|
||||
*mutex = NULL;
|
||||
}
|
||||
|
||||
@@ -69,9 +69,15 @@ osi_sem_take(osi_sem_t *sem, uint32_t timeout)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Deallocates a semaphore
|
||||
/** Deallocates a semaphore
|
||||
* @param sem the semaphore to delete
|
||||
* Note: Safe to call with NULL or uninitialized semaphore (IDFGH-16853)
|
||||
*/
|
||||
void osi_sem_free(osi_sem_t *sem)
|
||||
{
|
||||
if (sem == NULL || *sem == NULL) {
|
||||
return;
|
||||
}
|
||||
vSemaphoreDelete(*sem);
|
||||
*sem = NULL;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ config BT_GATTC_MAX_CACHE_CHAR
|
||||
config BT_GATTC_NOTIF_REG_MAX
|
||||
int "Max gattc notify(indication) register number"
|
||||
depends on BT_GATTC_ENABLE
|
||||
range 1 64
|
||||
range 1 255
|
||||
default 5
|
||||
help
|
||||
Maximum GATTC notify(indication) register number
|
||||
@@ -1359,6 +1359,16 @@ config BT_BLE_FEAT_CREATE_SYNC_ENH
|
||||
help
|
||||
Enable the create sync enhancements
|
||||
|
||||
config BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX
|
||||
int "Maximum retry count for periodic advertising create sync"
|
||||
depends on BT_BLE_50_EXTEND_SYNC_EN
|
||||
default 3
|
||||
range 0 16
|
||||
help
|
||||
Set the maximum retry count when periodic advertising create sync fails
|
||||
with error code 0x3E (Connection Failed to be Established).
|
||||
Set to 0 to disable retry. Default is 3.
|
||||
|
||||
menuconfig BT_BLE_FEAT_ISO_EN
|
||||
bool "Enable BLE 5.2 iso feature"
|
||||
depends on (BT_BLE_50_FEATURES_SUPPORTED && ((BT_CONTROLLER_ENABLED && SOC_BLE_AUDIO_SUPPORTED) || BT_CONTROLLER_DISABLED)) # NOERROR
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -283,6 +283,27 @@ typedef uint8_t esp_gap_ble_channels[ESP_GAP_BLE_CHANNELS_LEN];
|
||||
|
||||
#define VENDOR_HCI_CMD_MASK (0x3F << 10) /**!< 0xFC00 */
|
||||
|
||||
/**
|
||||
* @brief BLE time interval conversion macros
|
||||
*
|
||||
* These macros convert time values in milliseconds to BLE interval units.
|
||||
*
|
||||
* - Advertising interval: unit is 0.625ms (range: 20ms to 10240ms)
|
||||
* - Connection interval: unit is 1.25ms (range: 7.5ms to 4000ms)
|
||||
* - Scan interval/window: unit is 0.625ms
|
||||
* - Periodic advertising interval: unit is 1.25ms
|
||||
* - Supervision timeout: unit is 10ms (range: 100ms to 32000ms)
|
||||
*
|
||||
* @note If the input value is not an exact multiple of the unit, the result will be rounded to the nearest value.
|
||||
* For example, ESP_BLE_GAP_ADV_ITVL_MS(25) = 40 (25ms / 0.625ms = 40), but ESP_BLE_GAP_ADV_ITVL_MS(25.5) = 40 (rounded).
|
||||
*/
|
||||
#define ESP_BLE_GAP_ADV_ITVL_MS(t) ((uint16_t)((t) * 1000 / 625)) /*!< Convert advertising interval from ms to 0.625ms units. If input is not a multiple of 0.625ms, it will be rounded to the nearest value. */
|
||||
#define ESP_BLE_GAP_SCAN_ITVL_MS(t) ((uint16_t)((t) * 1000 / 625)) /*!< Convert scan interval from ms to 0.625ms units. If input is not a multiple of 0.625ms, it will be rounded to the nearest value. */
|
||||
#define ESP_BLE_GAP_SCAN_WIN_MS(t) ((uint16_t)((t) * 1000 / 625)) /*!< Convert scan window from ms to 0.625ms units. If input is not a multiple of 0.625ms, it will be rounded to the nearest value. */
|
||||
#define ESP_BLE_GAP_CONN_ITVL_MS(t) ((uint16_t)((t) * 1000 / 1250)) /*!< Convert connection interval from ms to 1.25ms units. If input is not a multiple of 1.25ms, it will be rounded to the nearest value. */
|
||||
#define ESP_BLE_GAP_PERIODIC_ADV_ITVL_MS(t) ((uint16_t)((t) * 1000 / 1250)) /*!< Convert periodic advertising interval from ms to 1.25ms units. If input is not a multiple of 1.25ms, it will be rounded to the nearest value. */
|
||||
#define ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(t) ((uint16_t)((t) / 10)) /*!< Convert supervision timeout from ms to 10ms units. If input is not a multiple of 10ms, it will be rounded to the nearest value. */
|
||||
|
||||
/* relate to BTM_BLE_AD_TYPE_xxx in stack/btm_ble_api.h */
|
||||
/// The type of advertising data(not adv_type)
|
||||
typedef enum {
|
||||
|
||||
@@ -528,6 +528,79 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
||||
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
|
||||
return FALSE;
|
||||
}
|
||||
} else if (p_data->hdr.event == BTA_GATTC_API_SEARCH_EVT) {
|
||||
/*
|
||||
* Fix for Use-After-Free (UAF) bug in service search with filter_uuid.
|
||||
*
|
||||
* Problem Description:
|
||||
* ====================
|
||||
* In BTA_GATTC_ServiceSearchRequest(), memory is allocated as:
|
||||
* [tBTA_GATTC_API_SEARCH structure][tBT_UUID data]
|
||||
*
|
||||
* The p_srvc_uuid pointer points to the tBT_UUID data located right after
|
||||
* the structure:
|
||||
* p_buf->p_srvc_uuid = (tBT_UUID *)(p_buf + 1);
|
||||
*
|
||||
* When this command is enqueued (e.g., during DISCOVER_ST state), the original
|
||||
* code only performed a shallow copy:
|
||||
* - Only sizeof(tBTA_GATTC_DATA) was allocated for cmd_data
|
||||
* - memcpy copied the pointer VALUE (not the pointed data)
|
||||
* - After the original message is freed by bta_sys_event(), the p_srvc_uuid
|
||||
* pointer becomes a dangling pointer pointing to freed memory
|
||||
*
|
||||
* Memory layout before fix:
|
||||
*
|
||||
* Original (p_data): Copy (cmd_data):
|
||||
* +------------------+----------+ +------------------+
|
||||
* | API_SEARCH | tBT_UUID | | API_SEARCH |
|
||||
* | p_srvc_uuid: --------► | | p_srvc_uuid: --------► (dangling!)
|
||||
* +------------------+----------+ +------------------+
|
||||
* ↑
|
||||
* After free(), this memory may be
|
||||
* overwritten by other allocations
|
||||
*
|
||||
* Solution:
|
||||
* =========
|
||||
* For BTA_GATTC_API_SEARCH_EVT with non-NULL p_srvc_uuid, we need to:
|
||||
* 1. Allocate extra space for tBT_UUID
|
||||
* 2. Copy the structure
|
||||
* 3. Update p_srvc_uuid to point to the new location
|
||||
* 4. Copy the tBT_UUID data
|
||||
*
|
||||
* Memory layout after fix:
|
||||
*
|
||||
* Copy (cmd_data):
|
||||
* +------------------+----------+
|
||||
* | API_SEARCH | tBT_UUID |
|
||||
* | p_srvc_uuid: --------► | (points to its own copy)
|
||||
* +------------------+----------+
|
||||
*/
|
||||
if (p_data->api_search.p_srvc_uuid != NULL) {
|
||||
/* Allocate space for structure + UUID data (deep copy) */
|
||||
len = sizeof(tBTA_GATTC_DATA) + sizeof(tBT_UUID);
|
||||
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(len)) != NULL) {
|
||||
memset(cmd_data, 0, len);
|
||||
/* Copy the structure */
|
||||
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA));
|
||||
/* Update pointer to point to the space after the structure */
|
||||
cmd_data->api_search.p_srvc_uuid = (tBT_UUID *)(cmd_data + 1);
|
||||
/* Copy the UUID data */
|
||||
memcpy(cmd_data->api_search.p_srvc_uuid,
|
||||
p_data->api_search.p_srvc_uuid, sizeof(tBT_UUID));
|
||||
} else {
|
||||
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
/* p_srvc_uuid is NULL, no extra space needed (search all services) */
|
||||
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
|
||||
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA));
|
||||
memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA));
|
||||
} else {
|
||||
APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
|
||||
memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA));
|
||||
|
||||
@@ -247,6 +247,12 @@
|
||||
#define UC_BT_BLE_FEAT_CREATE_SYNC_ENH FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX
|
||||
#define UC_BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX CONFIG_BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX
|
||||
#else
|
||||
#define UC_BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX 3
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLE_FEAT_ISO_EN
|
||||
#define UC_BT_BLE_FEAT_ISO_EN CONFIG_BT_BLE_FEAT_ISO_EN
|
||||
#else
|
||||
|
||||
@@ -296,6 +296,12 @@
|
||||
#define BLE_FEAT_CREATE_SYNC_ENH FALSE
|
||||
#endif
|
||||
|
||||
#ifdef UC_BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX
|
||||
#define BLE_FEAT_CREATE_SYNC_RETRY_MAX UC_BT_BLE_FEAT_CREATE_SYNC_RETRY_MAX
|
||||
#else
|
||||
#define BLE_FEAT_CREATE_SYNC_RETRY_MAX 3
|
||||
#endif
|
||||
|
||||
#if (UC_BT_BLE_FEAT_ISO_EN == TRUE)
|
||||
#define BLE_FEAT_ISO_EN TRUE
|
||||
#else
|
||||
|
||||
@@ -87,7 +87,7 @@ typedef struct {
|
||||
uint16_t ble_ext_adv_data_max_len;
|
||||
#endif // #if (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
uint16_t get_ble_periodic_advertiser_list_size;
|
||||
uint8_t get_ble_periodic_advertiser_list_size;
|
||||
#endif // #if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
} controller_local_param_t;
|
||||
|
||||
@@ -219,7 +219,7 @@ static void parse_ble_read_suggested_default_data_length_response(
|
||||
uint16_t *ble_default_packet_txtime_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 2 /* bytes after */);
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 4 /* bytes after: 2+2 */);
|
||||
if (stream) {
|
||||
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
|
||||
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
|
||||
@@ -235,7 +235,7 @@ static void parse_ble_read_adv_max_len_response(
|
||||
uint16_t *adv_max_len_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 1 /* bytes after */);
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 2 /* bytes after */);
|
||||
if (stream) {
|
||||
// Size: 2 Octets ; Value: 0x001F – 0x0672 ; Maximum supported advertising data length
|
||||
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
|
||||
@@ -246,7 +246,7 @@ static void parse_ble_read_adv_max_len_response(
|
||||
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
static void parse_ble_read_periodic_adv_list_size_response(
|
||||
BT_HDR *response,
|
||||
uint16_t *periodic_adv_list_size_ptr)
|
||||
uint8_t *periodic_adv_list_size_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_PERIOD_ADV_LIST_SIZE, 1 /* bytes after */);
|
||||
|
||||
@@ -114,7 +114,7 @@ typedef struct {
|
||||
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
void (*parse_ble_read_periodic_adv_list_size_response) (
|
||||
BT_HDR *response,
|
||||
uint16_t *periodic_advertiser_list_size
|
||||
uint8_t *periodic_advertiser_list_size
|
||||
);
|
||||
#endif // #if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
|
||||
@@ -39,6 +39,19 @@ typedef struct {
|
||||
tBTM_EXT_ADV_RECORD adv_record[MAX_BLE_ADV_INSTANCE] = {0};
|
||||
#endif // #if (BLE_50_EXTEND_ADV_EN == TRUE)
|
||||
|
||||
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
#if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
/* Create sync retry control block */
|
||||
typedef struct {
|
||||
bool in_use; /* Whether sync creation is in progress */
|
||||
uint8_t retry_count; /* Current retry count */
|
||||
tBTM_BLE_Periodic_Sync_Params params; /* Saved sync parameters for retry */
|
||||
} tBTM_BLE_SYNC_RETRY_CB;
|
||||
|
||||
static tBTM_BLE_SYNC_RETRY_CB sync_retry_cb = {0};
|
||||
#endif // #if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
#endif // #if (BLE_50_EXTEND_SYNC_EN == TRUE)
|
||||
|
||||
extern void btm_ble_inter_set(bool extble_inter);
|
||||
|
||||
#if !UC_BT_STACK_NO_LOG
|
||||
@@ -880,11 +893,19 @@ tBTM_STATUS BTM_BlePeriodicAdvCreateSync(tBTM_BLE_Periodic_Sync_Params *params)
|
||||
SET_BIT(option, 2);
|
||||
}
|
||||
#endif // (BLE_FEAT_CREATE_SYNC_ENH == TRUE)
|
||||
|
||||
#if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
/* Save parameters for potential retry */
|
||||
memcpy(&sync_retry_cb.params, params, sizeof(tBTM_BLE_Periodic_Sync_Params));
|
||||
sync_retry_cb.retry_count = 0;
|
||||
sync_retry_cb.in_use = true;
|
||||
#endif // #if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
if (!btsnd_hcic_ble_periodic_adv_create_sync(option, params->sid, params->addr_type,
|
||||
params->addr, params->sync_timeout, params->sync_cte_type)) {
|
||||
BTM_TRACE_ERROR("LE PA CreateSync cmd failed");
|
||||
status = BTM_ILLEGAL_VALUE;
|
||||
#if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
sync_retry_cb.in_use = false;
|
||||
#endif // #if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -942,6 +963,11 @@ tBTM_STATUS BTM_BlePeriodicAdvSyncCancel(void)
|
||||
tBTM_STATUS status = BTM_SUCCESS;
|
||||
tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0};
|
||||
|
||||
#if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
/* Clear retry state when sync is cancelled */
|
||||
sync_retry_cb.in_use = false;
|
||||
sync_retry_cb.retry_count = 0;
|
||||
#endif // #if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
if ((err = btsnd_hcic_ble_periodic_adv_create_sync_cancel()) != HCI_SUCCESS) {
|
||||
BTM_TRACE_ERROR("LE PA SyncCancel, cmd err=0x%x", err);
|
||||
status = BTM_HCI_ERROR | err;
|
||||
@@ -1399,6 +1425,48 @@ void btm_ble_periodic_adv_sync_establish_evt(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB *par
|
||||
return;
|
||||
}
|
||||
|
||||
#if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
/* Check if retry is needed for error 0x3E (Connection Failed to be Established) */
|
||||
if (params->status == HCI_ERR_CONN_FAILED_ESTABLISHMENT &&
|
||||
sync_retry_cb.in_use &&
|
||||
sync_retry_cb.retry_count < BLE_FEAT_CREATE_SYNC_RETRY_MAX) {
|
||||
|
||||
sync_retry_cb.retry_count++;
|
||||
BTM_TRACE_WARNING("%s, Create sync failed with 0x3E, retry %d/%d",
|
||||
__func__, sync_retry_cb.retry_count, BLE_FEAT_CREATE_SYNC_RETRY_MAX);
|
||||
|
||||
/* Build option from saved parameters */
|
||||
uint8_t option = 0x00;
|
||||
if (sync_retry_cb.params.filter_policy) {
|
||||
SET_BIT(option, 0);
|
||||
}
|
||||
#if (BLE_FEAT_CREATE_SYNC_ENH == TRUE)
|
||||
if (sync_retry_cb.params.reports_disabled) {
|
||||
SET_BIT(option, 1);
|
||||
}
|
||||
if (sync_retry_cb.params.filter_duplicates) {
|
||||
SET_BIT(option, 2);
|
||||
}
|
||||
#endif // (BLE_FEAT_CREATE_SYNC_ENH == TRUE)
|
||||
|
||||
/* Retry create sync with saved parameters */
|
||||
if (btsnd_hcic_ble_periodic_adv_create_sync(option,
|
||||
sync_retry_cb.params.sid,
|
||||
sync_retry_cb.params.addr_type,
|
||||
sync_retry_cb.params.addr,
|
||||
sync_retry_cb.params.sync_timeout,
|
||||
sync_retry_cb.params.sync_cte_type)) {
|
||||
/* Retry command sent successfully, wait for next event */
|
||||
return;
|
||||
}
|
||||
/* If retry command failed, fall through to report failure */
|
||||
BTM_TRACE_ERROR("%s, Retry create sync command failed", __func__);
|
||||
}
|
||||
|
||||
/* Clear retry state */
|
||||
sync_retry_cb.in_use = false;
|
||||
sync_retry_cb.retry_count = 0;
|
||||
#endif // #if (BLE_FEAT_CREATE_SYNC_RETRY_MAX > 0)
|
||||
memcpy(&cb_params.sync_estab, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB));
|
||||
|
||||
// If the user has register the callback function, should callback it to the application.
|
||||
|
||||
@@ -554,7 +554,13 @@ void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
|
||||
tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;
|
||||
|
||||
UNUSED(op_code);
|
||||
UNUSED(len);
|
||||
|
||||
/* Fix: Validate minimum length (opcode:1 + handle:2 + reason:1 = 4 bytes) */
|
||||
if (len < 4) {
|
||||
GATT_TRACE_ERROR("invalid error rsp len: %d", len);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
GATT_TRACE_DEBUG("%s", __func__);
|
||||
STREAM_TO_UINT8(opcode, p);
|
||||
@@ -800,6 +806,14 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
|
||||
handle_len = 4;
|
||||
}
|
||||
|
||||
/* Check value_len is sufficient before subtraction to prevent underflow */
|
||||
if (value_len < handle_len) {
|
||||
GATT_TRACE_ERROR("gatt_process_read_by_type_rsp: value_len(%d) < handle_len(%d), invalid response",
|
||||
value_len, handle_len);
|
||||
gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
value_len -= handle_len; /* subtract the handle pairs bytes */
|
||||
len -= 1;
|
||||
|
||||
|
||||
@@ -479,7 +479,13 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
|
||||
BOOLEAN is_need_dequeue_sr_cmd = FALSE;
|
||||
tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL;
|
||||
tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL;
|
||||
UNUSED(len);
|
||||
|
||||
/* Fix: Validate minimum length (flags: 1 byte) */
|
||||
if (len < 1) {
|
||||
GATT_TRACE_ERROR("invalid exec write req len: %d", len);
|
||||
gatt_send_error_rsp(p_tcb, GATT_INVALID_PDU, op_code, 0, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
#if GATT_CONFORMANCE_TESTING == TRUE
|
||||
if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) {
|
||||
@@ -1234,10 +1240,13 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle,
|
||||
|
||||
switch (op_code) {
|
||||
case GATT_SIGN_CMD_WRITE:
|
||||
if (op_code == GATT_SIGN_CMD_WRITE) {
|
||||
GATT_TRACE_DEBUG("Write CMD with data signing" );
|
||||
len -= GATT_AUTH_SIGN_LEN;
|
||||
/* Fix: Validate length before subtraction to prevent underflow */
|
||||
if (len < GATT_AUTH_SIGN_LEN) {
|
||||
GATT_TRACE_ERROR("signed write len too short: %d", len);
|
||||
return; /* GATT_SIGN_CMD_WRITE has no response */
|
||||
}
|
||||
GATT_TRACE_DEBUG("Write CMD with data signing" );
|
||||
len -= GATT_AUTH_SIGN_LEN;
|
||||
/* fall through */
|
||||
case GATT_CMD_WRITE:
|
||||
case GATT_REQ_WRITE:
|
||||
@@ -1473,7 +1482,13 @@ static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8
|
||||
UINT8 sec_flag, key_size, *p;
|
||||
UINT16 offset = 0, value_len = 0;
|
||||
|
||||
UNUSED (len);
|
||||
/* Fix: Validate length for GATT_REQ_READ_BLOB (needs offset: 2 bytes) */
|
||||
if (op_code == GATT_REQ_READ_BLOB && len < 2) {
|
||||
GATT_TRACE_ERROR("invalid read blob req len: %d", len);
|
||||
gatt_send_error_rsp(p_tcb, GATT_INVALID_PDU, op_code, handle, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_msg = (BT_HDR *)osi_calloc(buf_len)) == NULL) {
|
||||
GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n");
|
||||
|
||||
|
||||
@@ -715,6 +715,11 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
UINT16 cmd_len;
|
||||
UINT16 min_interval, max_interval, latency, timeout;
|
||||
|
||||
if (pkt_len < L2CAP_CMD_OVERHEAD) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - pkt too short: %d", pkt_len);
|
||||
return;
|
||||
}
|
||||
|
||||
p_pkt_end = p + pkt_len;
|
||||
|
||||
STREAM_TO_UINT8 (cmd_code, p);
|
||||
@@ -731,6 +736,10 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
case L2CAP_CMD_REJECT:
|
||||
case L2CAP_CMD_ECHO_RSP:
|
||||
case L2CAP_CMD_INFO_RSP:
|
||||
if (cmd_len < 2) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
p += 2;
|
||||
break;
|
||||
case L2CAP_CMD_ECHO_REQ:
|
||||
@@ -739,6 +748,10 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
break;
|
||||
|
||||
case L2CAP_CMD_BLE_UPDATE_REQ:
|
||||
if (cmd_len < 8) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
STREAM_TO_UINT16 (min_interval, p); /* 0x0006 - 0x0C80 */
|
||||
STREAM_TO_UINT16 (max_interval, p); /* 0x0006 - 0x0C80 */
|
||||
STREAM_TO_UINT16 (latency, p); /* 0x0000 - 0x03E8 */
|
||||
@@ -781,6 +794,10 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
break;
|
||||
|
||||
case L2CAP_CMD_BLE_UPDATE_RSP: {
|
||||
if (cmd_len < 2) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
UINT16 result = 0;
|
||||
STREAM_TO_UINT16(result, p); //result = 0 connection param accepted, result = 1 connection param rejected.
|
||||
UINT8 status = (result == 0) ? HCI_SUCCESS : HCI_ERR_PARAM_OUT_OF_RANGE;
|
||||
@@ -793,6 +810,10 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
break;
|
||||
}
|
||||
case L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ: {
|
||||
if (cmd_len < 10) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
tL2C_CCB *p_ccb = NULL;
|
||||
tL2C_RCB *p_rcb = NULL;
|
||||
UINT16 spsm;
|
||||
@@ -841,6 +862,10 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
|
||||
break;
|
||||
}
|
||||
case L2CAP_CMD_DISC_REQ: {
|
||||
if (cmd_len < 4) {
|
||||
L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len);
|
||||
return;
|
||||
}
|
||||
tL2C_CCB *p_ccb = NULL;
|
||||
UINT16 lcid;
|
||||
UINT16 rcid;
|
||||
|
||||
@@ -27,6 +27,24 @@ examples/bluetooth/bluedroid/ble:
|
||||
depends_filepatterns:
|
||||
- examples/bluetooth/bluedroid/ble/pytest_ble_test.py
|
||||
|
||||
examples/bluetooth/bluedroid/ble/ble_acl_latency/cent:
|
||||
<<: *bt_default_depends
|
||||
disable:
|
||||
- if: SOC_BLE_SUPPORTED != 1
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s3"]
|
||||
temporary: true
|
||||
reason: only enable build jobs for tested targets
|
||||
|
||||
examples/bluetooth/bluedroid/ble/ble_acl_latency/periph:
|
||||
<<: *bt_default_depends
|
||||
disable:
|
||||
- if: SOC_BLE_SUPPORTED != 1
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s3"]
|
||||
temporary: true
|
||||
reason: only enable build jobs for tested targets
|
||||
|
||||
examples/bluetooth/bluedroid/ble/ble_hid_device_demo:
|
||||
<<: *bt_default_depends
|
||||
disable:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -362,12 +362,16 @@ static void auto_io_gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_
|
||||
case ESP_GATTS_WRITE_EVT:
|
||||
ESP_LOGI(GATTS_TAG, "Characteristic write, value len %u, value ", param->write.len);
|
||||
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len);
|
||||
if (param->write.value[0]) {
|
||||
ESP_LOGI(GATTS_TAG, "LED ON!");
|
||||
led_on();
|
||||
if (param->write.len > 0) {
|
||||
if (param->write.value[0]) {
|
||||
ESP_LOGI(GATTS_TAG, "LED ON!");
|
||||
led_on();
|
||||
} else {
|
||||
ESP_LOGI(GATTS_TAG, "LED OFF!");
|
||||
led_off();
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(GATTS_TAG, "LED OFF!");
|
||||
led_off();
|
||||
ESP_LOGW(GATTS_TAG, "Empty write data received");
|
||||
}
|
||||
example_write_event_env(gatts_if, param);
|
||||
break;
|
||||
|
||||
@@ -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_acl_latency_cent)
|
||||
190
examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/README.md
Normal file
190
examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/README.md
Normal file
@@ -0,0 +1,190 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# ESP-IDF BLE ACL Latency Test - Central
|
||||
|
||||
This example demonstrates how to measure BLE ACL (Asynchronous Connection-Less) link latency. The central device scans for the peripheral, connects to it, and then performs a latency test by sending packets and measuring the round-trip time (RTT).
|
||||
|
||||
This demo should be used together with the `periph` (peripheral) example.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. The central scans for a device named `ESP_ACL_LATENCY`
|
||||
2. Upon finding the peripheral, it connects and performs MTU exchange
|
||||
3. After enabling notifications, the central sends 100 test packets (242 bytes each)
|
||||
4. For each packet, the central records the send time
|
||||
5. When the echoed packet is received via notification, it calculates RTT
|
||||
6. Finally, statistics (average, min, max latency, packet loss) are printed
|
||||
|
||||
## Test Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Central │ │ Peripheral │
|
||||
│ (GATT Client)│ │ (GATT Server)│
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ──────────── Connection Setup ─────────── │
|
||||
│ │
|
||||
│ 1. Scan for "ESP_ACL_LATENCY" │
|
||||
│ ─────────────────────────────────────────> │ Advertising
|
||||
│ │
|
||||
│ 2. Connect Request │
|
||||
│ ─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 3. Connection Established │
|
||||
│ <─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 4. MTU Exchange (517 bytes) │
|
||||
│ <─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 5. Connection Parameter Update (7.5ms) │
|
||||
│ <─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 6. Service Discovery │
|
||||
│ ─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 7. Enable Notification (CCCD) │
|
||||
│ ─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ──────────── Latency Test ───────────── │
|
||||
│ │
|
||||
│ 8. Write Test Packet [SEQ=0] ──┐ │
|
||||
│ Record send_time t1│ │
|
||||
│ ─────────────────────────────────────────> │
|
||||
│ │ │ Echo back
|
||||
│ 9. Notification [SEQ=0] │ │ via Notify
|
||||
│ Record recv_time t2│ │
|
||||
│ <───────────────────────────────────────── │
|
||||
│ │ │
|
||||
│ RTT = t2 - t1 ──┘ │
|
||||
│ Latency = RTT / 2 │
|
||||
│ │
|
||||
│ ... Repeat for SEQ 1-99 ... │
|
||||
│ │
|
||||
│ 10. Print Statistics │
|
||||
│ - Average/Min/Max Latency │
|
||||
│ - Packet Loss Rate │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ Central │ │ Peripheral │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## Test Configuration
|
||||
|
||||
The following parameters can be modified in `gattc_latency.h`:
|
||||
|
||||
| Parameter | Default Value | Description |
|
||||
|------------------------|---------------|------------------------------------|
|
||||
| TEST_PACKET_COUNT | 100 | Number of test packets to send |
|
||||
| TEST_PACKET_INTERVAL_MS| 20 | Interval between packets (ms) |
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* Two development boards with supported SoC (e.g., ESP32-C3-DevKitC, ESP32-DevKitC, etc.)
|
||||
* Two USB cables for power supply and programming
|
||||
|
||||
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information.
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
1. First, flash the `periph` example to one board:
|
||||
|
||||
```bash
|
||||
cd ../periph
|
||||
idf.py -p PORT1 flash
|
||||
```
|
||||
|
||||
2. Then, flash this `cent` example to another board:
|
||||
|
||||
```bash
|
||||
cd ../cent
|
||||
idf.py -p PORT2 flash
|
||||
```
|
||||
|
||||
### Monitor
|
||||
|
||||
Run `idf.py -p PORT monitor` to view the serial output of the central device.
|
||||
|
||||
(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
|
||||
|
||||
```
|
||||
I (362) BLE_ACL_LATENCY_CENT: BLE ACL Latency Test - Central
|
||||
I (372) phy_init: phy_version 310,dde1ba9,Jun 4 2024,16:38:11
|
||||
I (432) BLE_ACL_LATENCY_CENT: REG_EVT
|
||||
I (442) BLE_ACL_LATENCY_CENT: scan start success
|
||||
I (442) BLE_ACL_LATENCY_CENT: Central initialized, scanning for peripheral...
|
||||
I (622) BLE_ACL_LATENCY_CENT: searched device ESP_ACL_LATENCY
|
||||
I (622) BLE_ACL_LATENCY_CENT: connect to the remote device.
|
||||
I (622) BLE_ACL_LATENCY_CENT: stop scan successfully
|
||||
I (892) BLE_ACL_LATENCY_CENT: ESP_GATTC_CONNECT_EVT conn_id 0, if 4
|
||||
I (892) BLE_ACL_LATENCY_CENT: REMOTE BDA: 58:cf:79:ea:77:30
|
||||
I (892) BLE_ACL_LATENCY_CENT: open success
|
||||
I (1042) BLE_ACL_LATENCY_CENT: update connection params status = 0, min_int = 6, max_int = 6,conn_int = 6,latency = 0, timeout = 500
|
||||
I (1112) BLE_ACL_LATENCY_CENT: discover service complete conn_id 0
|
||||
I (1112) BLE_ACL_LATENCY_CENT: service found
|
||||
I (1122) BLE_ACL_LATENCY_CENT: ESP_GATTC_CFG_MTU_EVT, Status 0, MTU 517, conn_id 0
|
||||
I (1132) BLE_ACL_LATENCY_CENT: ESP_GATTC_REG_FOR_NOTIFY_EVT
|
||||
I (1172) BLE_ACL_LATENCY_CENT: write descr success
|
||||
I (1172) BLE_ACL_LATENCY_CENT: Initializing latency test...
|
||||
I (1172) LATENCY_TEST: Latency test initialized, conn_id=0, handle=42
|
||||
I (2182) BLE_ACL_LATENCY_CENT: Starting latency test...
|
||||
I (2182) LATENCY_TEST: ====================================
|
||||
I (2182) LATENCY_TEST: Starting latency test...
|
||||
I (2182) LATENCY_TEST: Packets: 100, Interval: 20 ms
|
||||
I (2182) LATENCY_TEST: ====================================
|
||||
SEQ= 0, RTT= 20040 us, Latency= 10020 us (10.02 ms)
|
||||
SEQ= 1, RTT= 18341 us, Latency= 9170 us (9.17 ms)
|
||||
SEQ= 2, RTT= 13339 us, Latency= 6669 us (6.67 ms)
|
||||
...
|
||||
SEQ= 99, RTT= 15835 us, Latency= 7917 us (7.92 ms)
|
||||
I (4192) LATENCY_TEST: All packets sent, waiting for responses...
|
||||
I (6192) LATENCY_TEST:
|
||||
I (6192) LATENCY_TEST: ========================================
|
||||
I (6192) LATENCY_TEST: Test Results Summary
|
||||
I (6192) LATENCY_TEST: ========================================
|
||||
I (6192) LATENCY_TEST: Total packets: 100
|
||||
I (6202) LATENCY_TEST: Received: 100
|
||||
I (6202) LATENCY_TEST: Lost: 0 (0.0%)
|
||||
I (6202) LATENCY_TEST: ----------------------------------------
|
||||
I (6212) LATENCY_TEST: Average latency: 7929 us (7.93 ms)
|
||||
I (6222) LATENCY_TEST: Min latency: 6667 us (6.67 ms)
|
||||
I (6222) LATENCY_TEST: Max latency: 10020 us (10.02 ms)
|
||||
I (6232) LATENCY_TEST: ========================================
|
||||
```
|
||||
|
||||
## Connection Parameters
|
||||
|
||||
The example uses optimized connection parameters for low latency:
|
||||
|
||||
| Parameter | Value | Description |
|
||||
|---------------------|----------|--------------------------------|
|
||||
| Connection Interval | 7.5 ms | Minimum allowed by BLE spec |
|
||||
| Slave Latency | 0 | No skipped connection events |
|
||||
| Supervision Timeout | 4000 ms | Connection timeout |
|
||||
| MTU | 517 | Maximum ATT MTU |
|
||||
|
||||
## Understanding the Results
|
||||
|
||||
- **RTT (Round-Trip Time)**: Time from sending a packet until receiving the echo
|
||||
- **Latency**: Estimated one-way delay (RTT / 2)
|
||||
- **Expected Results**: With 7.5ms connection interval, typical latency is 6-10ms
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "gattc_latency_demo.c" "latency_test.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#ifndef H_GATTC_LATENCY_
|
||||
#define H_GATTC_LATENCY_
|
||||
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Service and Characteristic UUIDs - must match peripheral */
|
||||
#define REMOTE_SERVICE_UUID 0x1234
|
||||
#define REMOTE_CHAR_UUID 0x5678
|
||||
|
||||
/* Profile ID */
|
||||
#define PROFILE_APP_ID 0
|
||||
#define INVALID_HANDLE 0
|
||||
|
||||
/* Remote device name */
|
||||
#define REMOTE_DEVICE_NAME "ESP_ACL_LATENCY"
|
||||
|
||||
/* Test configuration */
|
||||
#define TEST_PACKET_COUNT 100
|
||||
#define TEST_PACKET_INTERVAL_MS 20
|
||||
|
||||
/* Test packet structure */
|
||||
typedef struct {
|
||||
uint16_t seq_num;
|
||||
uint8_t payload[240];
|
||||
} __attribute__((packed)) test_packet_t;
|
||||
|
||||
/* Latency record */
|
||||
typedef struct {
|
||||
uint16_t seq;
|
||||
int64_t send_time_us;
|
||||
int64_t recv_time_us;
|
||||
bool received;
|
||||
} latency_record_t;
|
||||
|
||||
/* Latency test functions */
|
||||
void latency_test_init(uint16_t conn_id, uint16_t char_handle);
|
||||
void latency_test_start(void);
|
||||
void latency_test_handle_notify(uint8_t *data, uint16_t len);
|
||||
void latency_test_print_results(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* H_GATTC_LATENCY_ */
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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 "freertos/task.h"
|
||||
#include "gattc_latency.h"
|
||||
|
||||
#define GATTC_TAG "BLE_ACL_LATENCY_CENT"
|
||||
|
||||
#define PROFILE_NUM 1
|
||||
#define PROFILE_APP_ID 0
|
||||
|
||||
static bool connect = false;
|
||||
static bool get_server = false;
|
||||
static esp_gattc_char_elem_t *char_elem_result = NULL;
|
||||
static esp_gattc_descr_elem_t *descr_elem_result = NULL;
|
||||
|
||||
/* Declare static functions */
|
||||
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_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 gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
|
||||
/* Extern function */
|
||||
extern void latency_test_set_gattc_if(esp_gatt_if_t gattc_if);
|
||||
|
||||
static esp_bt_uuid_t remote_filter_service_uuid = {
|
||||
.len = ESP_UUID_LEN_16,
|
||||
.uuid = {.uuid16 = REMOTE_SERVICE_UUID,},
|
||||
};
|
||||
|
||||
static esp_bt_uuid_t remote_filter_char_uuid = {
|
||||
.len = ESP_UUID_LEN_16,
|
||||
.uuid = {.uuid16 = REMOTE_CHAR_UUID,},
|
||||
};
|
||||
|
||||
static esp_bt_uuid_t notify_descr_uuid = {
|
||||
.len = ESP_UUID_LEN_16,
|
||||
.uuid = {.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,},
|
||||
};
|
||||
|
||||
static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50), // 50ms
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30), // 30ms
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
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 char_handle;
|
||||
esp_bd_addr_t remote_bda;
|
||||
};
|
||||
|
||||
static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
|
||||
[PROFILE_APP_ID] = {
|
||||
.gattc_cb = gattc_profile_event_handler,
|
||||
.gattc_if = ESP_GATT_IF_NONE,
|
||||
},
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTC_REG_EVT:
|
||||
ESP_LOGI(GATTC_TAG, "REG_EVT");
|
||||
gl_profile_tab[PROFILE_APP_ID].gattc_if = gattc_if;
|
||||
esp_err_t scan_ret = esp_ble_gap_set_scan_params(&ble_scan_params);
|
||||
if (scan_ret){
|
||||
ESP_LOGE(GATTC_TAG, "set scan params error, error code = %x", scan_ret);
|
||||
}
|
||||
break;
|
||||
case ESP_GATTC_CONNECT_EVT:{
|
||||
ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d", p_data->connect.conn_id, gattc_if);
|
||||
gl_profile_tab[PROFILE_APP_ID].conn_id = p_data->connect.conn_id;
|
||||
memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
|
||||
ESP_LOGI(GATTC_TAG, "REMOTE BDA: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
p_data->connect.remote_bda[0], p_data->connect.remote_bda[1],
|
||||
p_data->connect.remote_bda[2], p_data->connect.remote_bda[3],
|
||||
p_data->connect.remote_bda[4], p_data->connect.remote_bda[5]);
|
||||
|
||||
/* Update connection parameters to minimum interval (7.5ms) for lower latency */
|
||||
esp_ble_conn_update_params_t conn_params = {0};
|
||||
memcpy(conn_params.bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
|
||||
conn_params.min_int = ESP_BLE_GAP_CONN_ITVL_MS(7.5); // 7.5ms - minimum allowed
|
||||
conn_params.max_int = ESP_BLE_GAP_CONN_ITVL_MS(7.5); // 7.5ms - minimum allowed
|
||||
conn_params.latency = 0;
|
||||
conn_params.timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(4000); // 4s
|
||||
esp_ble_gap_update_conn_params(&conn_params);
|
||||
|
||||
esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, p_data->connect.conn_id);
|
||||
if (mtu_ret){
|
||||
ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
if (param->open.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "open failed, status %d", p_data->open.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "open success");
|
||||
break;
|
||||
case ESP_GATTC_DIS_SRVC_CMPL_EVT:
|
||||
if (param->dis_srvc_cmpl.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "discover service failed, status %d", param->dis_srvc_cmpl.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "discover service 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);
|
||||
break;
|
||||
case ESP_GATTC_CFG_MTU_EVT:
|
||||
if (param->cfg_mtu.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG,"config mtu failed, error status = %x", param->cfg_mtu.status);
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id);
|
||||
break;
|
||||
case ESP_GATTC_SEARCH_RES_EVT: {
|
||||
ESP_LOGI(GATTC_TAG, "SEARCH RES: conn_id = %x is primary service %d", p_data->search_res.conn_id, p_data->search_res.is_primary);
|
||||
ESP_LOGI(GATTC_TAG, "start handle %d end handle %d current handle value %d", p_data->search_res.start_handle, p_data->search_res.end_handle, p_data->search_res.srvc_id.inst_id);
|
||||
if (p_data->search_res.srvc_id.uuid.len == ESP_UUID_LEN_16 && p_data->search_res.srvc_id.uuid.uuid.uuid16 == REMOTE_SERVICE_UUID) {
|
||||
ESP_LOGI(GATTC_TAG, "service found");
|
||||
get_server = true;
|
||||
gl_profile_tab[PROFILE_APP_ID].service_start_handle = p_data->search_res.start_handle;
|
||||
gl_profile_tab[PROFILE_APP_ID].service_end_handle = p_data->search_res.end_handle;
|
||||
ESP_LOGI(GATTC_TAG, "UUID16: %x", p_data->search_res.srvc_id.uuid.uuid.uuid16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT:
|
||||
if (p_data->search_cmpl.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
|
||||
break;
|
||||
}
|
||||
if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) {
|
||||
ESP_LOGI(GATTC_TAG, "Get service information from remote device");
|
||||
} else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) {
|
||||
ESP_LOGI(GATTC_TAG, "Get service information from flash");
|
||||
} else {
|
||||
ESP_LOGI(GATTC_TAG, "unknown service source");
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "ESP_GATTC_SEARCH_CMPL_EVT");
|
||||
if (get_server){
|
||||
uint16_t count = 0;
|
||||
esp_gatt_status_t status = esp_ble_gattc_get_attr_count( gattc_if,
|
||||
p_data->search_cmpl.conn_id,
|
||||
ESP_GATT_DB_CHARACTERISTIC,
|
||||
gl_profile_tab[PROFILE_APP_ID].service_start_handle,
|
||||
gl_profile_tab[PROFILE_APP_ID].service_end_handle,
|
||||
INVALID_HANDLE,
|
||||
&count);
|
||||
if (status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
|
||||
break;
|
||||
}
|
||||
|
||||
if (count > 0){
|
||||
char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count);
|
||||
if (!char_elem_result){
|
||||
ESP_LOGE(GATTC_TAG, "gattc no mem");
|
||||
break;
|
||||
}else{
|
||||
status = esp_ble_gattc_get_char_by_uuid( gattc_if,
|
||||
p_data->search_cmpl.conn_id,
|
||||
gl_profile_tab[PROFILE_APP_ID].service_start_handle,
|
||||
gl_profile_tab[PROFILE_APP_ID].service_end_handle,
|
||||
remote_filter_char_uuid,
|
||||
char_elem_result,
|
||||
&count);
|
||||
if (status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_char_by_uuid error");
|
||||
free(char_elem_result);
|
||||
char_elem_result = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (count > 0 && (char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)){
|
||||
gl_profile_tab[PROFILE_APP_ID].char_handle = char_elem_result[0].char_handle;
|
||||
esp_ble_gattc_register_for_notify (gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, char_elem_result[0].char_handle);
|
||||
}
|
||||
}
|
||||
free(char_elem_result);
|
||||
char_elem_result = NULL;
|
||||
}else{
|
||||
ESP_LOGE(GATTC_TAG, "no char found");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
ESP_LOGI(GATTC_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT");
|
||||
if (p_data->reg_for_notify.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "REG FOR NOTIFY failed: error status = %d", p_data->reg_for_notify.status);
|
||||
}else{
|
||||
uint16_t count = 0;
|
||||
uint16_t notify_en = 1;
|
||||
esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count( gattc_if,
|
||||
gl_profile_tab[PROFILE_APP_ID].conn_id,
|
||||
ESP_GATT_DB_DESCRIPTOR,
|
||||
gl_profile_tab[PROFILE_APP_ID].service_start_handle,
|
||||
gl_profile_tab[PROFILE_APP_ID].service_end_handle,
|
||||
gl_profile_tab[PROFILE_APP_ID].char_handle,
|
||||
&count);
|
||||
if (ret_status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
|
||||
}
|
||||
if (count > 0){
|
||||
descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count);
|
||||
if (!descr_elem_result){
|
||||
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
|
||||
}else{
|
||||
ret_status = esp_ble_gattc_get_descr_by_char_handle( gattc_if,
|
||||
gl_profile_tab[PROFILE_APP_ID].conn_id,
|
||||
p_data->reg_for_notify.handle,
|
||||
notify_descr_uuid,
|
||||
descr_elem_result,
|
||||
&count);
|
||||
if (ret_status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_descr_by_char_handle error");
|
||||
}
|
||||
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){
|
||||
ret_status = esp_ble_gattc_write_char_descr( gattc_if,
|
||||
gl_profile_tab[PROFILE_APP_ID].conn_id,
|
||||
descr_elem_result[0].handle,
|
||||
sizeof(notify_en),
|
||||
(uint8_t *)¬ify_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");
|
||||
}
|
||||
|
||||
free(descr_elem_result);
|
||||
descr_elem_result = NULL;
|
||||
}
|
||||
}
|
||||
else{
|
||||
ESP_LOGE(GATTC_TAG, "decsr not found");
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT:
|
||||
if (p_data->notify.is_notify){
|
||||
ESP_LOGD(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:");
|
||||
}else{
|
||||
ESP_LOGD(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive indicate value:");
|
||||
}
|
||||
ESP_LOGD(GATTC_TAG, "Received %d bytes of data", p_data->notify.value_len);
|
||||
|
||||
/* Handle latency test notification */
|
||||
latency_test_handle_notify(p_data->notify.value, p_data->notify.value_len);
|
||||
break;
|
||||
case ESP_GATTC_WRITE_DESCR_EVT:
|
||||
if (p_data->write.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "write descr failed, error status = %x", p_data->write.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "write descr success ");
|
||||
|
||||
/* After enabling notification, initialize and start latency test */
|
||||
ESP_LOGI(GATTC_TAG, "Initializing latency test...");
|
||||
latency_test_init(gl_profile_tab[PROFILE_APP_ID].conn_id, gl_profile_tab[PROFILE_APP_ID].char_handle);
|
||||
latency_test_set_gattc_if(gl_profile_tab[PROFILE_APP_ID].gattc_if);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
ESP_LOGI(GATTC_TAG, "Starting latency test...");
|
||||
latency_test_start();
|
||||
break;
|
||||
case ESP_GATTC_SRVC_CHG_EVT: {
|
||||
esp_bd_addr_t bda;
|
||||
memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t));
|
||||
ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_WRITE_CHAR_EVT:
|
||||
if (p_data->write.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTC_TAG, "write char failed, error status = %x", p_data->write.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(GATTC_TAG, "write char success ");
|
||||
break;
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
connect = false;
|
||||
get_server = false;
|
||||
ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
uint8_t *adv_name = NULL;
|
||||
uint8_t adv_name_len = 0;
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
|
||||
uint32_t duration = 30;
|
||||
esp_ble_gap_start_scanning(duration);
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||
if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(GATTC_TAG, "scan start failed, error status = %x", param->scan_start_cmpl.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "scan start success");
|
||||
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
|
||||
switch (scan_result->scan_rst.search_evt) {
|
||||
case ESP_GAP_SEARCH_INQ_RES_EVT:
|
||||
adv_name = esp_ble_resolve_adv_data_by_type(scan_result->scan_rst.ble_adv,
|
||||
scan_result->scan_rst.adv_data_len,
|
||||
ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
|
||||
if (adv_name != NULL) {
|
||||
if (strlen(REMOTE_DEVICE_NAME) == adv_name_len && strncmp((char *)adv_name, REMOTE_DEVICE_NAME, adv_name_len) == 0) {
|
||||
ESP_LOGI(GATTC_TAG, "searched device %s", REMOTE_DEVICE_NAME);
|
||||
if (connect == false) {
|
||||
connect = true;
|
||||
ESP_LOGI(GATTC_TAG, "connect to the remote device.");
|
||||
esp_ble_gap_stop_scanning();
|
||||
esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, scan_result->scan_rst.bda, scan_result->scan_rst.ble_addr_type, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_SEARCH_INQ_CMPL_EVT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||
if (param->scan_stop_cmpl.status != ESP_BT_STATUS_SUCCESS){
|
||||
ESP_LOGE(GATTC_TAG, "scan stop failed, error status = %x", param->scan_stop_cmpl.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "stop scan successfully");
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS){
|
||||
ESP_LOGE(GATTC_TAG, "adv stop failed, error status = %x", param->adv_stop_cmpl.status);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(GATTC_TAG, "stop adv successfully");
|
||||
break;
|
||||
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
||||
ESP_LOGI(GATTC_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
|
||||
param->update_conn_params.status,
|
||||
param->update_conn_params.min_int,
|
||||
param->update_conn_params.max_int,
|
||||
param->update_conn_params.conn_int,
|
||||
param->update_conn_params.latency,
|
||||
param->update_conn_params.timeout);
|
||||
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) {
|
||||
gl_profile_tab[PROFILE_APP_ID].gattc_if = gattc_if;
|
||||
} else {
|
||||
ESP_LOGI(GATTC_TAG, "reg app failed, app_id %04x, status %d",
|
||||
param->reg.app_id,
|
||||
param->reg.status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
int idx;
|
||||
for (idx = 0; idx < PROFILE_NUM; idx++) {
|
||||
if (gattc_if == ESP_GATT_IF_NONE || gattc_if == gl_profile_tab[idx].gattc_if) {
|
||||
if (gl_profile_tab[idx].gattc_cb) {
|
||||
gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(GATTC_TAG, "BLE ACL Latency Test - Central");
|
||||
|
||||
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(GATTC_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(GATTC_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(GATTC_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gattc_register_callback(esp_gattc_cb);
|
||||
if(ret){
|
||||
ESP_LOGE(GATTC_TAG, "%s gattc register failed, error code = %x", __func__, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gap_register_callback(esp_gap_cb);
|
||||
if(ret){
|
||||
ESP_LOGE(GATTC_TAG, "%s gap register failed, error code = %x", __func__, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gattc_app_register(PROFILE_APP_ID);
|
||||
if (ret){
|
||||
ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x", __func__, ret);
|
||||
}
|
||||
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(517);
|
||||
if (local_mtu_ret){
|
||||
ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
|
||||
}
|
||||
|
||||
ESP_LOGI(GATTC_TAG, "Central initialized, scanning for peripheral...");
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_random.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_gattc_api.h"
|
||||
#include "gattc_latency.h"
|
||||
|
||||
static const char *TAG = "LATENCY_TEST";
|
||||
|
||||
/* Global variables */
|
||||
static latency_record_t records[TEST_PACKET_COUNT];
|
||||
static uint16_t test_conn_id = 0;
|
||||
static uint16_t char_handle = 0;
|
||||
static test_packet_t send_packets[TEST_PACKET_COUNT];
|
||||
static bool test_running = false;
|
||||
static bool test_initialized = false;
|
||||
static esp_gatt_if_t test_gattc_if = 0;
|
||||
|
||||
/**
|
||||
* Fill random data
|
||||
*/
|
||||
static void
|
||||
fill_random_data(uint8_t *data, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
data[i] = esp_random() & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize latency test
|
||||
*/
|
||||
void
|
||||
latency_test_init(uint16_t conn_id, uint16_t handle)
|
||||
{
|
||||
test_conn_id = conn_id;
|
||||
char_handle = handle;
|
||||
test_running = false;
|
||||
test_initialized = true;
|
||||
|
||||
memset(records, 0, sizeof(records));
|
||||
memset(send_packets, 0, sizeof(send_packets));
|
||||
|
||||
ESP_LOGI(TAG, "Latency test initialized, conn_id=%d, handle=%d", conn_id, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set GATT interface
|
||||
*/
|
||||
void
|
||||
latency_test_set_gattc_if(esp_gatt_if_t gattc_if)
|
||||
{
|
||||
test_gattc_if = gattc_if;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send single test packet
|
||||
*/
|
||||
static int
|
||||
send_test_packet(uint16_t seq)
|
||||
{
|
||||
if (char_handle == 0 || test_gattc_if == 0) {
|
||||
ESP_LOGE(TAG, "Test not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare packet */
|
||||
test_packet_t *pkt = &send_packets[seq];
|
||||
pkt->seq_num = seq;
|
||||
fill_random_data(pkt->payload, sizeof(pkt->payload));
|
||||
|
||||
/* Record send time */
|
||||
records[seq].seq = seq;
|
||||
records[seq].send_time_us = esp_timer_get_time();
|
||||
records[seq].received = false;
|
||||
|
||||
/* Send write command (write without response) */
|
||||
esp_err_t ret = esp_ble_gattc_write_char(test_gattc_if,
|
||||
test_conn_id,
|
||||
char_handle,
|
||||
sizeof(test_packet_t),
|
||||
(uint8_t *)pkt,
|
||||
ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Write failed: %s", esp_err_to_name(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task
|
||||
*/
|
||||
static void
|
||||
latency_test_task(void *arg)
|
||||
{
|
||||
ESP_LOGI(TAG, "====================================");
|
||||
ESP_LOGI(TAG, "Starting latency test...");
|
||||
ESP_LOGI(TAG, "Packets: %d, Interval: %d ms", TEST_PACKET_COUNT, TEST_PACKET_INTERVAL_MS);
|
||||
ESP_LOGI(TAG, "====================================");
|
||||
|
||||
test_running = true;
|
||||
|
||||
/* Send all test packets */
|
||||
for (int i = 0; i < TEST_PACKET_COUNT; i++) {
|
||||
int rc = send_test_packet(i);
|
||||
if (rc != 0) {
|
||||
ESP_LOGW(TAG, "Send failed for seq=%d", i);
|
||||
}
|
||||
|
||||
/* Wait interval */
|
||||
vTaskDelay(pdMS_TO_TICKS(TEST_PACKET_INTERVAL_MS));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "All packets sent, waiting for responses...");
|
||||
|
||||
/* Wait for all responses */
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
|
||||
/* Print results */
|
||||
latency_test_print_results();
|
||||
|
||||
test_running = false;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start latency test
|
||||
*/
|
||||
void
|
||||
latency_test_start(void)
|
||||
{
|
||||
if (test_running) {
|
||||
ESP_LOGW(TAG, "Test already running");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_initialized || char_handle == 0) {
|
||||
ESP_LOGE(TAG, "Test not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
xTaskCreate(latency_test_task, "latency_test", 4096, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle notification
|
||||
*/
|
||||
void
|
||||
latency_test_handle_notify(uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (!test_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Record receive time */
|
||||
int64_t recv_time = esp_timer_get_time();
|
||||
|
||||
/* Parse packet */
|
||||
if (len < sizeof(test_packet_t)) {
|
||||
ESP_LOGW(TAG, "Invalid packet size: %d", len);
|
||||
return;
|
||||
}
|
||||
|
||||
test_packet_t *recv_pkt = (test_packet_t *)data;
|
||||
uint16_t seq = recv_pkt->seq_num;
|
||||
|
||||
/* Validate sequence number */
|
||||
if (seq >= TEST_PACKET_COUNT) {
|
||||
ESP_LOGW(TAG, "Invalid seq number: %d", seq);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify data consistency */
|
||||
if (memcmp(recv_pkt, &send_packets[seq], sizeof(test_packet_t)) != 0) {
|
||||
ESP_LOGW(TAG, "Data mismatch for seq=%d", seq);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Record receive time */
|
||||
records[seq].recv_time_us = recv_time;
|
||||
records[seq].received = true;
|
||||
|
||||
/* Calculate latency */
|
||||
int64_t rtt_us = recv_time - records[seq].send_time_us;
|
||||
int64_t latency_us = rtt_us / 2;
|
||||
|
||||
/* Print real-time result */
|
||||
printf("SEQ=%3d, RTT=%6lld us, Latency=%6lld us (%.2f ms)\n",
|
||||
seq, rtt_us, latency_us, (float)latency_us / 1000.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print statistics
|
||||
*/
|
||||
void
|
||||
latency_test_print_results(void)
|
||||
{
|
||||
int64_t total_latency = 0;
|
||||
int64_t min_latency = INT64_MAX;
|
||||
int64_t max_latency = 0;
|
||||
int valid_count = 0;
|
||||
int lost_count = 0;
|
||||
|
||||
/* Collect statistics */
|
||||
for (int i = 0; i < TEST_PACKET_COUNT; i++) {
|
||||
if (records[i].received) {
|
||||
int64_t rtt = records[i].recv_time_us - records[i].send_time_us;
|
||||
int64_t latency = rtt / 2;
|
||||
|
||||
total_latency += latency;
|
||||
valid_count++;
|
||||
|
||||
if (latency < min_latency) {
|
||||
min_latency = latency;
|
||||
}
|
||||
if (latency > max_latency) {
|
||||
max_latency = latency;
|
||||
}
|
||||
} else {
|
||||
lost_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print results */
|
||||
ESP_LOGI(TAG, "");
|
||||
ESP_LOGI(TAG, "========================================");
|
||||
ESP_LOGI(TAG, " Test Results Summary");
|
||||
ESP_LOGI(TAG, "========================================");
|
||||
ESP_LOGI(TAG, "Total packets: %d", TEST_PACKET_COUNT);
|
||||
ESP_LOGI(TAG, "Received: %d", valid_count);
|
||||
ESP_LOGI(TAG, "Lost: %d (%.1f%%)", lost_count,
|
||||
(float)lost_count * 100.0 / TEST_PACKET_COUNT);
|
||||
|
||||
if (valid_count > 0) {
|
||||
int64_t avg_latency = total_latency / valid_count;
|
||||
|
||||
ESP_LOGI(TAG, "----------------------------------------");
|
||||
ESP_LOGI(TAG, "Average latency: %lld us (%.2f ms)", avg_latency,
|
||||
(float)avg_latency / 1000.0);
|
||||
ESP_LOGI(TAG, "Min latency: %lld us (%.2f ms)", min_latency,
|
||||
(float)min_latency / 1000.0);
|
||||
ESP_LOGI(TAG, "Max latency: %lld us (%.2f ms)", max_latency,
|
||||
(float)max_latency / 1000.0);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "========================================");
|
||||
ESP_LOGI(TAG, "");
|
||||
}
|
||||
@@ -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_BT_ENABLED=y
|
||||
# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set
|
||||
@@ -0,0 +1,13 @@
|
||||
# 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_BT_BLE_50_FEATURES_SUPPORTED is not set
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set
|
||||
CONFIG_BT_LE_HCI_EVT_BUF_SIZE=257
|
||||
|
||||
# XTAL Freq Config
|
||||
CONFIG_XTAL_FREQ_26=y
|
||||
CONFIG_XTAL_FREQ=26
|
||||
@@ -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_acl_latency_periph)
|
||||
@@ -0,0 +1,124 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# ESP-IDF BLE ACL Latency Test - Peripheral
|
||||
|
||||
This example demonstrates how to measure BLE ACL (Asynchronous Connection-Less) link latency. The peripheral acts as a GATT server that echoes back any data written to it, allowing the central device to measure round-trip time (RTT) and calculate one-way latency.
|
||||
|
||||
This demo should be used together with the `cent` (central) example.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. The peripheral advertises with the device name `ESP_ACL_LATENCY`
|
||||
2. When a central device connects, the peripheral accepts the connection
|
||||
3. The peripheral provides a GATT service with a characteristic that supports both write and notify
|
||||
4. When data is written to the characteristic, the peripheral immediately echoes it back via notification
|
||||
5. The central measures the time between sending and receiving to calculate RTT and latency
|
||||
|
||||
## Test Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Central │ │ Peripheral │
|
||||
│ (GATT Client)│ │ (GATT Server)│
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ │ Advertising
|
||||
│ │ "ESP_ACL_LATENCY"
|
||||
│ Scan & Connect │
|
||||
│ ─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Connection Established │
|
||||
│ <─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ MTU Exchange, Service Discovery │
|
||||
│ <─────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ──────────── Latency Test ───────────── │
|
||||
│ │
|
||||
│ Write [SEQ=N, 242 bytes] │
|
||||
│ ─────────────────────────────────────────> │
|
||||
│ ┌───────┴───────┐
|
||||
│ │ Echo the same │
|
||||
│ │ data back via │
|
||||
│ │ Notification │
|
||||
│ └───────┬───────┘
|
||||
│ Notification [SEQ=N, 242 bytes] │
|
||||
│ <───────────────────────────────────────── │
|
||||
│ │
|
||||
│ ... Repeat for 100 packets ... │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ Central │ │ Peripheral │
|
||||
└──────────────┘ └──────────────┘
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ Latency Calculation │
|
||||
├─────────────────────────────────┤
|
||||
│ RTT = recv_time - send_time │
|
||||
│ One-way Latency = RTT / 2 │
|
||||
│ Typical Result: 6-10 ms │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* Two development boards with supported SoC (e.g., ESP32-C3-DevKitC, ESP32-DevKitC, etc.)
|
||||
* Two USB cables for power supply and programming
|
||||
|
||||
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information.
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board:
|
||||
|
||||
```bash
|
||||
idf.py -p PORT flash
|
||||
```
|
||||
|
||||
(Replace PORT with the serial port name, e.g., `/dev/ttyUSB0` on Linux or `COM3` on Windows)
|
||||
|
||||
### Monitor
|
||||
|
||||
Run `idf.py -p PORT monitor` to view the serial output.
|
||||
|
||||
(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
|
||||
|
||||
```
|
||||
I (362) BLE_ACL_LATENCY_PERIPH: BLE ACL Latency Test - Peripheral
|
||||
I (372) phy_init: phy_version 310,dde1ba9,Jun 4 2024,16:38:11
|
||||
I (432) BLE_ACL_LATENCY_PERIPH: GATT server register, status=0, app_id=0
|
||||
I (442) BLE_ACL_LATENCY_PERIPH: Create attribute table successfully, the number handle=4
|
||||
I (442) BLE_ACL_LATENCY_PERIPH: SERVICE_START_EVT, status=0, service_handle=40
|
||||
I (452) BLE_ACL_LATENCY_PERIPH: Advertising started
|
||||
I (462) BLE_ACL_LATENCY_PERIPH: Peripheral ready, waiting for connection...
|
||||
I (5532) BLE_ACL_LATENCY_PERIPH: ESP_GATTS_CONNECT_EVT, conn_id=0, remote 7c:df:a1:66:a6:00
|
||||
I (5892) BLE_ACL_LATENCY_PERIPH: MTU exchange, MTU=517
|
||||
I (5912) BLE_ACL_LATENCY_PERIPH: Connection params updated: status=0, conn_int=6, latency=0, timeout=500
|
||||
```
|
||||
|
||||
## GATT Service Structure
|
||||
|
||||
| Attribute | UUID | Properties | Description |
|
||||
|-----------|--------|-------------------|---------------------------------|
|
||||
| Service | 0x1234 | - | Latency test service |
|
||||
| Char | 0x5678 | Write, Notify | Write data, receive echo back |
|
||||
| CCCD | 0x2902 | Read, Write | Client Characteristic Config |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "gatts_latency_demo.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#ifndef H_GATTS_LATENCY_
|
||||
#define H_GATTS_LATENCY_
|
||||
|
||||
#include "esp_gatts_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Service UUID */
|
||||
#define LATENCY_SERVICE_UUID 0x1234
|
||||
#define LATENCY_CHAR_UUID 0x5678
|
||||
|
||||
/* Profile ID */
|
||||
#define PROFILE_APP_ID 0
|
||||
|
||||
/* Handle table */
|
||||
enum {
|
||||
LATENCY_IDX_SVC,
|
||||
LATENCY_IDX_CHAR,
|
||||
LATENCY_IDX_CHAR_VAL,
|
||||
LATENCY_IDX_CHAR_CFG,
|
||||
|
||||
LATENCY_IDX_NB,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* H_GATTS_LATENCY_ */
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_bt.h"
|
||||
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gatt_common_api.h"
|
||||
#include "gatts_latency.h"
|
||||
|
||||
#define GATTS_TAG "BLE_ACL_LATENCY_PERIPH"
|
||||
|
||||
#define DEVICE_NAME "ESP_ACL_LATENCY"
|
||||
#define GATTS_NUM_HANDLE 4
|
||||
|
||||
static uint8_t adv_config_done = 0;
|
||||
#define adv_config_flag (1 << 0)
|
||||
#define scan_rsp_config_flag (1 << 1)
|
||||
|
||||
/* Advertisement data */
|
||||
static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = false,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), // 7.5ms (minimum)
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20), // 20ms
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
.service_data_len = 0,
|
||||
.p_service_data = NULL,
|
||||
.service_uuid_len = 0,
|
||||
.p_service_uuid = NULL,
|
||||
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
|
||||
};
|
||||
|
||||
/* Scan response data */
|
||||
static esp_ble_adv_data_t scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
.service_data_len = 0,
|
||||
.p_service_data = NULL,
|
||||
.service_uuid_len = 0,
|
||||
.p_service_uuid = NULL,
|
||||
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
|
||||
};
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20), // 20ms
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40), // 40ms
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
};
|
||||
|
||||
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 */
|
||||
#define PROFILE_NUM 1
|
||||
#define PROFILE_APP_IDX 0
|
||||
|
||||
static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
|
||||
[PROFILE_APP_IDX] = {
|
||||
.gatts_cb = gatts_profile_event_handler,
|
||||
.gatts_if = ESP_GATT_IF_NONE,
|
||||
},
|
||||
};
|
||||
|
||||
/* Service */
|
||||
static const uint16_t GATTS_SERVICE_UUID = LATENCY_SERVICE_UUID;
|
||||
static const uint16_t GATTS_CHAR_UUID = LATENCY_CHAR_UUID;
|
||||
|
||||
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 uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
|
||||
static const uint8_t char_prop_write_notify = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
|
||||
static uint8_t char_ccc[2] = {0x00, 0x00};
|
||||
|
||||
/* Full Database Description - Used to add attributes into the database */
|
||||
static const esp_gatts_attr_db_t gatt_db[LATENCY_IDX_NB] =
|
||||
{
|
||||
/* Service Declaration */
|
||||
[LATENCY_IDX_SVC] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
|
||||
sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID), (uint8_t *)&GATTS_SERVICE_UUID}},
|
||||
|
||||
/* Characteristic Declaration */
|
||||
[LATENCY_IDX_CHAR] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
|
||||
sizeof(uint8_t), sizeof(uint8_t), (uint8_t *)&char_prop_write_notify}},
|
||||
|
||||
/* Characteristic Value */
|
||||
[LATENCY_IDX_CHAR_VAL] =
|
||||
{{ESP_GATT_RSP_BY_APP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
512, 0, NULL}},
|
||||
|
||||
/* Client Characteristic Configuration Descriptor */
|
||||
[LATENCY_IDX_CHAR_CFG] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
sizeof(uint16_t), sizeof(char_ccc), (uint8_t *)char_ccc}},
|
||||
};
|
||||
|
||||
static uint16_t handle_table[LATENCY_IDX_NB];
|
||||
|
||||
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_ADV_DATA_SET_COMPLETE_EVT:
|
||||
adv_config_done &= (~adv_config_flag);
|
||||
if (adv_config_done == 0){
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||
adv_config_done &= (~scan_rsp_config_flag);
|
||||
if (adv_config_done == 0){
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(GATTS_TAG, "Advertising start failed");
|
||||
} else {
|
||||
ESP_LOGI(GATTS_TAG, "Advertising started");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(GATTS_TAG, "Advertising stop failed");
|
||||
} else {
|
||||
ESP_LOGI(GATTS_TAG, "Advertising stopped");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
||||
ESP_LOGI(GATTS_TAG, "Connection params updated: 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(GATTS_TAG, "GATT server register, status=%d, app_id=%d", param->reg.status, param->reg.app_id);
|
||||
gl_profile_tab[PROFILE_APP_IDX].service_id.is_primary = true;
|
||||
gl_profile_tab[PROFILE_APP_IDX].service_id.id.inst_id = 0x00;
|
||||
gl_profile_tab[PROFILE_APP_IDX].service_id.id.uuid.len = ESP_UUID_LEN_16;
|
||||
gl_profile_tab[PROFILE_APP_IDX].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID;
|
||||
|
||||
esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(DEVICE_NAME);
|
||||
if (set_dev_name_ret){
|
||||
ESP_LOGE(GATTS_TAG, "Set device name failed, error code = %x", set_dev_name_ret);
|
||||
}
|
||||
|
||||
esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
|
||||
if (ret){
|
||||
ESP_LOGE(GATTS_TAG, "Config adv data failed, error code = %x", ret);
|
||||
}
|
||||
adv_config_done |= adv_config_flag;
|
||||
|
||||
ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
|
||||
if (ret){
|
||||
ESP_LOGE(GATTS_TAG, "Config scan response data failed, error code = %x", ret);
|
||||
}
|
||||
adv_config_done |= scan_rsp_config_flag;
|
||||
|
||||
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, LATENCY_IDX_NB, 0);
|
||||
if (create_attr_ret){
|
||||
ESP_LOGE(GATTS_TAG, "Create attr table failed, error code = %x", create_attr_ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_READ_EVT:
|
||||
ESP_LOGD(GATTS_TAG, "ESP_GATTS_READ_EVT");
|
||||
break;
|
||||
case ESP_GATTS_WRITE_EVT:
|
||||
if (!param->write.is_prep){
|
||||
ESP_LOGD(GATTS_TAG, "GATT_WRITE_EVT, handle=%d, value len=%d", param->write.handle, param->write.len);
|
||||
|
||||
if (handle_table[LATENCY_IDX_CHAR_VAL] == param->write.handle) {
|
||||
/* Echo the data back via notification */
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, handle_table[LATENCY_IDX_CHAR_VAL],
|
||||
param->write.len, param->write.value, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_EXEC_WRITE_EVT:
|
||||
case ESP_GATTS_MTU_EVT:
|
||||
ESP_LOGI(GATTS_TAG, "MTU exchange, MTU=%d", param->mtu.mtu);
|
||||
break;
|
||||
case ESP_GATTS_CONF_EVT:
|
||||
break;
|
||||
case ESP_GATTS_START_EVT:
|
||||
ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status=%d, service_handle=%d", param->start.status, param->start.service_handle);
|
||||
break;
|
||||
case ESP_GATTS_CONNECT_EVT:
|
||||
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id=%d, remote "ESP_BD_ADDR_STR"",
|
||||
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
|
||||
gl_profile_tab[PROFILE_APP_IDX].conn_id = param->connect.conn_id;
|
||||
break;
|
||||
case ESP_GATTS_DISCONNECT_EVT:
|
||||
ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT, reason=0x%x", param->disconnect.reason);
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
break;
|
||||
case ESP_GATTS_CREAT_ATTR_TAB_EVT:{
|
||||
if (param->add_attr_tab.status != ESP_GATT_OK){
|
||||
ESP_LOGE(GATTS_TAG, "Create attribute table failed, error code=0x%x", param->add_attr_tab.status);
|
||||
}
|
||||
else if (param->add_attr_tab.num_handle != LATENCY_IDX_NB){
|
||||
ESP_LOGE(GATTS_TAG, "Create attribute table abnormally, num_handle (%d) \
|
||||
doesn't equal to LATENCY_IDX_NB(%d)", param->add_attr_tab.num_handle, LATENCY_IDX_NB);
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(GATTS_TAG, "Create attribute table successfully, the number handle=%d",param->add_attr_tab.num_handle);
|
||||
memcpy(handle_table, param->add_attr_tab.handles, sizeof(handle_table));
|
||||
esp_ble_gatts_start_service(handle_table[LATENCY_IDX_SVC]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTS_STOP_EVT:
|
||||
case ESP_GATTS_OPEN_EVT:
|
||||
case ESP_GATTS_CANCEL_OPEN_EVT:
|
||||
case ESP_GATTS_CLOSE_EVT:
|
||||
case ESP_GATTS_LISTEN_EVT:
|
||||
case ESP_GATTS_CONGEST_EVT:
|
||||
case ESP_GATTS_UNREG_EVT:
|
||||
case ESP_GATTS_DELETE_EVT:
|
||||
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 == ESP_GATTS_REG_EVT) {
|
||||
if (param->reg.status == ESP_GATT_OK) {
|
||||
gl_profile_tab[PROFILE_APP_IDX].gatts_if = gatts_if;
|
||||
} else {
|
||||
ESP_LOGE(GATTS_TAG, "Reg app failed, app_id %04x, status %d",
|
||||
param->reg.app_id,
|
||||
param->reg.status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
do {
|
||||
int idx;
|
||||
for (idx = 0; idx < PROFILE_NUM; idx++) {
|
||||
if (gatts_if == ESP_GATT_IF_NONE || gatts_if == gl_profile_tab[idx].gatts_if) {
|
||||
if (gl_profile_tab[idx].gatts_cb) {
|
||||
gl_profile_tab[idx].gatts_cb(event, gatts_if, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ESP_LOGI(GATTS_TAG, "BLE ACL Latency Test - Peripheral");
|
||||
|
||||
/* 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(GATTS_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(GATTS_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(GATTS_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE(GATTS_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(GATTS_TAG, "gatts register error, error code = %x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gap_register_callback(gap_event_handler);
|
||||
if (ret){
|
||||
ESP_LOGE(GATTS_TAG, "gap register error, error code = %x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gatts_app_register(PROFILE_APP_ID);
|
||||
if (ret){
|
||||
ESP_LOGE(GATTS_TAG, "gatts app register error, error code = %x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(517);
|
||||
if (local_mtu_ret){
|
||||
ESP_LOGE(GATTS_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
|
||||
}
|
||||
|
||||
ESP_LOGI(GATTS_TAG, "Peripheral ready, waiting for connection...");
|
||||
}
|
||||
@@ -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_BT_ENABLED=y
|
||||
# CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set
|
||||
@@ -0,0 +1,13 @@
|
||||
# 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_BT_BLE_50_FEATURES_SUPPORTED is not set
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set
|
||||
CONFIG_BT_LE_HCI_EVT_BUF_SIZE=257
|
||||
|
||||
# XTAL Freq Config
|
||||
CONFIG_XTAL_FREQ_26=y
|
||||
CONFIG_XTAL_FREQ=26
|
||||
@@ -5,6 +5,55 @@
|
||||
|
||||
The purpose of the Apple Notification Center Service (ANCS) is to give Bluetooth accessories (that connect to iOS devices through a Bluetooth low-energy link) a simple and convenient way to access many kinds of notifications that are generated on iOS devices.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ ESP32 │ │ iOS Device │
|
||||
│ (ANCS Client)│ │ (ANCS Server)│
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Connection ─────────── │
|
||||
│ │
|
||||
│ 1. Start Advertising │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 2. iOS connects & pairs │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── Service Discovery ─────────── │
|
||||
│ │
|
||||
│ 3. Discover ANCS Service │
|
||||
│ (UUID: 7905F431-B5CE-4E99-A40F-4B1E122D00D0) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 4. Subscribe to Notification Source │
|
||||
│ (UUID: 9FBF120D-6301-42D9-8C58-25E699A21DBD) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 5. Subscribe to Data Source │
|
||||
│ (UUID: 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Notification Flow ─────────── │
|
||||
│ │
|
||||
│ 6. New notification on iOS │
|
||||
│ (Call, SMS, Email, App, etc.) │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ 7. Request notification details │
|
||||
│ (Write to Control Point) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 8. Receive notification attributes │
|
||||
│ (Title, Message, App, etc.) │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ ESP32 │ │ iOS Device │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -83,8 +83,8 @@ static uint8_t hidd_service_uuid128[] = {
|
||||
static esp_ble_adv_data_t adv_config = {
|
||||
.set_scan_rsp = false,
|
||||
.include_txpower = false,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), //slave connection min interval
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20), //slave connection max interval
|
||||
.appearance = ESP_BLE_APPEARANCE_GENERIC_HID,
|
||||
.service_uuid_len = sizeof(hidd_service_uuid128),
|
||||
.p_service_uuid = hidd_service_uuid128,
|
||||
@@ -99,8 +99,8 @@ static esp_ble_adv_data_t scan_rsp_config = {
|
||||
};
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x100,
|
||||
.adv_int_max = 0x100,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(160),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(160),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RPA_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -5,6 +5,52 @@
|
||||
|
||||
This example is to test the Bluetooth compatibility and mobile phones.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ ESP32 BLE │ │ Mobile Phone │
|
||||
│ (Compatibility) │ │ (LightBlue App) │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
│ 1. Initialize BLE │
|
||||
│ 2. Create GATT Services │
|
||||
│ 3. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Test Scenarios ─────────── │
|
||||
│ │
|
||||
│ Scan & Discover │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ │
|
||||
│ Connection Request │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ │
|
||||
│ Service Discovery │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ │
|
||||
│ Read Characteristic │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ Read Response │
|
||||
│ ════════════════════════════════════════════>│
|
||||
│ │
|
||||
│ Write Characteristic │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ Write Confirmation │
|
||||
│ ════════════════════════════════════════════>│
|
||||
│ │
|
||||
│ Enable Notification │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ Notification Data │
|
||||
│ ════════════════════════════════════════════>│
|
||||
│ │
|
||||
│ Disconnection │
|
||||
│ <═══════════════════════════════════════════>│
|
||||
│ │
|
||||
┌────────┴─────────┐ ┌────────┴─────────┐
|
||||
│ ESP32 BLE │ │ Mobile Phone │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -101,8 +101,8 @@ static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x20,
|
||||
.max_interval = 0x40,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(40),
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(80),
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //test_manufacturer,
|
||||
@@ -118,8 +118,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x20,
|
||||
.max_interval = 0x40,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(40),
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(80),
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
@@ -132,8 +132,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
#endif /* CONFIG_SET_RAW_ADV_DATA */
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x40,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -5,11 +5,43 @@
|
||||
|
||||
This example demonstrates Eddystone-compatible BLE scanning of eddystone frame, including UID and URL.
|
||||
|
||||
Eddystone is an open beacon protocol specification from Google aimed at improving “proximity-based experiences”
|
||||
Eddystone is an open beacon protocol specification from Google aimed at improving "proximity-based experiences"
|
||||
with support for both Android and iOS smart device platforms.
|
||||
|
||||
Learn more on [Beacons](https://developers.google.com/nearby/notifications/get-started) and [Eddystone](https://github.com/google/eddystone).
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Eddystone Sender │ │Eddystone Receiver│
|
||||
│ (Advertiser) │ │ (this example) │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
│ Broadcasting Eddystone Frames │ 1. Initialize BLE
|
||||
│ │ 2. Start Scanning
|
||||
│ │
|
||||
│ ─────────── Scanning ─────────── │
|
||||
│ │
|
||||
│ Eddystone UID Frame │
|
||||
│ ═══════════════════════════════════════════>│
|
||||
│ │ 3. Detect Eddystone
|
||||
│ │ 4. Parse UID Frame:
|
||||
│ │ - Namespace ID
|
||||
│ │ - Instance ID
|
||||
│ │ - RSSI
|
||||
│ │
|
||||
│ Eddystone URL Frame │
|
||||
│ ═══════════════════════════════════════════>│
|
||||
│ │ 5. Parse URL Frame:
|
||||
│ │ - Decode URL
|
||||
│ │ - TX Power
|
||||
│ │
|
||||
┌────────┴─────────┐ ┌────────┴─────────┐
|
||||
│ Eddystone Sender │ │Eddystone Receiver│
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -39,8 +39,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
|
||||
@@ -5,11 +5,43 @@
|
||||
|
||||
This example demonstrates Eddystone-compatible BLE sending of eddystone frame, including UID and URL and TLM.
|
||||
|
||||
Eddystone is an open beacon protocol specification from Google aimed at improving “proximity-based experiences”
|
||||
Eddystone is an open beacon protocol specification from Google aimed at improving "proximity-based experiences"
|
||||
with support for both Android and iOS smart device platforms.
|
||||
|
||||
Learn more on [Beacons](https://developers.google.com/nearby/notifications/get-started) and [Eddystone](https://github.com/google/eddystone).
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Eddystone Sender │ │ Eddystone Receiver│
|
||||
│ (Advertiser) │ │ (Scanner) │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
│ 1. Initialize BLE │
|
||||
│ 2. Configure Eddystone Frame: │
|
||||
│ - UID Frame (Namespace + Instance) │
|
||||
│ - URL Frame (Encoded URL) │
|
||||
│ - TLM Frame (Telemetry Data) │
|
||||
│ 3. Set Raw Advertising Data │
|
||||
│ 4. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Broadcasting ─────────── │
|
||||
│ │
|
||||
│ Eddystone Advertisement Packet │
|
||||
│ ═══════════════════════════════════════════>│
|
||||
│ (Broadcast periodically) │
|
||||
│ │
|
||||
│ │ Parse Frame:
|
||||
│ │ - UID: Namespace ID
|
||||
│ │ - URL: Decoded URL
|
||||
│ │ - TLM: Battery, Temp
|
||||
│ │
|
||||
┌────────┴─────────┐ ┌────────┴─────────┐
|
||||
│ Eddystone Sender │ │ Eddystone Receiver│
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -36,8 +36,8 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* par
|
||||
static void eddystone_send_raw(const esp_eddystone_result_t *res);
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -13,6 +13,54 @@ This example implement a BLE HID device profile related functions, in which the
|
||||
Users can choose different reports according to their own application scenarios.
|
||||
BLE HID profile inheritance and USB HID class.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ BLE HID │ │ Host │
|
||||
│ Device │ │ (PC/Phone) │
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Initialization ─────────── │
|
||||
│ │
|
||||
│ 1. Create HID Service │
|
||||
│ - HID Information Char │
|
||||
│ - Report Map Char │
|
||||
│ - Report Chars (Mouse/Keyboard/Consumer) │
|
||||
│ - HID Control Point Char │
|
||||
│ 2. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Connection & Pairing ─────────── │
|
||||
│ │
|
||||
│ Scan & Connect │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Pairing (Bonding) │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ Enable Report Notification │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── HID Data Transfer ─────────── │
|
||||
│ │
|
||||
│ Send Keyboard Report │
|
||||
│ (Key Press/Release) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Send Mouse Report │
|
||||
│ (X/Y Movement, Buttons) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Send Consumer Report │
|
||||
│ (Volume +/-, Media Control) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ BLE HID │ │ Host │
|
||||
│ Device │ │ (PC/Phone) │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -67,8 +67,8 @@ static esp_ble_adv_data_t hidd_adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), //slave connection min interval
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20), //slave connection max interval
|
||||
.appearance = 0x03c0, //HID Generic,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
@@ -80,8 +80,8 @@ static esp_ble_adv_data_t hidd_adv_data = {
|
||||
};
|
||||
|
||||
static esp_ble_adv_params_t hidd_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x30,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(30),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
//.peer_addr =
|
||||
|
||||
@@ -5,6 +5,61 @@
|
||||
|
||||
From welcoming people as they arrive at a sporting event to providing information about a nearby museum exhibit, iBeacon opens a new world of possibilities for location awareness, and countless opportunities for interactivity between iOS devices and iBeacon hardware.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ iBeacon System │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ iBeacon Sender │ │ iBeacon Receiver │
|
||||
│ (Advertiser) │ │ (Scanner) │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
│ ─────────── Sender Operation ─────────── │
|
||||
│ │
|
||||
│ 1. Initialize BLE │
|
||||
│ 2. Configure iBeacon Data: │
|
||||
│ - Proximity UUID (16 bytes) │
|
||||
│ - Major ID (2 bytes) │
|
||||
│ - Minor ID (2 bytes) │
|
||||
│ - TX Power (1 byte) │
|
||||
│ 3. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Broadcasting ─────────── │
|
||||
│ │
|
||||
│ iBeacon Advertisement Packet │
|
||||
│ ═══════════════════════════════════════════>│
|
||||
│ (Broadcast every ~100ms) │
|
||||
│ │
|
||||
│ │ ─── Receiver ───
|
||||
│ │
|
||||
│ │ 1. Start Scan
|
||||
│ │ 2. Receive Adv
|
||||
│ │ 3. Parse iBeacon:
|
||||
│ │ - UUID
|
||||
│ │ - Major/Minor
|
||||
│ │ - RSSI
|
||||
│ │ 4. Calculate
|
||||
│ │ Distance
|
||||
│ │
|
||||
┌────────┴─────────┐ ┌────────┴─────────┐
|
||||
│ iBeacon Sender │ │ iBeacon Receiver │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ iBeacon Packet Structure │
|
||||
├─────────────────────────────────┤
|
||||
│ Prefix: 9 bytes │
|
||||
│ UUID: 16 bytes │
|
||||
│ Major: 2 bytes │
|
||||
│ Minor: 2 bytes │
|
||||
│ TX Power: 1 byte │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -43,15 +43,15 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
#elif (IBEACON_MODE == IBEACON_SENDER)
|
||||
static esp_ble_adv_params_t ble_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_NONCONN_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -7,6 +7,52 @@
|
||||
|
||||
**This example relies on the BLE controller. please use the chip modules listed under Supported Targets.**
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ BLE Multi-Connection Central │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────┐
|
||||
│ Multi-Conn Cent │
|
||||
│ (GATTC + GATTS) │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
│ 1. Initialize GATT Client & Server
|
||||
│ 2. Set Random Address
|
||||
│ 3. Start Scanning
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ Scan for Peripherals │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Found peripheral?
|
||||
│
|
||||
├───────────────────┬───────────────────┬───────────────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ...
|
||||
│ Peripheral 1 │ │ Peripheral 2 │ │ Peripheral 3 │
|
||||
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
|
||||
│ │ │
|
||||
│ ─────────── For Each Peripheral ───────────
|
||||
│
|
||||
│ 4. Stop Scanning
|
||||
│ 5. Create Connection
|
||||
│ 6. Update Scheduling Length
|
||||
│ 7. Connection Established
|
||||
│ 8. Add to Peer List
|
||||
│ 9. Change Random Address
|
||||
│ 10. Resume Scanning
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ All peripherals connected (up to CONFIG limit) │
|
||||
│ Maintain multiple connections │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -101,7 +101,7 @@ const static esp_ble_conn_params_t phy_1m_conn_params = {
|
||||
.interval_min = BLE_PREF_CONN_ITVL_MS * 1000 / 1250,
|
||||
.interval_max = BLE_PREF_CONN_ITVL_MS * 1000 / 1250,
|
||||
.latency = 0,
|
||||
.supervision_timeout = 600,
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
.min_ce_len = BLE_PREF_CE_LEN,
|
||||
.max_ce_len = BLE_PREF_CE_LEN,
|
||||
};
|
||||
@@ -113,7 +113,7 @@ const static esp_ble_conn_params_t phy_2m_conn_params = {
|
||||
.interval_min = BLE_PREF_CONN_ITVL_MS * 1000 / 1250,
|
||||
.interval_max = BLE_PREF_CONN_ITVL_MS * 1000 / 1250,
|
||||
.latency = 0,
|
||||
.supervision_timeout = 600,
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
.min_ce_len = BLE_PREF_CE_LEN,
|
||||
.max_ce_len = BLE_PREF_CE_LEN,
|
||||
};
|
||||
@@ -124,7 +124,7 @@ const static esp_ble_conn_params_t phy_coded_conn_params = {
|
||||
.interval_min = BLE_PREF_CONN_ITVL_MS * 1000 / 1250,
|
||||
.interval_max = BLE_PREF_CONN_ITVL_MS * 1000 / 1250,
|
||||
.latency = 0,
|
||||
.supervision_timeout = 600,
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
.min_ce_len = BLE_PREF_CE_LEN,
|
||||
.max_ce_len = BLE_PREF_CE_LEN,
|
||||
};
|
||||
@@ -160,8 +160,8 @@ struct gatts_profile_inst
|
||||
|
||||
#if (BLE50_SUPPORTED == 0)
|
||||
esp_ble_adv_params_t legacy_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x20,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
@@ -171,8 +171,8 @@ esp_ble_adv_params_t legacy_adv_params = {
|
||||
#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,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
|
||||
@@ -7,6 +7,45 @@
|
||||
|
||||
**This example relies on the BLE controller. Please use the chip modules listed under Supported Targets.**
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ BLE Multi-Connection Peripheral │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
│ Multi-Conn Prph │ │ Centrals │
|
||||
│ (GATT Server) │ │ (1, 2, 3, ...) │
|
||||
└─────────┬─────────┘ └─────────┬─────────┘
|
||||
│ │
|
||||
│ 1. Initialize GATT Server │
|
||||
│ 2. Create Service │
|
||||
│ 3. Set Random Address │
|
||||
│ 4. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Connection Loop ───────────
|
||||
│ │
|
||||
│ Central 1 connects │
|
||||
│ <───────────────────────────│
|
||||
│ │
|
||||
│ 5. Stop Advertising │
|
||||
│ 6. Connection Established │
|
||||
│ 7. Change Random Address │
|
||||
│ 8. Restart Advertising │
|
||||
│ │
|
||||
│ Central 2 connects │
|
||||
│ <───────────────────────────│
|
||||
│ │
|
||||
│ ... Repeat for each Central ...
|
||||
│ │
|
||||
▼ │
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ Maintain multiple connections simultaneously │
|
||||
│ (Limited by CONFIG_BT_ACL_CONNECTIONS) │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -68,8 +68,8 @@ static esp_ble_gap_ext_adv_t ext_adv[1] = {
|
||||
|
||||
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,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
@@ -82,8 +82,8 @@ esp_ble_gap_ext_adv_params_t ext_adv_params = {
|
||||
};
|
||||
#else
|
||||
static esp_ble_adv_params_t legacy_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -9,6 +9,71 @@
|
||||
|
||||
This vendor-specific custom profile is implemented in [spp_client_demo.c](../ble_spp_client/main/spp_client_demo.c) and [spp_server_demo.c](../ble_spp_server/main/ble_spp_server_demo.c).
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ BLE SPP Data Flow │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
SPP Client SPP Server
|
||||
┌───────────┐ ┌───────────┐
|
||||
│ UART │ │ UART │
|
||||
│ Terminal │ │ Terminal │
|
||||
└─────┬─────┘ └─────┬─────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌───────────┐ ┌───────────┐
|
||||
│ uart_task │ │ uart_task │
|
||||
└─────┬─────┘ └─────┬─────┘
|
||||
│ │
|
||||
│ ─────────── Connection Phase ─────────── │
|
||||
│ │
|
||||
│ 1. Scan for SPP Server │ Advertising
|
||||
│ ───────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 2. Connect │
|
||||
│ ───────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 3. MTU Exchange (200 bytes) │
|
||||
│ <────────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ 4. Service Discovery │
|
||||
│ ───────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 5. Enable Notification (CCCD) │
|
||||
│ ───────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Data Exchange ─────────── │
|
||||
│ │
|
||||
│ UART Input ──> WriteNoRsp (SPP_DATA_RECV_CHAR) │
|
||||
│ ───────────────────────────────────────────────────> │──> UART Output
|
||||
│ │
|
||||
│ UART Output <── Notification (SPP_DATA_NOTIFY_CHAR) │
|
||||
│ <─────────────────────────────────────────────────── │<── UART Input
|
||||
│ │
|
||||
┌─────┴─────┐ ┌─────┴─────┐
|
||||
│ SPP Client│ │SPP Server │
|
||||
└───────────┘ └───────────┘
|
||||
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ SPP Characteristics │
|
||||
├─────────────────────────────────┤
|
||||
│ SPP_DATA_RECV (0xABF1) │
|
||||
│ - Client writes data here │
|
||||
│ │
|
||||
│ SPP_DATA_NOTIFY (0xABF2) │
|
||||
│ - Server sends data here │
|
||||
│ │
|
||||
│ SPP_COMMAND (0xABF3) │
|
||||
│ - Command channel │
|
||||
│ │
|
||||
│ SPP_STATUS (0xABF4) │
|
||||
│ - Status notification │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -89,8 +89,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
@@ -255,7 +255,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
|
||||
phy_1m_conn_params.interval_max = 32;
|
||||
phy_1m_conn_params.interval_min = 32;
|
||||
phy_1m_conn_params.latency = 0;
|
||||
phy_1m_conn_params.supervision_timeout = 600;
|
||||
phy_1m_conn_params.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000);
|
||||
esp_ble_gatt_creat_conn_params_t creat_conn_params = {0};
|
||||
memcpy(&creat_conn_params.remote_bda, scan_result->scan_rst.bda,ESP_BD_ADDR_LEN);
|
||||
creat_conn_params.remote_addr_type = scan_result->scan_rst.ble_addr_type;
|
||||
|
||||
@@ -5,6 +5,42 @@
|
||||
|
||||
For description of this application please refer to [ESP-IDF GATT CLIENT SPP Example](../ble_spp_client/README.md)
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
SPP Server SPP Client
|
||||
┌───────────┐ ┌───────────┐
|
||||
│ UART │ │ UART │
|
||||
│ Terminal │ │ Terminal │
|
||||
└─────┬─────┘ └─────┬─────┘
|
||||
│ │
|
||||
│ ─────────── Initialization ─────────── │
|
||||
│ │
|
||||
│ 1. Create SPP Service (UUID: 0xABF0) │
|
||||
│ 2. Add Characteristics │
|
||||
│ 3. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Connection ─────────── │
|
||||
│ │
|
||||
│ Scan & Connect │
|
||||
│ <─────────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Connection Established │
|
||||
│ ─────────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ ─────────── Data Exchange ─────────── │
|
||||
│ │
|
||||
│ UART Input ──> Notification (SPP_DATA_NOTIFY_CHAR) │
|
||||
│ ─────────────────────────────────────────────────────>│──> UART Output
|
||||
│ │
|
||||
│ UART Output <── WriteNoRsp (SPP_DATA_RECV_CHAR) │
|
||||
│ <─────────────────────────────────────────────────── │<── UART Input
|
||||
│ │
|
||||
┌─────┴─────┐ ┌─────┴─────┐
|
||||
│SPP Server │ │ SPP Client│
|
||||
└───────────┘ └───────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -52,11 +52,11 @@ static const uint16_t spp_service_uuid = 0xABF0;
|
||||
|
||||
static const uint8_t spp_adv_data[23] = {
|
||||
/* Flags */
|
||||
0x02,0x01,0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
/* Complete List of 16-bit Service Class UUIDs */
|
||||
0x03,0x03,0xF0,0xAB,
|
||||
0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xF0, 0xAB,
|
||||
/* Complete Local Name in advertising */
|
||||
0x0F,0x09, 'E', 'S', 'P', '_', 'S', 'P', 'P', '_', 'S', 'E', 'R','V', 'E', 'R'
|
||||
0x0F, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'S', 'P', 'P', '_', 'S', 'E', 'R','V', 'E', 'R'
|
||||
};
|
||||
|
||||
static uint16_t spp_mtu_size = SPP_GATT_MTU_SIZE;
|
||||
@@ -79,8 +79,8 @@ static esp_bd_addr_t spp_remote_bda = {0x0,};
|
||||
static uint16_t spp_handle_table[SPP_IDX_NB];
|
||||
|
||||
static esp_ble_adv_params_t spp_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -6,6 +6,49 @@
|
||||
This is the demo used to test the BLE throughput, this demo should used with throughput server demo together.
|
||||
The throughput of BLE can up to 720-767 Kbps between to ESP32 board.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Throughput │ │ Throughput │
|
||||
│ Client │ │ Server │
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Connection Setup ─────────── │
|
||||
│ │
|
||||
│ 1. Scan for "THROUGHPUT_DEMO" │ Advertising
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 2. Connect │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 3. MTU Exchange (517 bytes) │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ 4. Enable Notification │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Throughput Test ─────────── │
|
||||
│ │
|
||||
│ Mode 1: Notify Test (Server → Client) │
|
||||
│ <═══════════════════════════════════════════════ │
|
||||
│ Continuous notifications (514 bytes/packet) │
|
||||
│ │
|
||||
│ Mode 2: Write Test (Client → Server) │
|
||||
│ ═══════════════════════════════════════════════> │
|
||||
│ Continuous writes (514 bytes/packet) │
|
||||
│ │
|
||||
│ ─────────── Statistics ─────────── │
|
||||
│ │
|
||||
│ Calculate: Bytes/s, bits/s │
|
||||
│ Expected: 600-767 Kbps │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ Throughput │ │ Throughput │
|
||||
│ Client │ │ Server │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -95,8 +95,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
@@ -412,7 +412,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
|
||||
phy_1m_conn_params.interval_min = 32;
|
||||
#endif
|
||||
phy_1m_conn_params.latency = 0;
|
||||
phy_1m_conn_params.supervision_timeout = 600;
|
||||
phy_1m_conn_params.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000);
|
||||
|
||||
esp_ble_gatt_creat_conn_params_t creat_conn_params = {0};
|
||||
memcpy(&creat_conn_params.remote_bda, scan_result->scan_rst.bda, ESP_BD_ADDR_LEN);
|
||||
|
||||
@@ -6,6 +6,48 @@
|
||||
This is the demo used to test the BLE throughput, this demo should used with throughput client demo together.
|
||||
The throughput of BLE can up to 720-767 Kbps between to ESP32 board.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Throughput Server│ │ Throughput Client│
|
||||
│ (this example) │ │ │
|
||||
└────────┬─────────┘ └────────┬─────────┘
|
||||
│ │
|
||||
│ 1. Initialize BLE │
|
||||
│ 2. Create GATT Service │
|
||||
│ 3. Add Characteristic (Notify) │
|
||||
│ 4. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Connection Setup ─────────── │
|
||||
│ │
|
||||
│ Connection Request │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ │
|
||||
│ 5. Accept Connection │
|
||||
│ │
|
||||
│ MTU Exchange (517 bytes) │
|
||||
│ <═══════════════════════════════════════════>│
|
||||
│ │
|
||||
│ Enable Notification │
|
||||
│ <════════════════════════════════════════════│
|
||||
│ │
|
||||
│ ─────────── Throughput Test ─────────── │
|
||||
│ │
|
||||
│ Notification Data (large packets) │
|
||||
│ ════════════════════════════════════════════>│
|
||||
│ Notification Data (large packets) │
|
||||
│ ════════════════════════════════════════════>│
|
||||
│ ... (continuous data stream) ... │
|
||||
│ ════════════════════════════════════════════>│
|
||||
│ │ Calculate
|
||||
│ │ Throughput
|
||||
│ │
|
||||
┌────────┴─────────┐ ┌────────┴─────────┐
|
||||
│ Throughput Server│ │ Throughput Client│
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -127,8 +127,8 @@ static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x000C, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), //slave connection min interval
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(15), //slave connection max interval
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
@@ -143,8 +143,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006,
|
||||
.max_interval = 0x000C,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5),
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(15),
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
@@ -158,8 +158,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
#endif /* CONFIG_EXAMPLE_SET_RAW_ADV_DATA */
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
//.peer_addr =
|
||||
|
||||
@@ -5,6 +5,65 @@
|
||||
|
||||
This example shows how to use ESP APIs to create a GATT Client.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ GATT Client │ │ GATT Server │
|
||||
│ (this example)│ │(gatt_server) │
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Scanning Phase ─────────── │
|
||||
│ │
|
||||
│ 1. Start Scan │ Advertising
|
||||
│ ───────────────────────────────────────────────> │ "ESP_GATTS_DEMO"
|
||||
│ │
|
||||
│ 2. Found "ESP_GATTS_DEMO" │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── Connection Phase ─────────── │
|
||||
│ │
|
||||
│ 3. Connect │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 4. Connection Established │
|
||||
│ <─────────────────────────────────────────────────│
|
||||
│ │
|
||||
│ 5. MTU Exchange │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ ─────────── Service Discovery ─────────── │
|
||||
│ │
|
||||
│ 6. Discover Service (UUID: 0x00FF) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 7. Service Found │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ 8. Get Characteristic (UUID: 0xFF01) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Enable Notification ─────────── │
|
||||
│ │
|
||||
│ 9. Register for Notify │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 10. Write CCCD (Enable Notify) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Data Exchange ─────────── │
|
||||
│ │
|
||||
│ 11. Write Characteristic │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 12. Receive Notification │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ GATT Client │ │ GATT Server │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -69,8 +69,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,59 @@
|
||||
|
||||
This example shows how to use the ESP BLE security APIs to secure connect to and encrypt with peer devices.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Security │ │ Security │
|
||||
│ Client │ │ Server │
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Security Parameters Setup ───────── │
|
||||
│ │
|
||||
│ 1. Set IO Capability │
|
||||
│ 2. Set Auth Mode (Bonding, MITM, SC) │
|
||||
│ 3. Set Key Distribution │
|
||||
│ │
|
||||
│ ─────────── Connection Phase ─────────── │
|
||||
│ │
|
||||
│ 4. Scan for Server │ Advertising
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 5. Connect │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Connection Established │
|
||||
│ <─────────────────────────────────────────────────│
|
||||
│ │
|
||||
│ ─────────── Pairing & Encryption ─────────── │
|
||||
│ │
|
||||
│ 6. Start Encryption (esp_ble_set_encryption) │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ 7. Exchange Pairing Features │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ 8. Generate Keys (LTK, IRK, CSRK) │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ 9. Encrypt Link │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ 10. ESP_GAP_BLE_AUTH_CMPL_EVT │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── Secure Data Exchange ─────────── │
|
||||
│ │
|
||||
│ Encrypted GATT Operations │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ Security │ │ Security │
|
||||
│ Client │ │ Server │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -57,8 +57,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RPA_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,56 @@ To test this example, you can run [gatt_security_client_demo](../gatt_security_c
|
||||
|
||||
Please, check this [tutorial](tutorial/Gatt_Security_Server_Example_Walkthrough.md) for more information about this example.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ Security │ │ Security │
|
||||
│ Server │ │ Client │
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Initialization ─────────── │
|
||||
│ │
|
||||
│ 1. Set Security Parameters │
|
||||
│ - IO Capability │
|
||||
│ - Auth Requirements │
|
||||
│ - Key Size │
|
||||
│ 2. Create GATT Service │
|
||||
│ 3. Start Advertising │
|
||||
│ │
|
||||
│ ─────────── Connection ─────────── │
|
||||
│ │
|
||||
│ Scan & Connect │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── Security Request ─────────── │
|
||||
│ │
|
||||
│ ESP_GAP_BLE_SEC_REQ_EVT │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Send Security Response │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Pairing Process ─────────── │
|
||||
│ │
|
||||
│ Exchange Pairing Features │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ Key Generation & Distribution │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ Link Encrypted │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ ESP_GAP_BLE_AUTH_CMPL_EVT │
|
||||
│ ───────────────────────────────────────────────> │
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ Security │ │ Security │
|
||||
│ Server │ │ Client │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -49,8 +49,8 @@ static uint8_t sec_service_uuid[16] = {
|
||||
static esp_ble_adv_data_t heart_rate_adv_config = {
|
||||
.set_scan_rsp = false,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), //slave connection min interval
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20), //slave connection max interval
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
@@ -69,8 +69,8 @@ static esp_ble_adv_data_t heart_rate_scan_rsp_config = {
|
||||
};
|
||||
|
||||
static esp_ble_adv_params_t heart_rate_adv_params = {
|
||||
.adv_int_min = 0x100,
|
||||
.adv_int_max = 0x100,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(160),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(160),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RPA_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -13,6 +13,62 @@ To test this demo, we can run the [gatt_client_demo](../gatt_client), which can
|
||||
|
||||
Please, check this [tutorial](tutorial/Gatt_Server_Example_Walkthrough.md) for more information about this example.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ GATT Server │ │ GATT Client │
|
||||
│(this example)│ │(gatt_client) │
|
||||
└──────┬───────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ ─────────── Initialization ─────────── │
|
||||
│ │
|
||||
│ 1. Register GATT Server App │
|
||||
│ 2. Create Service (UUID: 0x00FF) │
|
||||
│ 3. Add Characteristic (UUID: 0xFF01) │
|
||||
│ 4. Add Descriptor (CCCD) │
|
||||
│ 5. Start Service │
|
||||
│ 6. Start Advertising "ESP_GATTS_DEMO" │
|
||||
│ │
|
||||
│ ─────────── Connection Phase ─────────── │
|
||||
│ │
|
||||
│ Scan & Connect │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Connection Established │
|
||||
│ ─────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ MTU Exchange │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ ─────────── Service Discovery ─────────── │
|
||||
│ │
|
||||
│ Service Discovery │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Return Service Info │
|
||||
│ ─────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ ─────────── Enable Notification ─────────── │
|
||||
│ │
|
||||
│ Write CCCD (0x0001) │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Notification Enabled │
|
||||
│ │
|
||||
│ ─────────── Data Exchange ─────────── │
|
||||
│ │
|
||||
│ Write Request │
|
||||
│ <─────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Send Notification │
|
||||
│ ─────────────────────────────────────────────────>│
|
||||
│ │
|
||||
┌──────┴───────┐ ┌──────┴───────┐
|
||||
│ GATT Server │ │ GATT Client │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -118,8 +118,8 @@ static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = false,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), //slave connection min interval
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20), //slave connection max interval
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
@@ -149,8 +149,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
#endif /* CONFIG_SET_RAW_ADV_DATA */
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
//.peer_addr =
|
||||
|
||||
@@ -7,6 +7,56 @@ This example shows how to create a GATT service with an attribute table defined
|
||||
|
||||
Please, check this [tutorial](tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md) for more information about this example.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ GATT Server Initialization Flow │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────┐
|
||||
│ GATT Server │
|
||||
└─────────┬─────────┘
|
||||
│
|
||||
│ 1. Define Attribute Table (gatt_db[])
|
||||
│ ┌─────────────────────────────────────┐
|
||||
│ │ [0] Service Declaration │
|
||||
│ │ [1] Char Declaration │
|
||||
│ │ [2] Char Value │
|
||||
│ │ [3] CCCD (Notify Enable) │
|
||||
│ │ ... │
|
||||
│ └─────────────────────────────────────┘
|
||||
│
|
||||
│ 2. esp_ble_gatts_create_attr_tab()
|
||||
│ - Create all attributes at once
|
||||
│
|
||||
│ 3. ESP_GATTS_CREAT_ATTR_TAB_EVT
|
||||
│ - Receive handle table
|
||||
│
|
||||
│ 4. esp_ble_gatts_start_service()
|
||||
│
|
||||
│ 5. Start Advertising
|
||||
│
|
||||
▼
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
│ GATT Server │ │ GATT Client │
|
||||
│ (Advertising) │ │ │
|
||||
└─────────┬─────────┘ └─────────┬─────────┘
|
||||
│ │
|
||||
│ Scan & Connect │
|
||||
│ <───────────────────────────────────────────│
|
||||
│ │
|
||||
│ Service Discovery │
|
||||
│ ───────────────────────────────────────────>│
|
||||
│ │
|
||||
│ Read/Write/Notify Operations │
|
||||
│ <──────────────────────────────────────────>│
|
||||
│ │
|
||||
┌─────────┴─────────┐ ┌─────────┴─────────┐
|
||||
│ GATT Server │ │ GATT Client │
|
||||
└───────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -94,8 +94,8 @@ static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5), //slave connection min interval
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20), //slave connection max interval
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //test_manufacturer,
|
||||
@@ -111,8 +111,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006,
|
||||
.max_interval = 0x0010,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5),
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20),
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
@@ -125,8 +125,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
#endif /* CONFIG_SET_RAW_ADV_DATA */
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -3,34 +3,169 @@
|
||||
|
||||
# ESP-IDF Gatt Client Multi Connection Example
|
||||
|
||||
This example shows the usage of APIs to create a GATT multi-connection client. It can be used to connect to three GATT servers at the same time.
|
||||
This example demonstrates how to use the BLE GATT client APIs to create a multi-connection client that can connect to multiple GATT servers simultaneously. By default, it supports connecting to up to 3 GATT servers at the same time.
|
||||
|
||||
To test this example, please run [gatt_server_demo](../gatt_server) to create three GATT server devices, namely ESP_GATTS_DEMO_a, ESP_GATTS_DEMO_b and ESP_GATTS_DEMO_c, `Gatt_client_multi_connection_demo` will connect to these three gatt server demos, and then exchange data.
|
||||
## How It Works
|
||||
|
||||
Please, check this [tutorial](tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md) for more information about this example.
|
||||
The example creates three GATT client profiles (A, B, C), each corresponding to one connection. It scans for devices with specific names, connects to them, discovers services, registers for notifications, and exchanges data with each server.
|
||||
|
||||
### Target Devices
|
||||
|
||||
The example searches for three GATT servers with the following names:
|
||||
- `ESP_GATTS_DEMO_a` → Profile A
|
||||
- `ESP_GATTS_DEMO_b` → Profile B
|
||||
- `ESP_GATTS_DEMO_c` → Profile C
|
||||
|
||||
You can use the [gatt_server](../gatt_server) example to create these server devices by modifying the device name.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ GATT Client Multi-Connection │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌──────────────────┐
|
||||
│ GATTC Client │
|
||||
│ (Multi-Connect) │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
│ 1. Start Scanning
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ Scan for BLE Devices │
|
||||
│ Looking for: ESP_GATTS_DEMO_a, _b, _c │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Found device matching name?
|
||||
│
|
||||
├─────────────────────┬─────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
|
||||
│ ESP_GATTS_DEMO_a│ │ ESP_GATTS_DEMO_b│ │ ESP_GATTS_DEMO_c│
|
||||
│ (Server A) │ │ (Server B) │ │ (Server C) │
|
||||
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ For Each Connected Device: │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ 2. Stop Scanning │
|
||||
│ 3. Connect (esp_ble_gattc_enh_open) │
|
||||
│ 4. MTU Exchange │
|
||||
│ 5. Service Discovery (UUID: 0x00FF) │
|
||||
│ 6. Find Characteristic (UUID: 0xFF01) │
|
||||
│ 7. Register for Notification │
|
||||
│ 8. Write CCCD (Enable Notification) │
|
||||
│ 9. Write Test Data (35 bytes) │
|
||||
│ 10. Resume Scanning for remaining devices │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ All 3 devices connected?
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ Stop Scanning │
|
||||
│ Continue receiving notifications from all devices │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
┌────────────────────────────────────┐
|
||||
│ Connection Sequence Detail │
|
||||
└────────────────────────────────────┘
|
||||
|
||||
Client Server
|
||||
│ │
|
||||
│ ─────────── Connection Phase ─────────── │
|
||||
│ │
|
||||
│ Connect Request │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Connection Established │
|
||||
│ <────────────────────────────────────────────────>│
|
||||
│ │
|
||||
│ MTU Exchange Request │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ MTU Exchange Response │
|
||||
│ <──────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── Service Discovery ─────────── │
|
||||
│ │
|
||||
│ Discover Services (UUID: 0x00FF) │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Service Found │
|
||||
│ <──────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Get Characteristic (UUID: 0xFF01) │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Characteristic Found │
|
||||
│ <──────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ ─────────── Enable Notification ─────────── │
|
||||
│ │
|
||||
│ Register for Notify │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Write CCCD (0x0001 = Enable Notify) │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ ─────────── Data Exchange ─────────── │
|
||||
│ │
|
||||
│ Write Characteristic (35 bytes test data) │
|
||||
│ ────────────────────────────────────────────────> │
|
||||
│ │
|
||||
│ Notification (data from server) │
|
||||
│ <──────────────────────────────────────────────── │
|
||||
│ │
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
### Hardware Required
|
||||
|
||||
* Four development boards with supported SoC:
|
||||
- One board for the GATT Client (this example)
|
||||
- Three boards for GATT Servers (using [gatt_server](../gatt_server) example)
|
||||
* Four USB cables for power supply and programming
|
||||
|
||||
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information.
|
||||
|
||||
### Setup GATT Servers
|
||||
|
||||
1. Flash the [gatt_server](../gatt_server) example to three separate boards
|
||||
2. Modify each server's device name before flashing:
|
||||
- Server 1: Change `ESP_GATT_DEMO` to `ESP_GATTS_DEMO_a`
|
||||
- Server 2: Change `ESP_GATT_DEMO` to `ESP_GATTS_DEMO_b`
|
||||
- Server 3: Change `ESP_GATT_DEMO` to `ESP_GATTS_DEMO_c`
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Set the correct chip target:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
The code can be modified to connect to more devices (up to 4 devices by default). If you need to connect to more devices (more than 4 devices), you need to change `BT/BLE MAX ACL CONNECTIONS` in menuconfig.
|
||||
**Connecting More Than 4 Devices:**
|
||||
|
||||
### Hardware Required
|
||||
The default maximum number of ACL connections is 4. To connect more devices:
|
||||
|
||||
* A development board with ESP32/ESP32-C3/ESP32-C2/ESP32-H2/ESP32-S3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for Power supply and programming
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it.
|
||||
Navigate to: `Component config` → `Bluetooth` → `Bluedroid Options` → `BT/BLE MAX ACL CONNECTIONS`
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
```bash
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
(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.
|
||||
|
||||
|
||||
@@ -92,8 +92,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -66,32 +66,32 @@ static esp_ble_ext_scan_params_t ext_scan_params = {
|
||||
};
|
||||
|
||||
const esp_ble_conn_params_t phy_1m_conn_params = {
|
||||
.scan_interval = 0x40,
|
||||
.scan_window = 0x40,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(40),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(40),
|
||||
.interval_min = 320,
|
||||
.interval_max = 320,
|
||||
.latency = 0,
|
||||
.supervision_timeout = 600,
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
.min_ce_len = 0,
|
||||
.max_ce_len = 0,
|
||||
};
|
||||
const esp_ble_conn_params_t phy_2m_conn_params = {
|
||||
.scan_interval = 0x40,
|
||||
.scan_window = 0x40,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(40),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(40),
|
||||
.interval_min = 320,
|
||||
.interval_max = 320,
|
||||
.latency = 0,
|
||||
.supervision_timeout = 600,
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
.min_ce_len = 0,
|
||||
.max_ce_len = 0,
|
||||
};
|
||||
const esp_ble_conn_params_t phy_coded_conn_params = {
|
||||
.scan_interval = 0x40,
|
||||
.scan_window = 0x40,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(40),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(40),
|
||||
.interval_min = 320, // 306-> 362Kbps
|
||||
.interval_max = 320,
|
||||
.latency = 0,
|
||||
.supervision_timeout = 600,
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
.min_ce_len = 0,
|
||||
.max_ce_len = 0,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -41,9 +41,9 @@
|
||||
static uint16_t profile_handle_table[HRS_IDX_NB];
|
||||
|
||||
static uint8_t ext_adv_raw_data[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb, 0x03, 0x03, 0xab, 0xcd,
|
||||
0x11, 0X09, 'E', 'S', 'P', '_', 'B', 'L', 'E', '5', '0', '_', 'S', 'E', 'R', 'V', 'E', 'R',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb, 0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xab, 0xcd,
|
||||
0x11, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'B', 'L', 'E', '5', '0', '_', 'S', 'E', 'R', 'V', 'E', 'R',
|
||||
};
|
||||
|
||||
static esp_ble_gap_ext_adv_t ext_adv[1] = {
|
||||
@@ -52,8 +52,8 @@ static esp_ble_gap_ext_adv_t ext_adv[1] = {
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
|
||||
.interval_min = 0x20,
|
||||
.interval_max = 0x20,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -110,9 +110,9 @@ const esp_ble_conn_params_t phy_1m_conn_params = {
|
||||
.latency = 0,
|
||||
.max_ce_len = 0,
|
||||
.min_ce_len = 0,
|
||||
.scan_interval = 0x40,
|
||||
.scan_window = 0x40,
|
||||
.supervision_timeout = 600,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(40),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(40),
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
};
|
||||
|
||||
const esp_ble_conn_params_t phy_2m_conn_params = {
|
||||
@@ -121,9 +121,9 @@ const esp_ble_conn_params_t phy_2m_conn_params = {
|
||||
.latency = 0,
|
||||
.max_ce_len = 0,
|
||||
.min_ce_len = 0,
|
||||
.scan_interval = 0x40,
|
||||
.scan_window = 0x40,
|
||||
.supervision_timeout = 600,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(40),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(40),
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
};
|
||||
|
||||
const esp_ble_conn_params_t phy_coded_conn_params = {
|
||||
@@ -132,9 +132,9 @@ const esp_ble_conn_params_t phy_coded_conn_params = {
|
||||
.latency = 0,
|
||||
.max_ce_len = 0,
|
||||
.min_ce_len = 0,
|
||||
.scan_interval = 0x40,
|
||||
.scan_window = 0x40,
|
||||
.supervision_timeout = 600,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(40),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(40),
|
||||
.supervision_timeout = ESP_BLE_GAP_SUPERVISION_TIMEOUT_MS(6000),
|
||||
};
|
||||
|
||||
struct gattc_profile_inst {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -98,12 +98,12 @@ static esp_attr_value_t gatts_demo_char1_val =
|
||||
};
|
||||
|
||||
static uint8_t ext_adv_raw_data[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb,
|
||||
0x03, 0x03, 0xab, 0xcd,
|
||||
0x11, 0x07, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00,
|
||||
0x11, 0x07, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x14, 0X09, 'T', 'H', 'R', 'O', 'U', 'G', 'H', 'P', 'U', 'T', '_', 'P', 'H', 'Y', '_', 'D', 'E', 'M', 'O',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xab, 0xcd,
|
||||
0x11, ESP_BLE_AD_TYPE_128SRV_CMPL, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00,
|
||||
0x11, ESP_BLE_AD_TYPE_128SRV_CMPL, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x14, ESP_BLE_AD_TYPE_NAME_CMPL, 'T', 'H', 'R', 'O', 'U', 'G', 'H', 'P', 'U', 'T', '_', 'P', 'H', 'Y', '_', 'D', 'E', 'M', 'O',
|
||||
};
|
||||
|
||||
static esp_ble_gap_ext_adv_t ext_adv[1] = {
|
||||
@@ -112,8 +112,8 @@ static esp_ble_gap_ext_adv_t ext_adv[1] = {
|
||||
|
||||
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,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -49,8 +49,8 @@ static SemaphoreHandle_t test_sem = NULL;
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
|
||||
.interval_min = 0x30,
|
||||
.interval_max = 0x30,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(30),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(30),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
@@ -64,8 +64,8 @@ esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
|
||||
.interval_min = 0x40,
|
||||
.interval_max = 0x40,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
@@ -79,8 +79,8 @@ esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
|
||||
esp_ble_gap_ext_adv_params_t legacy_adv_params = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND,
|
||||
.interval_min = 0x45,
|
||||
.interval_max = 0x45,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(43),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(43),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
@@ -94,8 +94,8 @@ esp_ble_gap_ext_adv_params_t legacy_adv_params = {
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_coded = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
|
||||
.interval_min = 0x50,
|
||||
.interval_max = 0x50,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(50),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(50),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
@@ -108,37 +108,37 @@ esp_ble_gap_ext_adv_params_t ext_adv_params_coded = {
|
||||
};
|
||||
|
||||
static uint8_t raw_adv_data_1m[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb,
|
||||
0x11, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
0x11, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
'D', 'V', '_', '1', 'M'
|
||||
};
|
||||
|
||||
static uint8_t raw_scan_rsp_data_2m[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb,
|
||||
0x11, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
0x11, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
'D', 'V', '_', '2', 'M'
|
||||
};
|
||||
|
||||
static uint8_t legacy_adv_data[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb,
|
||||
0x14, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
0x14, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
'D', 'V', '_', 'C', 'O', 'D', 'E', 'D'
|
||||
};
|
||||
|
||||
static uint8_t legacy_scan_rsp_data[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb,
|
||||
0x15, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
0x15, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
'D', 'V', '_', 'L', 'E', 'G', 'A', 'C', 'Y'
|
||||
};
|
||||
|
||||
static uint8_t raw_scan_rsp_data_coded[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x02, 0x0a, 0xeb,
|
||||
0x14, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
0x14, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
|
||||
'D', 'V', '_', 'C', 'O', 'D', 'E', 'D'
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -58,8 +58,8 @@ static SemaphoreHandle_t test_sem = NULL;
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_NONCONN_NONSCANNABLE_UNDIRECTED,
|
||||
.interval_min = 0x30,
|
||||
.interval_max = 0x30,
|
||||
.interval_min = ESP_BLE_GAP_ADV_ITVL_MS(30),
|
||||
.interval_max = ESP_BLE_GAP_ADV_ITVL_MS(30),
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
@@ -72,8 +72,8 @@ esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
};
|
||||
|
||||
static esp_ble_gap_periodic_adv_params_t periodic_adv_params = {
|
||||
.interval_min = 0x40, // 80 ms interval
|
||||
.interval_max = 0x40,
|
||||
.interval_min = ESP_BLE_GAP_PERIODIC_ADV_ITVL_MS(80),
|
||||
.interval_max = ESP_BLE_GAP_PERIODIC_ADV_ITVL_MS(80),
|
||||
.properties = 0, // Do not include TX power
|
||||
};
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ esp_attr_value_t gatts_initial_char_val = {
|
||||
};
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x060,
|
||||
.adv_int_max = 0x060,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(60),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(60),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RPA_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -145,8 +145,8 @@ static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x20,
|
||||
.max_interval = 0x40,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(40),
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(80),
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
@@ -162,8 +162,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006,
|
||||
.max_interval = 0x0010,
|
||||
.min_interval = ESP_BLE_GAP_CONN_ITVL_MS(7.5),
|
||||
.max_interval = ESP_BLE_GAP_CONN_ITVL_MS(20),
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
@@ -176,8 +176,8 @@ static esp_ble_adv_data_t scan_rsp_data = {
|
||||
#endif /* CONFIG_SET_RAW_ADV_DATA */
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_int_min = ESP_BLE_GAP_ADV_ITVL_MS(20),
|
||||
.adv_int_max = ESP_BLE_GAP_ADV_ITVL_MS(40),
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
@@ -215,8 +215,8 @@ static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_interval = ESP_BLE_GAP_SCAN_ITVL_MS(50),
|
||||
.scan_window = ESP_BLE_GAP_SCAN_WIN_MS(30),
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* BLE Combined Advertising and Scanning Example.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -18,8 +18,10 @@
|
||||
|
||||
static const char *TAG = "BLE_ADV_SCAN";
|
||||
|
||||
#define SCAN_LOCAL_NAME_MAX_LEN 32
|
||||
|
||||
typedef struct {
|
||||
char scan_local_name[32];
|
||||
char scan_local_name[SCAN_LOCAL_NAME_MAX_LEN];
|
||||
uint8_t name_len;
|
||||
} ble_scan_local_name_t;
|
||||
|
||||
@@ -56,7 +58,7 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
|
||||
host_rcv_data_t send_data;
|
||||
uint8_t *data_pkt;
|
||||
/* Check second byte for HCI event. If event opcode is 0x0e, the event is
|
||||
* HCI Command Complete event. Sice we have recieved "0x0e" event, we can
|
||||
* HCI Command Complete event. Sice we have received "0x0e" event, we can
|
||||
* check for byte 4 for command opcode and byte 6 for it's return status. */
|
||||
if (data[1] == 0x0e) {
|
||||
if (data[6] == 0) {
|
||||
@@ -114,7 +116,7 @@ static void hci_cmd_send_ble_scan_params(void)
|
||||
uint16_t scan_window = 0x30; /* 30 ms */
|
||||
|
||||
uint8_t own_addr_type = 0x00; /* Public Device Address (default). */
|
||||
uint8_t filter_policy = 0x00; /* Accept all packets excpet directed advertising packets (default). */
|
||||
uint8_t filter_policy = 0x00; /* Accept all packets except directed advertising packets (default). */
|
||||
uint16_t sz = make_cmd_ble_set_scan_params(hci_cmd_buf, scan_type, scan_interval, scan_window, own_addr_type, filter_policy);
|
||||
esp_vhci_host_send_packet(hci_cmd_buf, sz);
|
||||
}
|
||||
@@ -187,28 +189,58 @@ static void hci_cmd_send_ble_set_adv_data(void)
|
||||
ESP_LOGI(TAG, "Starting BLE advertising with name \"%s\"", adv_name);
|
||||
}
|
||||
|
||||
static esp_err_t get_local_name (uint8_t *data_msg, uint8_t data_len, ble_scan_local_name_t *scanned_packet)
|
||||
static esp_err_t get_local_name(uint8_t *data_msg, uint8_t data_len, ble_scan_local_name_t *scanned_packet)
|
||||
{
|
||||
uint8_t curr_ptr = 0, curr_len, curr_type;
|
||||
uint8_t curr_ptr = 0;
|
||||
|
||||
/* Initialize output structure */
|
||||
scanned_packet->name_len = 0;
|
||||
scanned_packet->scan_local_name[0] = '\0';
|
||||
|
||||
while (curr_ptr < data_len) {
|
||||
curr_len = data_msg[curr_ptr++];
|
||||
curr_type = data_msg[curr_ptr++];
|
||||
/* Ensure there is at least 1 byte for length field */
|
||||
if (curr_ptr >= data_len) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
uint8_t curr_len = data_msg[curr_ptr++];
|
||||
|
||||
/* Length of 0 indicates end of AD structures or invalid data */
|
||||
if (curr_len == 0) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Search for current data type and see if it contains name as data (0x08 or 0x09). */
|
||||
if (curr_type == 0x08 || curr_type == 0x09) {
|
||||
for (uint8_t i = 0; i < curr_len - 1; i += 1) {
|
||||
scanned_packet->scan_local_name[i] = data_msg[curr_ptr + i];
|
||||
}
|
||||
scanned_packet->name_len = curr_len - 1;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
/* Search for next data. Current length includes 1 octate for AD Type (2nd octate). */
|
||||
curr_ptr += curr_len - 1;
|
||||
/* Ensure there is at least 1 byte for type field */
|
||||
if (curr_ptr >= data_len) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
uint8_t curr_type = data_msg[curr_ptr++];
|
||||
|
||||
/* Calculate data field length (curr_len includes type byte) */
|
||||
uint8_t data_field_len = curr_len - 1;
|
||||
|
||||
/* Verify remaining buffer has enough data */
|
||||
if (curr_ptr + data_field_len > data_len) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Check for Local Name type (0x08: Shortened, 0x09: Complete) */
|
||||
if (curr_type == 0x08 || curr_type == 0x09) {
|
||||
/* Limit copy length to prevent buffer overflow */
|
||||
uint8_t copy_len = data_field_len;
|
||||
if (copy_len > SCAN_LOCAL_NAME_MAX_LEN - 1) {
|
||||
copy_len = SCAN_LOCAL_NAME_MAX_LEN - 1;
|
||||
}
|
||||
|
||||
memcpy(scanned_packet->scan_local_name, &data_msg[curr_ptr], copy_len);
|
||||
scanned_packet->scan_local_name[copy_len] = '\0'; /* Ensure null termination */
|
||||
scanned_packet->name_len = copy_len;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Move to next AD structure */
|
||||
curr_ptr += data_field_len;
|
||||
}
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user