在 ESP32 的 BLE 开发中,esp_ble_gatts_add_char_descr
是用于向特征(Characteristic)中添加描述符(Descriptor)的核心函数。描述符用于提供特征的额外信息(如客户端特征配置、用户描述等)。以下是该函数的详细说明、参数解析及示例代码:
函数原型
cpp
esp_err_t esp_ble_gatts_add_char_descr(
uint16_t service_handle, // 服务句柄
esp_bt_uuid_t *descr_uuid, // 描述符的 UUID
esp_gatt_perm_t perm, // 描述符的访问权限
esp_attr_value_t *descr_val, // 描述符的初始值(可选)
esp_attr_control_t *control // 描述符的属性控制(安全模式等)
);
参数详解
1. service_handle
(服务句柄)
- 来源 :由
esp_ble_gatts_create_service
或esp_ble_gatts_add_service
创建服务后返回的句柄。 - 作用:指定描述符所属的服务(实际属于服务中的某个特征)。
2. descr_uuid
(描述符 UUID)
- 类型 :
esp_bt_uuid_t
,描述符的标准或自定义 UUID。 - 常用预定义值 :
- CCCD(客户端特征配置描述符) :
0x2902
,用于启用通知(Notify)或指示(Indicate)。 - 用户描述符 :
0x2901
,用于提供人类可读的特征描述。 - 特征扩展属性 :
0x2900
,用于声明扩展属性。
- CCCD(客户端特征配置描述符) :
3. perm
(权限)
- 类型 :
esp_gatt_perm_t
,定义客户端对描述符的访问权限。 - 常用值 :
ESP_GATT_PERM_READ
:允许读ESP_GATT_PERM_WRITE
:允许写ESP_GATT_PERM_READ_ENCRYPTED
:需要加密读ESP_GATT_PERM_WRITE_ENCRYPTED
:需要加密写
4. descr_val
(初始值)
-
类型 :
esp_attr_value_t
,可选参数。如果设为NULL
,描述符初始值为空。 -
示例 :
cpp// CCCD 默认初始值为 0x0000(通知和指示均禁用) esp_attr_value_t descr_val = { .attr_max_len = 2, .attr_len = 2, .attr_value = {0x00, 0x00} };
5. control
(属性控制)
-
类型 :
esp_attr_control_t
,通常用于配置安全模式。如果不需要特殊配置,可设为NULL
。 -
示例 :
cppesp_attr_control_t control = { .auto_rsp = ESP_GATT_AUTO_RSP // 自动响应读/写请求 };
返回值
ESP_OK
:描述符添加成功。- 其他错误码:失败(如无效句柄、内存不足等)。
示例代码
步骤 1:添加特征
cpp
// 先添加特征(假设已创建服务)
uint16_t char_handle;
esp_ble_gatts_add_char(
service_handle,
&char_uuid,
perm,
property,
NULL, NULL, &char_handle
);
步骤 2:添加 CCCD 描述符
cpp
// 定义 CCCD 的 UUID(0x2902)
esp_bt_uuid_t cccd_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = 0x2902}
};
// 描述符权限(允许读写)
esp_gatt_perm_t perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
// 添加 CCCD 描述符
esp_err_t ret = esp_ble_gatts_add_char_descr(
service_handle,
&cccd_uuid,
perm,
NULL, // 初始值为空(客户端首次读取会返回 0x0000)
NULL // 使用默认控制
);
if (ret != ESP_OK) {
ESP_LOGE("GATTS", "添加描述符失败: %s", esp_err_to_name(ret));
}
步骤 3:处理客户端写入 CCCD 的事件
在 GATT 事件回调中处理客户端对 CCCD 的写入操作:
cpp
void gatts_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_WRITE_EVT: {
// 检查是否为 CCCD 描述符写入
if (param->write.handle == descr_handle) {
uint16_t cccd_value = *(uint16_t*)param->write.value;
if (cccd_value & ESP_GATT_CLIENT_CHAR_CFG_NOTIFY) {
ESP_LOGI("GATTS", "客户端启用了通知");
}
if (cccd_value & ESP_GATT_CLIENT_CHAR_CFG_INDICATE) {
ESP_LOGI("GATTS", "客户端启用了指示");
}
}
break;
}
// 其他事件处理...
}
}
关键注意事项
- 描述符必须属于某个特征 :
- 描述符必须添加到已存在的特征所属的服务中,且通常紧随特征添加之后调用。
- CCCD 的特殊性 :
- CCCD 的 UUID 必须为
0x2902
,且其值长度为 2 字节(0x0000 表示禁用,0x0001 启用通知,0x0002 启用指示)。
- CCCD 的 UUID 必须为
- 动态更新描述符值 :
- 使用
esp_ble_gatts_set_attr_value
可动态更新描述符值。
- 使用
- 权限匹配 :
- 若描述符支持写操作(如 CCCD),
perm
必须包含ESP_GATT_PERM_WRITE
。
- 若描述符支持写操作(如 CCCD),
错误排查
- 无效句柄 :确保
service_handle
有效且属于已创建的服务。 - UUID 错误 :CCCD 必须使用
0x2902
,否则客户端无法识别。 - 权限不足 :客户端尝试写入描述符时,若未配置
ESP_GATT_PERM_WRITE
会导致写入失败。
完整流程示意图
cpp
创建服务 (esp_ble_gatts_create_service)
|
v
添加特征 (esp_ble_gatts_add_char)
|
v
添加描述符 (esp_ble_gatts_add_char_descr)
|
v
启动服务 (esp_ble_gatts_start_service)
|
v
处理读写事件 (ESP_GATTS_READ_EVT / WRITE_EVT)
通过 esp_ble_gatts_add_char_descr
,可以为特征添加必要的描述符,实现更复杂的 BLE 交互逻辑(如通知、指示)。