--- title: 'GATT、ATT 屬性' disqus: kyleAlien --- GATT、ATT 屬性 === ## OverView of Content GATT & ATT 關係較為密切,所以一起說明 [TOC] ## GATT & ATT 協議棧位置 GATT (`Generic Attribute Profile`) 通用屬性配置、ATT (`Attribute Protocol`) 屬性協議 通俗一點來說,GATT 定義的是「結構」… 就有點像是 JSON / XML 這種結構,告訴接收到資料端要如何解釋資料 >  ### 傳輸腳色 - Client/Server * **GATT 使用 Client/Server 架構** * Server 端:**提供** 數據端; > 就像是小米智能手環,它提供了使用者的心律、睡眠、運動... 等等訊息,提供數據就是 Server 的腳色 * Client 端:**訪問** 數據端; > 智能手機開啟小米 APP,連接手環,讀取數據就是 Client 的腳色 :::warning * **跟 `Slave`、`Master` 有關係 ?** 1. **Slave、Master 與 C/S 架構 ++沒有絕對關係++,跟誰產生數據、誰接收數據沒關係!** 它要表的是數據的流向 > 就像軟體設計時所謂的高層模組、低層模組,呼叫方(高層模組)就是 `Master`,被呼叫方(低層模組)就是 `Slave` 2. Master 是主動發出 BLE 裝置連接方 (智能手機)、Slave 是被連接方 (小米手錶),**這個狀況是可以反轉的 !** > 可以由小米手錶發出連接,而智能手機被連接 ::: ### 認識 GATT - Profile 框架 * GATT 就是把 ATT 組織成我們在使用來牙時所熟悉的 `Service` / `Characteristic` / `Descriptor` 三元素;GATT 讓資料有以下特性… * **資料的結構化** * Service:功能集合(例如 Heart Rate Service) * Characteristic:資料點(例如 Heart Rate Measurement) * Descriptor:特性設定/說明(例如 CCCD 控制 notify) * **Discovery 探索流程** * 我連上 BLE 之後,怎麼列出你有哪些 services? * 某 service 底下有哪些 characteristics? * 某 characteristic 有哪些 descriptors? 這讓 GATT 有框架與流程的特色,而它定義出的框架稱之為 `Profile` ```mermaid graph LR; subgraph GATT Service Characteristic Descriptor end ``` * **GATT 的 Profile 框架**: 為了 **方便用戶在應用層管理數據**,GATT 定義了這種 GATT Profile 數據框架 (也稱為配置文件),其中有分為 `Profile`、`Server`、`Characteristic`,而這三者類似於一種 **樹形結構**,如下圖 >  而其中 `Characteristic` 又由成員 `Property`、`Value`、`Descriptor` 組成 >  :::info * **GATT 只定義了 Profile 的框架結構,而內部的數據需使用者自己定義**; 凡是根據該框架定義的結構都可稱為 **GATT Based Profile** ::: * 以下舉例小米智能手環提供的心律服務,它也是 Profile 中的一個服務,除了心律服務以外還可以有其他服務 > **如果 BLE Client 需要可主動通知 Server 的功能,`Descriptor` 就需要設置為 `通知開關`** > >  :::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 的編號 (這個編號是 **順序編號**) >  * 實際情況是 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 | >  ### 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/) >  ::: * 以小米手環心律為例,它可以透過 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) | >  ### 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 >  :::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` (類似雙重指標的概念 !) >  * 以小米手環心律為例,拓展其 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 有關 >  * 以小米手環心律為例,拓展其 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 層收到即可 >  2. 須回覆:這邊舉例 `Requeset`,類似 TCP * Client 端發送 REQ 後,需要等待回覆,才可執行下一個命令 * Server 端接收到 REQ 後,需回覆一個 RSP 數據,讓 Client 端 LL 層收到並傳送到應用層的 ATT >  ### PDU 格式協定 * Attribute PDU 傳送格式如下,其中包括 `Opcode`、`Parameters`、`Authentucation Signature` (非必須),**總長度為 `ATT_MTU`** >  | 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` >  * **Property 的特色**: 1. 一個 `Characteristic` 中有多個 2. 用來描述指向的 `Value Attribute`,表示該 `Value Attribute` 的特色,以小米手環的心跳感測為例 Property 代表心跳/分鐘該功能是 `通知 (Notify)` & `可讀 (Read)` >  ### 常用 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`
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.