--- title: 'GATT、ATT 屬性' disqus: kyleAlien --- GATT、ATT 屬性 === ## OverView of Content GATT & ATT 關係較為密切,所以一起說明 [TOC] ## BLE - GATT & ATT 協議棧位置 GATT (`Generic Attribute Profile`) 通用屬性配置、ATT (`Attribute Protocol`) 屬性協議 > ![](https://i.imgur.com/Sux8dwb.png) ### 傳輸腳色 - Client/Server * **GATT 使用 Client/Server 架構** * Server 端:**提供** 數據端; > 就像是小米智能手環,它提供了使用者的心律、睡眠、運動... 等等訊息,提供數據就是 Server 的腳色 * Client 端:**訪問** 數據端; > 智能手機開啟小米 APP,連接手環,讀取數據就是 Client 的腳色 :::warning * **跟 Slave、Master 有關係 ?** 1. **Slave、Master 與 C/S 架構沒有絕對關係,跟誰產生數據、誰接收數據沒關係!** 2. Master 是主動發出 BLE 裝置連接方 (智能手機)、Slave 是被連接方 (小米手錶),**這個狀況是可以反轉的 !** > 可以由小米手錶發出連接,而智能手機被連接 ::: ## GATT - Profile 概念 * **GATT Profile 框架**:為了 **方便用戶在應用層管理數據**,GATT 定義了這種 GATT Profile 數據框架 (也稱為配置文件),其中有分為 `Profile`、`Server`、`Characteristic` `Profile`、`Server`、`Characteristic` 三者類似於一種 **樹形結構**,如下圖 > ![](https://i.imgur.com/SGQU5Qi.png) 而其中 `Characteristic` 又由成員 `Property`、`Value`、`Descriptor` 組成 > ![](https://i.imgur.com/h55xCXg.png) :::info * **GATT 只定義了 Profile 的框架結構,而內部的數據需使用者自己定義**; 凡是根據該框架定義的結構都可稱為 **GATT Based Profile** ::: * 以下舉例小米智能手環提供的心律服務,它也是 Profile 中的一個服務,除了心律服務以外還可以有其他服務 > **如果 BLE Client 需要可主動通知 Server 的功能,`Descriptor` 就需要設置為 `通知開關`** > > ![](https://i.imgur.com/Re1QiWl.png) :::success * [**Bluetooth 聯盟**](https://www.bluetooth.com/specifications/specs/?status=active&show_latest_version=0&show_latest_version=1&keyword=Profile&filter=) 也有提供其他相關的 Profile 可以參考,當然用戶也可以自定義 Profile、Server ::: ### GATT - Profile 單元 * Profile 內所有的子元素 (包括 `Server`、`Characteristic`、`Value`、`Descriptor`) 都稱為 **++Attribute Cache++** (可稱為 Attribute),而這些 **==Attribute 也是傳輸的基本單位== !** :::info Client 端讀取 Attribute 的過程稱為 Discovery ::: * 在介紹 Profile 的時候有說可以自訂定義 `Server`、`Characteristic`,就是在定義記自己需要的 `Attribute` 已上面舉的小米手環心律例子,Attribute 如下 (其實就是遍歷 Profile Tree 結構) | 描述 | 名稱 | Attribute | | - | - | - | | `Service` | 心律 | o | | `Characteristic` | 心跳數據 | o | | `Value` | 心跳/分鐘 | o | | `Descriptor` | 通知開關 | o | | - | - | - | | `Service` | 設備訊息 | o | | `Characteristic` | 軟體版本 | o | | `Value` | 版本號 | o | | - | - | - | | `Characteristic` | 產品序號 | o | | `Value` | 序號 | o | ## ATT - Attribute 組成 ATT 就是只 Attribute,它由四個部分組成: 1. `Handle` 句柄 2. `Type` 類型 3. `Value` 數值 4. `Permission` 權限 (多個) ### Handle 句柄 - Attribute 替代名稱 * **Handle 的主要功能**:**替代名稱進程傳輸** (類似一個 Attribute 的 Nickname) Hanle 的大小為 2 Byte 的編號 (這個編號是 **順序編號**) > ![](https://i.imgur.com/YALoa2h.png) * 實際情況是 BLE 裝置 **最開始的 Handle 數值一般會留給 GAP** (Generic Access Profile),因為 **GAP 是每個藍芽必備的 Profile** :::info * Handle 並非必須數值,Attribute 可以不用特別設定,**空下也可以**,但仍需按照順序 > 可以只留下 Service Attribute 的 Handle,其他不設置 > > 1. 心律 `0x001` > 2. 設備訊息 `0x005` ::: | 描述 | 名稱 | Attribute Handle 數值 | | - | - | - | | `Service` | 心律 | 0x001 | | `Characteristic` | 心跳數據 | 0x002 | | `Value` | 心跳/分鐘 | 0x003 | | `Descriptor` | 通知開關 | 0x004 | | - | - | - | | `Service` | 設備訊息 | 0x005 | | `Characteristic` | 軟體版本 | 0x006 | | `Value` | 版本號 | 0x007 | | - | - | - | | `Characteristic` | 產品序號 | 0x008 | | `Value` | 序號 | 0x009 | > ![](https://i.imgur.com/bLWlKja.png) ### Type 類型 - Attribute 種類 * **Type 的主要功能**:**區分 Attribute 種類** Type 的大小為 16 byte,這 16 byte 又稱為 UUID;UUID 又分為兩類 1. 用戶自訂 UUID:16 byte 2. 藍芽定義 UUID:2 byte (簡化版 UUID),為了達到 **統一格式** * **藍芽的基礎 UUID 為 `0x00000000_00001000_80000080_5F9B34FB`,已這個 UUID 來往下拓展,拓展方式為 `0x0000<藍芽定義 UUID>_00001000_80000080_5F9B34FB`** * 藍芽定義了 `Device Name` 這個 `Characteristic` 的 2 byte 為 `0x2A00`,完整的 UUID 則是 `0x000002A00_00001000_80000080_5F9B34FB` (直接插入尖括號內) * 以下為 GATT Profile 常見的 Type 類型 | Type 名稱 | UUID | 描述 | | - | - | - | | `Primary Service` | 0x2800 | 主服務 | | `Secondary Service` | 0x2801 | 次服務 | | `Include` | 0x2802 | 包括 | | `Characteristic` | 0x2803 | 特徵、特性 | | `Characteristic Extended Properties` | 0x2900 | 拓展屬性特徵 | | `Characteristic User Description` | 0x2901 | 使用者描述特徵 | | `Client Characteristic Configuration` | 0x2902 | Client 端特徵設置 | | `Server Characteristic Configuration` | 0x2903 | Server 端特徵設置 | | `Characteristic Presentation Format` | 0x2904 | 特徵表現格式 | | `Characteristic Aggregate Format` | 0x2905 | 特徵總計格式 | :::success * 詳細的 GATT Profile 的 Type 類型,請看 [**官方文檔**](https://www.bluetooth.com/specifications/assigned-numbers/) > ![](https://i.imgur.com/aaPncUU.png) ::: * 以小米手環心律為例,它可以透過 GATT 定義自己的服務 | 描述 | 名稱 | Attribute Handle | Attribute Type | | - | - | - | - | | `Service` | 心律 | `0x001` | `0x2800` (主服務) | | `Characteristic` | 心跳數據 | `0x002` | `0x2803` (特徵) | | `Value` | 心跳/分鐘 | `0x003` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | | `Descriptor` | 通知開關 | `0x004` | `0x2902` (Client 端特徵設置) | | - | - | - | - | | `Service` | 設備訊息 | `0x005` | `0x2801` (次服務) | | `Characteristic` | 軟體版本 | `0x006` | `0x2803` (特徵) | | `Value` | 版本號 | `0x007` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | | - | - | - | - | | `Characteristic` | 產品序號 | `0x008` | `0x2803` (特徵) | | `Value` | 序號 | `0x009` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | > ![](https://i.imgur.com/iRPyCud.png) ### Value 數值 :::warning 接下來要說明的 **Attribute Value** 不是指定 Characteristic Value;每個 Attribute Value 是每個屬性都有的數值 > Characteristic Value 是 GATT 中的一個 Attribute 的種類 (`0x2803`) ::: * **Attribute Value 是一個 Attribute 中的實際數據**; * 用戶自訂的 UUID 的 Attribute,Value、長度是自訂的 * 對於藍芽定義的 2 Byte UUID 的 Attribute、Value 是要按照規範的 * 每個 Attribute 中都有數據 1. **Service 類型的 Value**:像是 `Primary Service`、`Secondary Service` 的數據 (Attribute Value) 就是 UUID > ![](https://i.imgur.com/hEuYo7w.png) :::info * **Type 是使用 UUID 來描述,這跟 Value 數值相同 ?** Ans:**不同 !** 1. **Type UUID**:是實際意義的 UUID,代表了某個種類 > 向是描述該 Type 是 MainServer 或是其他 2. **Value UUID**:則是代表 **它是某個服務代號** ::: 2. **Characteristic 類型的 Attribute Value**:描述 Properties(多個)、Value Attribute Handle、UUID 其中 `Value Attribute Handle` 指向了一個 Handle,該 Handle 又會指向另一個 `Attribute Value` (類似雙重指標的概念 !) > ![](https://i.imgur.com/Jgmdkqv.png) * 以小米手環心律為例,拓展其 Attribute Value | 描述 | 名稱 | Attribute Handle | Attribute Type | Attribute Value | | - | - | - | - | - | | `Service` | 心律 | `0x001` | `0x2800` (主服務) | 心律 UUID | | `Characteristic` | 心跳數據 | `0x002` | `0x2803` (特徵) | Properties + `0x003` + 心跳 UUID (0x2803) | | `Value` | 心跳/分鐘 | `0x003` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | 實際數值 | | `Descriptor` | 通知開關 | `0x004` | `0x2902` (Client 端特徵設置) | 通知開關 | | - | - | - | - | - | | `Service` | 設備訊息 | `0x005` | `0x2801` (次服務) | 設備訊息 UUID | | `Characteristic` | 軟體版本 | `0x006` | `0x2803` (特徵) | Properties + `0x007` + 心跳 UUID (0x2803) | | Value | 版本號 | `0x007` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | 實際版本號 | | - | - | - | - | - | | `Characteristic` | 產品序號 | `0x008` | `0x2803` (特徵) | Properties + `0x009` + 心跳 UUID (0x2803) | | `Value` | 序號 | `0x009` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | 實際序號 | ### Permission 權限 * **Attribute Permission 是管理 Attribute Value 是否可被讀取**,大部分的 Attribute Permission 都包含 read 權限,也就是可以讓 Client 端讀取 * Characteristic 的 Attribute Properties 通常與 Attribute Permission 有關 > ![](https://i.imgur.com/x3D9o2M.png) * 以小米手環心律為例,拓展其 Attribute Permission | 描述 | 名稱 | Attribute Handle | Attribute Type | Attribute Value | Attribute Permission | | - | - | - | - | - | - | | `Service` | 心律 | `0x001` | `0x2800` (主服務) | 心律 UUID | read | | `Characteristic` | 心跳數據 | `0x002` | `0x2803` (特徵) | Properties + `0x003` + 心跳 UUID (`0x2803`) | read | | `Value` | 心跳/分鐘 | `0x003` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | 實際數值 | read & write | | `Descriptor` | 通知開關 | `0x004` | `0x2902` (Client 端特徵設置) | 通知開關 | read | | - | - | - | - | - | - | | `Service` | 設備訊息 | `0x005` | `0x2801` (次服務) | 設備訊息 UUID | read | | `Characteristic` | 軟體版本 | `0x006` | `0x2803` (特徵) | Properties + `0x007` + 心跳 UUID (`0x2803`) | read | | `Value` | 版本號 | `0x007` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | 實際版本號 | read | | - | - | - | - | - | - | | `Characteristic` | 產品序號 | `0x008` | `0x2803` (特徵) | Properties + `0x009` + 心跳 UUID (`0x2803`) | read | | `Value` | 序號 | `0x009` | 不再範圍內,可用戶自訂 (預設 2 Byte,自訂 16 Byte) | 實際序號 | read | ## Attribute PDU 前面介紹完 Profile (`Attribute Caching`)、Attribute 組成;而 **Attribute PDU 則是 C/S 雙方傳遞數據的 ++協定++** ### PDU 方向協定 * ATT 還定義了 6 種 Attribute PDU 的類型,用來表明該數據要如何被處理 (用於區分 Server/Client 傳輸方向、是否需要回復) | Attribute PDU 類型 | 縮寫 | 方向 | 回覆 | | - | - | - | - | | `Command` | CMD | C -> S | x | | `Requeset` | REQ | C -> S | RSP | | `Response` | PSE | S -> C | x | | `Notification` | NTF | S -> C | x | | `Indication` | IND | S -> C | CFM | | `Confirmation` | CFN | C -> S | x | :::info * 縮寫在 PDU 中還會在前面串上 `ATT_WRITE_PDU_` + `縮寫` ::: * 要不要回覆,這是 ATT 層面的要不要回覆,跟 LL 層無關,而是否需要回覆也跟同步機制很像,類似於 UDP、TCP 機制 1. 不須回覆:這邊舉例 `Command`,類似 UDP * Client 端發送 CMD 後,並不需要等待回覆,就可以直接執行下一個命令 * Server 端接收到 CMD 後,隨意回覆一個數據,讓 Client 端 LL 層收到即可 > ![](https://i.imgur.com/2qqpRyn.png) 2. 須回覆:這邊舉例 `Requeset`,類似 TCP * Client 端發送 REQ 後,需要等待回覆,才可執行下一個命令 * Server 端接收到 REQ 後,需回覆一個 RSP 數據,讓 Client 端 LL 層收到並傳送到應用層的 ATT > ![](https://i.imgur.com/KjmhLFL.png) ### PDU 格式協定 * Attribute PDU 傳送格式如下,其中包括 `Opcode`、`Parameters`、`Authentucation Signature` (非必須),**總長度為 `ATT_MTU`** > ![](https://i.imgur.com/U0tIkDS.png) | Attribute PDU | 大小 (byte) | 功能 | 補充 | | - | - | - | - | | `Opcode` | 1 | 不同操作的 PDU | | | `Parameters` | 0~X | | 總長度 `ATT_MTU` - `Opcode` - `Authentucation Signature` | | `Authentucation Signature` | 12 | 驗證簽名 | 非必須,`ATT_SIGNED_WRITE_CMD` 會用到 | :::success * **`ATT_MTU` 是啥** ? L2CAP 層的 `Maximnm Transmission Unit`,表示著支持 `Attribute PDU` 的傳輸最大 Byte 數量 但 `ATT_MTU` 是跟硬體設備有關,所以通常是固定值 :::warning * **BLE 設備所需的 `ATT_MTU` 最少需要 23 Byte** :::: ::: * Server / Client 透過通訊來調整 `Attribute PDU` 大小 1. Client 端發起連接時,傳送 `Opcode = 0x02` 的 `ATT_EXCHANGE_NTU_REQ` (一個 request **同步** 命令) 命令 2. Server 端接收到指令後通過 ,傳送 `Opcode = 0x03` 的 `ATT_EXCHANGE_NTU_RSP` (一個 request **同步** 命令) 回覆 3. **這段通訊的目的是詢問對方最大的 `ATT_MTU` 大小,接著 ++取最小為兩者通訊單位++** ## 其他 ### Property 概念 :::info * Property 含意 在 ATT 中有定義 PDU 傳輸協定 (CMD、REQ、RSP、NTF、IND),還有各種 Attribute Value 來讀寫 PDU,**GATT 又把 ATT 的這些概念進行了一層封裝,就變成了 Property** ::: * **`Characteristic` 組成**:**至少需要兩個 Attribute 單元組成** 1. **`Characteristic Attribute`**:用來描述自身 2. **`Characteristic Attribute Value`**:它會指向另外一個 Handle,間接指向另外一個 `Attribute` > ![](https://i.imgur.com/COxAWM7.png) * **Property 的特色**: 1. 一個 `Characteristic` 中有多個 2. 用來描述指向的 `Value Attribute`,表示該 `Value Attribute` 的特色,以小米手環的心跳感測為例 Property 代表心跳/分鐘該功能是 `通知 (Notify)` & `可讀 (Read)` > ![](https://i.imgur.com/HYpxjVr.png) ### 常用 Property * 以下為幾種常見的 Property | Property | 發送方 | Client 對 `Characteristic Value` 行為 | Server 回應 | | -------| - | -------- | -------- | | Read | Client | 使用 `ATT_READ_REQ` **讀取** | 收到 Client 請求後回覆 `Characteristic Value` | | Write | Client | 使用 `ATT_READ_REQ` **寫入** | 收到 Client 請求後回覆 `ATT_READ_RSP` | | Write Without Response | Client | 使用 `ATT_READ_CMD` **寫入** | Server 可不回覆 | | Notify | Server | Client 可不用回覆 | Server 使用 `ATT_HANDLE_VALUE_NTF` 發送給 Client | | Indicate | Server | Client 收到回覆 `ATT_HANDLE_VALUE_CFM` | Server 使用 `ATT_HANDLE_VALUE_IND` 發送給 Client | :::info * 如果 Server 有 Notify、Indicate 其中任何一個屬性 **那該 `Characteristic Value Attribute` 後面就必須指向一個 CCCD (Client Characteristic Configuration) 類型的 `Descriptor Attribute`** (**0x2902**) 0 表示開放功能、反知 1 則關閉 ::: ## Appendix & FAQ :::info ::: ###### tags: `BLE`