在蓝牙协议栈中,GATT(Generic Attribute Profile)管理器初始化是定义设备数据服务、特征值(Characteristics)及其交互逻辑的核心步骤。GATT基于客户端-服务器模型,通过属性表(Attribute Table)管理数据。以下是详细的实现解析。
一、GATT的作用
GATT定义了蓝牙设备的数据结构,包括:
- 服务(Service):设备功能的逻辑分组(例如心率服务、电池服务)。
- 特征值(Characteristic):服务中的具体数据项(如心率值、电池电量),包含属性值和权限(读/写/通知)。
- 描述符(Descriptor):附加信息(如特征值的通知使能标志)。
二、GATT初始化关键步骤
1. 初始化GATT模块
// 示例(基于nRF52 SDK):
#include "ble_gatt.h"
// 初始化GATT协议栈
ret_code_t err_code = ble_gatt_init();
APP_ERROR_CHECK(err_code);
// 设置最大MTU(最大传输单元,默认为23字节,可扩展至247字节)
ble_gatt_conn_mtu_t mtu_params = { .conn_handle = BLE_CONN_HANDLE_INVALID };
err_code = sd_ble_gattc_exchange_mtu_request(BLE_CONN_HANDLE_INVALID, 247);
APP_ERROR_CHECK(err_code);
2. 定义服务与特征值
以“自定义温度传感器服务”为例
// 定义服务的UUID(自定义服务需使用128位UUID)
#define TEMP_SERVICE_UUID 0x1234 // 16位短UUID(若使用完整128位需定义数组)
// 定义特征值的UUID及属性
#define TEMP_VALUE_UUID 0x5678
#define TEMP_VALUE_CHAR_PROPS BLE_GATT_CHAR_PROP_NOTIFY | BLE_GATT_CHAR_PROP_READ
// 创建服务结构体
BLE_GATT_CHARACTERISTIC_DEF(temp_value_char,
TEMP_VALUE_UUID,
TEMP_VALUE_CHAR_PROPS,
sizeof(float), // 特征值数据长度(温度值为float类型)
BLE_GATT_PERM_READ_ENCRYPTED); // 权限:需加密读取
// 注册服务
ble_uuid_t service_uuid;
BLE_UUID_BLE_ASSIGN(service_uuid, TEMP_SERVICE_UUID);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&service_uuid,
&service_handle);
APP_ERROR_CHECK(err_code);
// 添加特征值到服务
err_code = sd_ble_gatts_characteristic_add(service_handle,
&temp_value_char,
&char_handle);
APP_ERROR_CHECK(err_code);
3. 配置特征值描述符(如CCCD)
// 配置客户端特征值配置描述符(CCCD,用于启用通知)
ble_gatts_attr_md_t cccd_md = {
.read_perm = { .sm = 1, .lv = 1 }, // 安全模式:需要加密
.write_perm = { .sm = 1, .lv = 1 },
.vloc = BLE_GATTS_VLOC_STACK // 描述符存储在协议栈中
};
// 关联CCCD到特征值
ble_gatts_char_md_t char_md = {
.char_props = TEMP_VALUE_CHAR_PROPS,
.p_cccd_md = &cccd_md
};
4. 处理GATT事件
注册事件回调,处理读写请求、通知确认等:
void gatt_evt_handler(ble_evt_t const *p_ble_evt) {
switch (p_ble_evt->header.evt_id) {
case BLE_GATTS_EVT_WRITE: // 处理写入请求
if (p_ble_evt->evt.gatts_evt.params.write.handle == char_handle.value_handle) {
// 更新温度值并触发通知
float new_temp = *(float*)p_ble_evt->evt.gatts_evt.params.write.data;
update_temperature(new_temp);
}
break;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: // 处理读/写授权请求
// 授权逻辑(例如验证用户权限)
sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle, BLE_GATT_STATUS_SUCCESS);
break;
case BLE_GATTS_EVT_HVN_TX_COMPLETE: // 通知发送完成
// 释放资源或准备下一次通知
break;
}
}
5. 发送通知(主动更新数据)
void send_temperature_notification(float temp) {
ble_gatts_hvx_params_t hvx_params = {
.handle = char_handle.value_handle,
.type = BLE_GATT_HVX_NOTIFICATION,
.p_len = sizeof(float),
.p_data = (uint8_t*)&temp
};
// 发送通知到已订阅的客户端
uint16_t conn_handle = get_active_connection();
sd_ble_gatts_hvx(conn_handle, &hvx_params);
}
三、关键参数详解
1. 特征值属性(char_props
)
BLE_GATT_CHAR_PROP_READ
:允许读取。BLE_GATT_CHAR_PROP_WRITE
:允许写入。BLE_GATT_CHAR_PROP_NOTIFY
:启用通知(需客户端订阅CCCD)。BLE_GATT_CHAR_PROP_INDICATE
:启用指示(需客户端确认)。
2. 权限设置(perm
)
BLE_GATT_PERM_READ_ENCRYPTED
:读取需加密。BLE_GATT_PERM_WRITE_SIGNED
:写入需签名验证。BLE_GATT_PERM_NONE
:无限制。
3. MTU(Maximum Transmission Unit)
- 默认23字节,通过MTU交换可扩展至247字节,提升数据传输效率。
四、常见问题与调试
1. 客户端无法读取特征值
- 原因:权限未配置或特征值未正确添加到服务。
- 解决:检查
perm
设置和sd_ble_gatts_characteristic_add
返回值。
2. 通知未触发
- 原因:客户端未启用CCCD或未正确发送通知。
- 解决:确认客户端已写入CCCD(值
0x0001
),检查sd_ble_gatts_hvx
返回值。
3. 数据长度超限
- 原因:写入数据超过特征值定义的长度。
- 解决:调整特征值声明中的
max_len
,或分片传输。
五、平台差异说明
- nRF52(SoftDevice):使用
sd_ble_gatts_*
系列API,需预编译协议栈。 - ESP32(ESP-IDF):通过
esp_ble_gatts_*
函数实现,支持动态服务注册。 - STM32WB(CubeMX):使用STM32CubeMX生成服务模板,集成属性表配置。
六、完整代码框架(基于nRF52)
通过合理初始化GATT管理器,蓝牙设备能够高效管理数据服务,支持灵活的数据交互场景(如传感器数据上报、设备控制指令等),是构建复杂BLE应用的核心基础。