From 086ebb7ccb2e62a60b3ec3d84dd39f2f14b98661 Mon Sep 17 00:00:00 2001 From: Zhang Hai Peng Date: Tue, 30 Dec 2025 11:53:38 +0800 Subject: [PATCH] feat(examples/bluedroid): add BLE ACL latency measurement example (cherry picked from commit 934970a06fc12973e2a1ec6381c9f78e5da6dbbb) Co-authored-by: zhanghaipeng --- examples/bluetooth/.build-test-rules.yml | 18 + .../ble/ble_acl_latency/cent/CMakeLists.txt | 6 + .../ble/ble_acl_latency/cent/README.md | 190 +++++++ .../ble_acl_latency/cent/main/CMakeLists.txt | 2 + .../ble_acl_latency/cent/main/gattc_latency.h | 56 ++ .../cent/main/gattc_latency_demo.c | 480 ++++++++++++++++++ .../ble_acl_latency/cent/main/latency_test.c | 261 ++++++++++ .../ble_acl_latency/cent/sdkconfig.defaults | 7 + .../cent/sdkconfig.defaults.esp32c2 | 13 + .../ble/ble_acl_latency/periph/CMakeLists.txt | 6 + .../ble/ble_acl_latency/periph/README.md | 124 +++++ .../periph/main/CMakeLists.txt | 2 + .../periph/main/gatts_latency.h | 37 ++ .../periph/main/gatts_latency_demo.c | 360 +++++++++++++ .../ble_acl_latency/periph/sdkconfig.defaults | 7 + .../periph/sdkconfig.defaults.esp32c2 | 13 + 16 files changed, 1582 insertions(+) create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/README.md create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults.esp32c2 create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/README.md create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency.h create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults create mode 100644 examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults.esp32c2 diff --git a/examples/bluetooth/.build-test-rules.yml b/examples/bluetooth/.build-test-rules.yml index d0e2fdb7fe..89a6d427b9 100644 --- a/examples/bluetooth/.build-test-rules.yml +++ b/examples/bluetooth/.build-test-rules.yml @@ -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: diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/CMakeLists.txt new file mode 100644 index 0000000000..ea7d4a20a9 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_acl_latency_cent) diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/README.md b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/README.md new file mode 100644 index 0000000000..4cb1e830c3 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/README.md @@ -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 +``` + +### 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. diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/CMakeLists.txt new file mode 100644 index 0000000000..3b695c9c7e --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "gattc_latency_demo.c" "latency_test.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h new file mode 100644 index 0000000000..ddd9c5409a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h @@ -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_ */ diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c new file mode 100644 index 0000000000..09bd9ee5ee --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c @@ -0,0 +1,480 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include "nvs.h" +#include "nvs_flash.h" + +#include "esp_bt.h" +#include "esp_gap_ble_api.h" +#include "esp_gattc_api.h" +#include "esp_gatt_defs.h" +#include "esp_bt_main.h" +#include "esp_gatt_common_api.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "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..."); +} diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c new file mode 100644 index 0000000000..23743d9482 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c @@ -0,0 +1,261 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#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, ""); +} diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults new file mode 100644 index 0000000000..a218a4da6a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults @@ -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 diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults.esp32c2 b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..9759e23400 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults.esp32c2 @@ -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 diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/CMakeLists.txt new file mode 100644 index 0000000000..333d1e60c4 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_acl_latency_periph) diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/README.md b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/README.md new file mode 100644 index 0000000000..3c5f102352 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/README.md @@ -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 +``` + +### 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. diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/CMakeLists.txt new file mode 100644 index 0000000000..5c6ae4829f --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "gatts_latency_demo.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency.h b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency.h new file mode 100644 index 0000000000..be0ca817aa --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency.h @@ -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_ */ diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c new file mode 100644 index 0000000000..c0e9cab82c --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c @@ -0,0 +1,360 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#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..."); +} diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults new file mode 100644 index 0000000000..a218a4da6a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults @@ -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 diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults.esp32c2 b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..9759e23400 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults.esp32c2 @@ -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