---
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`