Core_v6.2 Vol 3 Part F:Attribute Protocol(ATT)逐行学习计划与博客目录索引
0. 前言
我接下来准备系统学习 Bluetooth Core Specification v6.2 中的:
Vol 3, Part F:Attribute Protocol,简称 ATT
这部分是 BLE/GATT 协议栈里非常核心的一层。很多人一开始学 BLE,会直接冲到 GATT Service、Characteristic、Notification、CCCD,结果一抓包看到 ATT_READ_BY_TYPE_REQ、ATT_HANDLE_VALUE_NTF、ATT_ERROR_RSP,瞬间变成"蓝牙考古现场"。
其实 GATT 不是空中楼阁,它下面靠 ATT 托着。
ATT 就像 GATT 世界里的"快递协议":
GATT 说"我要读这个特征值",ATT 就负责把"读哪个 handle、怎么读、怎么回、错了报什么错"这件事讲清楚。
所以这一轮学习,我不打算泛泛而谈,而是准备按 逐行翻译 + 逐段讲解 + 表格整理 + 流程图 + 抓包对应 + 工程实现思考 的方式,完整学习 Vol 3 Part F。
本文作为整个 ATT 学习系列的目录索引,后续每篇文章都会挂在这个目录下。
1. ATT 在蓝牙协议栈中的位置
先看 ATT 在协议栈里的位置:
#mermaid-svg-s5SGWQ7uwt3MXt8E{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s5SGWQ7uwt3MXt8E .error-icon{fill:#552222;}#mermaid-svg-s5SGWQ7uwt3MXt8E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s5SGWQ7uwt3MXt8E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .marker.cross{stroke:#333333;}#mermaid-svg-s5SGWQ7uwt3MXt8E svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s5SGWQ7uwt3MXt8E p{margin:0;}#mermaid-svg-s5SGWQ7uwt3MXt8E .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .cluster-label text{fill:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .cluster-label span{color:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .cluster-label span p{background-color:transparent;}#mermaid-svg-s5SGWQ7uwt3MXt8E .label text,#mermaid-svg-s5SGWQ7uwt3MXt8E span{fill:#333;color:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .node rect,#mermaid-svg-s5SGWQ7uwt3MXt8E .node circle,#mermaid-svg-s5SGWQ7uwt3MXt8E .node ellipse,#mermaid-svg-s5SGWQ7uwt3MXt8E .node polygon,#mermaid-svg-s5SGWQ7uwt3MXt8E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .rough-node .label text,#mermaid-svg-s5SGWQ7uwt3MXt8E .node .label text,#mermaid-svg-s5SGWQ7uwt3MXt8E .image-shape .label,#mermaid-svg-s5SGWQ7uwt3MXt8E .icon-shape .label{text-anchor:middle;}#mermaid-svg-s5SGWQ7uwt3MXt8E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .rough-node .label,#mermaid-svg-s5SGWQ7uwt3MXt8E .node .label,#mermaid-svg-s5SGWQ7uwt3MXt8E .image-shape .label,#mermaid-svg-s5SGWQ7uwt3MXt8E .icon-shape .label{text-align:center;}#mermaid-svg-s5SGWQ7uwt3MXt8E .node.clickable{cursor:pointer;}#mermaid-svg-s5SGWQ7uwt3MXt8E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .arrowheadPath{fill:#333333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s5SGWQ7uwt3MXt8E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s5SGWQ7uwt3MXt8E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s5SGWQ7uwt3MXt8E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s5SGWQ7uwt3MXt8E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .cluster text{fill:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E .cluster span{color:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-s5SGWQ7uwt3MXt8E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s5SGWQ7uwt3MXt8E rect.text{fill:none;stroke-width:0;}#mermaid-svg-s5SGWQ7uwt3MXt8E .icon-shape,#mermaid-svg-s5SGWQ7uwt3MXt8E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s5SGWQ7uwt3MXt8E .icon-shape p,#mermaid-svg-s5SGWQ7uwt3MXt8E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s5SGWQ7uwt3MXt8E .icon-shape .label rect,#mermaid-svg-s5SGWQ7uwt3MXt8E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s5SGWQ7uwt3MXt8E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s5SGWQ7uwt3MXt8E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s5SGWQ7uwt3MXt8E :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Application / Profile
GATT
Generic Attribute Profile
ATT
Attribute Protocol
L2CAP
HCI
LE Link Layer / BR/EDR Controller
PHY / Radio
一句话理解:
GATT 负责定义"数据模型和业务操作",ATT 负责定义"这些操作在协议层怎么传"。
例如:
| GATT 层看到的动作 | ATT 层实际使用的 PDU |
|---|---|
| Exchange MTU | ATT_EXCHANGE_MTU_REQ / RSP |
| Discover Services | ATT_READ_BY_GROUP_TYPE_REQ / RSP |
| Discover Characteristics | ATT_READ_BY_TYPE_REQ / RSP |
| Read Characteristic | ATT_READ_REQ / RSP |
| Read Long Characteristic | ATT_READ_BLOB_REQ / RSP |
| Write Characteristic | ATT_WRITE_REQ / RSP |
| Write Without Response | ATT_WRITE_CMD |
| Long Write / Reliable Write | ATT_PREPARE_WRITE_REQ / RSP + ATT_EXECUTE_WRITE_REQ / RSP |
| Notification | ATT_HANDLE_VALUE_NTF |
| Indication | ATT_HANDLE_VALUE_IND / CFM |
所以学 ATT,不能只背 opcode。
要把它和 GATT Procedure、L2CAP、MTU、安全权限、抓包、代码实现一起串起来。
2. 本系列学习目标
本系列目标不是"看过一遍文档",而是达到下面这些能力:
| 能力 | 目标 |
|---|---|
| 读懂规范原文 | 能逐行解释 ATT 章节里的 shall、may、shall not |
| 看懂 ATT 抓包 | 能从 Wireshark / btmon / HCI snoop 里识别每个 ATT PDU |
| 理解 ATT 数据模型 | 明白 Attribute Type、Handle、Value、Permission 的关系 |
| 理解 ATT bearer | 明白 ATT 如何跑在 L2CAP 上,固定信道和动态信道有何区别 |
| 理解 MTU | 明白 ATT_MTU 如何影响读写、长属性和吞吐 |
| 理解读写流程 | 能解释 Read、Read Blob、Read Multiple、Write、Prepare Write、Execute Write |
| 理解 Server initiated | 能解释 Notification、Indication、Confirmation |
| 理解错误码 | 能根据 ATT_ERROR_RSP 判断问题原因 |
| 理解安全检查 | 能分析 Authentication、Authorization、Encryption、Key Size |
| 能指导实现 | 能写出 ATT Server / Client 的基本处理逻辑 |
3. 学习方法
每篇文章固定采用下面的结构。
text
1. 本节原文范围
2. 逐行翻译
3. 逐段解释
4. 关键术语表
5. PDU / 表格转换为 Markdown
6. Mermaid 流程图
7. 抓包对应
8. 工程实现注意点
9. 常见坑
10. 本篇小结
学习 ATT 不能只"读",还要"拆包"。
因为 ATT 很像蓝牙世界里的快递单:
-
Opcode 是快递类型;
-
Handle 是收件地址;
-
Value 是包裹内容;
-
Error Code 是快递异常;
-
MTU 是快递箱最大尺寸;
-
Permission 是门禁;
-
Encryption 是保安;
-
Authorization 是物业批准;
-
Indication 是"必须签收"的快递;
-
Notification 是"放门口不签收"的快递。
4. 系列文章总规划
整个 Vol 3 Part F 计划拆成 24 篇学习文档。
为什么是 24 篇?
因为 ATT 章节虽然不算 Core 里最长的部分,但它每个 PDU 都非常关键。如果一篇塞太多,就会变成"协议八宝粥",看着热闹,消化困难。
第一部分:ATT 总览与基础概念
第 1 篇:Vol 3 Part F 总览:ATT 到底解决什么问题?
对应章节
-
Part F 标题页
-
Contents
-
1 Introduction
-
1.1 Scope
-
1.3 Conventions
文章目标
搞清楚 ATT 的定位:
-
ATT 是什么;
-
ATT 和 GATT 的关系;
-
ATT 和 L2CAP 的关系;
-
ATT Client / Server 是什么;
-
为什么 ATT 是 GATT 的底层基础。
核心问题
| 问题 | 目标 |
|---|---|
| ATT 解决什么问题? | 发现、读取、写入、通知、指示 Attribute |
| ATT 是应用层吗? | 不是,它是 Host 层协议 |
| ATT 和 GATT 谁在上面? | GATT 在 ATT 上面 |
| ATT 能不能跑在 BR/EDR? | 可以,ATT/GATT 不是 LE 专属 |
| BLE 为什么强依赖 ATT/GATT? | BLE 使用 GATT 进行服务发现和数据交互 |
输出物
#mermaid-svg-PSRFpNwth87ccYRr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PSRFpNwth87ccYRr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PSRFpNwth87ccYRr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PSRFpNwth87ccYRr .error-icon{fill:#552222;}#mermaid-svg-PSRFpNwth87ccYRr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PSRFpNwth87ccYRr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PSRFpNwth87ccYRr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PSRFpNwth87ccYRr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PSRFpNwth87ccYRr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PSRFpNwth87ccYRr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PSRFpNwth87ccYRr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PSRFpNwth87ccYRr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PSRFpNwth87ccYRr .marker.cross{stroke:#333333;}#mermaid-svg-PSRFpNwth87ccYRr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PSRFpNwth87ccYRr p{margin:0;}#mermaid-svg-PSRFpNwth87ccYRr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PSRFpNwth87ccYRr .cluster-label text{fill:#333;}#mermaid-svg-PSRFpNwth87ccYRr .cluster-label span{color:#333;}#mermaid-svg-PSRFpNwth87ccYRr .cluster-label span p{background-color:transparent;}#mermaid-svg-PSRFpNwth87ccYRr .label text,#mermaid-svg-PSRFpNwth87ccYRr span{fill:#333;color:#333;}#mermaid-svg-PSRFpNwth87ccYRr .node rect,#mermaid-svg-PSRFpNwth87ccYRr .node circle,#mermaid-svg-PSRFpNwth87ccYRr .node ellipse,#mermaid-svg-PSRFpNwth87ccYRr .node polygon,#mermaid-svg-PSRFpNwth87ccYRr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PSRFpNwth87ccYRr .rough-node .label text,#mermaid-svg-PSRFpNwth87ccYRr .node .label text,#mermaid-svg-PSRFpNwth87ccYRr .image-shape .label,#mermaid-svg-PSRFpNwth87ccYRr .icon-shape .label{text-anchor:middle;}#mermaid-svg-PSRFpNwth87ccYRr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PSRFpNwth87ccYRr .rough-node .label,#mermaid-svg-PSRFpNwth87ccYRr .node .label,#mermaid-svg-PSRFpNwth87ccYRr .image-shape .label,#mermaid-svg-PSRFpNwth87ccYRr .icon-shape .label{text-align:center;}#mermaid-svg-PSRFpNwth87ccYRr .node.clickable{cursor:pointer;}#mermaid-svg-PSRFpNwth87ccYRr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PSRFpNwth87ccYRr .arrowheadPath{fill:#333333;}#mermaid-svg-PSRFpNwth87ccYRr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PSRFpNwth87ccYRr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PSRFpNwth87ccYRr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PSRFpNwth87ccYRr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PSRFpNwth87ccYRr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PSRFpNwth87ccYRr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PSRFpNwth87ccYRr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PSRFpNwth87ccYRr .cluster text{fill:#333;}#mermaid-svg-PSRFpNwth87ccYRr .cluster span{color:#333;}#mermaid-svg-PSRFpNwth87ccYRr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PSRFpNwth87ccYRr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PSRFpNwth87ccYRr rect.text{fill:none;stroke-width:0;}#mermaid-svg-PSRFpNwth87ccYRr .icon-shape,#mermaid-svg-PSRFpNwth87ccYRr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PSRFpNwth87ccYRr .icon-shape p,#mermaid-svg-PSRFpNwth87ccYRr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PSRFpNwth87ccYRr .icon-shape .label rect,#mermaid-svg-PSRFpNwth87ccYRr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PSRFpNwth87ccYRr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PSRFpNwth87ccYRr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PSRFpNwth87ccYRr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GATT Procedure
ATT PDU
L2CAP Channel
LE / BR/EDR Link
第 2 篇:Protocol Overview:ATT 协议模型
对应章节
- 2 Protocol overview
文章目标
从整体上理解 ATT 的通信模型。
核心问题
| 问题 | 说明 |
|---|---|
| 谁是 Client? | 发起 Request / Command 的一方 |
| 谁是 Server? | 保存 Attribute 并响应访问的一方 |
| Client 和 Server 是否固定? | 不固定,一个设备可以同时是 Client 和 Server |
| ATT 是点对点还是广播? | ATT 是基于 bearer 的点对点交互 |
| ATT 和 Attribute Database 是什么关系? | Server 维护 Attribute Database,Client 访问它 |
计划图
ATT Server ATT Client ATT Server ATT Client #mermaid-svg-BFSE96RvE55C74lY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BFSE96RvE55C74lY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BFSE96RvE55C74lY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BFSE96RvE55C74lY .error-icon{fill:#552222;}#mermaid-svg-BFSE96RvE55C74lY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BFSE96RvE55C74lY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BFSE96RvE55C74lY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BFSE96RvE55C74lY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BFSE96RvE55C74lY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BFSE96RvE55C74lY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BFSE96RvE55C74lY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BFSE96RvE55C74lY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BFSE96RvE55C74lY .marker.cross{stroke:#333333;}#mermaid-svg-BFSE96RvE55C74lY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BFSE96RvE55C74lY p{margin:0;}#mermaid-svg-BFSE96RvE55C74lY .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BFSE96RvE55C74lY text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-BFSE96RvE55C74lY .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BFSE96RvE55C74lY .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-BFSE96RvE55C74lY .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-BFSE96RvE55C74lY .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-BFSE96RvE55C74lY #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-BFSE96RvE55C74lY .sequenceNumber{fill:white;}#mermaid-svg-BFSE96RvE55C74lY #sequencenumber{fill:#333;}#mermaid-svg-BFSE96RvE55C74lY #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-BFSE96RvE55C74lY .messageText{fill:#333;stroke:none;}#mermaid-svg-BFSE96RvE55C74lY .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BFSE96RvE55C74lY .labelText,#mermaid-svg-BFSE96RvE55C74lY .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-BFSE96RvE55C74lY .loopText,#mermaid-svg-BFSE96RvE55C74lY .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-BFSE96RvE55C74lY .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BFSE96RvE55C74lY .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-BFSE96RvE55C74lY .noteText,#mermaid-svg-BFSE96RvE55C74lY .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-BFSE96RvE55C74lY .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BFSE96RvE55C74lY .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BFSE96RvE55C74lY .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BFSE96RvE55C74lY .actorPopupMenu{position:absolute;}#mermaid-svg-BFSE96RvE55C74lY .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-BFSE96RvE55C74lY .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BFSE96RvE55C74lY .actor-man circle,#mermaid-svg-BFSE96RvE55C74lY line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-BFSE96RvE55C74lY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Request / CommandResponse / Notification / Indication
第 3 篇:Protocol Requirements 总览:ATT 规范里的强制要求
对应章节
-
3 Protocol requirements
-
3.1 Introduction
文章目标
掌握 ATT 的基本规则,尤其是规范里的 shall、may、shall not。
核心问题
| 问题 | 说明 |
|---|---|
| Attribute 如何被访问? | 通过 Attribute Handle |
| Attribute 如何被识别? | 通过 Attribute Type,即 UUID |
| Attribute 是否可以重复? | 同一种 Attribute Type 可以出现多次 |
| 权限在哪里生效? | Attribute Permissions |
| 安全要求在哪里体现? | Authentication、Authorization、Encryption、Key Size |
第 4 篇:Attribute Type:UUID 是 Attribute 的身份证
对应章节
- 3.2.1 Attribute type
文章目标
理解 Attribute Type 和 UUID 的关系。
核心内容
| 概念 | 说明 |
|---|---|
| Attribute Type | 属性类型 |
| UUID | 通用唯一标识 |
| 16-bit UUID | Bluetooth SIG 分配的短 UUID |
| 32-bit UUID | 需要转换为 128-bit |
| 128-bit UUID | Vendor Specific UUID |
| Assigned Numbers | 官方编号表 |
工程思考
在代码里,Attribute Type 往往会设计成:
c
typedef struct {
uint8_t type; // 16-bit or 128-bit
uint8_t uuid[16];
} att_uuid_t;
常见坑
-
不要把 Handle 当 UUID;
-
不要把 Characteristic UUID 和 Declaration UUID 混在一起;
-
Vendor UUID 字节序很容易看花眼,蓝牙小端序能让新手怀疑人生。
第 5 篇:Attribute Handle:ATT 世界里的门牌号
对应章节
- 3.2.2 Attribute handle
文章目标
理解 Handle 的作用、范围、唯一性和生命周期。
核心内容
| 项 | 说明 |
|---|---|
| Handle 长度 | 16 bit |
| 有效范围 | 0x0001 到 0xFFFF |
0x0000 |
保留 |
| 唯一性 | 同一个 Server 上 handle 唯一 |
| 排序规则 | Attribute 按 handle 递增排列 |
| 访问方式 | Client 通过 handle 访问 Attribute |
示例
text
Handle 0x0001: Primary Service Declaration
Handle 0x0002: Characteristic Declaration
Handle 0x0003: Characteristic Value
Handle 0x0004: CCCD
工程思考
GATT DB 中最核心的字段就是 handle。
c
typedef struct {
uint16_t handle;
att_uuid_t type;
uint8_t *value;
uint16_t value_len;
uint16_t permissions;
} att_attr_t;
第 6 篇:Attribute Grouping、Value、Permissions
对应章节
-
3.2.3 Attribute handle grouping
-
3.2.4 Attribute value
-
3.2.5 Attribute permissions
文章目标
理解 Attribute 的完整结构。
Attribute 逻辑结构
#mermaid-svg-reKX0CA4djpGsaus{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-reKX0CA4djpGsaus .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-reKX0CA4djpGsaus .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-reKX0CA4djpGsaus .error-icon{fill:#552222;}#mermaid-svg-reKX0CA4djpGsaus .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-reKX0CA4djpGsaus .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-reKX0CA4djpGsaus .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-reKX0CA4djpGsaus .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-reKX0CA4djpGsaus .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-reKX0CA4djpGsaus .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-reKX0CA4djpGsaus .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-reKX0CA4djpGsaus .marker{fill:#333333;stroke:#333333;}#mermaid-svg-reKX0CA4djpGsaus .marker.cross{stroke:#333333;}#mermaid-svg-reKX0CA4djpGsaus svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-reKX0CA4djpGsaus p{margin:0;}#mermaid-svg-reKX0CA4djpGsaus .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-reKX0CA4djpGsaus .cluster-label text{fill:#333;}#mermaid-svg-reKX0CA4djpGsaus .cluster-label span{color:#333;}#mermaid-svg-reKX0CA4djpGsaus .cluster-label span p{background-color:transparent;}#mermaid-svg-reKX0CA4djpGsaus .label text,#mermaid-svg-reKX0CA4djpGsaus span{fill:#333;color:#333;}#mermaid-svg-reKX0CA4djpGsaus .node rect,#mermaid-svg-reKX0CA4djpGsaus .node circle,#mermaid-svg-reKX0CA4djpGsaus .node ellipse,#mermaid-svg-reKX0CA4djpGsaus .node polygon,#mermaid-svg-reKX0CA4djpGsaus .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-reKX0CA4djpGsaus .rough-node .label text,#mermaid-svg-reKX0CA4djpGsaus .node .label text,#mermaid-svg-reKX0CA4djpGsaus .image-shape .label,#mermaid-svg-reKX0CA4djpGsaus .icon-shape .label{text-anchor:middle;}#mermaid-svg-reKX0CA4djpGsaus .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-reKX0CA4djpGsaus .rough-node .label,#mermaid-svg-reKX0CA4djpGsaus .node .label,#mermaid-svg-reKX0CA4djpGsaus .image-shape .label,#mermaid-svg-reKX0CA4djpGsaus .icon-shape .label{text-align:center;}#mermaid-svg-reKX0CA4djpGsaus .node.clickable{cursor:pointer;}#mermaid-svg-reKX0CA4djpGsaus .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-reKX0CA4djpGsaus .arrowheadPath{fill:#333333;}#mermaid-svg-reKX0CA4djpGsaus .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-reKX0CA4djpGsaus .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-reKX0CA4djpGsaus .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-reKX0CA4djpGsaus .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-reKX0CA4djpGsaus .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-reKX0CA4djpGsaus .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-reKX0CA4djpGsaus .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-reKX0CA4djpGsaus .cluster text{fill:#333;}#mermaid-svg-reKX0CA4djpGsaus .cluster span{color:#333;}#mermaid-svg-reKX0CA4djpGsaus div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-reKX0CA4djpGsaus .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-reKX0CA4djpGsaus rect.text{fill:none;stroke-width:0;}#mermaid-svg-reKX0CA4djpGsaus .icon-shape,#mermaid-svg-reKX0CA4djpGsaus .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-reKX0CA4djpGsaus .icon-shape p,#mermaid-svg-reKX0CA4djpGsaus .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-reKX0CA4djpGsaus .icon-shape .label rect,#mermaid-svg-reKX0CA4djpGsaus .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-reKX0CA4djpGsaus .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-reKX0CA4djpGsaus .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-reKX0CA4djpGsaus :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Attribute
Handle
Type / UUID
Value
Permissions
核心问题
| 问题 | 说明 |
|---|---|
| Grouping 是什么? | 用一个起始 Attribute 表示一组 Attribute |
| Value 是什么? | 字节数组,对 ATT 来说是 opaque data |
| Permission 控制什么? | 读、写、加密、认证、授权等 |
| Permission 是 ATT PDU 字段吗? | 不是,通常是 Server 本地数据库属性 |
常见坑
ATT 只负责搬运 value,不理解 value 的业务含义。
就像快递员不关心你箱子里是键盘还是榴莲,他只负责送到。
第 7 篇:Control-point Attributes 与 Protocol Methods
对应章节
-
3.2.6 Control-point attributes
-
3.2.7 Protocol methods
文章目标
理解控制点属性和 ATT 方法分类。
核心内容
| 类型 | 说明 |
|---|---|
| Control-point Attribute | 不能读,只能写、通知或指示的控制属性 |
| Command | 不需要响应 |
| Request | 需要响应 |
| Response | 对 Request 的响应 |
| Notification | Server 主动发,不需要确认 |
| Indication | Server 主动发,需要确认 |
| Confirmation | Client 对 Indication 的确认 |
示例
text
写入某个控制点 Characteristic:
Client -> Server: ATT_WRITE_REQ / ATT_WRITE_CMD
Server 执行动作
Server -> Client: ATT_HANDLE_VALUE_IND / NTF 表示完成或状态变化
第 8 篇:ATT_MTU:协议包裹箱到底有多大?
对应章节
-
3.2.8 Exchanging MTU size
-
3.4.2 MTU exchange
-
3.4.2.1 ATT_EXCHANGE_MTU_REQ
-
3.4.2.2 ATT_EXCHANGE_MTU_RSP
文章目标
理解 ATT_MTU 的意义、交换流程和工程影响。
核心问题
| 问题 | 说明 |
|---|---|
| ATT_MTU 是什么? | ATT PDU 的最大长度 |
| 默认 MTU 是多少? | 通常默认值由上层定义,BLE 常见默认 23 |
| 实际使用 MTU 怎么确定? | 双方 Rx MTU 取最小值 |
| MTU 对吞吐有什么影响? | 影响单个 ATT PDU 可承载的 value 长度 |
| MTU 是否等于 L2CAP MTU? | 固定信道和动态信道情况下处理不同 |
MTU 流程
Device B Device A Device B Device A #mermaid-svg-UPX3vJHgP5lYq51I{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UPX3vJHgP5lYq51I .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UPX3vJHgP5lYq51I .error-icon{fill:#552222;}#mermaid-svg-UPX3vJHgP5lYq51I .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UPX3vJHgP5lYq51I .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UPX3vJHgP5lYq51I .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UPX3vJHgP5lYq51I .marker.cross{stroke:#333333;}#mermaid-svg-UPX3vJHgP5lYq51I svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UPX3vJHgP5lYq51I p{margin:0;}#mermaid-svg-UPX3vJHgP5lYq51I .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UPX3vJHgP5lYq51I text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-UPX3vJHgP5lYq51I .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UPX3vJHgP5lYq51I .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-UPX3vJHgP5lYq51I .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-UPX3vJHgP5lYq51I .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-UPX3vJHgP5lYq51I #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-UPX3vJHgP5lYq51I .sequenceNumber{fill:white;}#mermaid-svg-UPX3vJHgP5lYq51I #sequencenumber{fill:#333;}#mermaid-svg-UPX3vJHgP5lYq51I #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-UPX3vJHgP5lYq51I .messageText{fill:#333;stroke:none;}#mermaid-svg-UPX3vJHgP5lYq51I .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UPX3vJHgP5lYq51I .labelText,#mermaid-svg-UPX3vJHgP5lYq51I .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-UPX3vJHgP5lYq51I .loopText,#mermaid-svg-UPX3vJHgP5lYq51I .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-UPX3vJHgP5lYq51I .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UPX3vJHgP5lYq51I .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-UPX3vJHgP5lYq51I .noteText,#mermaid-svg-UPX3vJHgP5lYq51I .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-UPX3vJHgP5lYq51I .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UPX3vJHgP5lYq51I .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UPX3vJHgP5lYq51I .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UPX3vJHgP5lYq51I .actorPopupMenu{position:absolute;}#mermaid-svg-UPX3vJHgP5lYq51I .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-UPX3vJHgP5lYq51I .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UPX3vJHgP5lYq51I .actor-man circle,#mermaid-svg-UPX3vJHgP5lYq51I line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-UPX3vJHgP5lYq51I :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Effective ATT_MTU = min(A Rx MTU, B Rx MTU) ATT_EXCHANGE_MTU_REQ(Client Rx MTU)ATT_EXCHANGE_MTU_RSP(Server Rx MTU)
工程公式
text
Read Response 最大 Value 长度 = ATT_MTU - 1
Write Request 最大 Value 长度 = ATT_MTU - 3
Notification 最大 Value 长度 = ATT_MTU - 3
Indication 最大 Value 长度 = ATT_MTU - 3
Signed Write Command 最大 Value 长度 = ATT_MTU - 15
第 9 篇:Long Attribute Values 与 Atomic Operations
对应章节
-
3.2.9 Long attribute values
-
3.2.10 Atomic operations
文章目标
理解长属性读写和原子操作。
核心内容
| 概念 | 说明 |
|---|---|
| Long Attribute | 一个 PDU 装不下的 Attribute Value |
| Read Blob | 分段读取长属性 |
| Prepare Write | 分段准备写入 |
| Execute Write | 执行或取消队列写入 |
| Atomic Operation | 单个 Request/Command 不应被其他 bearer 干扰 |
| 最大 Attribute Value 长度 | 512 octets |
常见坑
-
ATT_READ_REQ只能读开头部分; -
ATT_READ_BLOB_REQ需要 offset; -
长写不是一个包搞定;
-
多 bearer 并发时,要注意属性访问的原子性;
-
长属性读写时,上层要处理 value 变化的一致性。
第 10 篇:ATT Bearer:ATT 怎么跑在 L2CAP 上?
对应章节
- 3.2.11 ATT bearers
文章目标
理解 ATT bearer、L2CAP fixed channel、dynamic channel、Enhanced ATT bearer。
核心内容
| 概念 | 说明 |
|---|---|
| ATT bearer | 用于发送 ATT PDU 的 L2CAP channel |
| LE fixed channel | LE Attribute Protocol fixed channel |
| Dynamic channel | 动态分配的 L2CAP channel |
| Unenhanced ATT bearer | 普通 ATT bearer |
| Enhanced ATT bearer | 基于增强 L2CAP 模式的 ATT bearer |
| EATT | Enhanced ATT,多 bearer 并发能力基础 |
图示
#mermaid-svg-RmYWbrfgPI3kKaWy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RmYWbrfgPI3kKaWy .error-icon{fill:#552222;}#mermaid-svg-RmYWbrfgPI3kKaWy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RmYWbrfgPI3kKaWy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RmYWbrfgPI3kKaWy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RmYWbrfgPI3kKaWy .marker.cross{stroke:#333333;}#mermaid-svg-RmYWbrfgPI3kKaWy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RmYWbrfgPI3kKaWy p{margin:0;}#mermaid-svg-RmYWbrfgPI3kKaWy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy .cluster-label text{fill:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy .cluster-label span{color:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy .cluster-label span p{background-color:transparent;}#mermaid-svg-RmYWbrfgPI3kKaWy .label text,#mermaid-svg-RmYWbrfgPI3kKaWy span{fill:#333;color:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy .node rect,#mermaid-svg-RmYWbrfgPI3kKaWy .node circle,#mermaid-svg-RmYWbrfgPI3kKaWy .node ellipse,#mermaid-svg-RmYWbrfgPI3kKaWy .node polygon,#mermaid-svg-RmYWbrfgPI3kKaWy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RmYWbrfgPI3kKaWy .rough-node .label text,#mermaid-svg-RmYWbrfgPI3kKaWy .node .label text,#mermaid-svg-RmYWbrfgPI3kKaWy .image-shape .label,#mermaid-svg-RmYWbrfgPI3kKaWy .icon-shape .label{text-anchor:middle;}#mermaid-svg-RmYWbrfgPI3kKaWy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RmYWbrfgPI3kKaWy .rough-node .label,#mermaid-svg-RmYWbrfgPI3kKaWy .node .label,#mermaid-svg-RmYWbrfgPI3kKaWy .image-shape .label,#mermaid-svg-RmYWbrfgPI3kKaWy .icon-shape .label{text-align:center;}#mermaid-svg-RmYWbrfgPI3kKaWy .node.clickable{cursor:pointer;}#mermaid-svg-RmYWbrfgPI3kKaWy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RmYWbrfgPI3kKaWy .arrowheadPath{fill:#333333;}#mermaid-svg-RmYWbrfgPI3kKaWy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RmYWbrfgPI3kKaWy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RmYWbrfgPI3kKaWy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RmYWbrfgPI3kKaWy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RmYWbrfgPI3kKaWy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RmYWbrfgPI3kKaWy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RmYWbrfgPI3kKaWy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RmYWbrfgPI3kKaWy .cluster text{fill:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy .cluster span{color:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RmYWbrfgPI3kKaWy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RmYWbrfgPI3kKaWy rect.text{fill:none;stroke-width:0;}#mermaid-svg-RmYWbrfgPI3kKaWy .icon-shape,#mermaid-svg-RmYWbrfgPI3kKaWy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RmYWbrfgPI3kKaWy .icon-shape p,#mermaid-svg-RmYWbrfgPI3kKaWy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RmYWbrfgPI3kKaWy .icon-shape .label rect,#mermaid-svg-RmYWbrfgPI3kKaWy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RmYWbrfgPI3kKaWy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RmYWbrfgPI3kKaWy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RmYWbrfgPI3kKaWy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT PDU
LE Fixed ATT Channel
L2CAP Dynamic Channel
Enhanced ATT Bearer
Unenhanced ATT Bearer
第二部分:ATT PDU 与事务模型
第 11 篇:Attribute PDU 总览:六大类型
对应章节
- 3.3 Attribute PDU
文章目标
理解 ATT PDU 的六种类型。
六大类型
| 类型 | 后缀 | 是否需要响应/确认 | 方向 |
|---|---|---|---|
| Command | CMD | 不需要 Response | Client → Server |
| Request | REQ | 需要 Response | Client → Server |
| Response | RSP | 对 Request 的响应 | Server → Client |
| Notification | NTF | 不需要 Confirmation | Server → Client |
| Indication | IND | 需要 Confirmation | Server → Client |
| Confirmation | CFM | 对 Indication 的确认 | Client → Server |
图示
Server Client Server Client #mermaid-svg-4Em7zMvTt4iWAfc6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4Em7zMvTt4iWAfc6 .error-icon{fill:#552222;}#mermaid-svg-4Em7zMvTt4iWAfc6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4Em7zMvTt4iWAfc6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4Em7zMvTt4iWAfc6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4Em7zMvTt4iWAfc6 .marker.cross{stroke:#333333;}#mermaid-svg-4Em7zMvTt4iWAfc6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4Em7zMvTt4iWAfc6 p{margin:0;}#mermaid-svg-4Em7zMvTt4iWAfc6 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4Em7zMvTt4iWAfc6 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4Em7zMvTt4iWAfc6 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4Em7zMvTt4iWAfc6 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4Em7zMvTt4iWAfc6 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4Em7zMvTt4iWAfc6 .sequenceNumber{fill:white;}#mermaid-svg-4Em7zMvTt4iWAfc6 #sequencenumber{fill:#333;}#mermaid-svg-4Em7zMvTt4iWAfc6 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4Em7zMvTt4iWAfc6 .messageText{fill:#333;stroke:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4Em7zMvTt4iWAfc6 .labelText,#mermaid-svg-4Em7zMvTt4iWAfc6 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .loopText,#mermaid-svg-4Em7zMvTt4iWAfc6 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4Em7zMvTt4iWAfc6 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4Em7zMvTt4iWAfc6 .noteText,#mermaid-svg-4Em7zMvTt4iWAfc6 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4Em7zMvTt4iWAfc6 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4Em7zMvTt4iWAfc6 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4Em7zMvTt4iWAfc6 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4Em7zMvTt4iWAfc6 .actorPopupMenu{position:absolute;}#mermaid-svg-4Em7zMvTt4iWAfc6 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-4Em7zMvTt4iWAfc6 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4Em7zMvTt4iWAfc6 .actor-man circle,#mermaid-svg-4Em7zMvTt4iWAfc6 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4Em7zMvTt4iWAfc6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Command,不要回复Request,需要回复ResponseNotification,不要确认Indication,需要确认Confirmation
第 12 篇:Attribute PDU Format、Sequential Protocol、Transaction
对应章节
-
3.3.1 Attribute PDU format
-
3.3.2 Sequential protocol
-
3.3.3 Transaction
文章目标
理解 ATT PDU 格式和事务模型。
ATT PDU 基本结构
text
+--------+----------------------+--------------------------+
| Opcode | Attribute Parameters | Authentication Signature |
+--------+----------------------+--------------------------+
核心问题
| 问题 | 说明 |
|---|---|
| Opcode 多大? | 1 octet |
| Parameters 是否固定? | 根据 PDU 类型变化 |
| Signature 什么时候存在? | Signed Write Command 等场景 |
| Sequential Protocol 是什么? | 一个 bearer 上的事务需要按顺序处理 |
| Transaction 是什么? | Request 与 Response 构成一次事务 |
工程坑
一个 ATT bearer 上不能无限并发 Request。
想并发?上 EATT,多开 bearer。
不然就像一个窗口排队办业务,后面的人再急,也得等前面那个大爷办完。
第三部分:ATT Protocol PDUs 逐个学习
第 13 篇:Error Handling:ATT_ERROR_RSP
对应章节
-
3.4.1 Error handling
-
3.4.1.1 ATT_ERROR_RSP
文章目标
掌握 ATT 错误响应格式和错误码定位方法。
ATT_ERROR_RSP 典型字段
| 字段 | 说明 |
|---|---|
| Attribute Opcode | 0x01 |
| Request Opcode In Error | 出错的请求 opcode |
| Attribute Handle In Error | 出错 handle |
| Error Code | 错误码 |
常见错误码
| 错误码 | 含义 |
|---|---|
0x01 |
Invalid Handle |
0x02 |
Read Not Permitted |
0x03 |
Write Not Permitted |
0x04 |
Invalid PDU |
0x05 |
Insufficient Authentication |
0x06 |
Request Not Supported |
0x07 |
Invalid Offset |
0x08 |
Insufficient Authorization |
0x09 |
Prepare Queue Full |
0x0A |
Attribute Not Found |
0x0B |
Attribute Not Long |
0x0C |
Encryption Key Size Too Short |
0x0D |
Invalid Attribute Value Length |
0x0E |
Unlikely Error |
0x0F |
Insufficient Encryption |
0x10 |
Unsupported Group Type |
0x11 |
Insufficient Resources |
0x12 |
Database Out Of Sync |
工程思考
看到 ATT_ERROR_RSP 不要慌。
它不是协议崩了,而是 Server 在跟你说:
"哥们,你这个请求我处理不了,原因写在 Error Code 里了。"
第 14 篇:Find Information:发现 Attribute Handle 与 Type
对应章节
-
3.4.3 Find information
-
3.4.3.1 ATT_FIND_INFORMATION_REQ
-
3.4.3.2 ATT_FIND_INFORMATION_RSP
文章目标
理解如何发现 Attribute Handle 与 Attribute Type 的映射。
典型用途
GATT 中 Discover All Characteristic Descriptors 常用它。
流程
Server Client Server Client #mermaid-svg-AQlhFr4Ra80QlL18{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AQlhFr4Ra80QlL18 .error-icon{fill:#552222;}#mermaid-svg-AQlhFr4Ra80QlL18 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AQlhFr4Ra80QlL18 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AQlhFr4Ra80QlL18 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AQlhFr4Ra80QlL18 .marker.cross{stroke:#333333;}#mermaid-svg-AQlhFr4Ra80QlL18 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AQlhFr4Ra80QlL18 p{margin:0;}#mermaid-svg-AQlhFr4Ra80QlL18 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-AQlhFr4Ra80QlL18 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-AQlhFr4Ra80QlL18 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-AQlhFr4Ra80QlL18 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-AQlhFr4Ra80QlL18 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-AQlhFr4Ra80QlL18 .sequenceNumber{fill:white;}#mermaid-svg-AQlhFr4Ra80QlL18 #sequencenumber{fill:#333;}#mermaid-svg-AQlhFr4Ra80QlL18 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-AQlhFr4Ra80QlL18 .messageText{fill:#333;stroke:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-AQlhFr4Ra80QlL18 .labelText,#mermaid-svg-AQlhFr4Ra80QlL18 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .loopText,#mermaid-svg-AQlhFr4Ra80QlL18 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-AQlhFr4Ra80QlL18 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-AQlhFr4Ra80QlL18 .noteText,#mermaid-svg-AQlhFr4Ra80QlL18 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-AQlhFr4Ra80QlL18 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-AQlhFr4Ra80QlL18 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-AQlhFr4Ra80QlL18 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-AQlhFr4Ra80QlL18 .actorPopupMenu{position:absolute;}#mermaid-svg-AQlhFr4Ra80QlL18 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-AQlhFr4Ra80QlL18 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-AQlhFr4Ra80QlL18 .actor-man circle,#mermaid-svg-AQlhFr4Ra80QlL18 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-AQlhFr4Ra80QlL18 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_FIND_INFORMATION_REQ(start, end)ATT_FIND_INFORMATION_RSP(format, handle-uuid list)
工程重点
| 字段 | 说明 |
|---|---|
| Starting Handle | 起始 handle |
| Ending Handle | 结束 handle |
| Format | 返回 UUID 是 16-bit 还是 128-bit |
| Information Data | Handle + UUID 列表 |
第 15 篇:Find By Type Value:按类型和值查找 Attribute
对应章节
-
3.4.3.3 ATT_FIND_BY_TYPE_VALUE_REQ
-
3.4.3.4 ATT_FIND_BY_TYPE_VALUE_RSP
文章目标
理解按 Attribute Type 和 Attribute Value 查找 handle range。
典型用途
GATT 中 Discover Primary Service by Service UUID。
流程
Server Client Server Client #mermaid-svg-w51G3F4ixxh3lOXL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-w51G3F4ixxh3lOXL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-w51G3F4ixxh3lOXL .error-icon{fill:#552222;}#mermaid-svg-w51G3F4ixxh3lOXL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-w51G3F4ixxh3lOXL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-w51G3F4ixxh3lOXL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-w51G3F4ixxh3lOXL .marker.cross{stroke:#333333;}#mermaid-svg-w51G3F4ixxh3lOXL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-w51G3F4ixxh3lOXL p{margin:0;}#mermaid-svg-w51G3F4ixxh3lOXL .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-w51G3F4ixxh3lOXL text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-w51G3F4ixxh3lOXL .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-w51G3F4ixxh3lOXL .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-w51G3F4ixxh3lOXL .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-w51G3F4ixxh3lOXL .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-w51G3F4ixxh3lOXL #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-w51G3F4ixxh3lOXL .sequenceNumber{fill:white;}#mermaid-svg-w51G3F4ixxh3lOXL #sequencenumber{fill:#333;}#mermaid-svg-w51G3F4ixxh3lOXL #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-w51G3F4ixxh3lOXL .messageText{fill:#333;stroke:none;}#mermaid-svg-w51G3F4ixxh3lOXL .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-w51G3F4ixxh3lOXL .labelText,#mermaid-svg-w51G3F4ixxh3lOXL .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-w51G3F4ixxh3lOXL .loopText,#mermaid-svg-w51G3F4ixxh3lOXL .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-w51G3F4ixxh3lOXL .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-w51G3F4ixxh3lOXL .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-w51G3F4ixxh3lOXL .noteText,#mermaid-svg-w51G3F4ixxh3lOXL .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-w51G3F4ixxh3lOXL .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-w51G3F4ixxh3lOXL .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-w51G3F4ixxh3lOXL .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-w51G3F4ixxh3lOXL .actorPopupMenu{position:absolute;}#mermaid-svg-w51G3F4ixxh3lOXL .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-w51G3F4ixxh3lOXL .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-w51G3F4ixxh3lOXL .actor-man circle,#mermaid-svg-w51G3F4ixxh3lOXL line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-w51G3F4ixxh3lOXL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_FIND_BY_TYPE_VALUE_REQ(start, end, type, value)ATT_FIND_BY_TYPE_VALUE_RSP(handle information list)
工程重点
这个 PDU 有点像数据库查询:
sql
SELECT handle_range
FROM attribute_db
WHERE type = xxx AND value = yyy;
第 16 篇:Read By Type:按 UUID 读取 Attribute
对应章节
-
3.4.4.1 ATT_READ_BY_TYPE_REQ
-
3.4.4.2 ATT_READ_BY_TYPE_RSP
文章目标
理解按 Attribute Type 读取 attribute value 的机制。
典型用途
| GATT Procedure | ATT PDU |
|---|---|
| Discover Characteristics | ATT_READ_BY_TYPE_REQ / RSP |
| Read Using Characteristic UUID | ATT_READ_BY_TYPE_REQ / RSP |
| Find Included Services | ATT_READ_BY_TYPE_REQ / RSP |
流程
Server Client Server Client #mermaid-svg-O9gPJQSGBvdi9SCE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O9gPJQSGBvdi9SCE .error-icon{fill:#552222;}#mermaid-svg-O9gPJQSGBvdi9SCE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O9gPJQSGBvdi9SCE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O9gPJQSGBvdi9SCE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O9gPJQSGBvdi9SCE .marker.cross{stroke:#333333;}#mermaid-svg-O9gPJQSGBvdi9SCE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O9gPJQSGBvdi9SCE p{margin:0;}#mermaid-svg-O9gPJQSGBvdi9SCE .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O9gPJQSGBvdi9SCE text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-O9gPJQSGBvdi9SCE .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-O9gPJQSGBvdi9SCE .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-O9gPJQSGBvdi9SCE #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-O9gPJQSGBvdi9SCE .sequenceNumber{fill:white;}#mermaid-svg-O9gPJQSGBvdi9SCE #sequencenumber{fill:#333;}#mermaid-svg-O9gPJQSGBvdi9SCE #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-O9gPJQSGBvdi9SCE .messageText{fill:#333;stroke:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O9gPJQSGBvdi9SCE .labelText,#mermaid-svg-O9gPJQSGBvdi9SCE .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .loopText,#mermaid-svg-O9gPJQSGBvdi9SCE .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-O9gPJQSGBvdi9SCE .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-O9gPJQSGBvdi9SCE .noteText,#mermaid-svg-O9gPJQSGBvdi9SCE .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-O9gPJQSGBvdi9SCE .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O9gPJQSGBvdi9SCE .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O9gPJQSGBvdi9SCE .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O9gPJQSGBvdi9SCE .actorPopupMenu{position:absolute;}#mermaid-svg-O9gPJQSGBvdi9SCE .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-O9gPJQSGBvdi9SCE .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O9gPJQSGBvdi9SCE .actor-man circle,#mermaid-svg-O9gPJQSGBvdi9SCE line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-O9gPJQSGBvdi9SCE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_READ_BY_TYPE_REQ(start, end, UUID)ATT_READ_BY_TYPE_RSP(length, attribute data list)
常见坑
返回列表中的每个 Attribute Data 长度必须一致。
如果不同长度的 value 混在一起,Server 需要按规范处理,不能随便塞一锅乱炖。
第 17 篇:Read Request 与 Read Blob:普通读与长读
对应章节
-
3.4.4.3 ATT_READ_REQ
-
3.4.4.4 ATT_READ_RSP
-
3.4.4.5 ATT_READ_BLOB_REQ
-
3.4.4.6 ATT_READ_BLOB_RSP
文章目标
理解普通读取和分段读取长属性。
普通读
Server Client Server Client #mermaid-svg-8jClhPaKbb1yOZ1P{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8jClhPaKbb1yOZ1P .error-icon{fill:#552222;}#mermaid-svg-8jClhPaKbb1yOZ1P .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8jClhPaKbb1yOZ1P .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8jClhPaKbb1yOZ1P .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8jClhPaKbb1yOZ1P .marker.cross{stroke:#333333;}#mermaid-svg-8jClhPaKbb1yOZ1P svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8jClhPaKbb1yOZ1P p{margin:0;}#mermaid-svg-8jClhPaKbb1yOZ1P .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8jClhPaKbb1yOZ1P text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8jClhPaKbb1yOZ1P .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-8jClhPaKbb1yOZ1P .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-8jClhPaKbb1yOZ1P #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-8jClhPaKbb1yOZ1P .sequenceNumber{fill:white;}#mermaid-svg-8jClhPaKbb1yOZ1P #sequencenumber{fill:#333;}#mermaid-svg-8jClhPaKbb1yOZ1P #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-8jClhPaKbb1yOZ1P .messageText{fill:#333;stroke:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8jClhPaKbb1yOZ1P .labelText,#mermaid-svg-8jClhPaKbb1yOZ1P .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .loopText,#mermaid-svg-8jClhPaKbb1yOZ1P .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8jClhPaKbb1yOZ1P .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-8jClhPaKbb1yOZ1P .noteText,#mermaid-svg-8jClhPaKbb1yOZ1P .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-8jClhPaKbb1yOZ1P .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8jClhPaKbb1yOZ1P .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8jClhPaKbb1yOZ1P .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8jClhPaKbb1yOZ1P .actorPopupMenu{position:absolute;}#mermaid-svg-8jClhPaKbb1yOZ1P .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-8jClhPaKbb1yOZ1P .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8jClhPaKbb1yOZ1P .actor-man circle,#mermaid-svg-8jClhPaKbb1yOZ1P line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-8jClhPaKbb1yOZ1P :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_READ_REQ(handle)ATT_READ_RSP(value)
长读
Server Client Server Client #mermaid-svg-gSVHSD9gE47FBeEe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gSVHSD9gE47FBeEe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gSVHSD9gE47FBeEe .error-icon{fill:#552222;}#mermaid-svg-gSVHSD9gE47FBeEe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gSVHSD9gE47FBeEe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gSVHSD9gE47FBeEe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gSVHSD9gE47FBeEe .marker.cross{stroke:#333333;}#mermaid-svg-gSVHSD9gE47FBeEe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gSVHSD9gE47FBeEe p{margin:0;}#mermaid-svg-gSVHSD9gE47FBeEe .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gSVHSD9gE47FBeEe text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gSVHSD9gE47FBeEe .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gSVHSD9gE47FBeEe .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-gSVHSD9gE47FBeEe .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gSVHSD9gE47FBeEe .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gSVHSD9gE47FBeEe #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gSVHSD9gE47FBeEe .sequenceNumber{fill:white;}#mermaid-svg-gSVHSD9gE47FBeEe #sequencenumber{fill:#333;}#mermaid-svg-gSVHSD9gE47FBeEe #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gSVHSD9gE47FBeEe .messageText{fill:#333;stroke:none;}#mermaid-svg-gSVHSD9gE47FBeEe .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gSVHSD9gE47FBeEe .labelText,#mermaid-svg-gSVHSD9gE47FBeEe .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gSVHSD9gE47FBeEe .loopText,#mermaid-svg-gSVHSD9gE47FBeEe .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gSVHSD9gE47FBeEe .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gSVHSD9gE47FBeEe .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gSVHSD9gE47FBeEe .noteText,#mermaid-svg-gSVHSD9gE47FBeEe .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gSVHSD9gE47FBeEe .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gSVHSD9gE47FBeEe .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gSVHSD9gE47FBeEe .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gSVHSD9gE47FBeEe .actorPopupMenu{position:absolute;}#mermaid-svg-gSVHSD9gE47FBeEe .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gSVHSD9gE47FBeEe .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gSVHSD9gE47FBeEe .actor-man circle,#mermaid-svg-gSVHSD9gE47FBeEe line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gSVHSD9gE47FBeEe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_READ_BLOB_REQ(handle, offset=0)ATT_READ_BLOB_RSP(part value)ATT_READ_BLOB_REQ(handle, offset=N)ATT_READ_BLOB_RSP(part value)
工程重点
| 场景 | 使用 PDU |
|---|---|
| Value 较短 | ATT_READ_REQ |
| Value 超过单个 PDU | ATT_READ_BLOB_REQ |
| offset 非法 | Invalid Offset |
| Attribute 不支持长读 | Attribute Not Long |
第 18 篇:Read Multiple 与 Read By Group Type
对应章节
-
3.4.4.7 ATT_READ_MULTIPLE_REQ
-
3.4.4.8 ATT_READ_MULTIPLE_RSP
-
3.4.4.9 ATT_READ_BY_GROUP_TYPE_REQ
-
3.4.4.10 ATT_READ_BY_GROUP_TYPE_RSP
文章目标
理解多 handle 读取和分组读取。
Read Multiple
用于一次读取多个 attribute value。
Server Client Server Client #mermaid-svg-HRYrXV61CbcjxDjC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HRYrXV61CbcjxDjC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HRYrXV61CbcjxDjC .error-icon{fill:#552222;}#mermaid-svg-HRYrXV61CbcjxDjC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HRYrXV61CbcjxDjC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HRYrXV61CbcjxDjC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HRYrXV61CbcjxDjC .marker.cross{stroke:#333333;}#mermaid-svg-HRYrXV61CbcjxDjC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HRYrXV61CbcjxDjC p{margin:0;}#mermaid-svg-HRYrXV61CbcjxDjC .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HRYrXV61CbcjxDjC text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-HRYrXV61CbcjxDjC .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HRYrXV61CbcjxDjC .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-HRYrXV61CbcjxDjC .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-HRYrXV61CbcjxDjC .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-HRYrXV61CbcjxDjC #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-HRYrXV61CbcjxDjC .sequenceNumber{fill:white;}#mermaid-svg-HRYrXV61CbcjxDjC #sequencenumber{fill:#333;}#mermaid-svg-HRYrXV61CbcjxDjC #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-HRYrXV61CbcjxDjC .messageText{fill:#333;stroke:none;}#mermaid-svg-HRYrXV61CbcjxDjC .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HRYrXV61CbcjxDjC .labelText,#mermaid-svg-HRYrXV61CbcjxDjC .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-HRYrXV61CbcjxDjC .loopText,#mermaid-svg-HRYrXV61CbcjxDjC .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-HRYrXV61CbcjxDjC .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HRYrXV61CbcjxDjC .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HRYrXV61CbcjxDjC .noteText,#mermaid-svg-HRYrXV61CbcjxDjC .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-HRYrXV61CbcjxDjC .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HRYrXV61CbcjxDjC .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HRYrXV61CbcjxDjC .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HRYrXV61CbcjxDjC .actorPopupMenu{position:absolute;}#mermaid-svg-HRYrXV61CbcjxDjC .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-HRYrXV61CbcjxDjC .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HRYrXV61CbcjxDjC .actor-man circle,#mermaid-svg-HRYrXV61CbcjxDjC line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-HRYrXV61CbcjxDjC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_READ_MULTIPLE_REQ(handle set)ATT_READ_MULTIPLE_RSP(value set)
Read By Group Type
GATT 中 Discover All Primary Services 的核心 PDU。
Server Client Server Client #mermaid-svg-2REowQ6REExs3Xcx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2REowQ6REExs3Xcx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2REowQ6REExs3Xcx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2REowQ6REExs3Xcx .error-icon{fill:#552222;}#mermaid-svg-2REowQ6REExs3Xcx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2REowQ6REExs3Xcx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2REowQ6REExs3Xcx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2REowQ6REExs3Xcx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2REowQ6REExs3Xcx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2REowQ6REExs3Xcx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2REowQ6REExs3Xcx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2REowQ6REExs3Xcx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2REowQ6REExs3Xcx .marker.cross{stroke:#333333;}#mermaid-svg-2REowQ6REExs3Xcx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2REowQ6REExs3Xcx p{margin:0;}#mermaid-svg-2REowQ6REExs3Xcx .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2REowQ6REExs3Xcx text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-2REowQ6REExs3Xcx .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2REowQ6REExs3Xcx .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-2REowQ6REExs3Xcx .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-2REowQ6REExs3Xcx .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-2REowQ6REExs3Xcx #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-2REowQ6REExs3Xcx .sequenceNumber{fill:white;}#mermaid-svg-2REowQ6REExs3Xcx #sequencenumber{fill:#333;}#mermaid-svg-2REowQ6REExs3Xcx #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-2REowQ6REExs3Xcx .messageText{fill:#333;stroke:none;}#mermaid-svg-2REowQ6REExs3Xcx .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2REowQ6REExs3Xcx .labelText,#mermaid-svg-2REowQ6REExs3Xcx .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-2REowQ6REExs3Xcx .loopText,#mermaid-svg-2REowQ6REExs3Xcx .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-2REowQ6REExs3Xcx .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2REowQ6REExs3Xcx .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-2REowQ6REExs3Xcx .noteText,#mermaid-svg-2REowQ6REExs3Xcx .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-2REowQ6REExs3Xcx .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2REowQ6REExs3Xcx .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2REowQ6REExs3Xcx .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2REowQ6REExs3Xcx .actorPopupMenu{position:absolute;}#mermaid-svg-2REowQ6REExs3Xcx .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-2REowQ6REExs3Xcx .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2REowQ6REExs3Xcx .actor-man circle,#mermaid-svg-2REowQ6REExs3Xcx line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-2REowQ6REExs3Xcx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_READ_BY_GROUP_TYPE_REQ(start, end, group type)ATT_READ_BY_GROUP_TYPE_RSP(attribute data list)
工程重点
| PDU | 常见用途 |
|---|---|
ATT_READ_MULTIPLE_REQ |
批量读多个固定长度 value |
ATT_READ_BY_GROUP_TYPE_REQ |
服务发现 |
ATT_READ_BY_GROUP_TYPE_RSP |
返回 group start/end handle 和 value |
第 19 篇:Read Multiple Variable:变长多属性读取
对应章节
-
3.4.4.11 ATT_READ_MULTIPLE_VARIABLE_REQ
-
3.4.4.12 ATT_READ_MULTIPLE_VARIABLE_RSP
文章目标
理解变长 attribute 的多值读取。
核心问题
| 问题 | 说明 |
|---|---|
| 为什么需要 Variable 版本? | 普通 Read Multiple 不适合变长 value |
| 返回格式是什么? | Length + Value tuple list |
| handle 顺序是否要求递增? | 返回顺序按请求顺序 |
| 是否可能超出 ATT_MTU? | 需要按 MTU 限制处理 |
第 20 篇:Writing Attributes:Write Request、Write Command、Signed Write
对应章节
-
3.4.5 Writing attributes
-
3.4.5.1 ATT_WRITE_REQ
-
3.4.5.2 ATT_WRITE_RSP
-
3.4.5.3 ATT_WRITE_CMD
-
3.4.5.4 ATT_SIGNED_WRITE_CMD
文章目标
理解三种写操作。
对比表
| PDU | 是否需要响应 | 是否有签名 | 典型用途 |
|---|---|---|---|
ATT_WRITE_REQ |
是 | 否 | 可靠写入,需要知道结果 |
ATT_WRITE_CMD |
否 | 否 | 高速写入,不关心响应 |
ATT_SIGNED_WRITE_CMD |
否 | 是 | 未加密 bearer 上的认证写 |
流程
Server Client Server Client #mermaid-svg-1ijCzri5Ne3omim7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1ijCzri5Ne3omim7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1ijCzri5Ne3omim7 .error-icon{fill:#552222;}#mermaid-svg-1ijCzri5Ne3omim7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1ijCzri5Ne3omim7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1ijCzri5Ne3omim7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1ijCzri5Ne3omim7 .marker.cross{stroke:#333333;}#mermaid-svg-1ijCzri5Ne3omim7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1ijCzri5Ne3omim7 p{margin:0;}#mermaid-svg-1ijCzri5Ne3omim7 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1ijCzri5Ne3omim7 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-1ijCzri5Ne3omim7 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1ijCzri5Ne3omim7 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-1ijCzri5Ne3omim7 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-1ijCzri5Ne3omim7 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-1ijCzri5Ne3omim7 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-1ijCzri5Ne3omim7 .sequenceNumber{fill:white;}#mermaid-svg-1ijCzri5Ne3omim7 #sequencenumber{fill:#333;}#mermaid-svg-1ijCzri5Ne3omim7 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-1ijCzri5Ne3omim7 .messageText{fill:#333;stroke:none;}#mermaid-svg-1ijCzri5Ne3omim7 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1ijCzri5Ne3omim7 .labelText,#mermaid-svg-1ijCzri5Ne3omim7 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-1ijCzri5Ne3omim7 .loopText,#mermaid-svg-1ijCzri5Ne3omim7 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-1ijCzri5Ne3omim7 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1ijCzri5Ne3omim7 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-1ijCzri5Ne3omim7 .noteText,#mermaid-svg-1ijCzri5Ne3omim7 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-1ijCzri5Ne3omim7 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1ijCzri5Ne3omim7 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1ijCzri5Ne3omim7 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1ijCzri5Ne3omim7 .actorPopupMenu{position:absolute;}#mermaid-svg-1ijCzri5Ne3omim7 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-1ijCzri5Ne3omim7 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1ijCzri5Ne3omim7 .actor-man circle,#mermaid-svg-1ijCzri5Ne3omim7 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-1ijCzri5Ne3omim7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不回复 校验签名,不回复 ATT_WRITE_REQ(handle, value)ATT_WRITE_RSPATT_WRITE_CMD(handle, value)ATT_SIGNED_WRITE_CMD(handle, value, signature)
工程重点
-
Write Request有错误响应; -
Write Command没有错误响应; -
Signed Write Command需要安全上下文; -
ATT_MTU - 3影响普通写 payload; -
ATT_MTU - 15影响 signed write payload。
第 21 篇:Queued Writes:Prepare Write 与 Execute Write
对应章节
-
3.4.6 Queued writes
-
3.4.6.1 ATT_PREPARE_WRITE_REQ
-
3.4.6.2 ATT_PREPARE_WRITE_RSP
-
3.4.6.3 ATT_EXECUTE_WRITE_REQ
-
3.4.6.4 ATT_EXECUTE_WRITE_RSP
文章目标
理解长写和可靠写。
流程
Server Client Server Client #mermaid-svg-FoqIUGZX9CJdgeHM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FoqIUGZX9CJdgeHM .error-icon{fill:#552222;}#mermaid-svg-FoqIUGZX9CJdgeHM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FoqIUGZX9CJdgeHM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FoqIUGZX9CJdgeHM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FoqIUGZX9CJdgeHM .marker.cross{stroke:#333333;}#mermaid-svg-FoqIUGZX9CJdgeHM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FoqIUGZX9CJdgeHM p{margin:0;}#mermaid-svg-FoqIUGZX9CJdgeHM .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-FoqIUGZX9CJdgeHM text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-FoqIUGZX9CJdgeHM .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-FoqIUGZX9CJdgeHM .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-FoqIUGZX9CJdgeHM #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-FoqIUGZX9CJdgeHM .sequenceNumber{fill:white;}#mermaid-svg-FoqIUGZX9CJdgeHM #sequencenumber{fill:#333;}#mermaid-svg-FoqIUGZX9CJdgeHM #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-FoqIUGZX9CJdgeHM .messageText{fill:#333;stroke:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-FoqIUGZX9CJdgeHM .labelText,#mermaid-svg-FoqIUGZX9CJdgeHM .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .loopText,#mermaid-svg-FoqIUGZX9CJdgeHM .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-FoqIUGZX9CJdgeHM .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-FoqIUGZX9CJdgeHM .noteText,#mermaid-svg-FoqIUGZX9CJdgeHM .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-FoqIUGZX9CJdgeHM .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-FoqIUGZX9CJdgeHM .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-FoqIUGZX9CJdgeHM .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-FoqIUGZX9CJdgeHM .actorPopupMenu{position:absolute;}#mermaid-svg-FoqIUGZX9CJdgeHM .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-FoqIUGZX9CJdgeHM .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-FoqIUGZX9CJdgeHM .actor-man circle,#mermaid-svg-FoqIUGZX9CJdgeHM line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-FoqIUGZX9CJdgeHM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ATT_PREPARE_WRITE_REQ(handle, offset=0, part value)ATT_PREPARE_WRITE_RSP(handle, offset=0, part value)ATT_PREPARE_WRITE_REQ(handle, offset=N, part value)ATT_PREPARE_WRITE_RSP(handle, offset=N, part value)ATT_EXECUTE_WRITE_REQ(flags)ATT_EXECUTE_WRITE_RSP
工程重点
| 点 | 说明 |
|---|---|
| Prepare Queue | Server 需要缓存写入片段 |
| Offset | 决定写入位置 |
| Echo 校验 | Response 需要回显部分字段 |
| Execute | 决定执行或取消 |
| Queue Full | 队列满时返回错误 |
| Reliable Write | Client 可校验 Server 回显内容 |
常见坑
Prepare Write 是"先放购物车",Execute Write 才是"提交订单"。
只 Prepare 不 Execute,就像加了一购物车东西但没付款,老板不会发货。
第 22 篇:Server Initiated:Notification、Indication、Confirmation
对应章节
-
3.4.7 Server initiated
-
3.4.7.1 ATT_HANDLE_VALUE_NTF
-
3.4.7.2 ATT_HANDLE_VALUE_IND
-
3.4.7.3 ATT_HANDLE_VALUE_CFM
-
3.4.7.4 ATT_MULTIPLE_HANDLE_VALUE_NTF
文章目标
理解 Server 主动发送数据的机制。
对比表
| 类型 | PDU | 是否需要确认 | 常见用途 |
|---|---|---|---|
| Notification | ATT_HANDLE_VALUE_NTF |
否 | 传感器连续上报 |
| Indication | ATT_HANDLE_VALUE_IND |
是 | 关键状态变化 |
| Confirmation | ATT_HANDLE_VALUE_CFM |
是,Client 回 Server | Indication 确认 |
| Multiple Notification | ATT_MULTIPLE_HANDLE_VALUE_NTF |
否 | 多 handle 合并通知 |
流程
Server Client Server Client #mermaid-svg-tpdTYVni4xAQ3adx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tpdTYVni4xAQ3adx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tpdTYVni4xAQ3adx .error-icon{fill:#552222;}#mermaid-svg-tpdTYVni4xAQ3adx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tpdTYVni4xAQ3adx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tpdTYVni4xAQ3adx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tpdTYVni4xAQ3adx .marker.cross{stroke:#333333;}#mermaid-svg-tpdTYVni4xAQ3adx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tpdTYVni4xAQ3adx p{margin:0;}#mermaid-svg-tpdTYVni4xAQ3adx .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tpdTYVni4xAQ3adx text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tpdTYVni4xAQ3adx .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tpdTYVni4xAQ3adx .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tpdTYVni4xAQ3adx .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tpdTYVni4xAQ3adx .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tpdTYVni4xAQ3adx #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tpdTYVni4xAQ3adx .sequenceNumber{fill:white;}#mermaid-svg-tpdTYVni4xAQ3adx #sequencenumber{fill:#333;}#mermaid-svg-tpdTYVni4xAQ3adx #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tpdTYVni4xAQ3adx .messageText{fill:#333;stroke:none;}#mermaid-svg-tpdTYVni4xAQ3adx .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tpdTYVni4xAQ3adx .labelText,#mermaid-svg-tpdTYVni4xAQ3adx .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tpdTYVni4xAQ3adx .loopText,#mermaid-svg-tpdTYVni4xAQ3adx .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tpdTYVni4xAQ3adx .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tpdTYVni4xAQ3adx .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tpdTYVni4xAQ3adx .noteText,#mermaid-svg-tpdTYVni4xAQ3adx .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tpdTYVni4xAQ3adx .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tpdTYVni4xAQ3adx .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tpdTYVni4xAQ3adx .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tpdTYVni4xAQ3adx .actorPopupMenu{position:absolute;}#mermaid-svg-tpdTYVni4xAQ3adx .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-tpdTYVni4xAQ3adx .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tpdTYVni4xAQ3adx .actor-man circle,#mermaid-svg-tpdTYVni4xAQ3adx line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tpdTYVni4xAQ3adx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Notification 不确认 ATT_HANDLE_VALUE_NTF(handle, value)ATT_HANDLE_VALUE_IND(handle, value)ATT_HANDLE_VALUE_CFM
工程重点
-
Notification 不保证应用层确认;
-
Indication 一次只能等待一个确认;
-
CCCD 属于 GATT 层,但会影响 ATT Notification/Indication 是否发送;
-
多 handle notification 有利于减少包开销。
第四部分:总结表、安全与实现
第 23 篇:Opcode Summary 与 Response Summary
对应章节
-
3.4.8 Attribute Opcode summary
-
3.4.9 Attribute PDU response summary
文章目标
把所有 ATT PDU 做成速查表。
计划输出表
| PDU | Opcode | 类型 | 参数 | 成功响应 | 是否可能 Error |
|---|---|---|---|---|---|
ATT_ERROR_RSP |
0x01 |
Response | Error fields | 无 | 否 |
ATT_EXCHANGE_MTU_REQ |
0x02 |
Request | Client Rx MTU | ATT_EXCHANGE_MTU_RSP |
是 |
ATT_EXCHANGE_MTU_RSP |
0x03 |
Response | Server Rx MTU | 无 | 否 |
ATT_FIND_INFORMATION_REQ |
0x04 |
Request | Start/End Handle | ATT_FIND_INFORMATION_RSP |
是 |
ATT_FIND_INFORMATION_RSP |
0x05 |
Response | Format + Info | 无 | 否 |
ATT_FIND_BY_TYPE_VALUE_REQ |
0x06 |
Request | Start/End/Type/Value | ATT_FIND_BY_TYPE_VALUE_RSP |
是 |
ATT_FIND_BY_TYPE_VALUE_RSP |
0x07 |
Response | Handle Info List | 无 | 否 |
ATT_READ_BY_TYPE_REQ |
0x08 |
Request | Start/End/UUID | ATT_READ_BY_TYPE_RSP |
是 |
ATT_READ_BY_TYPE_RSP |
0x09 |
Response | Attribute Data List | 无 | 否 |
ATT_READ_REQ |
0x0A |
Request | Handle | ATT_READ_RSP |
是 |
ATT_READ_RSP |
0x0B |
Response | Value | 无 | 否 |
ATT_READ_BLOB_REQ |
0x0C |
Request | Handle/Offset | ATT_READ_BLOB_RSP |
是 |
ATT_READ_BLOB_RSP |
0x0D |
Response | Part Value | 无 | 否 |
ATT_READ_MULTIPLE_REQ |
0x0E |
Request | Handle Set | ATT_READ_MULTIPLE_RSP |
是 |
ATT_READ_MULTIPLE_RSP |
0x0F |
Response | Value Set | 无 | 否 |
ATT_READ_BY_GROUP_TYPE_REQ |
0x10 |
Request | Start/End/UUID | ATT_READ_BY_GROUP_TYPE_RSP |
是 |
ATT_READ_BY_GROUP_TYPE_RSP |
0x11 |
Response | Attribute Data List | 无 | 否 |
ATT_WRITE_REQ |
0x12 |
Request | Handle/Value | ATT_WRITE_RSP |
是 |
ATT_WRITE_RSP |
0x13 |
Response | 无 | 无 | 否 |
ATT_PREPARE_WRITE_REQ |
0x16 |
Request | Handle/Offset/Part Value | ATT_PREPARE_WRITE_RSP |
是 |
ATT_PREPARE_WRITE_RSP |
0x17 |
Response | Handle/Offset/Part Value | 无 | 否 |
ATT_EXECUTE_WRITE_REQ |
0x18 |
Request | Flags | ATT_EXECUTE_WRITE_RSP |
是 |
ATT_EXECUTE_WRITE_RSP |
0x19 |
Response | 无 | 无 | 否 |
ATT_HANDLE_VALUE_NTF |
0x1B |
Notification | Handle/Value | 无 | 否 |
ATT_HANDLE_VALUE_IND |
0x1D |
Indication | Handle/Value | ATT_HANDLE_VALUE_CFM |
否 |
ATT_HANDLE_VALUE_CFM |
0x1E |
Confirmation | 无 | 无 | 否 |
ATT_READ_MULTIPLE_VARIABLE_REQ |
0x20 |
Request | Set Of Handles | ATT_READ_MULTIPLE_VARIABLE_RSP |
是 |
ATT_READ_MULTIPLE_VARIABLE_RSP |
0x21 |
Response | Length Value Tuple List | 无 | 否 |
ATT_MULTIPLE_HANDLE_VALUE_NTF |
0x23 |
Notification | Tuple List | 无 | 否 |
ATT_WRITE_CMD |
0x52 |
Command | Handle/Value | 无 | 否 |
ATT_SIGNED_WRITE_CMD |
0xD2 |
Command | Handle/Value/Signature | 无 | 否 |
第 24 篇:Security Considerations、References、Appendix 与系列总结
对应章节
-
4 Security considerations
-
5 References
-
Appendix A Changes to PDU names
文章目标
总结 ATT 的安全检查和版本兼容注意事项。
安全检查重点
| 安全项 | 说明 |
|---|---|
| Authentication | 是否完成认证 |
| Authorization | 是否被授权 |
| Encryption | 是否加密 |
| Encryption Key Size | 密钥长度是否足够 |
| Permission | Attribute 本地权限 |
| Error Code | 安全失败时返回的错误码 |
安全检查顺序
Server 应该优先检查安全条件。
否则可能出现信息泄露,比如攻击者通过不同错误码推测 attribute 长度或存在性。
Appendix 重点
旧版 PDU 名称与新版 PDU 名称对照。
这对读老代码、老协议栈、旧抓包工具特别有用。
5. 系列文章目录索引
下面是最终目录索引,后续每写完一篇,就在这里补链接。
| 编号 | 文章标题 | 对应章节 | 状态 |
|---|---|---|---|
| 01 | Vol 3 Part F 总览:ATT 到底解决什么问题? | Part F 标题、1 Introduction | 待写 |
| 02 | Protocol Overview:ATT 协议模型 | 2 Protocol overview | 待写 |
| 03 | Protocol Requirements 总览:ATT 规范里的强制要求 | 3、3.1 | 待写 |
| 04 | Attribute Type:UUID 是 Attribute 的身份证 | 3.2.1 | 待写 |
| 05 | Attribute Handle:ATT 世界里的门牌号 | 3.2.2 | 待写 |
| 06 | Attribute Grouping、Value、Permissions | 3.2.3--3.2.5 | 待写 |
| 07 | Control-point Attributes 与 Protocol Methods | 3.2.6--3.2.7 | 待写 |
| 08 | ATT_MTU:协议包裹箱到底有多大? | 3.2.8、3.4.2 | 待写 |
| 09 | Long Attribute Values 与 Atomic Operations | 3.2.9--3.2.10 | 待写 |
| 10 | ATT Bearer:ATT 怎么跑在 L2CAP 上? | 3.2.11 | 待写 |
| 11 | Attribute PDU 总览:六大类型 | 3.3 | 待写 |
| 12 | Attribute PDU Format、Sequential Protocol、Transaction | 3.3.1--3.3.3 | 待写 |
| 13 | Error Handling:ATT_ERROR_RSP | 3.4.1 | 待写 |
| 14 | Find Information:发现 Attribute Handle 与 Type | 3.4.3.1--3.4.3.2 | 待写 |
| 15 | Find By Type Value:按类型和值查找 Attribute | 3.4.3.3--3.4.3.4 | 待写 |
| 16 | Read By Type:按 UUID 读取 Attribute | 3.4.4.1--3.4.4.2 | 待写 |
| 17 | Read Request 与 Read Blob:普通读与长读 | 3.4.4.3--3.4.4.6 | 待写 |
| 18 | Read Multiple 与 Read By Group Type | 3.4.4.7--3.4.4.10 | 待写 |
| 19 | Read Multiple Variable:变长多属性读取 | 3.4.4.11--3.4.4.12 | 待写 |
| 20 | Writing Attributes:Write Request、Write Command、Signed Write | 3.4.5 | 待写 |
| 21 | Queued Writes:Prepare Write 与 Execute Write | 3.4.6 | 待写 |
| 22 | Server Initiated:Notification、Indication、Confirmation | 3.4.7 | 待写 |
| 23 | Opcode Summary 与 Response Summary | 3.4.8--3.4.9 | 待写 |
| 24 | Security Considerations、References、Appendix 与系列总结 | 4、5、Appendix A | 待写 |
6. 推荐文件归档方式
为了后续持续维护,建议目录这样组织:
text
bluetooth-core-v6.2-study/
└── vol3-partf-att/
├── 00-index.md
├── 01-att-overview.md
├── 02-protocol-overview.md
├── 03-protocol-requirements.md
├── 04-attribute-type.md
├── 05-attribute-handle.md
├── 06-attribute-grouping-value-permissions.md
├── 07-control-point-protocol-methods.md
├── 08-att-mtu.md
├── 09-long-attribute-atomic.md
├── 10-att-bearer.md
├── 11-att-pdu-types.md
├── 12-pdu-format-sequential-transaction.md
├── 13-error-response.md
├── 14-find-information.md
├── 15-find-by-type-value.md
├── 16-read-by-type.md
├── 17-read-and-read-blob.md
├── 18-read-multiple-read-by-group-type.md
├── 19-read-multiple-variable.md
├── 20-writing-attributes.md
├── 21-queued-writes.md
├── 22-server-initiated.md
├── 23-opcode-response-summary.md
├── 24-security-appendix-summary.md
├── images/
├── captures/
└── notes/
7. 每篇文章建议产出内容
每篇文章至少包含以下内容:
| 模块 | 说明 |
|---|---|
| 原文范围 | 标明学习的是哪一小节 |
| 逐行翻译 | 每句规范原文对应中文解释 |
| 段落讲解 | 用工程语言解释它到底想表达什么 |
| 术语表 | 本文涉及的关键术语 |
| 表格转换 | 原文表格整理为 Markdown |
| 流程图 | 使用 Mermaid 表示交互过程 |
| 抓包对应 | 给出 Wireshark / HCI log 中的对应字段 |
| 代码思考 | ATT Client / Server 如何实现 |
| 常见坑 | 工程实现和调试中容易踩的坑 |
| 小结 | 本篇核心观点 |
8. ATT 学习中的重点难点排序
P0:必须完全掌握
| 内容 | 原因 |
|---|---|
| Attribute Type / Handle / Value / Permission | ATT 数据模型基础 |
| ATT Bearer | 关系到 L2CAP、EATT、BR/EDR/LE 承载 |
| ATT_MTU | 关系到吞吐、分片、长读写 |
| Request / Response 模型 | 所有同步操作基础 |
| Error Response | 调试必看 |
| Read / Write | GATT 操作基本盘 |
| Notification / Indication | BLE 数据上报核心 |
| Security Considerations | 权限、安全、隐私相关 |
P1:需要深入理解
| 内容 | 原因 |
|---|---|
| Read By Type | GATT 发现特征值常用 |
| Read By Group Type | GATT 服务发现核心 |
| Prepare Write / Execute Write | OTA、长写、可靠写场景 |
| Signed Write | 安全相关,虽然不一定常用 |
| Multiple Handle Value Notification | 新特性和性能优化相关 |
| Enhanced ATT Bearer | EATT 并发基础 |
P2:可后续结合 GATT 再强化
| 内容 | 原因 |
|---|---|
| Find By Type Value | GATT Primary Service by UUID 使用 |
| Read Multiple Variable | 特定场景使用 |
| Appendix PDU name changes | 读旧资料、旧代码时有用 |
9. 抓包学习建议
后续每篇文章都尽量找对应抓包。
推荐抓包来源
| 工具 | 用途 |
|---|---|
| Wireshark | 分析 btsnoop / pcap |
| Android HCI snoop log | 手机 BLE 抓包 |
| Linux btmon | BlueZ 协议栈调试 |
| nRF Connect | 触发 GATT 操作 |
| Ellisys / Frontline | 专业空口分析 |
| 自研日志 | 协议栈内部状态机分析 |
抓包观察路径
text
HCI ACL Data
└── L2CAP
└── ATT
├── Opcode
├── Handle
├── Value
└── Error Code
建议抓的典型流程
| 流程 | 观察的 ATT PDU |
|---|---|
| 连接后交换 MTU | ATT_EXCHANGE_MTU_REQ / RSP |
| 发现服务 | ATT_READ_BY_GROUP_TYPE_REQ / RSP |
| 发现特征值 | ATT_READ_BY_TYPE_REQ / RSP |
| 发现描述符 | ATT_FIND_INFORMATION_REQ / RSP |
| 读特征值 | ATT_READ_REQ / RSP |
| 写 CCCD | ATT_WRITE_REQ / RSP |
| Notification | ATT_HANDLE_VALUE_NTF |
| Indication | ATT_HANDLE_VALUE_IND / CFM |
| 长读 | ATT_READ_BLOB_REQ / RSP |
| 长写 | ATT_PREPARE_WRITE_REQ / RSP + ATT_EXECUTE_WRITE_REQ / RSP |
| 权限错误 | ATT_ERROR_RSP |
10. ATT 与 GATT 的配套学习关系
学习 ATT 时,要始终和 GATT 对照。
#mermaid-svg-KKeqj6UKiSTmpO8P{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KKeqj6UKiSTmpO8P .error-icon{fill:#552222;}#mermaid-svg-KKeqj6UKiSTmpO8P .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KKeqj6UKiSTmpO8P .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KKeqj6UKiSTmpO8P .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KKeqj6UKiSTmpO8P .marker.cross{stroke:#333333;}#mermaid-svg-KKeqj6UKiSTmpO8P svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KKeqj6UKiSTmpO8P p{margin:0;}#mermaid-svg-KKeqj6UKiSTmpO8P .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P .cluster-label text{fill:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P .cluster-label span{color:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P .cluster-label span p{background-color:transparent;}#mermaid-svg-KKeqj6UKiSTmpO8P .label text,#mermaid-svg-KKeqj6UKiSTmpO8P span{fill:#333;color:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P .node rect,#mermaid-svg-KKeqj6UKiSTmpO8P .node circle,#mermaid-svg-KKeqj6UKiSTmpO8P .node ellipse,#mermaid-svg-KKeqj6UKiSTmpO8P .node polygon,#mermaid-svg-KKeqj6UKiSTmpO8P .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KKeqj6UKiSTmpO8P .rough-node .label text,#mermaid-svg-KKeqj6UKiSTmpO8P .node .label text,#mermaid-svg-KKeqj6UKiSTmpO8P .image-shape .label,#mermaid-svg-KKeqj6UKiSTmpO8P .icon-shape .label{text-anchor:middle;}#mermaid-svg-KKeqj6UKiSTmpO8P .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KKeqj6UKiSTmpO8P .rough-node .label,#mermaid-svg-KKeqj6UKiSTmpO8P .node .label,#mermaid-svg-KKeqj6UKiSTmpO8P .image-shape .label,#mermaid-svg-KKeqj6UKiSTmpO8P .icon-shape .label{text-align:center;}#mermaid-svg-KKeqj6UKiSTmpO8P .node.clickable{cursor:pointer;}#mermaid-svg-KKeqj6UKiSTmpO8P .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KKeqj6UKiSTmpO8P .arrowheadPath{fill:#333333;}#mermaid-svg-KKeqj6UKiSTmpO8P .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KKeqj6UKiSTmpO8P .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KKeqj6UKiSTmpO8P .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KKeqj6UKiSTmpO8P .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KKeqj6UKiSTmpO8P .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KKeqj6UKiSTmpO8P .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KKeqj6UKiSTmpO8P .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KKeqj6UKiSTmpO8P .cluster text{fill:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P .cluster span{color:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KKeqj6UKiSTmpO8P .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KKeqj6UKiSTmpO8P rect.text{fill:none;stroke-width:0;}#mermaid-svg-KKeqj6UKiSTmpO8P .icon-shape,#mermaid-svg-KKeqj6UKiSTmpO8P .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KKeqj6UKiSTmpO8P .icon-shape p,#mermaid-svg-KKeqj6UKiSTmpO8P .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KKeqj6UKiSTmpO8P .icon-shape .label rect,#mermaid-svg-KKeqj6UKiSTmpO8P .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KKeqj6UKiSTmpO8P .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KKeqj6UKiSTmpO8P .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KKeqj6UKiSTmpO8P :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GATT Procedure
ATT PDU
L2CAP Channel
Attribute Database
对照表
| GATT 概念 | ATT 对应 |
|---|---|
| Service | 一组 Attribute |
| Characteristic Declaration | Attribute |
| Characteristic Value | Attribute |
| Descriptor | Attribute |
| UUID | Attribute Type |
| Handle | Attribute Handle |
| Read Characteristic | ATT_READ_REQ / RSP |
| Write Characteristic | ATT_WRITE_REQ / RSP |
| Notify | ATT_HANDLE_VALUE_NTF |
| Indicate | ATT_HANDLE_VALUE_IND / CFM |
11. ATT Server 实现思路
学习完 ATT 后,应该能设计一个最小 ATT Server。
ATT Server 核心模块
#mermaid-svg-Ho02aOTLL2nUC5J6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ho02aOTLL2nUC5J6 .error-icon{fill:#552222;}#mermaid-svg-Ho02aOTLL2nUC5J6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ho02aOTLL2nUC5J6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .marker.cross{stroke:#333333;}#mermaid-svg-Ho02aOTLL2nUC5J6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ho02aOTLL2nUC5J6 p{margin:0;}#mermaid-svg-Ho02aOTLL2nUC5J6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .cluster-label text{fill:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .cluster-label span{color:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .cluster-label span p{background-color:transparent;}#mermaid-svg-Ho02aOTLL2nUC5J6 .label text,#mermaid-svg-Ho02aOTLL2nUC5J6 span{fill:#333;color:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .node rect,#mermaid-svg-Ho02aOTLL2nUC5J6 .node circle,#mermaid-svg-Ho02aOTLL2nUC5J6 .node ellipse,#mermaid-svg-Ho02aOTLL2nUC5J6 .node polygon,#mermaid-svg-Ho02aOTLL2nUC5J6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .rough-node .label text,#mermaid-svg-Ho02aOTLL2nUC5J6 .node .label text,#mermaid-svg-Ho02aOTLL2nUC5J6 .image-shape .label,#mermaid-svg-Ho02aOTLL2nUC5J6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ho02aOTLL2nUC5J6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .rough-node .label,#mermaid-svg-Ho02aOTLL2nUC5J6 .node .label,#mermaid-svg-Ho02aOTLL2nUC5J6 .image-shape .label,#mermaid-svg-Ho02aOTLL2nUC5J6 .icon-shape .label{text-align:center;}#mermaid-svg-Ho02aOTLL2nUC5J6 .node.clickable{cursor:pointer;}#mermaid-svg-Ho02aOTLL2nUC5J6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .arrowheadPath{fill:#333333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ho02aOTLL2nUC5J6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ho02aOTLL2nUC5J6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ho02aOTLL2nUC5J6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ho02aOTLL2nUC5J6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .cluster text{fill:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 .cluster span{color:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ho02aOTLL2nUC5J6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ho02aOTLL2nUC5J6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ho02aOTLL2nUC5J6 .icon-shape,#mermaid-svg-Ho02aOTLL2nUC5J6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ho02aOTLL2nUC5J6 .icon-shape p,#mermaid-svg-Ho02aOTLL2nUC5J6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ho02aOTLL2nUC5J6 .icon-shape .label rect,#mermaid-svg-Ho02aOTLL2nUC5J6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ho02aOTLL2nUC5J6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ho02aOTLL2nUC5J6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ho02aOTLL2nUC5J6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} L2CAP RX
ATT PDU Parser
Opcode Dispatcher
Attribute Database
Permission Check
Security Check
Response Builder
L2CAP TX
伪代码
c
void att_handle_pdu(att_bearer_t *bearer, uint8_t *pdu, uint16_t len)
{
uint8_t opcode = pdu[0];
switch (opcode) {
case ATT_EXCHANGE_MTU_REQ:
att_handle_exchange_mtu_req(bearer, pdu, len);
break;
case ATT_FIND_INFORMATION_REQ:
att_handle_find_information_req(bearer, pdu, len);
break;
case ATT_READ_REQ:
att_handle_read_req(bearer, pdu, len);
break;
case ATT_WRITE_REQ:
att_handle_write_req(bearer, pdu, len);
break;
case ATT_WRITE_CMD:
att_handle_write_cmd(bearer, pdu, len);
break;
case ATT_PREPARE_WRITE_REQ:
att_handle_prepare_write_req(bearer, pdu, len);
break;
case ATT_EXECUTE_WRITE_REQ:
att_handle_execute_write_req(bearer, pdu, len);
break;
default:
if (is_request(opcode)) {
att_send_error_rsp(bearer, opcode, 0x0000, ATT_ERR_REQUEST_NOT_SUPPORTED);
}
break;
}
}
12. ATT Client 实现思路
ATT Client 重点是事务管理。
Client 需要维护
| 状态 | 说明 |
|---|---|
| bearer MTU | 当前 ATT_MTU |
| pending request | 当前等待响应的 request |
| timeout timer | 响应超时计时 |
| callback | 上层 GATT procedure 回调 |
| transaction queue | 请求队列 |
| indication state | 是否需要发送 confirmation |
Client 状态机
#mermaid-svg-Rrn33kSTFRVFSsqz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Rrn33kSTFRVFSsqz .error-icon{fill:#552222;}#mermaid-svg-Rrn33kSTFRVFSsqz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Rrn33kSTFRVFSsqz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Rrn33kSTFRVFSsqz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Rrn33kSTFRVFSsqz .marker.cross{stroke:#333333;}#mermaid-svg-Rrn33kSTFRVFSsqz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Rrn33kSTFRVFSsqz p{margin:0;}#mermaid-svg-Rrn33kSTFRVFSsqz defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-Rrn33kSTFRVFSsqz g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-Rrn33kSTFRVFSsqz g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-Rrn33kSTFRVFSsqz g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-Rrn33kSTFRVFSsqz g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Rrn33kSTFRVFSsqz g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-Rrn33kSTFRVFSsqz .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Rrn33kSTFRVFSsqz .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-Rrn33kSTFRVFSsqz .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-Rrn33kSTFRVFSsqz .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Rrn33kSTFRVFSsqz .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-Rrn33kSTFRVFSsqz .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Rrn33kSTFRVFSsqz .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-Rrn33kSTFRVFSsqz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Rrn33kSTFRVFSsqz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Rrn33kSTFRVFSsqz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Rrn33kSTFRVFSsqz .edgeLabel .label text{fill:#333;}#mermaid-svg-Rrn33kSTFRVFSsqz .label div .edgeLabel{color:#333;}#mermaid-svg-Rrn33kSTFRVFSsqz .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-Rrn33kSTFRVFSsqz .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-Rrn33kSTFRVFSsqz .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-Rrn33kSTFRVFSsqz .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-Rrn33kSTFRVFSsqz .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-Rrn33kSTFRVFSsqz .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Rrn33kSTFRVFSsqz .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Rrn33kSTFRVFSsqz #statediagram-barbEnd{fill:#333333;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Rrn33kSTFRVFSsqz .cluster-label,#mermaid-svg-Rrn33kSTFRVFSsqz .nodeLabel{color:#131300;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-Rrn33kSTFRVFSsqz .note-edge{stroke-dasharray:5;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-note text{fill:black;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram-note .nodeLabel{color:black;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagram .edgeLabel{color:red;}#mermaid-svg-Rrn33kSTFRVFSsqz #dependencyStart,#mermaid-svg-Rrn33kSTFRVFSsqz #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-Rrn33kSTFRVFSsqz .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Rrn33kSTFRVFSsqz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Send ATT Request
Receive Response
Receive Error Response
Timeout
Idle
RequestSent
WaitingResponse
Error
Timeout
13. 本系列最终验收标准
学完 24 篇后,应该能完成下面这些任务。
| 任务 | 验收标准 |
|---|---|
| 拆解任意 ATT PDU | 能指出 opcode、参数、方向、响应关系 |
| 分析 GATT 发现服务流程 | 能对应到 ATT_READ_BY_GROUP_TYPE |
| 分析 GATT 发现特征值流程 | 能对应到 ATT_READ_BY_TYPE |
| 分析 CCCD 写入 | 能对应到 ATT_WRITE_REQ |
| 分析 Notification | 能对应到 ATT_HANDLE_VALUE_NTF |
| 分析 Indication | 能对应到 ATT_HANDLE_VALUE_IND / CFM |
| 分析长读 | 能解释 offset 和 Read Blob |
| 分析长写 | 能解释 Prepare Queue 和 Execute |
| 分析错误码 | 能根据 ATT_ERROR_RSP 定位权限、安全、handle、offset 问题 |
| 设计最小 ATT Server | 能完成基础 request/response 处理 |
| 设计最小 ATT Client | 能完成事务管理和超时处理 |
| 对接 GATT | 能说明 GATT Procedure 和 ATT PDU 映射关系 |
14. 总结
Vol 3 Part F:Attribute Protocol 是 BLE/GATT 学习中的核心章节。
如果把 GATT 比作"超市货架",那么:
-
Attribute 是货架上的商品;
-
Handle 是商品编号;
-
UUID 是商品类别;
-
Value 是商品内容;
-
Permission 是购买权限;
-
ATT Request 是顾客提需求;
-
ATT Response 是店员回答;
-
Notification 是店员主动喊"新货到了";
-
Indication 是店员喊完还要你点头确认;
-
ATT_MTU 是购物袋大小;
-
Error Response 是店员告诉你"这东西不能买"。
这个系列计划拆成 24 篇文章,按照规范目录逐行学习,从基础概念到每一个 PDU,再到安全、抓包和工程实现。后续学习就按照这个索引逐篇推进。