目錄
概述
1 GATT 基本概念
1.1?GATT 的介紹
1.2?GATT 的角色
?1.3?核心組件
1.4?客戶端操作
2 ?bt_gatt_discover函數的功能和應用
2.1?函數介紹
?2.1 發現類型(Discover Type)
3?典型使用流程
3.1 服務發現示例
3.2 級聯發現模式
?3.3 按UUID過濾發現
?3.4 發現描述符
4 錯誤處理?
4.1 常見錯誤碼
4.2?錯誤處理示例
5 性能優化建議
6 資源管理注意事項
6.1?參數生命周期
6.2?取消發現
概述
本文介紹了藍牙低功耗(BLE)中的GATT協議及其核心功能。GATT定義了BLE設備通過服務和特征交換數據的標準框架,包含服務器和客戶端兩種角色。文章詳細解析了GATT的核心組件(服務、特性、描述符)及其層級結構,并以Zephyr協議棧中的bt_gatt_discover函數為例,說明其參數配置、發現類型和使用流程,包括服務發現、級聯發現、UUID過濾等典型應用場景。最后還列舉了常見錯誤碼及處理方法,為BLE應用的開發提供了實用指導。
1 GATT 基本概念
1.1?GATT 的介紹
GATT (Generic Attribute Profile) 是?Bluetooth Low Energy (BLE)?的核心協議,定義了?數據通信的標準框架,使BLE設備能夠通過?服務(Services)?和?特征(Characteristics)?交換數據。
1.2?GATT 的角色
角色 | 說明 | 典型設備 |
---|---|---|
GATT 服務器(Server) | 存儲并提供數據(如傳感器數據) | 心率帶、溫度計 |
GATT 客戶端(Client) | 讀取或寫入服務器數據 | 手機、中央設備 |
?1.3?核心組件
?1)層級結構
GATT Profile
├── Services (服務)
│ ├── Characteristics (特性)
│ │ ├── Value (值)
│ │ ├── Descriptors (描述符)
│ │ │ └── Client Characteristic Configuration (CCC)
│ │ └── Properties (屬性)
│ └── Includes (包含服務)
└── Attributes (屬性)
2)?關鍵組件說明
組件 | 說明 | 示例UUID |
---|---|---|
服務(Service) | 功能邏輯集合 | 0x180A (設備信息服務) |
特性(Characteristic) | 服務中的數據項 | 0x2A29 (廠商名稱) |
描述符(Descriptor) | 特性的元數據 | 0x2902 (CCC描述符) |
屬性(Attribute) | 數據庫基本單元 | 由協議棧管理 |
1.4?客戶端操作
操作 | 函數(Zephyr示例) | 說明 |
---|---|---|
發現服務 | bt_gatt_discover() | 掃描遠程設備的GATT表 |
讀取特征值 | bt_gatt_read() | 讀取數據(如電池電量) |
寫入特征值 | bt_gatt_write() | 發送命令或配置 |
啟用通知 | bt_gatt_subscribe() | 訂閱實時數據(如心率) |
2 ?bt_gatt_discover函數的功能和應用
2.1?函數介紹
bt_gatt_discover
?是 Zephyr BLE 協議棧中用于發現遠程設備 GATT 服務的核心函數,下面我將從多個維度進行詳細說明:
1)?函數原型與參數
int bt_gatt_discover(struct bt_conn *conn,struct bt_gatt_discover_params *params
);
2)參數說明:
-
conn:已建立的BLE連接句柄
-
params:發現參數結構體,包含以下關鍵字段:
struct bt_gatt_discover_params {const struct bt_uuid *uuid; // 目標UUID(可選過濾條件)uint16_t start_handle; // 起始屬性句柄(通常0x0001)uint16_t end_handle; // 結束屬性句柄(通常0xFFFF)enum bt_gatt_discover_type type; // 發現類型void (*func)(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params);
};
?2.1 發現類型(Discover Type)
類型枚舉值 | 說明 | 對應ATT操作 |
---|---|---|
BT_GATT_DISCOVER_PRIMARY | 發現主服務 | ATT Read By Group Type Req |
BT_GATT_DISCOVER_SECONDARY | 發現次要服務 | ATT Read By Group Type Req |
BT_GATT_DISCOVER_INCLUDE | 發現包含的服務 | ATT Read By Type Req |
BT_GATT_DISCOVER_CHARACTERISTIC | 發現特性 | ATT Read By Type Req |
BT_GATT_DISCOVER_DESCRIPTOR | 發現描述符 | ATT Find Information Req |
BT_GATT_DISCOVER_STD_CHAR_DESC | 發現標準特性描述符 | ATT Read By Type Req |
3?典型使用流程
3.1 服務發現示例
static struct bt_gatt_discover_params discover_params;static void discover_cb(struct bt_conn *conn,const struct bt_gatt_attr *attr,struct bt_gatt_discover_params *params)
{if (!attr) {printk("Discovery complete\n");return;}switch (params->type) {case BT_GATT_DISCOVER_PRIMARY: {struct bt_gatt_service_val *svc = attr->user_data;printk("Service found: start_handle=0x%04X, end_handle=0x%04X\n",attr->handle, svc->end_handle);break;}case BT_GATT_DISCOVER_CHARACTERISTIC: {struct bt_gatt_chrc *chrc = attr->user_data;printk("Characteristic: handle=0x%04X, properties=0x%02X\n",chrc->value_handle, chrc->properties);break;}}
}void start_discovery(struct bt_conn *conn)
{discover_params.uuid = NULL; // 發現所有主服務discover_params.start_handle = 0x0001;discover_params.end_handle = 0xFFFF;discover_params.type = BT_GATT_DISCOVER_PRIMARY;discover_params.func = discover_cb;int err = bt_gatt_discover(conn, &discover_params);if (err) {printk("Discovery failed to start (err %d)\n", err);}
}
3.2 級聯發現模式
static void discover_chars(struct bt_conn *conn, uint16_t start, uint16_t end)
{static struct bt_gatt_discover_params params;params.start_handle = start;params.end_handle = end;params.type = BT_GATT_DISCOVER_CHARACTERISTIC;params.func = discover_cb;bt_gatt_discover(conn, ¶ms);
}static void discover_cb(...)
{if (params->type == BT_GATT_DISCOVER_PRIMARY) {// 發現主服務后繼續發現特性struct bt_gatt_service_val *svc = attr->user_data;discover_chars(conn, attr->handle + 1, svc->end_handle);}
}
?3.3 按UUID過濾發現
static struct bt_uuid_16 find_uuid = BT_UUID_INIT_16(0x180F); // 電池服務void find_battery_service(struct bt_conn *conn)
{discover_params.uuid = &find_uuid.uuid;discover_params.type = BT_GATT_DISCOVER_PRIMARY;bt_gatt_discover(conn, &discover_params);
}
?3.4 發現描述符
void discover_descriptors(struct bt_conn *conn, uint16_t start, uint16_t end)
{discover_params.start_handle = start;discover_params.end_handle = end;discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;bt_gatt_discover(conn, &discover_params);
}
4 錯誤處理?
4.1 常見錯誤碼
錯誤碼 | 說明 |
---|---|
0 | 成功 |
-ENOTCONN | 未建立連接 |
-EINVAL | 無效參數 |
-ENOMEM | 內存不足 |
-EOPNOTSUPP | 操作不支持 |
4.2?錯誤處理示例
int err = bt_gatt_discover(conn, ¶ms);
if (err) {printk("Error %d during discovery\n", err);switch (err) {case -ENOTCONN:// 處理連接問題break;case -EINVAL:// 檢查參數有效性break;}
}
5 性能優化建議
分階段發現:先發現服務,再根據需要發現特性和描述符
緩存發現結果:避免重復發現
合理設置句柄范圍:縮小start_handle/end_handle范圍
使用UUID過濾:減少不必要的發現操作
6 資源管理注意事項
6.1?參數生命周期
// 錯誤:使用棧變量(函數返回后失效)
void start_temp_discovery(struct bt_conn *conn) {struct bt_gatt_discover_params temp_params = {...};bt_gatt_discover(conn, &temp_params); // 危險!
}// 正確:使用靜態或動態分配
static struct bt_gatt_discover_params persistent_params;
6.2?取消發現
void cancel_discovery(struct bt_conn *conn) {bt_gatt_discover_cancel(conn, &discover_params);
}