#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)
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;
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;
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;
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;
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;
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;
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;
#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))
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;
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;
*******************************************************************************/
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
// 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);
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
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;
}
#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)) {
#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
-Subproject commit d1e2bce5585b2aded165a886aced8dacfbac9dee
+Subproject commit 34b64038d090fa172d9757e2fce293ff26e0a08c
--- /dev/null
+#
+# 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
--- /dev/null
+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))
--- /dev/null
+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
+
+
--- /dev/null
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
--- /dev/null
+/*
+ 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 <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "nvs.h"
+#include "nvs_flash.h"
+
+#include "esp_bt.h"
+#include "esp_gap_ble_api.h"
+#include "esp_gattc_api.h"
+#include "esp_gatt_defs.h"
+#include "esp_bt_main.h"
+#include "esp_gatt_common_api.h"
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/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) */
+}
+
--- /dev/null
+# Override some defaults so BT stack is enabled
+# by default in this example
+CONFIG_BT_ENABLED=y
+CONFIG_GATTS_NOTIFY_THROUGHPUT=y
--- /dev/null
+#
+# 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
--- /dev/null
+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))
+
--- /dev/null
+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
+
+
--- /dev/null
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}
--- /dev/null
+# Override some defaults so BT stack is enabled
+# by default in this example
+CONFIG_BT_ENABLED=y
+CONFIG_GATTS_NOTIFY_THROUGHPUT=y