diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/CMakeLists.txt b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/CMakeLists.txt new file mode 100644 index 0000000000..0944282d56 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/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(throughput_client_demo) diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/README.md b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/README.md new file mode 100644 index 0000000000..3f45d784fe --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/README.md @@ -0,0 +1,121 @@ +| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# ESP-IDF BLE 50 throughput GATT CLIENT Test + +This is the demo used to test the BLE 5.0 throughput, this demo should used with throughput server demo together. + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using: + +```bash +idf.py set-target +``` + +To configure the project, you can follow these steps: + +1. We can only test notify or write throughput at the same time, this demo default to test the notify throughput, if want to test the write throughput, +please set: `idf.py menuconfig --> Component config --> Example 'GATT CLIENT THROUGHPUT' Config --->` then select the `test the gattc write throughput` option. +2. This demo only test unidirectional throughput, if you want to test the bidirectional throughput please change the demo by yourself. +3. Should change the CPU frequency to 160 MHZ in the `idf.py menuconfig` and `Component config ---> ESP System Settings ---> CPU frequency (160 MHz)`. +4. In order to maximize throughput, please test in a clean environment without many BLE devices working and esure both test devices are ESP32 series. + +### Hardware Required + +* A development board with supported SoC (e.g., ESP32-C3-DevKitM-1, ESP32-C6-DevKitC-1, etc.) +* A USB cable for Power supply and programming + +See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +### Throughput on 1M PHY +``` +I (458) GATTC_DEMO_PHY: Legacy adv, adv type 0x13 data len 31 +I (458) GATTC_DEMO_PHY: Extend adv, adv type 0x1 data len 67 +I (458) GATTC_DEMO_PHY: searched device THROUGHPUT_PHY_DEMO +I (458) GATTC_DEMO_PHY: Device found ec:da:3b:0f:2d:b6 +I (468) Adv name: THROUGHPUT_PHY_D +I (468) Adv name: EMO +I (468) GATTC_DEMO_PHY: Stop extend scan and create aux open, primary_phy 1 secondary phy 1 +I (488) GATTC_DEMO_PHY: Scanning stop successfully +I (768) GATTC_DEMO_PHY: Connected, conn_id 0, remote ec:da:3b:0f:2d:b6 +I (768) GATTC_DEMO_PHY: Open successfully, MTU 23 +I (778) GATTC_DEMO_PHY: Read PHY, status 0, TX_PHY 1, RX_PHY 1 +I (4928) GATTC_DEMO_PHY: MTU exchange, status 0, MTU 517 +I (4928) GATTC_DEMO_PHY: Service search result +I (4928) GATTC_DEMO_PHY: Service found +I (4928) GATTC_DEMO_PHY: UUID16: ff +I (4938) GATTC_DEMO_PHY: Service search complete +I (4938) GATTC_DEMO_PHY: Notification register successfully +I (5448) GATTC_DEMO_PHY: Descriptor write successfully +I (6438) GATTC_DEMO_PHY: Notify Bit rate = 97642 Byte/s, = 781136 bit/s, time = 0s +I (8438) GATTC_DEMO_PHY: Notify Bit rate = 97390 Byte/s, = 779120 bit/s, time = 2s +I (10438) GATTC_DEMO_PHY: Notify Bit rate = 97233 Byte/s, = 777864 bit/s, time = 4s +I (12438) GATTC_DEMO_PHY: Notify Bit rate = 97178 Byte/s, = 777424 bit/s, time = 6s +I (14438) GATTC_DEMO_PHY: Notify Bit rate = 97142 Byte/s, = 777136 bit/s, time = 8s +``` + +### Throughput on 2M PHY +``` +I (648) GATTC_DEMO_PHY: Read PHY, status 0, TX_PHY 2, RX_PHY 2 +I (4798) GATTC_DEMO_PHY: MTU exchange, status 0, MTU 517 +I (4798) GATTC_DEMO_PHY: Service search result +I (4798) GATTC_DEMO_PHY: Service found +I (4798) GATTC_DEMO_PHY: UUID16: ff +I (4808) GATTC_DEMO_PHY: Service search complete +I (4808) GATTC_DEMO_PHY: Notification register successfully +I (5318) GATTC_DEMO_PHY: Descriptor write successfully +I (6448) GATTC_DEMO_PHY: Notify Bit rate = 175369 Byte/s, = 1402952 bit/s, time = 1s +I (8448) GATTC_DEMO_PHY: Notify Bit rate = 175270 Byte/s, = 1402160 bit/s, time = 3s +I (10448) GATTC_DEMO_PHY: Notify Bit rate = 175251 Byte/s, = 1402008 bit/s, time = 5s +I (12448) GATTC_DEMO_PHY: Notify Bit rate = 175242 Byte/s, = 1401936 bit/s, time = 7s +I (14448) GATTC_DEMO_PHY: Notify Bit rate = 175192 Byte/s, = 1401536 bit/s, time = 9s +``` + +### Throughput on 500K PHY (Coded S2) +``` +I (908) GATTC_DEMO_PHY: Read PHY, status 0, TX_PHY 3, RX_PHY 3 +I (5058) GATTC_DEMO_PHY: MTU exchange, status 0, MTU 517 +I (5058) GATTC_DEMO_PHY: Service search result +I (5058) GATTC_DEMO_PHY: Service found +I (5068) GATTC_DEMO_PHY: UUID16: ff +I (5068) GATTC_DEMO_PHY: Service search complete +I (5068) GATTC_DEMO_PHY: Notification register successfully +I (5578) GATTC_DEMO_PHY: Descriptor write successfully +I (6468) GATTC_DEMO_PHY: Notify Bit rate = 44292 Byte/s, = 354336 bit/s, time = 0s +I (8468) GATTC_DEMO_PHY: Notify Bit rate = 43961 Byte/s, = 351688 bit/s, time = 2s +I (10468) GATTC_DEMO_PHY: Notify Bit rate = 43898 Byte/s, = 351184 bit/s, time = 4s +I (12468) GATTC_DEMO_PHY: Notify Bit rate = 43871 Byte/s, = 350968 bit/s, time = 6s +I (14468) GATTC_DEMO_PHY: Notify Bit rate = 43857 Byte/s, = 350856 bit/s, time = 8s +``` + +### Throughput on 125K PHY (Coded S8) +``` +I (778) GATTC_DEMO_PHY: Read PHY, status 0, TX_PHY 3, RX_PHY 3 +I (4928) GATTC_DEMO_PHY: MTU exchange, status 0, MTU 517 +I (4938) GATTC_DEMO_PHY: Service search result +I (4938) GATTC_DEMO_PHY: Service found +I (4938) GATTC_DEMO_PHY: UUID16: ff +I (4938) GATTC_DEMO_PHY: Service search complete +I (4938) GATTC_DEMO_PHY: Notification register successfully +I (5448) GATTC_DEMO_PHY: Descriptor write successfully +I (6448) GATTC_DEMO_PHY: Notify Bit rate = 13459 Byte/s, = 107672 bit/s, time = 0s +I (8448) GATTC_DEMO_PHY: Notify Bit rate = 13395 Byte/s, = 107160 bit/s, time = 2s +I (10448) GATTC_DEMO_PHY: Notify Bit rate = 13383 Byte/s, = 107064 bit/s, time = 4s +I (12448) GATTC_DEMO_PHY: Notify Bit rate = 13378 Byte/s, = 107024 bit/s, time = 6s +I (14448) GATTC_DEMO_PHY: Notify Bit rate = 13375 Byte/s, = 107000 bit/s, time = 8s +``` + +## 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_50/ble50_throughput/throughput_client/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/CMakeLists.txt new file mode 100644 index 0000000000..4695587a07 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "example_ble_client_throughput.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/Kconfig.projbuild b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/Kconfig.projbuild new file mode 100644 index 0000000000..e3c97e084a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/Kconfig.projbuild @@ -0,0 +1,14 @@ +menu "Example 'GATT CLIENT THROUGHPUT' Config" + + config GATTS_NOTIFY_THROUGHPUT + bool "test the gatts notify throughput" + help + If this config item is set, then the 'GATTC_WRITE_THROUGHPUT' config should be close, it can't test both + write or notify at the same time at this demo + + config GATTC_WRITE_THROUGHPUT + bool "test the gattc write throughput" + help + If this config item is set, then the 'GATTS_NOTIFY_THROUGHPUT' config should be close, it can't test both + write or notify at the same time at this demo +endmenu diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c new file mode 100644 index 0000000000..09e321ad85 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c @@ -0,0 +1,649 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/**************************************************************************** +* +* This is the demo to test the BLE throughput. It should be used together with throughput_server demo. +* +****************************************************************************/ + +#include "sdkconfig.h" +#include +#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/semphr.h" +#include "freertos/task.h" +#include "esp_timer.h" + +/********************************************************** + * Thread/Task reference + **********************************************************/ +#ifdef CONFIG_BLUEDROID_PINNED_TO_CORE +#define BLUETOOTH_TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) +#else +#define BLUETOOTH_TASK_PINNED_TO_CORE (0) +#endif + +#define REMOTE_SERVICE_UUID 0x00FF +#define REMOTE_NOTIFY_CHAR_UUID 0xFF01 +#define PROFILE_NUM 1 +#define PROFILE_A_APP_ID 0 +#define INVALID_HANDLE 0 +#define SECOND_TO_USECOND 1000000 + +#define EXT_SCAN_DURATION 0 +#define EXT_SCAN_PERIOD 0 + +static const char *GATTC_TAG = "GATTC_DEMO_PHY"; +static const char remote_device_name[] = "THROUGHPUT_PHY_DEMO"; +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; +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) +static bool start = false; +static uint64_t notify_len = 0; +static uint64_t start_time = 0; +static uint64_t current_time = 0; +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + +#if (CONFIG_GATTC_WRITE_THROUGHPUT) +#define GATTC_WRITE_LEN 495 + +static bool can_send_write = false; +static SemaphoreHandle_t gattc_semaphore; +uint8_t write_data[GATTC_WRITE_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f}; +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + +static bool is_connect = false; + +/* 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); + + +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_NOTIFY_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_ext_scan_params_t ext_scan_params = { + .own_addr_type = BLE_ADDR_TYPE_RPA_PUBLIC, + .filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE, + .cfg_mask = ESP_BLE_GAP_EXT_SCAN_CFG_CODE_MASK | ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK, + .uncoded_cfg = {BLE_SCAN_TYPE_PASSIVE, 40, 40}, + .coded_cfg = {BLE_SCAN_TYPE_PASSIVE, 40, 40}, +}; + +// If the interference in the air is severe, the connection interval can be reduced. +const esp_ble_gap_conn_params_t phy_1m_conn_params = { + .interval_max = 104, // 130ms + .interval_min = 104, + .latency = 0, + .max_ce_len = 0, + .min_ce_len = 0, + .scan_interval = 0x40, + .scan_window = 0x40, + .supervision_timeout = 600, +}; + +const esp_ble_gap_conn_params_t phy_2m_conn_params = { + .interval_max = 104, // 130ms + .interval_min = 104, + .latency = 0, + .max_ce_len = 0, + .min_ce_len = 0, + .scan_interval = 0x40, + .scan_window = 0x40, + .supervision_timeout = 600, +}; + +const esp_ble_gap_conn_params_t phy_coded_conn_params = { + .interval_max = 104, // 130ms + .interval_min = 104, + .latency = 0, + .max_ce_len = 0, + .min_ce_len = 0, + .scan_interval = 0x40, + .scan_window = 0x40, + .supervision_timeout = 600, +}; + +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; +}; + +/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */ +static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gattc_cb = gattc_profile_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +static uint8_t check_sum(uint8_t *addr, uint16_t count) +{ + uint32_t sum = 0; + + if (addr == NULL || count == 0) { + return 0; + } + + for(int i = 0; i < count; i++) { + sum = sum + addr[i]; + } + + while (sum >> 8) { + sum = (sum & 0xff) + (sum >> 8); + } + + return (uint8_t)~sum; +} + +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, "GATT client register, status %d, app_id %d, gattc_if %d", param->reg.status, param->reg.app_id, gattc_if); + esp_err_t scan_ret = esp_ble_gap_set_ext_scan_params(&ext_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, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR"", p_data->connect.conn_id, + ESP_BD_ADDR_HEX(p_data->connect.remote_bda)); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id; + memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t)); + 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); + } + esp_ble_gap_read_phy(p_data->connect.remote_bda); + 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 successfully, MTU %u", param->open.mtu); + break; + case ESP_GATTC_CFG_MTU_EVT: + is_connect = true; + ESP_LOGI(GATTC_TAG, "MTU exchange, status %d, MTU %d", param->cfg_mtu.status, param->cfg_mtu.mtu); + esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + ESP_LOGI(GATTC_TAG, "Service search result"); + esp_gatt_srvc_id_t *srvc_id =(esp_gatt_srvc_id_t *)&p_data->search_res.srvc_id; + if (srvc_id->id.uuid.len == ESP_UUID_LEN_16 && srvc_id->id.uuid.uuid.uuid16 == REMOTE_SERVICE_UUID) { + ESP_LOGI(GATTC_TAG, "Service found"); + get_server = true; + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle = p_data->search_res.start_handle; + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle = p_data->search_res.end_handle; + ESP_LOGI(GATTC_TAG, "UUID16: %x", srvc_id->id.uuid.uuid.uuid16); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: + if (p_data->search_cmpl.status != ESP_GATT_OK){ + ESP_LOGE(GATTC_TAG, "Service search failed, status %x", p_data->search_cmpl.status); + break; + } + ESP_LOGI(GATTC_TAG, "Service search complete"); + 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_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_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_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_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; + } + + /* Every service has only one char in our 'throughput_server' demo, so we use first 'char_elem_result' */ + if (count > 0 && (char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)){ + gl_profile_tab[PROFILE_A_APP_ID].char_handle = char_elem_result[0].char_handle; + esp_ble_gattc_register_for_notify (gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, char_elem_result[0].char_handle); + } + } + /* free char_elem_result */ + free(char_elem_result); + char_elem_result = NULL; + }else{ + ESP_LOGE(GATTC_TAG, "no char found"); + } + } + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + if (p_data->reg_for_notify.status != ESP_GATT_OK){ + ESP_LOGE(GATTC_TAG, "Notification register failed, status %d", p_data->reg_for_notify.status); + }else{ + ESP_LOGI(GATTC_TAG, "Notification register successfully"); + 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_A_APP_ID].conn_id, + ESP_GATT_DB_DESCRIPTOR, + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, + gl_profile_tab[PROFILE_A_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_A_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"); + } + + /* Every char has only one descriptor in our 'throughput_server' demo, so we use first 'descr_elem_result' */ + if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG){ + ret_status = esp_ble_gattc_write_char_descr( gattc_if, + gl_profile_tab[PROFILE_A_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 */ + free(descr_elem_result); + } + } + else{ + ESP_LOGE(GATTC_TAG, "decsr not found"); + } + + } + break; + } + case ESP_GATTC_NOTIFY_EVT: { +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + if (p_data->notify.is_notify && + (p_data->notify.value[p_data->notify.value_len - 1] == + check_sum(p_data->notify.value, p_data->notify.value_len - 1))){ + notify_len += p_data->notify.value_len; + } else { + ESP_LOGE(GATTC_TAG, "Indication received, value:"); + } + if (start == false) { + start_time = esp_timer_get_time(); + start = true; + break; + } + +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + break; + } + case ESP_GATTC_WRITE_DESCR_EVT: + if (p_data->write.status != ESP_GATT_OK) { + ESP_LOGE(GATTC_TAG, "Descriptor write failed, status %x", p_data->write.status); + break; + } + ESP_LOGI(GATTC_TAG, "Descriptor write successfully"); +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + can_send_write = true; + xSemaphoreGive(gattc_semaphore); +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + 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, "Service change from "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(bda)); + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: + if (p_data->write.status != ESP_GATT_OK) { + ESP_LOGE(GATTC_TAG, "Characteristic write failed, status %x", p_data->write.status); + break; + } + break; + case ESP_GATTC_DISCONNECT_EVT: + is_connect = false; + get_server = false; +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + start = false; + start_time = 0; + current_time = 0; + notify_len = 0; +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + ESP_LOGI(GATTC_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", + ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason); + break; + case ESP_GATTC_CONGEST_EVT: +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + if (param->congest.congested) { + can_send_write = false; + } else { + can_send_write = true; + xSemaphoreGive(gattc_semaphore); + } +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + break; + default: + break; + } +} + +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { + case ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT: { + if (param->set_ext_scan_params.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTC_TAG,"Extend scanning parameters set failed, status %x", param->set_ext_scan_params.status); + break; + } + esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION,EXT_SCAN_PERIOD); + break; + } + case ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT: + //scan start complete event to indicate scan start successfully or failed + if (param->ext_scan_start.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTC_TAG, "Extended scanning start failed, status %x", param->ext_scan_start.status); + break; + } + ESP_LOGI(GATTC_TAG, "Extended scanning start successfully"); + break; + case ESP_GAP_BLE_EXT_ADV_REPORT_EVT: { + uint8_t *adv_name = NULL; + uint8_t adv_name_len = 0; + if(param->ext_adv_report.params.event_type & ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) { + ESP_LOGI(GATTC_TAG, "Legacy adv, adv type 0x%x data len %d", param->ext_adv_report.params.event_type, param->ext_adv_report.params.adv_data_len); + } else { + ESP_LOGI(GATTC_TAG, "Extend adv, adv type 0x%x data len %d", param->ext_adv_report.params.event_type, param->ext_adv_report.params.adv_data_len); + } + adv_name = esp_ble_resolve_adv_data_by_type(param->ext_adv_report.params.adv_data, + param->ext_adv_report.params.adv_data_len, + ESP_BLE_AD_TYPE_NAME_CMPL, + &adv_name_len); + if (!connect && 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); + connect = true; + esp_ble_gap_stop_ext_scan(); + ESP_LOGI(GATTC_TAG, "Device found "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(param->ext_adv_report.params.addr)); + ESP_LOG_BUFFER_CHAR("Adv name", adv_name, adv_name_len); + ESP_LOGI(GATTC_TAG, "Stop extend scan and create aux open, primary_phy %d secondary phy %d", param->ext_adv_report.params.primary_phy, param->ext_adv_report.params.secondly_phy); + esp_ble_gap_prefer_ext_connect_params_set(param->ext_adv_report.params.addr, + ESP_BLE_GAP_PHY_1M_PREF_MASK | ESP_BLE_GAP_PHY_2M_PREF_MASK | ESP_BLE_GAP_PHY_CODED_PREF_MASK, + &phy_1m_conn_params, &phy_2m_conn_params, &phy_coded_conn_params); + esp_ble_gattc_aux_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, + param->ext_adv_report.params.addr, + param->ext_adv_report.params.addr_type, true); + } + break; + } + case ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT: + if (param->ext_scan_stop.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTC_TAG, "Scanning stop failed, status %x", param->ext_scan_stop.status); + break; + } + ESP_LOGI(GATTC_TAG, "Scanning stop successfully"); + break; + case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT: + if (param->ext_adv_stop.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTC_TAG, "Advertising stop failed, status %x", param->ext_adv_stop.status); + break; + } + ESP_LOGI(GATTC_TAG, "Advertising stop successfully"); + break; + case ESP_GAP_BLE_READ_PHY_COMPLETE_EVT: + ESP_LOGI(GATTC_TAG, "Read PHY, status %x, TX_PHY %u, RX_PHY %u", param->read_phy.status, + param->read_phy.tx_phy, param->read_phy.rx_phy); + break; + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + ESP_LOGI(GATTC_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d", + param->update_conn_params.status, + param->update_conn_params.conn_int, + param->update_conn_params.latency, + param->update_conn_params.timeout); + break; + default: + break; + } +} + +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) +{ + /* If event is register event, store the gattc_if for each profile */ + if (event == ESP_GATTC_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.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; + } + } + + /* If the gattc_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + 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); +} + +#if (CONFIG_GATTC_WRITE_THROUGHPUT) +static void throughput_client_task(void *param) +{ + vTaskDelay(2000 / portTICK_PERIOD_MS); + uint8_t sum = check_sum(write_data, sizeof(write_data) - 1); + write_data[GATTC_WRITE_LEN - 1] = sum; + + while(1) { + + if (!can_send_write) { + int res = xSemaphoreTake(gattc_semaphore, portMAX_DELAY); + assert(res == pdTRUE); + } else { + if (is_connect) { + int free_buff_num = esp_ble_get_cur_sendable_packets_num(gl_profile_tab[PROFILE_A_APP_ID].conn_id); + if(free_buff_num > 0) { + for( ; free_buff_num > 0; free_buff_num--) { + // the app data set to 490 just for divided into two packages to send in the low layer + // when the packet length set to 251. + esp_ble_gattc_write_char(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].char_handle, + sizeof(write_data), write_data, + ESP_GATT_WRITE_TYPE_NO_RSP, + ESP_GATT_AUTH_REQ_NONE); + } + } else { //Add the vTaskDelay to prevent this task from consuming the CPU all the time, causing low-priority tasks to not be executed at all. + vTaskDelay( 10 / portTICK_PERIOD_MS ); + } + } else { + vTaskDelay(300 / portTICK_PERIOD_MS ); + } + } + + } +} +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) +static void throughput_cal_task(void *param) +{ + while (1) + { + vTaskDelay(2000 / portTICK_PERIOD_MS); + if(is_connect){ + uint32_t bit_rate = 0; + if (start_time) { + current_time = esp_timer_get_time(); + bit_rate = notify_len * SECOND_TO_USECOND / (current_time - start_time); + ESP_LOGI(GATTC_TAG, "Notify Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time = %ds", + bit_rate, bit_rate<<3, (int)((current_time - start_time) / SECOND_TO_USECOND)); + } else { + ESP_LOGI(GATTC_TAG, "Notify Bit rate = 0 Byte/s, = 0 bit/s"); + } + } + } + +} +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + +void app_main(void) +{ + // Initialize NVS. + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + ESP_LOGE(GATTC_TAG, "%s initialize controller failed, error code = %x", __func__, ret); + return; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(GATTC_TAG, "%s enable controller failed, error code = %x", __func__, ret); + return; + } + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(GATTC_TAG, "%s init bluetooth failed, error code = %x", __func__, ret); + return; + } + + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed, error code = %x", __func__, ret); + return; + } + + //register the callback function to the gap module + 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; + } + + //register the callback function to the gattc module + 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_gattc_app_register(PROFILE_A_APP_ID); + if (ret){ + ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x", __func__, ret); + } + // set the maximum MTU for used. + 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); + } +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + // The task is only created on the CPU core that Bluetooth is working on, + // preventing the sending task from using the un-updated Bluetooth state on another CPU. + xTaskCreatePinnedToCore(&throughput_client_task, "throughput_client_task", 4096, NULL, 10, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); +#endif + +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + xTaskCreatePinnedToCore(&throughput_cal_task, "throughput_cal_task", 4096, NULL, 9, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); +#endif + +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + gattc_semaphore = xSemaphoreCreateBinary(); + if (!gattc_semaphore) { + ESP_LOGE(GATTC_TAG, "%s, init fail, the gattc semaphore create fail.", __func__); + return; + } +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ +} diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults new file mode 100644 index 0000000000..5976aa8c25 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults @@ -0,0 +1,10 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_BT_ENABLED=y +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. +# CONFIG_BT_LE_50_FEATURE_SUPPORT=n +CONFIG_GATTS_NOTIFY_THROUGHPUT=y +CONFIG_GATTC_WRITE_THROUGHPUT=n diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32c2 b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..783c146dc1 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32c2 @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c2" +CONFIG_BT_ENABLED=y +CONFIG_BT_LE_50_FEATURE_SUPPORT=y +CONFIG_BT_LE_HCI_EVT_BUF_SIZE=257 +CONFIG_XTAL_FREQ_26=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32c3 b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..641ed53e33 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32c3 @@ -0,0 +1,5 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c3" +CONFIG_BT_ENABLED=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32s3 b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..f15a09ef2a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults.esp32s3 @@ -0,0 +1,5 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_BT_ENABLED=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/CMakeLists.txt b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/CMakeLists.txt new file mode 100644 index 0000000000..33fc2e240a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/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(throughput_server_demo) diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/README.md b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/README.md new file mode 100644 index 0000000000..599ca5023d --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/README.md @@ -0,0 +1,68 @@ +| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | + +# ESP-IDF BLE 50 throughput GATT SERVER Test + +This is the demo used to test the BLE throughput, this demo should used with throughput client demo together. + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using: + +```bash +idf.py set-target +``` +To configure the project, you can follow these steps: + +1. We can only test notify or write throughput at the same time, this demo default to test the notify throughput, if want to test the write throughput, +please set: `idf.py menuconfig --> Component config --> Example 'GATT SERVER THROUGHPUT' Config --->` then select the `test the gattc write throughput` option. +2. This demo only test unidirectional throughput, if you want to test the bidirectional throughput please change the demo by yourself. +3. Should change the CPU frequency to 160 MHZ in the `idf.py menuconfig` and `Component config ---> ESP System Settings ---> CPU frequency (160 MHz)`. +4. In order to maximize throughput, please test in a clean environment without many BLE devices working and esure both test devices are ESP32 series. + +### Hardware Required + +* A development board with supported SoC (e.g., ESP32-C3-DevKitM-1, ESP32-C6-DevKitC-1, etc.) +* A USB cable for Power supply and programming + +See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (377) main_task: Started on CPU0 +I (377) main_task: Calling app_main() +I (377) BLE_INIT: BT controller compile version [d752dea] +I (377) BLE_INIT: Bluetooth MAC: ec:da:3b:0f:2d:b6 +I (377) phy_init: phy_version 1180,01f2a49,Jun 4 2024,16:34:25 +I (437) GATTS_DEMO_PHY: GATT server register, status 0, app_id 0 +I (437) GATTS_DEMO_PHY: Extended advertising params set, status 0 +I (437) GATTS_DEMO_PHY: Service create, status 0, service_handle 40 +I (447) GATTS_DEMO_PHY: Extended advertising data set, status 0 +I (447) GATTS_DEMO_PHY: Service start, status 0, service_handle 40 +I (457) GATTS_DEMO_PHY: Characteristic add, status 0, attr_handle 42, service_handle 40 +I (467) GATTS_DEMO_PHY: the gatts demo char length = 3 +I (467) GATTS_DEMO_PHY: prf_char[0] =11 +I (477) GATTS_DEMO_PHY: prf_char[1] =22 +I (477) GATTS_DEMO_PHY: prf_char[2] =33 +I (477) GATTS_DEMO_PHY: Extended advertising start, status 0 +I (487) GATTS_DEMO_PHY: Descriptor add, status 0, attr_handle 43, service_handle 40 +I (497) main_task: Returned from app_main() +I (8957) GATTS_DEMO_PHY: Extended advertising terminated, status 0 +I (8957) GATTS_DEMO_PHY: Advertising successfully ended with a connection being created +I (9087) GATTS_DEMO_PHY: Connected, conn_id 0, remote 68:67:25:4d:11:fa +I (13117) GATTS_DEMO_PHY: MTU exchange, MTU 517 +I (13637) GATTS_DEMO_PHY: Notification enable +``` + +## 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_50/ble50_throughput/throughput_server/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/CMakeLists.txt new file mode 100644 index 0000000000..419488adc3 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "example_ble_server_throughput.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/Kconfig.projbuild b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..27b29ab0dd --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/Kconfig.projbuild @@ -0,0 +1,37 @@ +menu "Example 'GATT SERVER THROUGHPUT' Config" + + config EXAMPLE_GATTS_NOTIFY_THROUGHPUT + bool "test the gatts notify throughput" + help + If this config item is set, then the 'EXAMPLE_GATTC_WRITE_THROUGHPUT' config should be close, it can't test + both write or notify at the same time at this demo + + config EXAMPLE_GATTC_WRITE_THROUGHPUT + bool "test the gattc write throughput" + help + If this config item is set, then the 'EXAMPLE_GATTS_NOTIFY_THROUGHPUT' config should be close, it can't + test both write or notify at the same time at this demo + + choice EXAMPLE_THROUGHPUT_PHY + prompt "BLE PHY mode" + default EXAMPLE_THROUGHPUT_1M_PHY + help + Define BT BLE PHY mode + + config EXAMPLE_THROUGHPUT_1M_PHY + bool "1M PHY" + config EXAMPLE_THROUGHPUT_2M_PHY + bool "2M PHY" + config EXAMPLE_THROUGHPUT_CODED_PHY_S2 + bool "Coded PHY S2" + config EXAMPLE_THROUGHPUT_CODED_PHY_S8 + bool "Coded PHY S8" + endchoice + + config EXAMPLE_THROUGHPUT_PHY + int + default 0 if EXAMPLE_THROUGHPUT_1M_PHY + default 1 if EXAMPLE_THROUGHPUT_2M_PHY + default 2 if EXAMPLE_THROUGHPUT_CODED_PHY_S2 + default 3 if EXAMPLE_THROUGHPUT_CODED_PHY_S8 +endmenu diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c new file mode 100644 index 0000000000..85b74ced38 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c @@ -0,0 +1,647 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/**************************************************************************** +* +* This is the demo to test the BLE throughput. It should be used together with throughput_client demo. +* +****************************************************************************/ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "freertos/semphr.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_defs.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_gatt_common_api.h" +#include "esp_timer.h" + +#include "sdkconfig.h" + +/********************************************************** + * Thread/Task reference + **********************************************************/ +#ifdef CONFIG_BLUEDROID_PINNED_TO_CORE +#define BLUETOOTH_TASK_PINNED_TO_CORE (CONFIG_BLUEDROID_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BLUEDROID_PINNED_TO_CORE : tskNO_AFFINITY) +#else +#define BLUETOOTH_TASK_PINNED_TO_CORE (0) +#endif + +#define SECOND_TO_USECOND 1000000 + +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) +#define GATTS_NOTIFY_LEN 495 +static SemaphoreHandle_t gatts_semaphore; +static bool can_send_notify = false; +static uint8_t indicate_data[GATTS_NOTIFY_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}; + +#endif /* #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) */ + +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) +static bool start = false; +static uint64_t write_len = 0; +static uint64_t start_time = 0; +static uint64_t current_time = 0; +#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ + +static bool is_connect = false; +///Declare the static function +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + +#define GATTS_SERVICE_UUID_TEST_A 0x00FF +#define GATTS_CHAR_UUID_TEST_A 0xFF01 +#define GATTS_DESCR_UUID_TEST_A 0x3333 +#define GATTS_NUM_HANDLE_TEST_A 4 + + + +#define GATTS_SERVICE_UUID_TEST_B 0x00EE +#define GATTS_CHAR_UUID_TEST_B 0xEE01 +#define GATTS_DESCR_UUID_TEST_B 0x2222 +#define GATTS_NUM_HANDLE_TEST_B 4 + +#define TEST_DEVICE_NAME "THROUGHPUT_PHY_DEMO" +#define TEST_MANUFACTURER_DATA_LEN 17 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 +#define EXT_ADV_HANDLE 0 +#define NUM_EXT_ADV_SET 1 +#define EXT_ADV_DURATION 0 +#define EXT_ADV_MAX_EVENTS 0 + +#define PREPARE_BUF_MAX_SIZE 1024 + +static const char *GATTS_TAG = "GATTS_DEMO_PHY"; +static uint8_t char1_str[] = {0x11,0x22,0x33}; +static esp_gatt_char_prop_t a_property = 0; + +static esp_attr_value_t gatts_demo_char1_val = +{ + .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX, + .attr_len = sizeof(char1_str), + .attr_value = char1_str, +}; + +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', +}; + +static esp_ble_gap_ext_adv_t ext_adv[1] = { + [0] = {EXT_ADV_HANDLE, EXT_ADV_DURATION, EXT_ADV_MAX_EVENTS}, +}; + +esp_ble_gap_ext_adv_params_t ext_adv_params = { + .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE, + .interval_min = 0x20, + .interval_max = 0x20, + .channel_map = ADV_CHNL_ALL, + .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + .primary_phy = ESP_BLE_GAP_PHY_1M, + .max_skip = 0, + #if (CONFIG_EXAMPLE_THROUGHPUT_1M_PHY) + .secondary_phy = ESP_BLE_GAP_PHY_1M, + #elif (CONFIG_EXAMPLE_THROUGHPUT_2M_PHY) + .secondary_phy = ESP_BLE_GAP_PHY_2M, + #else + .secondary_phy = ESP_BLE_GAP_PHY_CODED, + #endif + .sid = 0, + .scan_req_notif = false, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE, +}; + +#define PROFILE_NUM 1 +#define PROFILE_A_APP_ID 0 + +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; +}; + +/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ +static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gatts_cb = gatts_profile_a_event_handler, + .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; + +typedef struct { + uint8_t *prepare_buf; + int prepare_len; +} prepare_type_env_t; + +static prepare_type_env_t a_prepare_write_env; + +extern void esp_ble_switch_phy_coded(bool phy_500k); +void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); +void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); + +static uint8_t check_sum(uint8_t *addr, uint16_t count) +{ + uint32_t sum = 0; + + if (addr == NULL || count == 0) { + return 0; + } + + for(int i = 0; i < count; i++) { + sum = sum + addr[i]; + } + + while (sum >> 8) { + sum = (sum & 0xff) + (sum >> 8); + } + + return (uint8_t)~sum; +} + + +static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + switch (event) { + case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT: + ESP_LOGI(GATTS_TAG,"Extended advertising params set, status %d", param->ext_adv_set_params.status); + esp_ble_gap_config_ext_adv_data_raw(EXT_ADV_HANDLE, sizeof(ext_adv_raw_data), &ext_adv_raw_data[0]); + break; + case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT: + ESP_LOGI(GATTS_TAG,"Extended advertising data set, status %d", param->ext_adv_data_set.status); + esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); + break; + case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: + ESP_LOGI(GATTS_TAG, "Extended advertising start, status %d", param->ext_adv_data_set.status); + break; + case ESP_GAP_BLE_ADV_TERMINATED_EVT: + ESP_LOGI(GATTS_TAG, "Extended advertising terminated, status %d", param->adv_terminate.status); + if(param->adv_terminate.status == 0x00) { + ESP_LOGI(GATTS_TAG, "Advertising successfully ended with a connection being created"); + } + break; + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + ESP_LOGI(GATTS_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d", + param->update_conn_params.status, + param->update_conn_params.conn_int, + param->update_conn_params.latency, + param->update_conn_params.timeout); + break; + default: + break; + } +} + +void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){ + esp_gatt_status_t status = ESP_GATT_OK; + if (param->write.need_rsp) { + if (param->write.is_prep) { + if (param->write.offset > PREPARE_BUF_MAX_SIZE) { + status = ESP_GATT_INVALID_OFFSET; + } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { + status = ESP_GATT_INVALID_ATTR_LEN; + } + + if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) { + prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t)); + prepare_write_env->prepare_len = 0; + if (prepare_write_env->prepare_buf == NULL) { + ESP_LOGE(GATTS_TAG, "Gatt_server prep no mem"); + status = ESP_GATT_NO_RESOURCES; + } + } + + esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t)); + if (gatt_rsp) { + gatt_rsp->attr_value.len = param->write.len; + gatt_rsp->attr_value.handle = param->write.handle; + gatt_rsp->attr_value.offset = param->write.offset; + gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; + memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); + esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp); + + if (response_err != ESP_OK) { + ESP_LOGE(GATTS_TAG, "Send response error\n"); + } + free(gatt_rsp); + } else { + ESP_LOGE(GATTS_TAG, "malloc failed, no resource to send response error\n"); + status = ESP_GATT_NO_RESOURCES; + } + + if (status != ESP_GATT_OK) { + return; + } + memcpy(prepare_write_env->prepare_buf + param->write.offset, + param->write.value, + param->write.len); + prepare_write_env->prepare_len += param->write.len; + + }else { + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL); + } + } +} + +void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){ + if (param->exec_write.exec_write_flag != ESP_GATT_PREP_WRITE_EXEC){ + ESP_LOGI(GATTS_TAG,"Prepare write cancel"); + } + if (prepare_write_env->prepare_buf) { + free(prepare_write_env->prepare_buf); + prepare_write_env->prepare_buf = NULL; + } + prepare_write_env->prepare_len = 0; +} + +static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { + 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_A_APP_ID].service_id.is_primary = true; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A; + gl_profile_tab[PROFILE_A_APP_ID].gatts_if = gatts_if; + esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(TEST_DEVICE_NAME); + if (set_dev_name_ret){ + ESP_LOGE(GATTS_TAG, "set device name failed, error code = %x", set_dev_name_ret); + } + esp_ble_gap_ext_adv_set_params(EXT_ADV_HANDLE, &ext_adv_params); + esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A); + break; + case ESP_GATTS_READ_EVT: { + ESP_LOGI(GATTS_TAG, "Characteristic read, conn_id %d, trans_id %" PRIu32 ", handle %d", param->read.conn_id, param->read.trans_id, param->read.handle); + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.len = 4; + rsp.attr_value.value[0] = 0xde; + rsp.attr_value.value[1] = 0xed; + rsp.attr_value.value[2] = 0xbe; + rsp.attr_value.value[3] = 0xef; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_OK, &rsp); + break; + } + case ESP_GATTS_WRITE_EVT: { +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) + if (!param->write.is_prep){ + if (gl_profile_tab[PROFILE_A_APP_ID].descr_handle == param->write.handle && param->write.len == 2){ + uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0]; + if (descr_value == 0x0001){ + if (a_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){ + + ESP_LOGI(GATTS_TAG, "Notification enable"); + can_send_notify = true; + xSemaphoreGive(gatts_semaphore); + } + }else if (descr_value == 0x0002){ + if (a_property & ESP_GATT_CHAR_PROP_BIT_INDICATE){ + ESP_LOGI(GATTS_TAG, "Indication enable"); + uint8_t indicate_data[600]; + for (int i = 0; i < sizeof(indicate_data); ++i) + { + indicate_data[i] = i%0xff; + } + + for (int j = 0; j < 1000; j++) { + //the size of indicate_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle, + sizeof(indicate_data), indicate_data, true); + } + } + } + else if (descr_value == 0x0000){ + can_send_notify = false; + a_property = 0; + ESP_LOGI(GATTS_TAG, "Notification/Indication disable"); + }else{ + ESP_LOGE(GATTS_TAG, "Unknown descriptor value"); + ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len); + } + + } + } +#endif /* #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) */ + example_write_event_env(gatts_if, &a_prepare_write_env, param); +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + if (param->write.handle == gl_profile_tab[PROFILE_A_APP_ID].char_handle) { + // The last value byte is the checksum data, should used to check the data is received corrected or not. + if (param->write.value[param->write.len - 1] == + check_sum(param->write.value, param->write.len - 1)) { + write_len += param->write.len; + } + + if (start == false) { + start_time = esp_timer_get_time(); + start = true; + break; + } + } +#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ + + break; + } + case ESP_GATTS_EXEC_WRITE_EVT: + ESP_LOGI(GATTS_TAG,"Execute write"); +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_CANCEL) { + if (write_len > a_prepare_write_env.prepare_len) { + write_len -= a_prepare_write_env.prepare_len; + } else { + write_len = 0; + } + } +#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ + esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + example_exec_write_event_env(&a_prepare_write_env, param); + break; + case ESP_GATTS_MTU_EVT: + ESP_LOGI(GATTS_TAG, "MTU exchange, MTU %d", param->mtu.mtu); + is_connect = true; + break; + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_CREATE_EVT: + ESP_LOGI(GATTS_TAG, "Service create, status %d, service_handle %d", param->create.status, param->create.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A; + + esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle); + a_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY; + esp_err_t add_char_ret = esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].char_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + a_property, + &gatts_demo_char1_val, NULL); + if (add_char_ret){ + ESP_LOGE(GATTS_TAG, "add char failed, error code =%x",add_char_ret); + } + break; + case ESP_GATTS_ADD_INCL_SRVC_EVT: + break; + case ESP_GATTS_ADD_CHAR_EVT: { + uint16_t length = 0; + const uint8_t *prf_char; + ESP_LOGI(GATTS_TAG, "Characteristic add, status %d, attr_handle %d, service_handle %d", + param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle); + gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16; + gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG; + esp_err_t get_attr_ret = esp_ble_gatts_get_attr_value(param->add_char.attr_handle, &length, &prf_char); + if (get_attr_ret == ESP_FAIL){ + ESP_LOGE(GATTS_TAG, "ILLEGAL HANDLE"); + } + + ESP_LOGI(GATTS_TAG, "the gatts demo char length = %x", length); + for(int i = 0; i < length; i++){ + ESP_LOGI(GATTS_TAG, "prf_char[%x] =%x",i,prf_char[i]); + } + esp_err_t add_descr_ret = esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid, + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL); + if (add_descr_ret){ + ESP_LOGE(GATTS_TAG, "add char descr failed, error code =%x", add_descr_ret); + } + break; + } + case ESP_GATTS_ADD_CHAR_DESCR_EVT: + + gl_profile_tab[PROFILE_A_APP_ID].descr_handle = param->add_char_descr.attr_handle; + ESP_LOGI(GATTS_TAG, "Descriptor add, status %d, attr_handle %d, service_handle %d", + param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle); + break; + case ESP_GATTS_DELETE_EVT: + break; + case ESP_GATTS_START_EVT: + ESP_LOGI(GATTS_TAG, "Service start, status %d, service_handle %d", + param->start.status, param->start.service_handle); + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: { + ESP_LOGI(GATTS_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"", + param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda)); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + break; + } + case ESP_GATTS_DISCONNECT_EVT: + is_connect = false; + ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x", + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); + esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); + break; + case ESP_GATTS_CONF_EVT: + break; + case ESP_GATTS_OPEN_EVT: + case ESP_GATTS_CANCEL_OPEN_EVT: + case ESP_GATTS_CLOSE_EVT: + case ESP_GATTS_LISTEN_EVT: + break; + case ESP_GATTS_CONGEST_EVT: +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) + if (param->congest.congested) { + can_send_notify = false; + } else { + can_send_notify = true; + xSemaphoreGive(gatts_semaphore); + } +#endif /* #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) */ + break; + default: + break; + } +} + +static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + /* If event is register event, store the gatts_if for each profile */ + if (event == ESP_GATTS_REG_EVT) { + if (param->reg.status == ESP_GATT_OK) { + gl_profile_tab[param->reg.app_id].gatts_if = gatts_if; + } else { + ESP_LOGI(GATTS_TAG, "Reg app failed, app_id %04x, status %d", + param->reg.app_id, + param->reg.status); + return; + } + } + + /* If the gatts_if equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) { + if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */ + gatts_if == 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); +} + +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) +void throughput_server_task(void *param) +{ + vTaskDelay(2000 / portTICK_PERIOD_MS); + uint8_t sum = check_sum(indicate_data, sizeof(indicate_data) - 1); + // Added the check sum in the last data value. + indicate_data[GATTS_NOTIFY_LEN - 1] = sum; + + while(1) { + if (!can_send_notify) { + int res = xSemaphoreTake(gatts_semaphore, portMAX_DELAY); + assert(res == pdTRUE); + } else { + if (is_connect) { + int free_buff_num = esp_ble_get_cur_sendable_packets_num(gl_profile_tab[PROFILE_A_APP_ID].conn_id); + if(free_buff_num > 0) { + for( ; free_buff_num > 0; free_buff_num--) { + esp_ble_gatts_send_indicate(gl_profile_tab[PROFILE_A_APP_ID].gatts_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].char_handle, + sizeof(indicate_data), indicate_data, false); + } + } else { //Add the vTaskDelay to prevent this task from consuming the CPU all the time, causing low-priority tasks to not be executed at all. + vTaskDelay( 10 / portTICK_PERIOD_MS ); + } + } else { + vTaskDelay(300 / portTICK_PERIOD_MS); + } + } + + } +} +#endif + +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) +void throughput_cal_task(void *param) +{ + while (1) + { + uint32_t bit_rate = 0; + vTaskDelay(2000 / portTICK_PERIOD_MS); + if (is_connect && start_time) { + current_time = esp_timer_get_time(); + bit_rate = write_len * SECOND_TO_USECOND / (current_time - start_time); + ESP_LOGI(GATTS_TAG, "GATTC write Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time %d", + bit_rate, bit_rate<<3, (int)((current_time - start_time) / SECOND_TO_USECOND)); + } + + } + +} +#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ + +void app_main(void) +{ + esp_err_t ret; + + // 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", __func__); + return; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(GATTS_TAG, "%s enable controller failed", __func__); + return; + } + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(GATTS_TAG, "%s init bluetooth failed", __func__); + return; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(GATTS_TAG, "%s enable bluetooth failed", __func__); + return; + } + + #if (CONFIG_EXAMPLE_THROUGHPUT_CODED_PHY_S2) + // Should be invoked after the Controller and Host inited and enabled + esp_ble_switch_phy_coded(true); + #endif /*#if (EXAMPLE_THROUGHPUT_CODED_PHY_S2)*/ + + 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_A_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); + } + +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) + // The task is only created on the CPU core that Bluetooth is working on, + // preventing the sending task from using the un-updated Bluetooth state on another CPU. + xTaskCreatePinnedToCore(&throughput_server_task, "throughput_server_task", 4096, NULL, 15, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); +#endif + +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + xTaskCreatePinnedToCore(&throughput_cal_task, "throughput_cal_task", 4096, NULL, 14, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); +#endif + +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) + gatts_semaphore = xSemaphoreCreateBinary(); + if (!gatts_semaphore) { + ESP_LOGE(GATTS_TAG, "%s, init fail, the gatts semaphore create fail.", __func__); + return; + } +#endif /* #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) */ + return; +} diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults new file mode 100644 index 0000000000..83bac6e78a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults @@ -0,0 +1,11 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT=y +CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT=n +CONFIG_BT_ENABLED=y +CONFIG_EXAMPLE_THROUGHPUT_PHY=0 +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. +# CONFIG_BT_LE_50_FEATURE_SUPPORT=n diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32c2 b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32c2 new file mode 100644 index 0000000000..783c146dc1 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32c2 @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c2" +CONFIG_BT_ENABLED=y +CONFIG_BT_LE_50_FEATURE_SUPPORT=y +CONFIG_BT_LE_HCI_EVT_BUF_SIZE=257 +CONFIG_XTAL_FREQ_26=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32c3 b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..641ed53e33 --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32c3 @@ -0,0 +1,5 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c3" +CONFIG_BT_ENABLED=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32s3 b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..f15a09ef2a --- /dev/null +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults.esp32s3 @@ -0,0 +1,5 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_BT_ENABLED=y