【Bluetooth-SIG】【CoreV6.2】【Vol3 Part F】【Attribute Protocol(ATT)逐行学习计划与博客目录索引】

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_REQATT_HANDLE_VALUE_NTFATT_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 章节里的 shallmayshall 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 的基本规则,尤其是规范里的 shallmayshall 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
有效范围 0x00010xFFFF
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,再到安全、抓包和工程实现。后续学习就按照这个索引逐篇推进。