Merge branch 'feat/optimize_cble50y24_109_v5.4' into 'release/v5.4'

feat(bt/bluedroid): Added throughput mode for BLE SPP example (v5.4)

See merge request espressif/esp-idf!38693
This commit is contained in:
Island
2025-04-27 10:15:05 +08:00
7 changed files with 476 additions and 220 deletions

View File

@@ -0,0 +1,15 @@
menu "Example 'SPP CLIENT' Config"
choice EXAMPLE_SPP_CLIENT
prompt "SPP TRANSPORT mode"
default EXAMPLE_SPP_THROUGHPUT
help
Define SPP TRANSPORT mode
config EXAMPLE_SPP_THROUGHPUT
bool "Throughput"
config EXAMPLE_SPP_RELIABLE
bool "Reliable"
endchoice
endmenu

View File

@@ -27,14 +27,18 @@
#include "esp_gatt_common_api.h" #include "esp_gatt_common_api.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "esp_timer.h"
#define GATTC_TAG "GATTC_SPP_DEMO" #define GATTC_TAG "GATTC_SPP_DEMO"
#define PROFILE_NUM 1 #define PROFILE_NUM 1
#define PROFILE_APP_ID 0 #define PROFILE_APP_ID 0
#define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
#define BT_BD_ADDR_HEX(addr) addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]
#define ESP_GATT_SPP_SERVICE_UUID 0xABF0 #define ESP_GATT_SPP_SERVICE_UUID 0xABF0
#define SCAN_ALL_THE_TIME 0 #define SCAN_ALL_THE_TIME 0
#define SPP_GATT_MTU_SIZE (512)
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
struct gattc_profile_inst { struct gattc_profile_inst {
esp_gattc_cb_t gattc_cb; esp_gattc_cb_t gattc_cb;
@@ -98,14 +102,18 @@ static uint16_t cmd = 0;
static uint16_t spp_srv_start_handle = 0; static uint16_t spp_srv_start_handle = 0;
static uint16_t spp_srv_end_handle = 0; static uint16_t spp_srv_end_handle = 0;
static uint16_t spp_gattc_if = 0xff; static uint16_t spp_gattc_if = 0xff;
static uint16_t count = SPP_IDX_NB;
static esp_gattc_db_elem_t *db = NULL;
static QueueHandle_t cmd_reg_queue = NULL;
QueueHandle_t spp_uart_queue = NULL;
static bool connect = false;
static char * notify_value_p = NULL; static char * notify_value_p = NULL;
static int notify_value_offset = 0; static int notify_value_offset = 0;
static int notify_value_count = 0; static int notify_value_count = 0;
static uint16_t count = SPP_IDX_NB; static bool start = false;
static esp_gattc_db_elem_t *db = NULL; static uint64_t notify_len = 0;
static esp_ble_gap_cb_param_t scan_rst; static uint64_t start_time = 0;
static QueueHandle_t cmd_reg_queue = NULL; static uint64_t current_time = 0;
QueueHandle_t spp_uart_queue = NULL;
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
static uint8_t heartbeat_s[9] = {'E','s','p','r','e','s','s','i','f'}; static uint8_t heartbeat_s[9] = {'E','s','p','r','e','s','s','i','f'};
@@ -121,40 +129,33 @@ static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
{ {
uint8_t handle = 0; uint8_t handle = 0;
if(p_data->notify.is_notify == true){
ESP_LOGI(GATTC_TAG,"+NOTIFY:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len);
}else{
ESP_LOGI(GATTC_TAG,"+INDICATE:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len);
}
handle = p_data->notify.handle; handle = p_data->notify.handle;
if(db == NULL) { if (db == NULL) {
ESP_LOGE(GATTC_TAG, " %s db is NULL", __func__); ESP_LOGE(GATTC_TAG, " %s db is NULL", __func__);
return; return;
} }
if(handle == db[SPP_IDX_SPP_DATA_NTY_VAL].attribute_handle){
#ifdef SPP_DEBUG_MODE if (handle == db[SPP_IDX_SPP_DATA_NTY_VAL].attribute_handle) {
ESP_LOG_BUFFER_CHAR(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len); if ((p_data->notify.value[0] == '#') && (p_data->notify.value[1] == '#')) {
#else if ((++notify_value_count) != p_data->notify.value[3]) {
if((p_data->notify.value[0] == '#')&&(p_data->notify.value[1] == '#')){
if((++notify_value_count) != p_data->notify.value[3]){
if(notify_value_p != NULL){ if(notify_value_p != NULL){
free(notify_value_p); free(notify_value_p);
} }
notify_value_count = 0; notify_value_count = 0;
notify_value_p = NULL; notify_value_p = NULL;
notify_value_offset = 0; notify_value_offset = 0;
ESP_LOGE(GATTC_TAG,"notify value count is not continuous,%s",__func__); ESP_LOGE(GATTC_TAG,"notify value count is not continuous, %s", __func__);
return; return;
} }
if(p_data->notify.value[3] == 1){ if (p_data->notify.value[3] == 1) {
notify_value_p = (char *)malloc(((spp_mtu_size-7)*(p_data->notify.value[2]))*sizeof(char)); notify_value_p = (char *)malloc(((spp_mtu_size-7)*(p_data->notify.value[2]))*sizeof(char));
if(notify_value_p == NULL){ if (notify_value_p == NULL) {
ESP_LOGE(GATTC_TAG, "malloc failed,%s L#%d",__func__,__LINE__); ESP_LOGE(GATTC_TAG, "malloc failed, %s L#%d", __func__, __LINE__);
notify_value_count = 0; notify_value_count = 0;
return; return;
} }
memcpy((notify_value_p + notify_value_offset),(p_data->notify.value + 4),(p_data->notify.value_len - 4)); memcpy((notify_value_p + notify_value_offset), (p_data->notify.value + 4), (p_data->notify.value_len - 4));
if(p_data->notify.value[2] == p_data->notify.value[3]){ if (p_data->notify.value[2] == p_data->notify.value[3]) {
uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset)); uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset));
free(notify_value_p); free(notify_value_p);
notify_value_p = NULL; notify_value_p = NULL;
@@ -162,9 +163,9 @@ static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
return; return;
} }
notify_value_offset += (p_data->notify.value_len - 4); notify_value_offset += (p_data->notify.value_len - 4);
}else if(p_data->notify.value[3] <= p_data->notify.value[2]){ } else if (p_data->notify.value[3] <= p_data->notify.value[2]) {
memcpy((notify_value_p + notify_value_offset),(p_data->notify.value + 4),(p_data->notify.value_len - 4)); memcpy((notify_value_p + notify_value_offset), (p_data->notify.value + 4), (p_data->notify.value_len - 4));
if(p_data->notify.value[3] == p_data->notify.value[2]){ if (p_data->notify.value[3] == p_data->notify.value[2]) {
uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset)); uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset));
free(notify_value_p); free(notify_value_p);
notify_value_count = 0; notify_value_count = 0;
@@ -174,14 +175,13 @@ static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
} }
notify_value_offset += (p_data->notify.value_len - 4); notify_value_offset += (p_data->notify.value_len - 4);
} }
}else{ } else {
uart_write_bytes(UART_NUM_0, (char *)(p_data->notify.value), p_data->notify.value_len); uart_write_bytes(UART_NUM_0, (char *)(p_data->notify.value), p_data->notify.value_len);
} }
#endif } else if (handle == ((db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle)) {
}else if(handle == ((db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle)){
ESP_LOG_BUFFER_CHAR(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len); ESP_LOG_BUFFER_CHAR(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);
//TODO:server notify status characteristic //TODO:server notify status characteristic
}else{ } else {
ESP_LOG_BUFFER_CHAR(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len); ESP_LOG_BUFFER_CHAR(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);
} }
} }
@@ -189,6 +189,7 @@ static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
static void free_gattc_srv_db(void) static void free_gattc_srv_db(void)
{ {
is_connect = false; is_connect = false;
connect = false;
spp_gattc_if = 0xff; spp_gattc_if = 0xff;
spp_conn_id = 0; spp_conn_id = 0;
spp_mtu_size = 23; spp_mtu_size = 23;
@@ -198,7 +199,7 @@ static void free_gattc_srv_db(void)
notify_value_p = NULL; notify_value_p = NULL;
notify_value_offset = 0; notify_value_offset = 0;
notify_value_count = 0; notify_value_count = 0;
if(db){ if (db) {
free(db); free(db);
db = NULL; db = NULL;
} }
@@ -210,7 +211,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
uint8_t adv_name_len = 0; uint8_t adv_name_len = 0;
esp_err_t err; esp_err_t err;
switch(event){ switch (event) {
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
if((err = param->scan_param_cmpl.status) != ESP_BT_STATUS_SUCCESS){ if((err = param->scan_param_cmpl.status) != ESP_BT_STATUS_SUCCESS){
ESP_LOGE(GATTC_TAG, "Scan param set failed: %s", esp_err_to_name(err)); ESP_LOGE(GATTC_TAG, "Scan param set failed: %s", esp_err_to_name(err));
@@ -224,51 +225,52 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
//scan start complete event to indicate scan start successfully or failed //scan start complete event to indicate scan start successfully or failed
if ((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) { if ((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTC_TAG, "Scan start failed: %s", esp_err_to_name(err)); ESP_LOGE(GATTC_TAG, "Scanning start failed, err %s", esp_err_to_name(err));
break; break;
} }
ESP_LOGI(GATTC_TAG, "Scan start successfully"); ESP_LOGI(GATTC_TAG, "Scanning start successfully");
break; break;
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
if ((err = param->scan_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS) { if ((err = param->scan_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTC_TAG, "Scan stop failed: %s", esp_err_to_name(err)); ESP_LOGE(GATTC_TAG, "Scanning stop failed, err %s", esp_err_to_name(err));
break; break;
} }
ESP_LOGI(GATTC_TAG, "Scan stop successfully"); ESP_LOGI(GATTC_TAG, "Scanning stop successfully");
if (is_connect == false) {
ESP_LOGI(GATTC_TAG, "Connect to the remote device.");
esp_ble_gatt_creat_conn_params_t creat_conn_params = {0};
memcpy(&creat_conn_params.remote_bda, scan_rst.scan_rst.bda,ESP_BD_ADDR_LEN);
creat_conn_params.remote_addr_type = scan_rst.scan_rst.ble_addr_type;
creat_conn_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
creat_conn_params.is_direct = true;
creat_conn_params.is_aux = false;
creat_conn_params.phy_mask = 0x0;
esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_APP_ID].gattc_if,
&creat_conn_params);
}
break; break;
case ESP_GAP_BLE_SCAN_RESULT_EVT: { case ESP_GAP_BLE_SCAN_RESULT_EVT: {
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
switch (scan_result->scan_rst.search_evt) { switch (scan_result->scan_rst.search_evt) {
case ESP_GAP_SEARCH_INQ_RES_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_by_type(scan_result->scan_rst.ble_adv, adv_name = esp_ble_resolve_adv_data_by_type(scan_result->scan_rst.ble_adv,
scan_result->scan_rst.adv_data_len + scan_result->scan_rst.scan_rsp_len, scan_result->scan_rst.adv_data_len + scan_result->scan_rst.scan_rsp_len,
ESP_BLE_AD_TYPE_NAME_CMPL, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
&adv_name_len); ESP_LOGI(GATTC_TAG, "Scan result, device "ESP_BD_ADDR_STR", name len %u", ESP_BD_ADDR_HEX(scan_result->scan_rst.bda), 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_LOG_BUFFER_CHAR(GATTC_TAG, adv_name, adv_name_len);
ESP_LOGI(GATTC_TAG, " "); if (adv_name != NULL && strncmp((char *)adv_name, device_name, adv_name_len) == 0) {
if (adv_name != NULL) { if (connect == false) {
if ( strncmp((char *)adv_name, device_name, adv_name_len) == 0) { connect = true;
memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
esp_ble_gap_stop_scanning(); esp_ble_gap_stop_scanning();
ESP_LOGI(GATTC_TAG, "Connect to the remote device.");
esp_ble_conn_params_t phy_1m_conn_params = {0};
phy_1m_conn_params.interval_max = 32;
phy_1m_conn_params.interval_min = 32;
phy_1m_conn_params.latency = 0;
phy_1m_conn_params.supervision_timeout = 600;
esp_ble_gatt_creat_conn_params_t creat_conn_params = {0};
memcpy(&creat_conn_params.remote_bda, scan_result->scan_rst.bda,ESP_BD_ADDR_LEN);
creat_conn_params.remote_addr_type = scan_result->scan_rst.ble_addr_type;
creat_conn_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
creat_conn_params.is_direct = true;
creat_conn_params.is_aux = false;
creat_conn_params.phy_mask = ESP_BLE_PHY_1M_PREF_MASK;
creat_conn_params.phy_1m_conn_params = &phy_1m_conn_params;
esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_APP_ID].gattc_if,
&creat_conn_params);
} }
} }
break; break;
case ESP_GAP_SEARCH_INQ_CMPL_EVT: case ESP_GAP_SEARCH_INQ_CMPL_EVT:
ESP_LOGI(GATTC_TAG, "Scan complete");
break; break;
default: default:
break; break;
@@ -276,12 +278,19 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
break; break;
} }
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
if ((err = param->adv_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS){ if ((err = param->adv_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTC_TAG, "Adv stop failed: %s", esp_err_to_name(err)); ESP_LOGE(GATTC_TAG, "Advertising stop failed, err %s", esp_err_to_name(err));
}else { } else {
ESP_LOGI(GATTC_TAG, "Stop adv successfully"); ESP_LOGI(GATTC_TAG, "Advertising stop successfully");
} }
break; break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(GATTC_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d",
param->update_conn_params.status,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
default: default:
break; break;
} }
@@ -289,7 +298,6 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par
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 esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{ {
ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d", event, gattc_if);
/* If event is register event, store the gattc_if for each profile */ /* If event is register event, store the gattc_if for each profile */
if (event == ESP_GATTC_REG_EVT) { if (event == ESP_GATTC_REG_EVT) {
@@ -321,40 +329,56 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
switch (event) { switch (event) {
case ESP_GATTC_REG_EVT: case ESP_GATTC_REG_EVT:
ESP_LOGI(GATTC_TAG, "REG EVT, set scan params"); ESP_LOGI(GATTC_TAG, "GATT client register, status %d, app_id %d, gattc_if %d", param->reg.status, param->reg.app_id, gattc_if);
esp_ble_gap_set_scan_params(&ble_scan_params); esp_ble_gap_set_scan_params(&ble_scan_params);
break; break;
case ESP_GATTC_CONNECT_EVT: case ESP_GATTC_CONNECT_EVT:
ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT: conn_id=%d, gatt_if = %d", spp_conn_id, gattc_if); ESP_LOGI(GATTC_TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR"", p_data->connect.conn_id,
ESP_LOGI(GATTC_TAG, "REMOTE BDA:"); ESP_BD_ADDR_HEX(p_data->connect.remote_bda));
ESP_LOG_BUFFER_HEX(GATTC_TAG, gl_profile_tab[PROFILE_APP_ID].remote_bda, sizeof(esp_bd_addr_t)); memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
spp_gattc_if = gattc_if; spp_gattc_if = gattc_if;
is_connect = true; is_connect = true;
spp_conn_id = p_data->connect.conn_id; spp_conn_id = p_data->connect.conn_id;
memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid); esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid);
break; break;
case ESP_GATTC_OPEN_EVT:
if (param->open.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "Open failed, status %d", p_data->open.status);
break;
}
ESP_LOGI(GATTC_TAG, "Open successfully, MTU %u", p_data->open.mtu);
break;
case ESP_GATTC_DISCONNECT_EVT: case ESP_GATTC_DISCONNECT_EVT:
ESP_LOGI(GATTC_TAG, "disconnect"); ESP_LOGI(GATTC_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason);
free_gattc_srv_db(); free_gattc_srv_db();
start = false;
start_time = 0;
current_time = 0;
notify_len = 0;
esp_ble_gap_start_scanning(SCAN_ALL_THE_TIME); esp_ble_gap_start_scanning(SCAN_ALL_THE_TIME);
break; break;
case ESP_GATTC_SEARCH_RES_EVT: case ESP_GATTC_SEARCH_RES_EVT:
ESP_LOGI(GATTC_TAG, "ESP_GATTC_SEARCH_RES_EVT: start_handle = %d, end_handle = %d, UUID:0x%04x",p_data->search_res.start_handle,p_data->search_res.end_handle,p_data->search_res.srvc_id.uuid.uuid.uuid16); ESP_LOGI(GATTC_TAG, "Service search result, start_handle %d, end_handle %d, UUID:0x%04x",
p_data->search_res.start_handle, p_data->search_res.end_handle, p_data->search_res.srvc_id.uuid.uuid.uuid16);
spp_srv_start_handle = p_data->search_res.start_handle; spp_srv_start_handle = p_data->search_res.start_handle;
spp_srv_end_handle = p_data->search_res.end_handle; spp_srv_end_handle = p_data->search_res.end_handle;
break; break;
case ESP_GATTC_SEARCH_CMPL_EVT: case ESP_GATTC_SEARCH_CMPL_EVT:
ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d", spp_conn_id, p_data->search_cmpl.status); ESP_LOGI(GATTC_TAG, "Service search complete, conn_id %x, status %d", spp_conn_id, p_data->search_cmpl.status);
esp_ble_gattc_send_mtu_req(gattc_if, spp_conn_id); esp_ble_gattc_send_mtu_req(gattc_if, spp_conn_id);
break; break;
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
ESP_LOGI(GATTC_TAG,"Index = %d,status = %d,handle = %d",cmd, p_data->reg_for_notify.status, p_data->reg_for_notify.handle); ESP_LOGI(GATTC_TAG,"Notification register, index %d, status %d, handle %d", cmd, p_data->reg_for_notify.status, p_data->reg_for_notify.handle);
if(p_data->reg_for_notify.status != ESP_GATT_OK){ if (p_data->reg_for_notify.status != ESP_GATT_OK) {
ESP_LOGE(GATTC_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT, status = %d", p_data->reg_for_notify.status);
break; break;
} }
uint16_t notify_en = 1; uint16_t notify_en = 0x01;
#ifdef CONFIG_EXAMPLE_SPP_RELIABLE
if (cmd == SPP_IDX_SPP_DATA_NTY_VAL) {
notify_en = 0x02;
}
#endif
esp_ble_gattc_write_char_descr( esp_ble_gattc_write_char_descr(
spp_gattc_if, spp_gattc_if,
spp_conn_id, spp_conn_id,
@@ -363,31 +387,33 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
(uint8_t *)&notify_en, (uint8_t *)&notify_en,
ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE); ESP_GATT_AUTH_REQ_NONE);
break; break;
} }
case ESP_GATTC_NOTIFY_EVT: case ESP_GATTC_NOTIFY_EVT:
ESP_LOGI(GATTC_TAG,"ESP_GATTC_NOTIFY_EVT"); if (p_data->notify.is_notify){
// ESP_LOGI(GATTC_TAG, "Notification received, handle %d", param->notify.handle);
}else{
// ESP_LOGI(GATTC_TAG, "Indication received, handle %d", param->notify.handle);
}
notify_event_handler(p_data); notify_event_handler(p_data);
break; break;
case ESP_GATTC_READ_CHAR_EVT: case ESP_GATTC_READ_CHAR_EVT:
ESP_LOGI(GATTC_TAG,"ESP_GATTC_READ_CHAR_EVT"); ESP_LOGI(GATTC_TAG,"Characteristic read");
break; break;
case ESP_GATTC_WRITE_CHAR_EVT: case ESP_GATTC_WRITE_CHAR_EVT:
ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_CHAR_EVT:status = %d,handle = %d", param->write.status, param->write.handle); if (param->write.status) {
if(param->write.status != ESP_GATT_OK){ ESP_LOGI(GATTC_TAG,"Characteristic write, status %d, handle %d", param->write.status, param->write.handle);
ESP_LOGE(GATTC_TAG, "ESP_GATTC_WRITE_CHAR_EVT, error status = %d", p_data->write.status);
break;
} }
break; break;
case ESP_GATTC_PREP_WRITE_EVT: case ESP_GATTC_PREP_WRITE_EVT:
ESP_LOGI(GATTC_TAG, "Prepare write");
break; break;
case ESP_GATTC_EXEC_EVT: case ESP_GATTC_EXEC_EVT:
ESP_LOGI(GATTC_TAG, "Execute write %d", param->exec_cmpl.status);
break; break;
case ESP_GATTC_WRITE_DESCR_EVT: case ESP_GATTC_WRITE_DESCR_EVT:
ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_DESCR_EVT: status =%d,handle = %d", p_data->write.status, p_data->write.handle); ESP_LOGI(GATTC_TAG,"Descriptor write, status %d, handle %d", p_data->write.status, p_data->write.handle);
if(p_data->write.status != ESP_GATT_OK){ if(p_data->write.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "ESP_GATTC_WRITE_DESCR_EVT, error status = %d", p_data->write.status);
break; break;
} }
switch(cmd){ switch(cmd){
@@ -396,64 +422,64 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
xQueueSend(cmd_reg_queue, &cmd,10/portTICK_PERIOD_MS); xQueueSend(cmd_reg_queue, &cmd,10/portTICK_PERIOD_MS);
break; break;
case SPP_IDX_SPP_STATUS_VAL: case SPP_IDX_SPP_STATUS_VAL:
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
cmd = SPP_IDX_SPP_HEARTBEAT_VAL; cmd = SPP_IDX_SPP_HEARTBEAT_VAL;
xQueueSend(cmd_reg_queue, &cmd, 10/portTICK_PERIOD_MS); xQueueSend(cmd_reg_queue, &cmd, 10/portTICK_PERIOD_MS);
#endif #endif
break; break;
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
case SPP_IDX_SPP_HEARTBEAT_VAL: case SPP_IDX_SPP_HEARTBEAT_VAL:
xQueueSend(cmd_heartbeat_queue, &cmd, 10/portTICK_PERIOD_MS); xQueueSend(cmd_heartbeat_queue, &cmd, 10/portTICK_PERIOD_MS);
break; break;
#endif #endif
default: default:
break; break;
}; };
break; break;
case ESP_GATTC_CFG_MTU_EVT: case ESP_GATTC_CFG_MTU_EVT:
ESP_LOGI(GATTC_TAG, "MTU exchange, status %d, MTU %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
if(p_data->cfg_mtu.status != ESP_OK){ if(p_data->cfg_mtu.status != ESP_OK){
break; break;
} }
ESP_LOGI(GATTC_TAG,"+MTU:%d", p_data->cfg_mtu.mtu);
spp_mtu_size = p_data->cfg_mtu.mtu; spp_mtu_size = p_data->cfg_mtu.mtu;
db = (esp_gattc_db_elem_t *)malloc(count*sizeof(esp_gattc_db_elem_t)); db = (esp_gattc_db_elem_t *)malloc(count*sizeof(esp_gattc_db_elem_t));
if(db == NULL){ if(db == NULL){
ESP_LOGE(GATTC_TAG,"%s:malloc db failed",__func__); ESP_LOGE(GATTC_TAG, "Malloc db failed");
break; break;
} }
if(esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK){ if(esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG,"%s:get db failed",__func__); ESP_LOGE(GATTC_TAG, "Get db failed");
break; break;
} }
if(count != SPP_IDX_NB){ if(count != SPP_IDX_NB){
ESP_LOGE(GATTC_TAG,"%s:get db count != SPP_IDX_NB, count = %d, SPP_IDX_NB = %d",__func__,count,SPP_IDX_NB); ESP_LOGE(GATTC_TAG, "Get db count != SPP_IDX_NB, count = %d, SPP_IDX_NB = %d", count, SPP_IDX_NB);
break; break;
} }
for(int i = 0;i < SPP_IDX_NB;i++){ for(int i = 0;i < SPP_IDX_NB;i++){
switch((db+i)->type){ switch((db+i)->type){
case ESP_GATT_DB_PRIMARY_SERVICE: case ESP_GATT_DB_PRIMARY_SERVICE:
ESP_LOGI(GATTC_TAG,"attr_type = PRIMARY_SERVICE,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",\ ESP_LOGI(GATTC_TAG, "PRIMARY_SERVICE, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x",
(db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16);
break; break;
case ESP_GATT_DB_SECONDARY_SERVICE: case ESP_GATT_DB_SECONDARY_SERVICE:
ESP_LOGI(GATTC_TAG,"attr_type = SECONDARY_SERVICE,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",\ ESP_LOGI(GATTC_TAG, "SECONDARY_SERVICE, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x",
(db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16);
break; break;
case ESP_GATT_DB_CHARACTERISTIC: case ESP_GATT_DB_CHARACTERISTIC:
ESP_LOGI(GATTC_TAG,"attr_type = CHARACTERISTIC,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",\ ESP_LOGI(GATTC_TAG, "CHARACTERISTIC, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x",
(db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16);
break; break;
case ESP_GATT_DB_DESCRIPTOR: case ESP_GATT_DB_DESCRIPTOR:
ESP_LOGI(GATTC_TAG,"attr_type = DESCRIPTOR,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",\ ESP_LOGI(GATTC_TAG, "DESCRIPTOR, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x",
(db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16);
break; break;
case ESP_GATT_DB_INCLUDED_SERVICE: case ESP_GATT_DB_INCLUDED_SERVICE:
ESP_LOGI(GATTC_TAG,"attr_type = INCLUDED_SERVICE,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",\ ESP_LOGI(GATTC_TAG, "INCLUDED_SERVICE, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x",
(db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16);
break; break;
case ESP_GATT_DB_ALL: case ESP_GATT_DB_ALL:
ESP_LOGI(GATTC_TAG,"attr_type = ESP_GATT_DB_ALL,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",\ ESP_LOGI(GATTC_TAG, "GATT_DB_ALL, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x",
(db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16);
break; break;
default: default:
@@ -464,6 +490,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
xQueueSend(cmd_reg_queue, &cmd, 10/portTICK_PERIOD_MS); xQueueSend(cmd_reg_queue, &cmd, 10/portTICK_PERIOD_MS);
break; break;
case ESP_GATTC_SRVC_CHG_EVT: case ESP_GATTC_SRVC_CHG_EVT:
ESP_LOGI(GATTC_TAG, "Service change from "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(p_data->srvc_chg.remote_bda));
break; break;
default: default:
break; break;
@@ -473,20 +500,20 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
void spp_client_reg_task(void* arg) void spp_client_reg_task(void* arg)
{ {
uint16_t cmd_id; uint16_t cmd_id;
for(;;) { for (;;) {
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
if(xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY)) { if (xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY)) {
if(db != NULL) { if (db != NULL) {
if(cmd_id == SPP_IDX_SPP_DATA_NTY_VAL){ if (cmd_id == SPP_IDX_SPP_DATA_NTY_VAL) {
ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d", cmd_id, (db+SPP_IDX_SPP_DATA_NTY_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle); ESP_LOGI(GATTC_TAG,"Index %d, UUID 0x%04x, handle %d", cmd_id, (db+SPP_IDX_SPP_DATA_NTY_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle);
esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle); esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle);
}else if(cmd_id == SPP_IDX_SPP_STATUS_VAL){ } else if(cmd_id == SPP_IDX_SPP_STATUS_VAL) {
ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d", cmd_id, (db+SPP_IDX_SPP_STATUS_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle); ESP_LOGI(GATTC_TAG,"Index %d, UUID 0x%04x, handle %d", cmd_id, (db+SPP_IDX_SPP_STATUS_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle);
esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle); esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle);
} }
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
else if(cmd_id == SPP_IDX_SPP_HEARTBEAT_VAL){ else if (cmd_id == SPP_IDX_SPP_HEARTBEAT_VAL) {
ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d", cmd_id, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle); ESP_LOGI(GATTC_TAG,"Index %d, UUID 0x%04x, handle %d", cmd_id, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle);
esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle); esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle);
} }
#endif #endif
@@ -500,11 +527,11 @@ void spp_heart_beat_task(void * arg)
{ {
uint16_t cmd_id; uint16_t cmd_id;
for(;;) { for (;;) {
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(50 / portTICK_PERIOD_MS);
if(xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY)) { if (xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY)) {
while(1){ while(1){
if((is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_HEARTBEAT_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))){ if ((is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_HEARTBEAT_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) {
esp_ble_gattc_write_char( spp_gattc_if, esp_ble_gattc_write_char( spp_gattc_if,
spp_conn_id, spp_conn_id,
(db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle, (db+SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle,
@@ -513,7 +540,7 @@ void spp_heart_beat_task(void * arg)
ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE); ESP_GATT_AUTH_REQ_NONE);
vTaskDelay(5000 / portTICK_PERIOD_MS); vTaskDelay(5000 / portTICK_PERIOD_MS);
}else{ } else {
ESP_LOGI(GATTC_TAG,"disconnect"); ESP_LOGI(GATTC_TAG,"disconnect");
break; break;
} }
@@ -540,10 +567,8 @@ void ble_client_appRegister(void)
ESP_LOGE(GATTC_TAG, "gattc register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg))); ESP_LOGE(GATTC_TAG, "gattc register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
return; return;
} }
esp_ble_gattc_app_register(PROFILE_APP_ID); esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(SPP_GATT_MTU_SIZE);
if (local_mtu_ret) {
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(200);
if (local_mtu_ret){
ESP_LOGE(GATTC_TAG, "set local MTU failed: %s", esp_err_to_name_r(local_mtu_ret, err_msg, sizeof(err_msg))); ESP_LOGE(GATTC_TAG, "set local MTU failed: %s", esp_err_to_name_r(local_mtu_ret, err_msg, sizeof(err_msg)));
} }
@@ -554,6 +579,7 @@ void ble_client_appRegister(void)
cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t)); cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t));
xTaskCreate(spp_heart_beat_task, "spp_heart_beat_task", 2048, NULL, 10, NULL); xTaskCreate(spp_heart_beat_task, "spp_heart_beat_task", 2048, NULL, 10, NULL);
#endif #endif
esp_ble_gattc_app_register(PROFILE_APP_ID);
} }
void uart_task(void *pvParameters) void uart_task(void *pvParameters)
@@ -567,20 +593,40 @@ void uart_task(void *pvParameters)
case UART_DATA: case UART_DATA:
if (event.size && (is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) { if (event.size && (is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) {
uint8_t * temp = NULL; uint8_t * temp = NULL;
size_t offset = 0;
size_t send_len = 0;
temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size); temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size);
if(temp == NULL){ if(temp == NULL){
ESP_LOGE(GATTC_TAG, "malloc failed,%s L#%d", __func__, __LINE__); ESP_LOGE(GATTC_TAG, "malloc failed,%s L#%d", __func__, __LINE__);
break; break;
} }
memset(temp, 0x0, event.size); uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY);
uart_read_bytes(UART_NUM_0,temp,event.size,portMAX_DELAY); while (offset < event.size) {
esp_ble_gattc_write_char( spp_gattc_if, send_len = MIN(spp_mtu_size - 3, event.size - offset);
#ifdef CONFIG_EXAMPLE_SPP_THROUGHPUT
if (esp_ble_get_cur_sendable_packets_num(spp_conn_id) > 0) {
esp_ble_gattc_write_char(spp_gattc_if,
spp_conn_id, spp_conn_id,
(db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle, (db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle,
event.size, send_len,
temp, temp + offset,
ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE);
} else {
//Add the vTaskDelay to prevent this task from consuming the CPU all the time, causing low-priority tasks to not be executed at all.
vTaskDelay(10 / portTICK_PERIOD_MS);
}
#else
esp_ble_gattc_write_char(spp_gattc_if,
spp_conn_id,
(db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle,
send_len,
temp + offset,
ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE); ESP_GATT_AUTH_REQ_NONE);
#endif
offset += send_len;
}
free(temp); free(temp);
} }
break; break;
@@ -600,7 +646,7 @@ static void spp_uart_init(void)
.parity = UART_PARITY_DISABLE, .parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1, .stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_RTS, .flow_ctrl = UART_HW_FLOWCTRL_RTS,
.rx_flow_ctrl_thresh = 122, .rx_flow_ctrl_thresh = 124,
.source_clk = UART_SCLK_DEFAULT, .source_clk = UART_SCLK_DEFAULT,
}; };
@@ -610,18 +656,21 @@ static void spp_uart_init(void)
uart_param_config(UART_NUM_0, &uart_config); uart_param_config(UART_NUM_0, &uart_config);
//Set UART pins //Set UART pins
uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
xTaskCreate(uart_task, "uTask", 2048, (void*)UART_NUM_0, 8, NULL); xTaskCreate(uart_task, "uTask", 4096, (void*)UART_NUM_0, 8, NULL);
} }
void app_main(void) void app_main(void)
{ {
esp_err_t ret; esp_err_t ret;
spp_uart_init();
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); 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(); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
nvs_flash_init(); nvs_flash_init();
ret = esp_bt_controller_init(&bt_cfg); ret = esp_bt_controller_init(&bt_cfg);
if (ret) { if (ret) {
ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret)); ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
@@ -648,5 +697,4 @@ void app_main(void)
} }
ble_client_appRegister(); ble_client_appRegister();
spp_uart_init();
} }

View File

@@ -1,2 +1,9 @@
idf_component_register(SRCS "ble_spp_server_demo.c" set(srcs "ble_spp_server_demo.c")
if(CONFIG_EXAMPLE_ENABLE_RF_TESTING_CONFIGURATION_COMMAND)
list(APPEND srcs
"rf_tesing_configuration_cmd.c")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".") INCLUDE_DIRS ".")

View File

@@ -0,0 +1,29 @@
menu "Example 'SPP SERVER' Config"
choice EXAMPLE_SPP_SERVER
prompt "SPP TRANSPORT mode"
default EXAMPLE_SPP_THROUGHPUT
help
Define SPP TRANSPORT mode
config EXAMPLE_SPP_THROUGHPUT
bool "Throughput"
config EXAMPLE_SPP_RELIABLE
bool "Reliable"
endchoice
config EXAMPLE_ENABLE_RF_EMC_TEST_MODE
bool
prompt "Enable BLE EMC test mode for RF"
default n
help
Use this option to enable BLE EMC test mode for RF.
config EXAMPLE_ENABLE_RF_TESTING_CONFIGURATION_COMMAND
bool
prompt "Enable Configuration command during RF testing"
default n
help
Use this option to enable configuration command when this example is used for RF testing.
endmenu

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -22,7 +22,10 @@
#include "esp_bt_device.h" #include "esp_bt_device.h"
#include "ble_spp_server_demo.h" #include "ble_spp_server_demo.h"
#include "esp_gatt_common_api.h" #include "esp_gatt_common_api.h"
#include "esp_timer.h"
#if (CONFIG_EXAMPLE_ENABLE_RF_TESTING_CONFIGURATION_COMMAND)
#include "rf_tesing_configuration_cmd.h"
#endif // CONFIG_EXAMPLE_ENABLE_RF_TESTING_CONFIGURATION_COMMAND
#define GATTS_TABLE_TAG "GATTS_SPP_DEMO" #define GATTS_TABLE_TAG "GATTS_SPP_DEMO"
#define SPP_PROFILE_NUM 1 #define SPP_PROFILE_NUM 1
@@ -39,20 +42,24 @@ static const uint16_t spp_service_uuid = 0xABF0;
#define ESP_GATT_UUID_SPP_COMMAND_RECEIVE 0xABF3 #define ESP_GATT_UUID_SPP_COMMAND_RECEIVE 0xABF3
#define ESP_GATT_UUID_SPP_COMMAND_NOTIFY 0xABF4 #define ESP_GATT_UUID_SPP_COMMAND_NOTIFY 0xABF4
#define SPP_GATT_MTU_SIZE (512)
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
#define ESP_GATT_UUID_SPP_HEARTBEAT 0xABF5 #define ESP_GATT_UUID_SPP_HEARTBEAT 0xABF5
#endif #endif
#define BLUETOOTH_TASK_PINNED_TO_CORE (0)
static const uint8_t spp_adv_data[23] = { static const uint8_t spp_adv_data[23] = {
/* Flags */ /* Flags */
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06, 0x02,0x01,0x06,
/* Complete List of 16-bit Service Class UUIDs */ /* Complete List of 16-bit Service Class UUIDs */
0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xF0, 0xAB, 0x03,0x03,0xF0,0xAB,
/* Complete Local Name in advertising */ /* Complete Local Name in advertising */
0x0F, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'S', 'P', 'P', '_', 'S', 'E', 'R', 'V', 'E', 'R' 0x0F,0x09, 'E', 'S', 'P', '_', 'S', 'P', 'P', '_', 'S', 'E', 'R','V', 'E', 'R'
}; };
static uint16_t spp_mtu_size = 23; static uint16_t spp_mtu_size = SPP_GATT_MTU_SIZE;
static uint16_t spp_conn_id = 0xffff; static uint16_t spp_conn_id = 0xffff;
static esp_gatt_if_t spp_gatts_if = 0xff; static esp_gatt_if_t spp_gatts_if = 0xff;
QueueHandle_t spp_uart_queue = NULL; QueueHandle_t spp_uart_queue = NULL;
@@ -138,6 +145,11 @@ static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_C
static const uint8_t char_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t char_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_NOTIFY;
static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE_NR|ESP_GATT_CHAR_PROP_BIT_READ; static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE_NR|ESP_GATT_CHAR_PROP_BIT_READ;
#ifdef CONFIG_EXAMPLE_SPP_THROUGHPUT
static const uint8_t spp_data_notity_char_prop = char_prop_read_notify;
#else
static const uint8_t spp_data_notity_char_prop = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_INDICATE;
#endif
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_WRITE_NR|ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_WRITE_NR|ESP_GATT_CHAR_PROP_BIT_NOTIFY;
@@ -179,17 +191,17 @@ static const esp_gatts_attr_db_t spp_gatt_db[SPP_IDX_NB] =
//SPP - data receive characteristic Declaration //SPP - data receive characteristic Declaration
[SPP_IDX_SPP_DATA_RECV_CHAR] = [SPP_IDX_SPP_DATA_RECV_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}}, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},
//SPP - data receive characteristic Value //SPP - data receive characteristic Value
[SPP_IDX_SPP_DATA_RECV_VAL] = [SPP_IDX_SPP_DATA_RECV_VAL] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_data_receive_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_data_receive_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
SPP_DATA_MAX_LEN,sizeof(spp_data_receive_val), (uint8_t *)spp_data_receive_val}}, SPP_DATA_MAX_LEN, sizeof(spp_data_receive_val), (uint8_t *)spp_data_receive_val}},
//SPP - data notify characteristic Declaration //SPP - data notify characteristic Declaration
[SPP_IDX_SPP_DATA_NOTIFY_CHAR] = [SPP_IDX_SPP_DATA_NOTIFY_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}}, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&spp_data_notity_char_prop}},
//SPP - data notify characteristic Value //SPP - data notify characteristic Value
[SPP_IDX_SPP_DATA_NTY_VAL] = [SPP_IDX_SPP_DATA_NTY_VAL] =
@@ -199,38 +211,38 @@ static const esp_gatts_attr_db_t spp_gatt_db[SPP_IDX_NB] =
//SPP - data notify characteristic - Client Characteristic Configuration Descriptor //SPP - data notify characteristic - Client Characteristic Configuration Descriptor
[SPP_IDX_SPP_DATA_NTF_CFG] = [SPP_IDX_SPP_DATA_NTF_CFG] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
sizeof(uint16_t),sizeof(spp_data_notify_ccc), (uint8_t *)spp_data_notify_ccc}}, sizeof(uint16_t), sizeof(spp_data_notify_ccc), (uint8_t *)spp_data_notify_ccc}},
//SPP - command characteristic Declaration //SPP - command characteristic Declaration
[SPP_IDX_SPP_COMMAND_CHAR] = [SPP_IDX_SPP_COMMAND_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}}, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},
//SPP - command characteristic Value //SPP - command characteristic Value
[SPP_IDX_SPP_COMMAND_VAL] = [SPP_IDX_SPP_COMMAND_VAL] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_command_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_command_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
SPP_CMD_MAX_LEN,sizeof(spp_command_val), (uint8_t *)spp_command_val}}, SPP_CMD_MAX_LEN, sizeof(spp_command_val), (uint8_t *)spp_command_val}},
//SPP - status characteristic Declaration //SPP - status characteristic Declaration
[SPP_IDX_SPP_STATUS_CHAR] = [SPP_IDX_SPP_STATUS_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}}, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}},
//SPP - status characteristic Value //SPP - status characteristic Value
[SPP_IDX_SPP_STATUS_VAL] = [SPP_IDX_SPP_STATUS_VAL] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_status_uuid, ESP_GATT_PERM_READ, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_status_uuid, ESP_GATT_PERM_READ,
SPP_STATUS_MAX_LEN,sizeof(spp_status_val), (uint8_t *)spp_status_val}}, SPP_STATUS_MAX_LEN, sizeof(spp_status_val), (uint8_t *)spp_status_val}},
//SPP - status characteristic - Client Characteristic Configuration Descriptor //SPP - status characteristic - Client Characteristic Configuration Descriptor
[SPP_IDX_SPP_STATUS_CFG] = [SPP_IDX_SPP_STATUS_CFG] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
sizeof(uint16_t),sizeof(spp_status_ccc), (uint8_t *)spp_status_ccc}}, sizeof(uint16_t), sizeof(spp_status_ccc), (uint8_t *)spp_status_ccc}},
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
//SPP - Heart beat characteristic Declaration //SPP - Heart beat characteristic Declaration
[SPP_IDX_SPP_HEARTBEAT_CHAR] = [SPP_IDX_SPP_HEARTBEAT_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}}, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},
//SPP - Heart beat characteristic Value //SPP - Heart beat characteristic Value
[SPP_IDX_SPP_HEARTBEAT_VAL] = [SPP_IDX_SPP_HEARTBEAT_VAL] =
@@ -240,7 +252,7 @@ static const esp_gatts_attr_db_t spp_gatt_db[SPP_IDX_NB] =
//SPP - Heart beat characteristic - Client Characteristic Configuration Descriptor //SPP - Heart beat characteristic - Client Characteristic Configuration Descriptor
[SPP_IDX_SPP_HEARTBEAT_CFG] = [SPP_IDX_SPP_HEARTBEAT_CFG] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
sizeof(uint16_t),sizeof(spp_data_notify_ccc), (uint8_t *)spp_heart_beat_ccc}}, sizeof(uint16_t), sizeof(spp_data_notify_ccc), (uint8_t *)spp_heart_beat_ccc}},
#endif #endif
}; };
@@ -312,7 +324,7 @@ static void print_write_buffer(void)
{ {
temp_spp_recv_data_node_p1 = SppRecvDataBuff.first_node; temp_spp_recv_data_node_p1 = SppRecvDataBuff.first_node;
while(temp_spp_recv_data_node_p1 != NULL){ while (temp_spp_recv_data_node_p1 != NULL) {
uart_write_bytes(UART_NUM_0, (char *)(temp_spp_recv_data_node_p1->node_buff), temp_spp_recv_data_node_p1->len); uart_write_bytes(UART_NUM_0, (char *)(temp_spp_recv_data_node_p1->node_buff), temp_spp_recv_data_node_p1->len);
temp_spp_recv_data_node_p1 = temp_spp_recv_data_node_p1->next_node; temp_spp_recv_data_node_p1 = temp_spp_recv_data_node_p1->next_node;
} }
@@ -344,42 +356,53 @@ void uart_task(void *pvParameters)
break; break;
} }
temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size); temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size);
if(temp == NULL){ if (temp == NULL) {
ESP_LOGE(GATTS_TABLE_TAG, "%s malloc.1 failed", __func__); ESP_LOGE(GATTS_TABLE_TAG, "%s malloc.1 failed", __func__);
break; break;
} }
memset(temp,0x0,event.size); uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY);
uart_read_bytes(UART_NUM_0,temp,event.size,portMAX_DELAY); if (event.size <= (spp_mtu_size - 3)) {
if(event.size <= (spp_mtu_size - 3)){ #ifdef CONFIG_EXAMPLE_ENABLE_RF_EMC_TEST_MODE
esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL],event.size, temp, false); ESP_LOG_BUFFER_HEX("TX", temp, event.size);
}else if(event.size > (spp_mtu_size - 3)){ #endif
if((event.size%(spp_mtu_size - 7)) == 0){ #ifdef CONFIG_EXAMPLE_SPP_THROUGHPUT
total_num = event.size/(spp_mtu_size - 7); if(esp_ble_get_cur_sendable_packets_num(spp_conn_id) > 0) {
}else{ esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL], event.size, temp, false);
total_num = event.size/(spp_mtu_size - 7) + 1; } else {
//Add the vTaskDelay to prevent this task from consuming the CPU all the time, causing low-priority tasks to not be executed at all.
vTaskDelay(10 / portTICK_PERIOD_MS);
}
#else
esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL], event.size, temp, true);
#endif
} else if (event.size > (spp_mtu_size - 3)) {
if ((event.size % (spp_mtu_size - 7)) == 0) {
total_num = event.size / (spp_mtu_size - 7);
} else {
total_num = event.size / (spp_mtu_size - 7) + 1;
} }
current_num = 1; current_num = 1;
ntf_value_p = (uint8_t *)malloc((spp_mtu_size-3)*sizeof(uint8_t)); ntf_value_p = (uint8_t *)malloc((spp_mtu_size-3)*sizeof(uint8_t));
if(ntf_value_p == NULL){ if (ntf_value_p == NULL) {
ESP_LOGE(GATTS_TABLE_TAG, "%s malloc.2 failed", __func__); ESP_LOGE(GATTS_TABLE_TAG, "%s malloc.2 failed", __func__);
free(temp); free(temp);
break; break;
} }
while(current_num <= total_num){ while (current_num <= total_num) {
if(current_num < total_num){ if (current_num < total_num) {
ntf_value_p[0] = '#'; ntf_value_p[0] = '#';
ntf_value_p[1] = '#'; ntf_value_p[1] = '#';
ntf_value_p[2] = total_num; ntf_value_p[2] = total_num;
ntf_value_p[3] = current_num; ntf_value_p[3] = current_num;
memcpy(ntf_value_p + 4,temp + (current_num - 1)*(spp_mtu_size-7),(spp_mtu_size-7)); memcpy(ntf_value_p + 4, temp + (current_num - 1)*(spp_mtu_size-7), (spp_mtu_size-7));
esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL],(spp_mtu_size-3), ntf_value_p, false); esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL], (spp_mtu_size-3), ntf_value_p, false);
}else if(current_num == total_num){ } else if(current_num == total_num) {
ntf_value_p[0] = '#'; ntf_value_p[0] = '#';
ntf_value_p[1] = '#'; ntf_value_p[1] = '#';
ntf_value_p[2] = total_num; ntf_value_p[2] = total_num;
ntf_value_p[3] = current_num; ntf_value_p[3] = current_num;
memcpy(ntf_value_p + 4,temp + (current_num - 1)*(spp_mtu_size-7),(event.size - (current_num - 1)*(spp_mtu_size - 7))); memcpy(ntf_value_p + 4, temp + (current_num - 1)*(spp_mtu_size-7), (event.size - (current_num - 1)*(spp_mtu_size - 7)));
esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL],(event.size - (current_num - 1)*(spp_mtu_size - 7) + 4), ntf_value_p, false); esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL], (event.size - (current_num - 1)*(spp_mtu_size - 7) + 4), ntf_value_p, false);
} }
vTaskDelay(20 / portTICK_PERIOD_MS); vTaskDelay(20 / portTICK_PERIOD_MS);
current_num++; current_num++;
@@ -405,17 +428,17 @@ static void spp_uart_init(void)
.parity = UART_PARITY_DISABLE, .parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1, .stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_RTS, .flow_ctrl = UART_HW_FLOWCTRL_RTS,
.rx_flow_ctrl_thresh = 122, .rx_flow_ctrl_thresh = 124,
.source_clk = UART_SCLK_DEFAULT, .source_clk = UART_SCLK_DEFAULT,
}; };
//Install UART driver, and get the queue. //Install UART driver, and get the queue.
uart_driver_install(UART_NUM_0, 4096, 8192, 10,&spp_uart_queue,0); uart_driver_install(UART_NUM_0, 4096, 8192, 10, &spp_uart_queue,0);
//Set UART parameters //Set UART parameters
uart_param_config(UART_NUM_0, &uart_config); uart_param_config(UART_NUM_0, &uart_config);
//Set UART pins //Set UART pins
uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
xTaskCreate(uart_task, "uTask", 2048, (void*)UART_NUM_0, 8, NULL); xTaskCreate(uart_task, "uTask", 4096, (void*)UART_NUM_0, 8, NULL);
} }
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
@@ -448,10 +471,10 @@ void spp_cmd_task(void * arg)
{ {
uint8_t * cmd_id; uint8_t * cmd_id;
for(;;){ for (;;) {
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(50 / portTICK_PERIOD_MS);
if(xQueueReceive(cmd_cmd_queue, &cmd_id, portMAX_DELAY)) { if(xQueueReceive(cmd_cmd_queue, &cmd_id, portMAX_DELAY)) {
ESP_LOG_BUFFER_CHAR(GATTS_TABLE_TAG,(char *)(cmd_id),strlen((char *)cmd_id)); ESP_LOG_BUFFER_CHAR(GATTS_TABLE_TAG, (char *)(cmd_id), strlen((char *)cmd_id));
free(cmd_id); free(cmd_id);
} }
} }
@@ -460,7 +483,11 @@ void spp_cmd_task(void * arg)
static void spp_task_init(void) static void spp_task_init(void)
{ {
#ifdef CONFIG_EXAMPLE_ENABLE_RF_TESTING_CONFIGURATION_COMMAND
rf_testing_configuration_command_enable();
#else
spp_uart_init(); spp_uart_init();
#endif // CONFIG_EXAMPLE_ENABLE_RF_TESTING_CONFIGURATION_COMMAND
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t)); cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t));
@@ -468,23 +495,36 @@ static void spp_task_init(void)
#endif #endif
cmd_cmd_queue = xQueueCreate(10, sizeof(uint32_t)); cmd_cmd_queue = xQueueCreate(10, sizeof(uint32_t));
xTaskCreate(spp_cmd_task, "spp_cmd_task", 2048, NULL, 10, NULL); xTaskCreate(spp_cmd_task, "spp_cmd_task", 4096, NULL, 10, NULL);
} }
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{ {
esp_err_t err;
ESP_LOGI(GATTS_TABLE_TAG, "GAP_EVT, event %d", event);
switch (event) { switch (event) {
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
esp_ble_gap_start_advertising(&spp_adv_params); esp_ble_gap_start_advertising(&spp_adv_params);
break; break;
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
//advertising start complete event to indicate advertising start successfully or failed //advertising start complete event to indicate advertising start successfully or failed
if((err = param->adv_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) { if(param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTS_TABLE_TAG, "Advertising start failed: %s", esp_err_to_name(err)); ESP_LOGE(GATTS_TABLE_TAG, "Advertising start failed, status %d", param->adv_start_cmpl.status);
break;
} }
ESP_LOGI(GATTS_TABLE_TAG, "Advertising start successfully");
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTS_TABLE_TAG, "Advertising stop failed, status %d", param->adv_stop_cmpl.status);
break;
}
ESP_LOGI(GATTS_TABLE_TAG, "Advertising stop successfully");
break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d",
param->update_conn_params.status,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break; break;
default: default:
break; break;
@@ -496,95 +536,109 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
esp_ble_gatts_cb_param_t *p_data = (esp_ble_gatts_cb_param_t *) param; esp_ble_gatts_cb_param_t *p_data = (esp_ble_gatts_cb_param_t *) param;
uint8_t res = 0xff; uint8_t res = 0xff;
ESP_LOGI(GATTS_TABLE_TAG, "event = %x",event);
switch (event) { switch (event) {
case ESP_GATTS_REG_EVT: case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "%s %d", __func__, __LINE__); ESP_LOGI(GATTS_TABLE_TAG, "GATT server register, status %d, app_id %d, gatts_if %d", param->reg.status, param->reg.app_id, gatts_if);
esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
ESP_LOGI(GATTS_TABLE_TAG, "%s %d", __func__, __LINE__);
esp_ble_gap_config_adv_data_raw((uint8_t *)spp_adv_data, sizeof(spp_adv_data)); esp_ble_gap_config_adv_data_raw((uint8_t *)spp_adv_data, sizeof(spp_adv_data));
ESP_LOGI(GATTS_TABLE_TAG, "%s %d", __func__, __LINE__);
esp_ble_gatts_create_attr_tab(spp_gatt_db, gatts_if, SPP_IDX_NB, SPP_SVC_INST_ID); esp_ble_gatts_create_attr_tab(spp_gatt_db, gatts_if, SPP_IDX_NB, SPP_SVC_INST_ID);
break; break;
case ESP_GATTS_READ_EVT: case ESP_GATTS_READ_EVT:
res = find_char_and_desr_index(p_data->read.handle); ESP_LOGI(GATTS_TABLE_TAG, "Characteristic read");
if(res == SPP_IDX_SPP_STATUS_VAL){
//TODO:client read the status characteristic
}
break; break;
case ESP_GATTS_WRITE_EVT: { case ESP_GATTS_WRITE_EVT: {
// ESP_LOGI(GATTS_TABLE_TAG, "Characteristic write, conn_id %d, handle %d", param->write.conn_id, param->write.handle);
res = find_char_and_desr_index(p_data->write.handle); res = find_char_and_desr_index(p_data->write.handle);
if(p_data->write.is_prep == false){ if (p_data->write.is_prep == false) {
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_WRITE_EVT : handle = %d", res); if (res == SPP_IDX_SPP_COMMAND_VAL) {
if(res == SPP_IDX_SPP_COMMAND_VAL){
uint8_t * spp_cmd_buff = NULL; uint8_t * spp_cmd_buff = NULL;
spp_cmd_buff = (uint8_t *)malloc((spp_mtu_size - 3) * sizeof(uint8_t)); spp_cmd_buff = (uint8_t *)malloc((spp_mtu_size - 3) * sizeof(uint8_t));
if(spp_cmd_buff == NULL){ if(spp_cmd_buff == NULL){
ESP_LOGE(GATTS_TABLE_TAG, "%s malloc failed", __func__); ESP_LOGE(GATTS_TABLE_TAG, "%s malloc failed", __func__);
break; break;
} }
memset(spp_cmd_buff,0x0,(spp_mtu_size - 3)); memset(spp_cmd_buff, 0x0, (spp_mtu_size - 3));
memcpy(spp_cmd_buff,p_data->write.value,p_data->write.len); memcpy(spp_cmd_buff, p_data->write.value, p_data->write.len);
xQueueSend(cmd_cmd_queue,&spp_cmd_buff,10/portTICK_PERIOD_MS); xQueueSend(cmd_cmd_queue, &spp_cmd_buff, 10/portTICK_PERIOD_MS);
}else if(res == SPP_IDX_SPP_DATA_NTF_CFG){ } else if (res == SPP_IDX_SPP_DATA_NTF_CFG) {
if((p_data->write.len == 2)&&(p_data->write.value[0] == 0x01)&&(p_data->write.value[1] == 0x00)){ if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP data notification enable");
enable_data_ntf = true; enable_data_ntf = true;
}else if((p_data->write.len == 2)&&(p_data->write.value[0] == 0x00)&&(p_data->write.value[1] == 0x00)){ } else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x02) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP data indication enable");
enable_data_ntf = true;
} else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x00) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP data notification/indication disable");
enable_data_ntf = false; enable_data_ntf = false;
} }
} else if (res == SPP_IDX_SPP_STATUS_CFG) {
if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP status notification enable");
} else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x00) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP status notification disable");
}
} }
#ifdef SUPPORT_HEARTBEAT #ifdef SUPPORT_HEARTBEAT
else if(res == SPP_IDX_SPP_HEARTBEAT_CFG){ else if (res == SPP_IDX_SPP_HEARTBEAT_CFG) {
if((p_data->write.len == 2)&&(p_data->write.value[0] == 0x01)&&(p_data->write.value[1] == 0x00)){ if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP heartbeat notification enable");
enable_heart_ntf = true; enable_heart_ntf = true;
}else if((p_data->write.len == 2)&&(p_data->write.value[0] == 0x00)&&(p_data->write.value[1] == 0x00)){ } else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x00) && (p_data->write.value[1] == 0x00)) {
ESP_LOGI(GATTS_TABLE_TAG, "SPP heartbeat notification disable");
enable_heart_ntf = false; enable_heart_ntf = false;
} }
}else if(res == SPP_IDX_SPP_HEARTBEAT_VAL){ } else if (res == SPP_IDX_SPP_HEARTBEAT_VAL) {
if((p_data->write.len == sizeof(heartbeat_s))&&(memcmp(heartbeat_s,p_data->write.value,sizeof(heartbeat_s)) == 0)){ if ((p_data->write.len == sizeof(heartbeat_s)) && (memcmp(heartbeat_s, p_data->write.value, sizeof(heartbeat_s)) == 0)) {
heartbeat_count_num = 0; heartbeat_count_num = 0;
} }
} }
#endif #endif
else if(res == SPP_IDX_SPP_DATA_RECV_VAL){ else if (res == SPP_IDX_SPP_DATA_RECV_VAL) {
#ifdef SPP_DEBUG_MODE #ifdef CONFIG_EXAMPLE_ENABLE_RF_EMC_TEST_MODE
ESP_LOG_BUFFER_CHAR(GATTS_TABLE_TAG,(char *)(p_data->write.value),p_data->write.len); ESP_LOG_BUFFER_HEX("RX", p_data->write.value, p_data->write.len);
#else #else
uart_write_bytes(UART_NUM_0, (char *)(p_data->write.value), p_data->write.len); uart_write_bytes(UART_NUM_0, (char *)(p_data->write.value), p_data->write.len);
#endif #endif
}else{ } else {
//TODO: //TODO:
} }
}else if((p_data->write.is_prep == true)&&(res == SPP_IDX_SPP_DATA_RECV_VAL)){ } else if ((p_data->write.is_prep == true) && (res == SPP_IDX_SPP_DATA_RECV_VAL)) {
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_PREP_WRITE_EVT : handle = %d", res);
store_wr_buffer(p_data); store_wr_buffer(p_data);
} }
break; break;
} }
case ESP_GATTS_EXEC_WRITE_EVT:{ case ESP_GATTS_EXEC_WRITE_EVT: {
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT"); ESP_LOGI(GATTS_TABLE_TAG, "Execute write");
if(p_data->exec_write.exec_write_flag){ if (p_data->exec_write.exec_write_flag) {
print_write_buffer(); print_write_buffer();
free_write_buffer(); free_write_buffer();
} }
break; break;
} }
case ESP_GATTS_RESPONSE_EVT:
break;
case ESP_GATTS_MTU_EVT: case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "MTU exchange, MTU %d", param->mtu.mtu);
spp_mtu_size = p_data->mtu.mtu; spp_mtu_size = p_data->mtu.mtu;
break; break;
case ESP_GATTS_CONF_EVT: case ESP_GATTS_CONF_EVT:
if (param->conf.status) {
ESP_LOGI(GATTS_TABLE_TAG, "Confirm received, status %d, handle %d", param->conf.status, param->conf.handle);
}
break; break;
case ESP_GATTS_UNREG_EVT: case ESP_GATTS_UNREG_EVT:
break; break;
case ESP_GATTS_DELETE_EVT: case ESP_GATTS_DELETE_EVT:
break; break;
case ESP_GATTS_START_EVT: case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "Service start, status %d, service_handle %d",
param->start.status, param->start.service_handle);
break; break;
case ESP_GATTS_STOP_EVT: case ESP_GATTS_STOP_EVT:
break; break;
case ESP_GATTS_CONNECT_EVT: case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"",
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
spp_conn_id = p_data->connect.conn_id; spp_conn_id = p_data->connect.conn_id;
spp_gatts_if = gatts_if; spp_gatts_if = gatts_if;
is_connected = true; is_connected = true;
@@ -595,6 +649,8 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
#endif #endif
break; break;
case ESP_GATTS_DISCONNECT_EVT: case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
spp_mtu_size = 23; spp_mtu_size = 23;
is_connected = false; is_connected = false;
enable_data_ntf = false; enable_data_ntf = false;
@@ -615,11 +671,11 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
case ESP_GATTS_CONGEST_EVT: case ESP_GATTS_CONGEST_EVT:
break; break;
case ESP_GATTS_CREAT_ATTR_TAB_EVT:{ case ESP_GATTS_CREAT_ATTR_TAB_EVT:{
ESP_LOGI(GATTS_TABLE_TAG, "The number handle =%x",param->add_attr_tab.num_handle); ESP_LOGI(GATTS_TABLE_TAG, "The number handle %x",param->add_attr_tab.num_handle);
if (param->add_attr_tab.status != ESP_GATT_OK){ if (param->add_attr_tab.status != ESP_GATT_OK) {
ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table failed, error code=0x%x", param->add_attr_tab.status); ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table failed, error code 0x%x", param->add_attr_tab.status);
} }
else if (param->add_attr_tab.num_handle != SPP_IDX_NB){ else if (param->add_attr_tab.num_handle != SPP_IDX_NB) {
ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, SPP_IDX_NB); ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, SPP_IDX_NB);
} }
else { else {
@@ -636,8 +692,6 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{ {
ESP_LOGI(GATTS_TABLE_TAG, "EVT %d, gatts if %d", event, gatts_if);
/* If event is register event, store the gatts_if for each profile */ /* If event is register event, store the gatts_if for each profile */
if (event == ESP_GATTS_REG_EVT) { if (event == ESP_GATTS_REG_EVT) {
if (param->reg.status == ESP_GATT_OK) { if (param->reg.status == ESP_GATT_OK) {
@@ -666,6 +720,8 @@ void app_main(void)
esp_err_t ret; esp_err_t ret;
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
spp_task_init();
// Initialize NVS // Initialize NVS
ret = nvs_flash_init(); ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
@@ -705,9 +761,7 @@ void app_main(void)
esp_ble_gap_register_callback(gap_event_handler); esp_ble_gap_register_callback(gap_event_handler);
esp_ble_gatts_app_register(ESP_SPP_APP_ID); esp_ble_gatts_app_register(ESP_SPP_APP_ID);
spp_task_init(); esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(SPP_GATT_MTU_SIZE);
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
if (local_mtu_ret){ if (local_mtu_ret){
ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret); ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
} }

View File

@@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#define PROMPT_STR CONFIG_IDF_TARGET
static struct {
struct arg_int *cmd_params;
struct arg_end *end;
} rf_testing_set_tx_power_cmd_args;
static int rf_testing_set_ble_tx_power_command(int argc, char **argv)
{
esp_err_t ret = ESP_OK;
int nerrors = arg_parse(argc, argv, (void **) &rf_testing_set_tx_power_cmd_args);
if (nerrors != 0) {
arg_print_errors(stderr, rf_testing_set_tx_power_cmd_args.end, argv[0]);
return 1;
}
ESP_LOGI(__func__, "Set tx power level '%d'", rf_testing_set_tx_power_cmd_args.cmd_params->ival[0]);
if (rf_testing_set_tx_power_cmd_args.cmd_params->ival[0] > 15) {
return 2;
}
ret = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, rf_testing_set_tx_power_cmd_args.cmd_params->ival[0]);
if (ret != ESP_OK) {
return 3;
}
return 0;
}
static int rf_testing_get_ble_tx_power_command(int argc, char **argv)
{
esp_power_level_t power_level = 0xFF;
power_level = esp_ble_tx_power_get(ESP_BLE_PWR_TYPE_DEFAULT);
printf("\nCurrent BLE TX power is %d level\n", power_level);
return 0;
}
esp_err_t esp_console_register_set_ble_tx_power_command(void)
{
rf_testing_set_tx_power_cmd_args.cmd_params = arg_int1("i", "index", "<index>","tx power level index");
rf_testing_set_tx_power_cmd_args.end = arg_end(1);
esp_console_cmd_t command = {
.command = "set_ble_tx_power",
.help = "Set default ble tx power",
.func = &rf_testing_set_ble_tx_power_command,
.argtable = &rf_testing_set_tx_power_cmd_args
};
return esp_console_cmd_register(&command);
}
esp_err_t esp_console_register_get_ble_tx_power_command(void)
{
esp_console_cmd_t command = {
.command = "get_ble_tx_power",
.help = "Get ble tx power during RF test",
.func = &rf_testing_get_ble_tx_power_command,
};
return esp_console_cmd_register(&command);
}
esp_err_t rf_testing_configuration_command_enable(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = 256;
esp_console_register_set_ble_tx_power_command();
esp_console_register_get_ble_tx_power_command();
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
ESP_ERROR_CHECK(esp_console_start_repl(repl));
return ESP_OK;
}

View File

@@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#ifndef __DTM_CONFIGURATION_COMMAND_H__
#define __DTM_CONFIGURATION_COMMAND_H__
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
esp_err_t rf_testing_configuration_command_enable(void);
#endif