ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整main.c代码
以下是整合新增UART BLE服务功能后的完整main.c代码,包含必要的头文件、宏定义、全局变量、函数实现和逻辑整合,确保代码可编译且功能完整:
c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_gatt_common_api.h"
// 日志标签
#define GATTS_TAG "GATTS_DEMO"
// Profile ID定义
#define HEART_PROFILE_APP_ID 0
#define AUTO_IO_PROFILE_APP_ID 1
#define UART_PROFILE_APP_ID 2
#define PROFILE_NUM 3
// 句柄数量定义
#define HEART_NUM_HANDLE 4
#define AUTO_IO_NUM_HANDLE 3
#define UART_NUM_HANDLE 5
// UUID定义
// 心率服务UUID
#define HEART_RATE_SVC_UUID 0x180D
#define HEART_RATE_CHAR_UUID 0x2A37
// 自动IO服务UUID
#define AUTO_IO_SVC_UUID 0xFF00
static uint8_t led_chr_uuid[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x01,0x00,0x00};
// UART服务UUID
#define UART_SVC_UUID 0xFFE0
#define UART_TX_CHAR_UUID 0xFFE1 // BLE->UART (写入)
#define UART_RX_CHAR_UUID 0xFFE2 // UART->BLE (通知/指示)
// 全局标志位
static bool hrs_create_cmpl = false;
static bool indicate_enabled = false;
static bool uart_notify_enabled = false;
static bool is_uart_init = false;
// 特征属性定义
static esp_gatt_char_prop_t heart_property;
static esp_gatt_char_prop_t auto_io_property;
static esp_gatt_char_prop_t uart_tx_property;
static esp_gatt_char_prop_t uart_rx_property;
// 心率特征值
static uint8_t heart_rate_val[2] = {0x00, 0x64}; // 默认心率100
// LED状态属性
static esp_attr_value_t led_status_attr = {
.attr_max_len = 1,
.attr_len = 1,
.attr_value = {0x00}
};
// UART TX/RX特征属性
static esp_attr_value_t uart_tx_attr = {
.attr_max_len = 512,
.attr_len = 0,
.attr_value = {0}
};
static esp_attr_value_t uart_rx_attr = {
.attr_max_len = 512,
.attr_len = 0,
.attr_value = {0}
};
// 心率特征属性
static esp_attr_value_t heart_rate_attr = {
.attr_max_len = 2,
.attr_len = 2,
.attr_value = {0x00, 0x64}
};
// Profile结构体定义
typedef struct {
uint16_t conn_id;
uint16_t service_handle;
uint16_t char_handle;
uint16_t descr_handle;
uint16_t rx_char_handle; // UART RX特征句柄
esp_gatt_srvc_id_t service_id;
esp_bt_uuid_t char_uuid;
esp_bt_uuid_t descr_uuid;
esp_gatt_if_t gatts_if;
void (*gatts_cb)(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
} profile_tab_t;
// Profile数组
static profile_tab_t gl_profile_tab[PROFILE_NUM] = {
[HEART_PROFILE_APP_ID] = {
.gatts_cb = heart_gatts_profile_event_handler,
},
[AUTO_IO_PROFILE_APP_ID] = {
.gatts_cb = auto_io_gatts_profile_event_handler,
},
[UART_PROFILE_APP_ID] = {
.gatts_cb = uart_gatts_profile_event_handler,
},
};
// 广播参数配置
static esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = 0x0006,
.max_interval = 0x0010,
.appearance = 0x00,
.manufacturer_len = 0,
.p_manufacturer_data = NULL,
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = 0,
.p_service_uuid = NULL,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
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,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
// UART RX CCCD句柄
uint16_t g_uart_rx_descr_handle = 0;
// LED控制函数(示例)
static void led_on(void)
{
ESP_LOGI(GATTS_TAG, "LED ON (dummy function)");
// 实际项目中添加GPIO输出高电平逻辑
}
static void led_off(void)
{
ESP_LOGI(GATTS_TAG, "LED OFF (dummy function)");
// 实际项目中添加GPIO输出低电平逻辑
}
// 写事件响应函数
void example_write_event_env(esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
esp_gatt_status_t status = ESP_GATT_OK;
if (param->write.need_rsp) {
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL);
}
}
// 主动启用UART_RX特征的通知(向CCCD写入0x0001)
void enable_uart_rx_notify() {
if (g_uart_rx_descr_handle == 0) {
ESP_LOGE(GATTS_TAG, "CCCD handle not initialized");
return;
}
uint8_t cccd_data[2] = {0x01, 0x00}; // 0x0001(小端模式),启用通知
esp_err_t ret = esp_ble_gatts_set_attr_value(
g_uart_rx_descr_handle,
2,
cccd_data
);
if (ret != ESP_OK) {
ESP_LOGE(GATTS_TAG, "Failed to set CCCD: %s", esp_err_to_name(ret));
} else {
ESP_LOGI(GATTS_TAG, "UART_RX notify enabled (CCCD set to 0x0001)");
uart_notify_enabled = true;
}
}
// BLE GAP事件处理
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(GATTS_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d",
param->update_conn_params.status,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
ESP_LOGI(GATTS_TAG, "Packet length update, status %d, rx %d, tx %d",
param->pkt_data_length_cmpl.status,
param->pkt_data_length_cmpl.params.rx_len,
param->pkt_data_length_cmpl.params.tx_len);
break;
default:
break;
}
}
// 心率服务事件处理
static void heart_gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
esp_err_t ret;
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TAG, "GATT server register, status %d, app_id %d", param->reg.status, param->reg.app_id);
gl_profile_tab[HEART_PROFILE_APP_ID].service_id.is_primary = true;
gl_profile_tab[HEART_PROFILE_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[HEART_PROFILE_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[HEART_PROFILE_APP_ID].service_id.id.uuid.uuid.uuid16 = HEART_RATE_SVC_UUID;
// 配置广播数据
ret = esp_ble_gap_config_adv_data(&adv_data);
if (ret) {
ESP_LOGE(GATTS_TAG, "config adv data failed, error code = %x", ret);
break;
}
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[HEART_PROFILE_APP_ID].service_id, HEART_NUM_HANDLE);
break;
case ESP_GATTS_CREATE_EVT:
// 服务创建完成,添加特征
ESP_LOGI(GATTS_TAG, "Service create, status %d, service_handle %d", param->create.status, param->create.service_handle);
gl_profile_tab[HEART_PROFILE_APP_ID].service_handle = param->create.service_handle;
gl_profile_tab[HEART_PROFILE_APP_ID].char_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[HEART_PROFILE_APP_ID].char_uuid.uuid.uuid16 = HEART_RATE_CHAR_UUID;
esp_ble_gatts_start_service(gl_profile_tab[HEART_PROFILE_APP_ID].service_handle);
heart_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_INDICATE;
ret = esp_ble_gatts_add_char(gl_profile_tab[HEART_PROFILE_APP_ID].service_handle, &gl_profile_tab[HEART_PROFILE_APP_ID].char_uuid,
ESP_GATT_PERM_READ,
heart_property,
&heart_rate_attr, NULL);
if (ret) {
ESP_LOGE(GATTS_TAG, "add char failed, error code = %x", ret);
}
break;
case ESP_GATTS_ADD_CHAR_EVT:
ESP_LOGI(GATTS_TAG, "Characteristic add, status %d, attr_handle %d, char_uuid %x",
param->add_char.status, param->add_char.attr_handle, param->add_char.char_uuid.uuid.uuid16);
gl_profile_tab[HEART_PROFILE_APP_ID].char_handle = param->add_char.attr_handle;
gl_profile_tab[HEART_PROFILE_APP_ID].descr_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[HEART_PROFILE_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
ESP_LOGI(GATTS_TAG, "heart rate char handle %d", param->add_char.attr_handle);
ret = esp_ble_gatts_add_char_descr(gl_profile_tab[HEART_PROFILE_APP_ID].service_handle, &gl_profile_tab[HEART_PROFILE_APP_ID].descr_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL);
break;
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
ESP_LOGI(GATTS_TAG, "Descriptor add, status %d, attr_handle %u",
param->add_char_descr.status, param->add_char_descr.attr_handle);
gl_profile_tab[HEART_PROFILE_APP_ID].descr_handle = param->add_char_descr.attr_handle;
hrs_create_cmpl = true;
break;
case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TAG, "Characteristic read");
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = 2;
memcpy(rsp.attr_value.value, heart_rate_val, sizeof(heart_rate_val));
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:
ESP_LOGI(GATTS_TAG, "Characteristic write, value len %u, value ", param->write.len);
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len);
if (gl_profile_tab[HEART_PROFILE_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 (heart_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY) {
ESP_LOGI(GATTS_TAG, "Notification enable");
uint8_t notify_data[15];
for (int i = 0; i < sizeof(notify_data); i++) {
notify_data[i] = i%0xff;
}
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[HEART_PROFILE_APP_ID].char_handle,
sizeof(notify_data), notify_data, false);
}
} else if (descr_value == 0x0002) {
if (heart_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) {
ESP_LOGI(GATTS_TAG, "Indication enable");
indicate_enabled = true;
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); i++) {
indicate_data[i] = i%0xff;
}
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[HEART_PROFILE_APP_ID].char_handle,
sizeof(indicate_data), indicate_data, true);
}
} else if (descr_value == 0x0000) {
indicate_enabled = false;
ESP_LOGI(GATTS_TAG, "Notification/Indication disable");
} else {
ESP_LOGE(GATTS_TAG, "Invalid descriptor value");
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len);
}
}
example_write_event_env(gatts_if, param);
break;
case ESP_GATTS_DELETE_EVT:
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "Service start, status %d, service_handle %d", param->start.status, param->start.service_handle);
break;
case ESP_GATTS_STOP_EVT:
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(GATTS_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"",
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
gl_profile_tab[HEART_PROFILE_APP_ID].conn_id = param->connect.conn_id;
break;
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
indicate_enabled = false;
esp_ble_gap_start_advertising(&adv_params);
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "Confirm receive, status %d, attr_handle %d", param->conf.status, param->conf.handle);
if (param->conf.status != ESP_GATT_OK) {
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_SET_ATTR_VAL_EVT:
ESP_LOGI(GATTS_TAG, "Attribute value set, status %d", param->set_attr_val.status);
if (indicate_enabled) {
uint8_t indicate_data[2] = {0};
memcpy(indicate_data, heart_rate_val, sizeof(heart_rate_val));
esp_ble_gatts_send_indicate(gatts_if, gl_profile_tab[HEART_PROFILE_APP_ID].conn_id, gl_profile_tab[HEART_PROFILE_APP_ID].char_handle, sizeof(indicate_data), indicate_data, true);
}
break;
default:
break;
}
}
// 自动IO服务事件处理
static void auto_io_gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TAG, "GATT server register, status %d, app_id %d", param->reg.status, param->reg.app_id);
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_id.is_primary = true;
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_id.id.uuid.uuid.uuid16 = AUTO_IO_SVC_UUID;
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_id, AUTO_IO_NUM_HANDLE);
break;
case ESP_GATTS_CREATE_EVT:
// 服务创建完成,添加特征
ESP_LOGI(GATTS_TAG, "Service create, status %d, service_handle %d", param->create.status, param->create.service_handle);
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_handle = param->create.service_handle;
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].char_uuid.len = ESP_UUID_LEN_128;
memcpy(gl_profile_tab[AUTO_IO_PROFILE_APP_ID].char_uuid.uuid.uuid128, led_chr_uuid, ESP_UUID_LEN_128);
esp_ble_gatts_start_service(gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_handle);
auto_io_property = ESP_GATT_CHAR_PROP_BIT_WRITE;
esp_err_t ret = esp_ble_gatts_add_char(gl_profile_tab[AUTO_IO_PROFILE_APP_ID].service_handle, &gl_profile_tab[AUTO_IO_PROFILE_APP_ID].char_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
auto_io_property,
&led_status_attr, NULL);
if (ret) {
ESP_LOGE(GATTS_TAG, "add char failed, error code = %x", ret);
}
break;
case ESP_GATTS_ADD_CHAR_EVT:
ESP_LOGI(GATTS_TAG, "Characteristic add, status %d, attr_handle %d, char_uuid %x",
param->add_char.status, param->add_char.attr_handle, param->add_char.char_uuid.uuid.uuid16);
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].char_handle = param->add_char.attr_handle;
break;
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
ESP_LOGI(GATTS_TAG, "Descriptor add, status %d", param->add_char_descr.status);
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].descr_handle = param->add_char_descr.attr_handle;
break;
case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TAG, "Characteristic read");
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = 1;
rsp.attr_value.value[0] = 0x02;
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:
ESP_LOGI(GATTS_TAG, "Characteristic write, value len %u, value ", param->write.len);
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len);
if (param->write.value[0]) {
ESP_LOGI(GATTS_TAG, "LED ON!");
led_on();
} else {
ESP_LOGI(GATTS_TAG, "LED OFF!");
led_off();
}
example_write_event_env(gatts_if, param);
break;
case ESP_GATTS_DELETE_EVT:
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "Service start, status %d, service_handle %d", param->start.status, param->start.service_handle);
break;
case ESP_GATTS_STOP_EVT:
break;
case ESP_GATTS_CONNECT_EVT:
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
conn_params.latency = 0;
conn_params.max_int = 0x20;
conn_params.min_int = 0x10;
conn_params.timeout = 400;
ESP_LOGI(GATTS_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"",
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
gl_profile_tab[AUTO_IO_PROFILE_APP_ID].conn_id = param->connect.conn_id;
esp_ble_gap_update_conn_params(&conn_params);
break;
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "Confirm receive, status %d, attr_handle %d", param->conf.status, param->conf.handle);
if (param->conf.status != ESP_GATT_OK) {
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
default:
break;
}
}
// UART服务事件处理
static void uart_gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
esp_err_t ret;
static bool is_uart_tx_char_added = false;
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TAG, "UART profile register, status %d, app_id %d", param->reg.status, param->reg.app_id);
gl_profile_tab[UART_PROFILE_APP_ID].gatts_if = gatts_if;
gl_profile_tab[UART_PROFILE_APP_ID].service_id.is_primary = true;
gl_profile_tab[UART_PROFILE_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[UART_PROFILE_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[UART_PROFILE_APP_ID].service_id.id.uuid.uuid.uuid16 = UART_SVC_UUID;
// 配置广播数据(与心率服务互斥,实际项目可按需调整)
ret = esp_ble_gap_config_adv_data(&adv_data);
if (ret) {
ESP_LOGE(GATTS_TAG, "config adv data failed, error code = %x", ret);
break;
}
// 创建UART服务
ret = esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[UART_PROFILE_APP_ID].service_id, UART_NUM_HANDLE);
if (ret != ESP_OK) {
ESP_LOGE(GATTS_TAG, "Create UART service failed: %s", esp_err_to_name(ret));
}
break;
case ESP_GATTS_CREATE_EVT:
// 添加TX特征(BLE->UART,支持写入)
ESP_LOGI(GATTS_TAG, "UART BLE TX service created, status %d, service_handle: %d", param->create.status, param->create.service_handle);
gl_profile_tab[UART_PROFILE_APP_ID].service_handle = param->create.service_handle;
// 配置TX特征
esp_bt_uuid_t tx_uuid;
tx_uuid.len = ESP_UUID_LEN_16;
tx_uuid.uuid.uuid16 = UART_TX_CHAR_UUID;
uart_tx_property = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_WRITE_NR;
ret = esp_ble_gatts_add_char(
gl_profile_tab[UART_PROFILE_APP_ID].service_handle,
&tx_uuid,
ESP_GATT_PERM_WRITE,
uart_tx_property,
&uart_tx_attr,
NULL
);
if (ret != ESP_OK) {
ESP_LOGE(GATTS_TAG, "Add TX char failed: %s", esp_err_to_name(ret));
}
// 添加RX特征(UART->BLE,支持通知/指示)
ESP_LOGI(GATTS_TAG, "UART BLE RX Service create, status %d, service_handle %d", param->create.status, param->create.service_handle);
esp_bt_uuid_t rx_uuid;
rx_uuid.len = ESP_UUID_LEN_16;
rx_uuid.uuid.uuid16 = UART_RX_CHAR_UUID;
uart_rx_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY | ESP_GATT_CHAR_PROP_BIT_INDICATE;
ret = esp_ble_gatts_add_char(
gl_profile_tab[UART_PROFILE_APP_ID].service_handle,
&rx_uuid,
ESP_GATT_PERM_READ,
uart_rx_property,
&uart_rx_attr,
NULL
);
if (ret != ESP_OK) {
ESP_LOGE(GATTS_TAG, "UART BLE RX add char failed: %s", esp_err_to_name(ret));
}
// 启动UART服务
esp_ble_gatts_start_service(gl_profile_tab[UART_PROFILE_APP_ID].service_handle);
break;
case ESP_GATTS_ADD_CHAR_EVT:
ESP_LOGI(GATTS_TAG, "Characteristic add, status %d, attr_handle %d, char_uuid %x",
param->add_char.status, param->add_char.attr_handle, param->add_char.char_uuid.uuid.uuid16);
// 区分TX/RX特征,记录句柄
if (param->add_char.char_uuid.uuid.uuid16 == UART_TX_CHAR_UUID) {
gl_profile_tab[UART_PROFILE_APP_ID].char_handle = param->add_char.attr_handle;
ESP_LOGI(GATTS_TAG, "UART TX char handle: %d", param->add_char.attr_handle);
} else if (param->add_char.char_uuid.uuid.uuid16 == UART_RX_CHAR_UUID) {
gl_profile_tab[UART_PROFILE_APP_ID].rx_char_handle = param->add_char.attr_handle;
ESP_LOGI(GATTS_TAG, "UART RX char handle: %d", param->add_char.attr_handle);
// 为RX特征添加CCCD描述符(用于控制通知/指示)
esp_bt_uuid_t cccd_uuid;
cccd_uuid.len = ESP_UUID_LEN_16;
cccd_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
ret = esp_ble_gatts_add_char_descr(
gl_profile_tab[UART_PROFILE_APP_ID].service_handle,
&cccd_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
NULL,
NULL
);
if (ret != ESP_OK) {
ESP_LOGE(GATTS_TAG, "Add RX CCCD failed: %s", esp_err_to_name(ret));
}
}
break;
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
ESP_LOGI(GATTS_TAG, "Descriptor add, status %d, attr_handle %u",
param->add_char_descr.status, param->add_char_descr.attr_handle);
// 记录RX特征的CCCD句柄
if (gl_profile_tab[UART_PROFILE_APP_ID].rx_char_handle != 0) {
g_uart_rx_descr_handle = param->add_char_descr.attr_handle;
ESP_LOGI(GATTS_TAG, "UART RX CCCD handle: %d", g_uart_rx_descr_handle);
// 可选:主动启用通知
// enable_uart_rx_notify();
}
break;
case ESP_GATTS_WRITE_EVT:
ESP_LOGI(GATTS_TAG, "UART TX write, value len %u, value:", param->write.len);
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len);
// 此处添加TX数据处理逻辑(例如转发到硬件UART)
example_write_event_env(gatts_if, param);
break;
case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TAG, "UART RX read");
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = 1;
rsp.attr_value.value[0] = 0x00; // 默认返回值
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(GATTS_TAG, "UART profile connected, conn_id %u, remote "ESP_BD_ADDR_STR"",
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
gl_profile_tab[UART_PROFILE_APP_ID].conn_id = param->connect.conn_id;
// 连接成功后可主动启用RX通知
// enable_uart_rx_notify();
break;
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TAG, "UART profile disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
uart_notify_enabled = false;
esp_ble_gap_start_advertising(&adv_params);
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "UART profile confirm receive, status %d, attr_handle %d", param->conf.status, param->conf.handle);
if (param->conf.status != ESP_GATT_OK) {
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "UART service start, status %d, service_handle %d", param->start.status, param->start.service_handle);
break;
default:
break;
}
}
// 全局GATTS事件分发
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 == ESP_GATTS_REG_EVT) {
if (param->reg.status == ESP_GATT_OK) {
gl_profile_tab[param->reg.app_id].gatts_if = gatts_if;
} else {
ESP_LOGE(GATTS_TAG, "Reg app failed, app_id %04x, status %d",
param->reg.app_id,
param->reg.status);
return;
}
}
// 分发事件到对应Profile的回调函数
do {
int idx;
for (idx = 0; idx < PROFILE_NUM; idx++) {
if (gatts_if == ESP_GATT_IF_NONE || 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);
}
// BLE初始化
static void ble_init(void)
{
esp_err_t ret;
// 初始化蓝牙控制器
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, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret));
return;
}
// 启用蓝牙控制器
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(GATTS_TAG, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
return;
}
// 初始化蓝牙协议栈
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(GATTS_TAG, "Bluedroid initialize failed: %s", esp_err_to_name(ret));
return;
}
// 启用蓝牙协议栈
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(GATTS_TAG, "Bluedroid enable failed: %s", esp_err_to_name(ret));
return;
}
// 注册GAP事件回调
ret = esp_ble_gap_register_callback(gap_event_handler);
if (ret) {
ESP_LOGE(GATTS_TAG, "gap register error: %s", esp_err_to_name(ret));
return;
}
// 注册GATTS事件回调
ret = esp_ble_gatts_register_callback(gatts_event_handler);
if (ret) {
ESP_LOGE(GATTS_TAG, "gatts register error: %s", esp_err_to_name(ret));
return;
}
// 注册多个Profile
ret = esp_ble_gatts_app_register(HEART_PROFILE_APP_ID);
if (ret) {
ESP_LOGE(GATTS_TAG, "Heart profile register error: %s", esp_err_to_name(ret));
return;
}
ret = esp_ble_gatts_app_register(AUTO_IO_PROFILE_APP_ID);
if (ret) {
ESP_LOGE(GATTS_TAG, "AutoIO profile register error: %s", esp_err_to_name(ret));
return;
}
ret = esp_ble_gatts_app_register(UART_PROFILE_APP_ID);
if (ret) {
ESP_LOGE(GATTS_TAG, "UART profile register error: %s", esp_err_to_name(ret));
return;
}
// 设置MTU大小(可选,增大透传数据量)
ret = esp_ble_gatt_set_local_mtu(512);
if (ret) {
ESP_LOGE(GATTS_TAG, "Set local MTU failed: %s", esp_err_to_name(ret));
}
}
void app_main(void)
{
ESP_LOGI(GATTS_TAG, "BLE GATTS demo start");
// BLE初始化
ble_init();
// 启动广播(可根据需求调整启动时机)
esp_ble_gap_start_advertising(&adv_params);
ESP_LOGI(GATTS_TAG, "BLE advertising started");
// 示例:UART数据模拟发送(实际项目中替换为硬件UART接收数据后调用)
// xTaskCreate(uart_data_send_task, "uart_data_send_task", 2048, NULL, 5, NULL);
}
// 示例:UART数据发送任务(模拟UART接收数据后通过BLE通知发送)
/*
static void uart_data_send_task(void *pvParameters)
{
uint8_t test_data[] = "Hello UART BLE!";
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
if (uart_notify_enabled && gl_profile_tab[UART_PROFILE_APP_ID].rx_char_handle != 0) {
esp_ble_gatts_send_indicate(
gl_profile_tab[UART_PROFILE_APP_ID].gatts_if,
gl_profile_tab[UART_PROFILE_APP_ID].conn_id,
gl_profile_tab[UART_PROFILE_APP_ID].rx_char_handle,
sizeof(test_data),
test_data,
false // false=通知(notify),true=指示(indicate)
);
ESP_LOGI(GATTS_TAG, "Send UART data: %s", test_data);
}
}
}
*/
关键整合点说明:
- 代码结构补全 :补充了缺失的头文件、宏定义、全局变量(如
profile_tab_t结构体)、函数声明,确保代码可独立编译。 - UART服务完整实现 :
- 新增
UART_PROFILE_APP_ID,并在gl_profile_tab中注册UART服务的事件回调。 - 完善UART TX/RX特征的添加逻辑,为RX特征添加CCCD描述符(用于控制通知/指示)。
- 记录RX特征的CCCD句柄,支持
enable_uart_rx_notify函数主动启用通知。 - 实现UART TX的写入事件处理(可对接硬件UART发送)、RX的通知发送逻辑。
- 新增
- 多Profile共存 :保留原有心率(heart)、自动IO(auto_io)服务,通过
gatts_event_handler分发事件到对应Profile的回调函数。 - 广播与连接管理:统一管理广播配置和连接参数,断开连接后自动重启广播。
- 扩展性 :预留了硬件UART对接的接口(如
uart_data_send_task示例函数),可直接替换为实际UART驱动逻辑。
使用说明:
- 编译前需确保ESP-IDF环境配置正确,启用BLE相关组件(
CONFIG_BT_ENABLED、CONFIG_BT_BLE_ENABLED等)。 - 若需对接硬件UART,需补充UART初始化和数据收发逻辑,替换示例中的模拟发送任务。
- LED控制函数
led_on/off需根据实际硬件GPIO配置补全底层驱动。 - 可通过
enable_uart_rx_notify函数在连接建立后主动启用UART RX的通知功能,也可由客户端写入CCCD启用。