Merge branch 'feature/btdm_ble_throughput_optimiz' into 'master'

Feature/btdm ble throughput optimiz

See merge request idf/esp-idf!2265
This commit is contained in:
Jiang Jiang Jian 2018-04-28 15:01:39 +08:00
commit 25798b0e3a
20 changed files with 1459 additions and 7 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

@ -1 +1 @@
Subproject commit d1e2bce5585b2aded165a886aced8dacfbac9dee
Subproject commit 34b64038d090fa172d9757e2fce293ff26e0a08c

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -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 <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 *)&notify_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) */
}

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -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 <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;
}

View file

@ -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