diff --git a/components/bt/bluedroid/api/esp_gattc_api.c b/components/bt/bluedroid/api/esp_gattc_api.c index 32d840fe9..fc431d6fb 100644 --- a/components/bt/bluedroid/api/esp_gattc_api.c +++ b/components/bt/bluedroid/api/esp_gattc_api.c @@ -19,6 +19,9 @@ #include "btc/btc_manage.h" #include "btc_gattc.h" #include "btc_gatt_util.h" +#include "stack/l2cdefs.h" +#include "stack/l2c_api.h" + #if (GATTC_INCLUDED == TRUE) esp_err_t esp_ble_gattc_register_callback(esp_gattc_cb_t callback) @@ -326,6 +329,11 @@ esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR; @@ -345,6 +353,11 @@ esp_err_t esp_ble_gattc_read_multiple(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_MULTIPLE_CHAR; @@ -371,6 +384,11 @@ esp_err_t esp_ble_gattc_read_char_descr (esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_CHAR_DESCR; @@ -393,6 +411,11 @@ esp_err_t esp_ble_gattc_write_char(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR; @@ -418,6 +441,11 @@ esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_WRITE_CHAR_DESCR; @@ -443,6 +471,11 @@ esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_PREPARE_WRITE; @@ -468,6 +501,11 @@ esp_err_t esp_ble_gattc_prepare_write_char_descr(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_DEBUG("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_PREPARE_WRITE_CHAR_DESCR; diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index fc1dc81ca..8eab2e48c 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -20,6 +20,9 @@ #include "btc_gatts.h" #include "btc_gatt_util.h" #include "common/bt_target.h" +#include "stack/l2cdefs.h" +#include "stack/l2c_api.h" + #if (GATTS_INCLUDED == TRUE) #define COPY_TO_GATTS_ARGS(_gatt_args, _arg, _arg_type) memcpy(_gatt_args, _arg, sizeof(_arg_type)) @@ -257,6 +260,11 @@ esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, conn_id)) { + LOG_ERROR("%s, the l2cap chanel is congest.", __func__); + return ESP_FAIL; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_SEND_INDICATE; diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index 42f6d97ae..c72cd6328 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -692,7 +692,7 @@ void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) cb_data.req_data.status = status; cb_data.req_data.conn_id = p_msg->api_indicate.hdr.layer_specific; - cb_data.req_data.value =(uint8_t *)osi_malloc(p_msg->api_indicate.len); + cb_data.req_data.value = (uint8_t *)osi_malloc(p_msg->api_indicate.len); if (cb_data.req_data.value != NULL){ memset(cb_data.req_data.value, 0, p_msg->api_indicate.len); cb_data.req_data.data_len = p_msg->api_indicate.len; diff --git a/components/bt/bluedroid/stack/include/stack/l2c_api.h b/components/bt/bluedroid/stack/include/stack/l2c_api.h index 641412f89..147ed6c74 100644 --- a/components/bt/bluedroid/stack/include/stack/l2c_api.h +++ b/components/bt/bluedroid/stack/include/stack/l2c_api.h @@ -1225,6 +1225,9 @@ extern UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr); *******************************************************************************/ extern UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport); +extern BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, UINT16 handle); + + #endif /* (BLE_INCLUDED == TRUE) */ #ifdef __cplusplus diff --git a/components/bt/bluedroid/stack/l2cap/l2c_api.c b/components/bt/bluedroid/stack/l2cap/l2c_api.c index d31b62a6f..3c7745af9 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_api.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_api.c @@ -1816,7 +1816,7 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf) // If already congested, do not accept any more packets if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) { - L2CAP_TRACE_ERROR ("L2CAP - CID: 0x%04x cannot send, already congested \ + L2CAP_TRACE_ERROR ("L2CAP - CID: 0x%04x cannot send, already congested\ xmit_hold_q.count: %u buff_quota: %u", fixed_cid, fixed_queue_length(p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->xmit_hold_q), p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->buff_quota); @@ -1840,6 +1840,17 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf) return (L2CAP_DW_SUCCESS); } +BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, UINT16 handle) +{ + tL2C_LCB *p_lcb; + p_lcb = l2cu_find_lcb_by_handle(handle); + + if (p_lcb != NULL) { + return p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent; + } + + return TRUE; +} /******************************************************************************* ** ** Function L2CA_RemoveFixedChnl diff --git a/components/bt/bluedroid/stack/l2cap/l2c_ble.c b/components/bt/bluedroid/stack/l2cap/l2c_ble.c index 6c9a287ac..b96729517 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_ble.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_ble.c @@ -914,7 +914,7 @@ void l2c_link_processs_ble_num_bufs (UINT16 num_lm_ble_bufs) num_lm_ble_bufs = L2C_DEF_NUM_BLE_BUF_SHARED; l2cb.num_lm_acl_bufs -= L2C_DEF_NUM_BLE_BUF_SHARED; } - L2CAP_TRACE_DEBUG("#####################################num_lm_ble_bufs = %d",num_lm_ble_bufs); + L2CAP_TRACE_DEBUG("num_lm_ble_bufs = %d",num_lm_ble_bufs); l2cb.num_lm_ble_bufs = l2cb.controller_le_xmit_window = num_lm_ble_bufs; } diff --git a/components/bt/bluedroid/stack/l2cap/l2c_link.c b/components/bt/bluedroid/stack/l2cap/l2c_link.c index 96fef0677..87026363c 100644 --- a/components/bt/bluedroid/stack/l2cap/l2c_link.c +++ b/components/bt/bluedroid/stack/l2cap/l2c_link.c @@ -1096,10 +1096,10 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) #if (BLE_INCLUDED == TRUE) while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE))) - && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) + && (p_lcb->sent_not_acked <= p_lcb->link_xmit_quota)) #else while ( (l2cb.controller_xmit_window != 0) - && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) + && (p_lcb->sent_not_acked <= p_lcb->link_xmit_quota)) #endif { if (list_is_empty(p_lcb->link_xmit_data_q)) { @@ -1118,7 +1118,7 @@ void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) #if (BLE_INCLUDED == TRUE) while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) || (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE))) - && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) + && (p_lcb->sent_not_acked <= p_lcb->link_xmit_quota)) #else while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) #endif diff --git a/components/bt/lib b/components/bt/lib index d1e2bce55..34b64038d 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit d1e2bce5585b2aded165a886aced8dacfbac9dee +Subproject commit 34b64038d090fa172d9757e2fce293ff26e0a08c diff --git a/examples/bluetooth/ble_throughput/throughput_client/Makefile b/examples/bluetooth/ble_throughput/throughput_client/Makefile new file mode 100644 index 000000000..b08f8da82 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_client/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := throughput_client_demo + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_throughput/throughput_client/README.md b/examples/bluetooth/ble_throughput/throughput_client/README.md new file mode 100644 index 000000000..142f8cff8 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_client/README.md @@ -0,0 +1,12 @@ +ESP-IDF BLE throughput GATT CLIENT demo +======================== + +This is the demo used to test the BLE throughput, this demo should used with throughput server demo together. +The throughput of BLE can up to 720-767 bits/s between to ESP32 board. +Note: +1. In order to maximize throughput, we need to set the uart print baud rate at 921600 or more (make menuconfig --> Component config --> ESP32-specific --> UART console baud rate --> 921600(or 1500000)); +2. 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: make menuconfig --> Component config --> Example 'GATT CLIENT THROUGHPUT' Config ---> then select the 'test the gattc write throughput' option +3. This demo only test unidirectional throughput, if you want to test the bidirectional throughput please change the demo by yourself. +4. Should change the CPU frequency to 240MHz in the make menuconfig --> Component config ---> ESP32-specific ---> CPU frequency (240 MHz) +5. Should change the bluetooth controller and Bluedroid run in different Core in the make menuconfig --> Component config ---> Bluetooth ---> The cpu core which bluetooth controller run (Core 0 (PRO CPU)) & Bluedroid Enable ---> The cpu core which Bluedroid run (Core 1 (APP CPU)) diff --git a/examples/bluetooth/ble_throughput/throughput_client/main/Kconfig b/examples/bluetooth/ble_throughput/throughput_client/main/Kconfig new file mode 100644 index 000000000..d1b44f5d3 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_client/main/Kconfig @@ -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/ble_throughput/throughput_client/main/component.mk b/examples/bluetooth/ble_throughput/throughput_client/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_client/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/ble_throughput/throughput_client/main/example_ble_client_throughput.c b/examples/bluetooth/ble_throughput/throughput_client/main/example_ble_client_throughput.c new file mode 100644 index 000000000..a124e96e7 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_client/main/example_ble_client_throughput.c @@ -0,0 +1,585 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + + + +/**************************************************************************** +* +* This file is for gatt client. It can scan ble device, connect one device. +* Run the gatt_server demo, the client demo will automatically connect to the gatt_server demo. +* Client demo will enable gatt_server's notify after connection. Then the two devices will exchange +* data. +* +****************************************************************************/ + +#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" + +#define GATTC_TAG "GATTC_DEMO" +#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 + +static const char remote_device_name[] = "THROUGHPUT_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 490 + +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_connecet = false; + +/* eclare 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_scan_params_t ble_scan_params = { + .scan_type = BLE_SCAN_TYPE_ACTIVE, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_interval = 0x50, + .scan_window = 0x30 +}; + +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, "REG_EVT"); + 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: { + is_connecet = true; + ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d", p_data->connect.conn_id, gattc_if); + 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_LOGI(GATTC_TAG, "REMOTE BDA:"); + esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_A_APP_ID].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); + } + 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_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); + 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, "ESP_GATTC_SEARCH_RES_EVT"); + 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, "search service failed, error status = %x", p_data->search_cmpl.status); + break; + } + 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_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"); + } + + 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"); + }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"); + } + + /* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used 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); + }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_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"); + } + + /* Erery char have only one descriptor in our 'ESP_GATTS_DEMO' demo, so we used 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, "ESP_GATTC_NOTIFY_EVT, receive indicate value:"); + } + if (start == false) { + start_time = esp_timer_get_time(); + start = true; + break; + } + +#else /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + esp_log_buffer_hex(GATTC_TAG, p_data->notify.value, p_data->notify.value_len); +#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, "write descr failed, error status = %x", p_data->write.status); + break; + } + ESP_LOGI(GATTC_TAG, "write descr success "); +#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, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:"); + esp_log_buffer_hex(GATTC_TAG, bda, sizeof(esp_bd_addr_t)); + 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_LOGI(GATTC_TAG, "write char success "); + break; + case ESP_GATTC_DISCONNECT_EVT: + is_connecet = 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, "ESP_GATTC_DISCONNECT_EVT, reason = %d", 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) +{ + uint8_t *adv_name = NULL; + uint8_t adv_name_len = 0; + switch (event) { + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { + //the unit of the duration is second + uint32_t duration = 30; + esp_ble_gap_start_scanning(duration); + break; + } + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + //scan start complete event to indicate scan start successfully or failed + 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: + esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6); + ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len); + adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, + ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); + ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len); + esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len); + ESP_LOGI(GATTC_TAG, "\n"); + 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\n", remote_device_name); + if (connect == false) { + connect = true; + ESP_LOGI(GATTC_TAG, "connect to the remote device."); + esp_ble_gap_set_prefer_conn_params(scan_result->scan_rst.bda, 32, 32, 0, 600); + esp_ble_gap_stop_scanning(); + esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, BLE_ADDR_TYPE_PUBLIC, 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 connetion 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 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); +} + +static void throughput_client_task(void *param) +{ + vTaskDelay(2000 / portTICK_PERIOD_MS); +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + uint8_t sum = check_sum(write_data, sizeof(write_data) - 1); + write_data[GATTC_WRITE_LEN - 1] = sum; +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + + while(1) { +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + vTaskDelay(2000 / portTICK_PERIOD_MS); + 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 = %d Btye/s, = %d 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 Btye/s, = 0 bit/s"); + } +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + if (!can_send_write) { + int res = xSemaphoreTake(gattc_semaphore, portMAX_DELAY); + assert(res == pdTRUE); + } else { + if (is_connecet) { + // 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); + } + } +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + + } +} + +void app_main() +{ + // Initialize NVS. + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + 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\n", __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\n", __func__, ret); + return; + } + + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(GATTC_TAG, "%s init bluetooth failed, error code = %x\n", __func__, ret); + return; + } + + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed, error code = %x\n", __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\n", __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\n", __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\n", __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); + } + + xTaskCreate(&throughput_client_task, "throughput_client_task", 4096, NULL, 10, NULL); +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + gattc_semaphore = xSemaphoreCreateMutex(); + 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/ble_throughput/throughput_client/sdkconfig.defaults b/examples/bluetooth/ble_throughput/throughput_client/sdkconfig.defaults new file mode 100644 index 000000000..e4b501910 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_client/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_GATTS_NOTIFY_THROUGHPUT=y diff --git a/examples/bluetooth/ble_throughput/throughput_server/Makefile b/examples/bluetooth/ble_throughput/throughput_server/Makefile new file mode 100644 index 000000000..952790fa0 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_server/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := throughput_server_demos + +COMPONENT_ADD_INCLUDEDIRS := components/include + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/ble_throughput/throughput_server/README.md b/examples/bluetooth/ble_throughput/throughput_server/README.md new file mode 100644 index 000000000..dd45acbfc --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_server/README.md @@ -0,0 +1,13 @@ +ESP-IDF BLE throughput GATT SERVER demo +======================== + +This is the demo used to test the BLE throughput, this demo should used with throughput client demo together. +The throughput of BLE can up to 720-767 bits/s between to ESP32 board. +Note: +1. In order to maximize throughput, we need to set the uart print baud rate at 921600 or more (make menuconfig --> Component config --> ESP32-specific --> UART console baud rate --> 921600(or 1500000)); +2. 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: make menuconfig --> Component config --> Example 'GATT SERVER THROUGHPUT' Config ---> then select the 'test the gattc write throughput' option +3. This demo only test unidirectional throughput, if you want to test the bidirectional throughput please change the demo by yourself. +4. Should change the CPU frequency to 240MHz in the make menuconfig --> Component config ---> ESP32-specific ---> CPU frequency (240 MHz) +5. Should change the bluetooth controller and Bluedroid run in different Core in the make menuconfig --> Component config ---> Bluetooth ---> The cpu core which bluetooth controller run (Core 0 (PRO CPU)) & Bluedroid Enable ---> The cpu core which Bluedroid run (Core 1 (APP CPU)) + diff --git a/examples/bluetooth/ble_throughput/throughput_server/main/Kconfig b/examples/bluetooth/ble_throughput/throughput_server/main/Kconfig new file mode 100644 index 000000000..fa0098ab4 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_server/main/Kconfig @@ -0,0 +1,23 @@ +menu "Example 'GATT SERVER THROUGHPUT' Config" + +config SET_RAW_ADV_DATA + bool "Use raw data for advertising packets and scan response data" + help + If this config item is set, raw binary data will be used to generate advertising & scan response data. + This option uses the esp_ble_gap_config_adv_data_raw() and esp_ble_gap_config_scan_rsp_data_raw() functions. + + If this config item is unset, advertising & scan response data is provided via a higher-level esp_ble_adv_data_t structure. + The lower layer will generate the BLE packets. This option has higher overhead at runtime. + +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/ble_throughput/throughput_server/main/component.mk b/examples/bluetooth/ble_throughput/throughput_server/main/component.mk new file mode 100644 index 000000000..a98f634ea --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_server/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/ble_throughput/throughput_server/main/example_ble_server_throughput.c b/examples/bluetooth/ble_throughput/throughput_server/main/example_ble_server_throughput.c new file mode 100644 index 000000000..9bb06a579 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_server/main/example_ble_server_throughput.c @@ -0,0 +1,709 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#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_main.h" +#include "esp_gatt_common_api.h" + +#include "sdkconfig.h" + +#define SECOND_TO_USECOND 1000000 + +#define GATTS_TAG "GATTS_DEMO" + +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) +#define GATTS_NOTIFY_LEN 490 +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_GATTS_NOTIFY_THROUGHPUT) */ + +#if (CONFIG_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_GATTC_WRITE_THROUGHPUT) */ + +static bool is_connecet = 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_DEMO" +#define TEST_MANUFACTURER_DATA_LEN 17 + +#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 + +#define PREPARE_BUF_MAX_SIZE 1024 + +uint8_t char1_str[] = {0x11,0x22,0x33}; +esp_gatt_char_prop_t a_property = 0; +esp_gatt_char_prop_t b_property = 0; + +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 adv_config_done = 0; +#define adv_config_flag (1 << 0) +#define scan_rsp_config_flag (1 << 1) + +#ifdef CONFIG_SET_RAW_ADV_DATA +static uint8_t raw_adv_data[] = { + 0x02, 0x01, 0x06, + 0x02, 0x0a, 0xeb, 0x03, 0x03, 0xab, 0xcd +}; +static uint8_t raw_scan_rsp_data[] = { + 0x0f, 0x09, 0x45, 0x53, 0x50, 0x5f, 0x47, 0x41, 0x54, 0x54, 0x53, 0x5f, 0x44, + 0x45, 0x4d, 0x4f +}; +#else + +static uint8_t adv_service_uuid128[32] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, + //second uuid, 32bit, [12], [13], [14], [15] is the value + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, +}; + +// The length of adv data must be less than 31 bytes +//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56}; +//adv data +static esp_ble_adv_data_t adv_data = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x20, + .max_interval = 0x40, + .appearance = 0x00, + .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, + .p_manufacturer_data = NULL, //&test_manufacturer[0], + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 32, + .p_service_uuid = adv_service_uuid128, + .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, + .min_interval = 0x20, + .max_interval = 0x40, + .appearance = 0x00, + .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, + .p_manufacturer_data = NULL, //&test_manufacturer[0], + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 32, + .p_service_uuid = adv_service_uuid128, + .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), +}; + +#endif /* CONFIG_SET_RAW_ADV_DATA */ + +static esp_ble_adv_params_t adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x40, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + //.peer_addr = + //.peer_addr_type = + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +#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; + +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) { +#ifdef CONFIG_SET_RAW_ADV_DATA + case ESP_GAP_BLE_ADV_DATA_RAW_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_RAW_SET_COMPLETE_EVT: + adv_config_done &= (~scan_rsp_config_flag); + if (adv_config_done==0){ + esp_ble_gap_start_advertising(&adv_params); + } + break; +#else + 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; +#endif + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + //advertising start complete event to indicate advertising start successfully or failed + if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TAG, "Advertising start failed\n"); + } + 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\n"); + } + else { + ESP_LOGI(GATTS_TAG, "Stop adv successfully\n"); + } + break; + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + ESP_LOGI(GATTS_TAG, "update connetion 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; + } +} + +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 (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\n"); + status = ESP_GATT_NO_RESOURCES; + } + } else { + if(param->write.offset > PREPARE_BUF_MAX_SIZE || + prepare_write_env->prepare_len > param->write.offset) { + status = ESP_GATT_INVALID_OFFSET; + } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { + status = ESP_GATT_INVALID_ATTR_LEN; + } + } + + esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t)); + 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); + 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_log_buffer_hex(GATTS_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len); + }else{ + ESP_LOGI(GATTS_TAG,"ESP_GATT_PREP_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, "REGISTER_APP_EVT, status %d, app_id %d\n", 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); + } +#ifdef CONFIG_SET_RAW_ADV_DATA + esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data)); + if (raw_adv_ret){ + ESP_LOGE(GATTS_TAG, "config raw adv data failed, error code = %x ", raw_adv_ret); + } + adv_config_done |= adv_config_flag; + esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data)); + if (raw_scan_ret){ + ESP_LOGE(GATTS_TAG, "config raw scan rsp data failed, error code = %x", raw_scan_ret); + } + adv_config_done |= scan_rsp_config_flag; +#else + //config adv data + 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; + //config scan response data + 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; + +#endif + 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, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", 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_GATTS_NOTIFY_THROUGHPUT) + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d", param->write.conn_id, param->write.trans_id, param->write.handle); + if (!param->write.is_prep){ + ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len); + esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len); + 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, "notify 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, "indicate 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, "notify/indicate disable "); + }else{ + ESP_LOGE(GATTS_TAG, "unknown descr value"); + esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len); + } + + } + } +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + example_write_event_env(gatts_if, &a_prepare_write_env, param); +#if (CONFIG_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_GATTC_WRITE_THROUGHPUT) */ + + break; + } + case ESP_GATTS_EXEC_WRITE_EVT: + ESP_LOGI(GATTS_TAG,"ESP_GATTS_EXEC_WRITE_EVT"); +#if (CONFIG_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_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, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu); + break; + case ESP_GATTS_UNREG_EVT: + break; + case ESP_GATTS_CREATE_EVT: + ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d\n", 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, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d\n", + 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\n", length); + for(int i = 0; i < length; i++){ + ESP_LOGI(GATTS_TAG, "prf_char[%x] =%x\n",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, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n", + 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_EVT, status %d, service_handle %d\n", + param->start.status, param->start.service_handle); + break; + case ESP_GATTS_STOP_EVT: + break; + case ESP_GATTS_CONNECT_EVT: { + is_connecet = true; + esp_ble_conn_update_params_t conn_params = {0}; + memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t)); + /* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */ + conn_params.latency = 0; + conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms + conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms + conn_params.timeout = 400; // timeout = 400*10ms = 4000ms + ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:", + param->connect.conn_id, + param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2], + param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]); + gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + //start sent the update connection parameters to the peer device. + //esp_ble_gap_update_conn_params(&conn_params); + break; + } + case ESP_GATTS_DISCONNECT_EVT: + is_connecet = false; + ESP_LOGI(GATTS_TAG, "ESP_GATTS_DISCONNECT_EVT"); + esp_ble_gap_start_advertising(&adv_params); + break; + case ESP_GATTS_CONF_EVT: + ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT, status %d", param->conf.status); + if (param->conf.status != ESP_GATT_OK){ + esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len); + } +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + start_time = false; + current_time = 0; + write_len = 0; +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + 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_GATTS_NOTIFY_THROUGHPUT) + if (param->congest.congested) { + can_send_notify = false; + } else { + can_send_notify = true; + xSemaphoreGive(gatts_semaphore); + } +#endif /* #if (CONFIG_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\n", + 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); +} + +void throughput_server_task(void *param) +{ + vTaskDelay(2000 / portTICK_PERIOD_MS); +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + 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; +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + + while(1) { +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + if (!can_send_notify) { + ESP_LOGI(GATTS_TAG, "==="); + int res = xSemaphoreTake(gatts_semaphore, portMAX_DELAY); + assert(res == pdTRUE); + } else { + if (is_connecet) { + 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); + } + } +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + uint32_t bit_rate = 0; + vTaskDelay(2000 / portTICK_PERIOD_MS); + if (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 = %d Btye/s, = %d bit/s, time = %ds", + bit_rate, bit_rate<<3, (int)((current_time - start_time) / SECOND_TO_USECOND)); + } else { + ESP_LOGI(GATTS_TAG, "GATTC write Bit rate = 0 Btye/s, = 0 bit/s"); + } +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + + } +} + +void app_main() +{ + esp_err_t ret; + + // Initialize NVS. + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + 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\n", __func__); + return; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + ESP_LOGE(GATTS_TAG, "%s enable controller failed\n", __func__); + return; + } + ret = esp_bluedroid_init(); + if (ret) { + ESP_LOGE(GATTS_TAG, "%s init bluetooth failed\n", __func__); + return; + } + ret = esp_bluedroid_enable(); + if (ret) { + ESP_LOGE(GATTS_TAG, "%s enable bluetooth failed\n", __func__); + 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_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); + } + + xTaskCreate(&throughput_server_task, "throughput_server_task", 4048, NULL, 15, NULL); +#if (CONFIG_GATTS_NOTIFY_THROUGHPUT) + gatts_semaphore = xSemaphoreCreateMutex(); + if (!gatts_semaphore) { + ESP_LOGE(GATTS_TAG, "%s, init fail, the gatts semaphore create fail.", __func__); + return; + } +#endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ + return; +} diff --git a/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults b/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults new file mode 100644 index 000000000..e4b501910 --- /dev/null +++ b/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_GATTS_NOTIFY_THROUGHPUT=y