1. BLE GATT 服务设计全景

各位同学,欢迎来到《GATT 服务设计与开发》的第一章。我是你们这门课的老朋友,一个在蓝牙低功耗领域摸爬滚打多年的工程师。今天咱们不聊虚的,直接切入正题——GATT 协议到底是什么?它凭什么能成为 BLE 应用开发的基石?

说实话,我刚开始接触 BLE 时,也被那一堆缩写搞得头晕:HCI、L2CAP、ATT、GATT、GAP…… 但后来我发现,只要抓住一条主线,整个协议栈就清晰了。这条主线就是:数据怎么组织,数据怎么交互。GATT 就是负责「数据怎么组织」的那一层。

1.1 BLE 协议栈架构:分层看更清楚

BLE 协议栈不是铁板一块,它分了好几层。每一层各司其职,就像公司里的不同部门。我习惯把它分成三大部分:

  • Controller(控制器):负责最底层的射频收发、链路控制。包括物理层(PHY)和链路层(LL)。
  • Host(主机):负责协议逻辑、数据封装、安全管理。包括 L2CAP、ATT、GATT、GAP、SM(安全管理)等。
  • Application(应用层):你写的业务代码,直接使用 GATT/GAP API。

你想想看,如果让你从零开始写一个蓝牙应用,你肯定不想去管射频时序对不对、数据包重传怎么处理。这些脏活累活,Controller 和 Host 已经帮你干了。你只需要关心:我要暴露什么数据?对方怎么读写? 这就是 GATT 的舞台。

核心观点: GATT 位于 Host 层的最顶部,直接面向应用。它不关心数据怎么传过去,只关心数据长什么样、怎么访问。

下面这张图,是我自己画的一个简化版 BLE 协议栈分层。每次带新人时,我都会先让他们看这张图。

BLE 协议栈分层架构 Application(应用层) GATT(通用属性协议) GAP(通用访问协议) ATT(属性协议) L2CAP(逻辑链路控制与适配协议) HCI(主机控制器接口) Link Layer(链路层) PHY(物理层) Host Controller

我的经验: 实际开发中,你几乎不需要碰 HCI 以下的层。但理解 ATT 和 L2CAP 的边界很有用——比如遇到 MTU 协商问题时,你就知道该去 L2CAP 层找原因。

1.2 GATT 与 GAP 的关系:一个管连接,一个管数据

很多初学者会把 GATT 和 GAP 搞混。我打个比方你就明白了:

  • GAP(通用访问协议) 负责「怎么找到对方、怎么建立连接」。它管广播、扫描、连接建立、连接参数更新。说白了,GAP 是「社交礼仪」。
  • GATT(通用属性协议) 负责「连接建立之后,数据怎么交换」。它管服务、特征、描述符的定义和读写。GATT 是「业务逻辑」。

举个例子:你打开手机蓝牙,扫描到一个智能手环。这个「扫描」过程是 GAP 的事。你连上手环,读取心率数据——这个「读取」过程就是 GATT 的事了。

维度 GAP GATT
职责 设备发现、连接建立、安全配对 数据组织、属性读写、通知/指示
角色 Central / Peripheral(中心/外设) Client / Server(客户端/服务端)
核心概念 广播、扫描、连接间隔、白名单 Service、Characteristic、Descriptor
工作时机 连接前 + 连接中 连接建立后

注意: GAP 角色和 GATT 角色是独立的。一个 GAP Peripheral 设备可以是 GATT Server,也可以是 GATT Client。我曾经见过有人把这两个角色混为一谈,结果在设计中把角色搞反了,调试了两天才发现。

1.3 GATT 的角色:Server 与 Client

GATT 定义了两种角色:

  • GATT Server:持有数据的一方。它定义 Service、Characteristic、Descriptor,并响应 Client 的读写请求。
  • GATT Client:发起数据访问的一方。它发现 Server 上的服务,然后读写对应的 Characteristic。

我习惯这样记:Server 是「数据仓库」,Client 是「取货员」。仓库里有什么货、怎么摆放,是 Server 说了算。取货员只能按照仓库的规则来取货。

在实际项目中,最常见的组合是:

  • 智能设备(如手环、传感器)作为 GATT Server
  • 手机 App 作为 GATT Client

但这不是绝对的。比如手机给手环同步配置数据时,手机也可以作为 Server,手环作为 Client。嗯,这里要注意:角色是动态的,取决于谁提供数据,谁消费数据。

1.4 GATT 的核心概念:Service、Characteristic、Descriptor

这是 GATT 最核心的内容,也是你设计蓝牙服务时必须掌握的三板斧。咱们一个一个说。

1.4.1 Service(服务)

Service 是一组相关功能的集合。比如「心率服务」包含心率测量值、心率测量位置等数据。每个 Service 由一个 16-bit 或 128-bit UUID 唯一标识。

蓝牙 SIG(蓝牙技术联盟)定义了很多标准 Service,比如:

  • 0x180D — 心率服务(Heart Rate Service)
  • 0x180F — 电池服务(Battery Service)
  • 0x181A — 环境感知服务(Environmental Sensing Service)

你也可以自定义 Service,用 128-bit UUID。我在项目中就经常这么干——标准服务不够用时,自己定义一套。

1.4.2 Characteristic(特征)

Characteristic 是 GATT 数据交换的最小单元。一个 Service 可以包含多个 Characteristic。每个 Characteristic 包含:

  • Value:实际的数据值(比如心率数值 72 bpm)
  • Properties:访问权限(读、写、通知、指示等)
  • Permissions:安全级别(是否需要配对、加密等)

举个例子,「心率服务」里有一个 Characteristic 叫「心率测量值」,它的 UUID 是 0x2A37。这个 Characteristic 的 Properties 通常包含「通知」——因为心率数据是实时变化的,Server 需要主动推送给 Client。

关键点: Characteristic 的 Properties 和 Permissions 是两个不同的概念。Properties 告诉 Client「我能做什么」,Permissions 告诉底层协议栈「谁可以做什么」。我见过有人把这两个搞混,导致明明设置了可读,但 Client 读不到数据——其实是 Permissions 没配好。

1.4.3 Descriptor(描述符)

Descriptor 是 Characteristic 的附加信息。它用来描述 Characteristic 的配置、范围、单位等。最常用的 Descriptor 是:

  • Client Characteristic Configuration Descriptor (CCCD):UUID 0x2902。用来启用或禁用通知/指示。Client 往这个 Descriptor 写 0x0001 启用通知,写 0x0000 禁用。
  • Characteristic User Description:UUID 0x2901。用来给 Characteristic 加一个人类可读的名称。

Descriptor 不是必须的。但如果你要用通知功能,CCCD 就是必须的。我记得有一次调试一个心率设备,Client 收不到通知,查了半天才发现是 CCCD 没有正确写入。从那以后,我每次都会先检查 CCCD 的配置。

1.5 三者关系:一张图说清楚

Service、Characteristic、Descriptor 的关系是树形的:

Service / Characteristic / Descriptor 层级关系 Service (0x180D) Characteristic (0x2A37) Characteristic (0x2A38) Descriptor (0x2902) Descriptor (0x2901) Descriptor (0x2902) Descriptor 一个 Service 包含多个 Characteristic 一个 Characteristic 包含零个或多个 Descriptor

你看,一个 Service 下面可以挂多个 Characteristic,每个 Characteristic 下面又可以挂多个 Descriptor。这就是 GATT 的数据组织方式——层次化、结构化。

1.6 一个完整的 GATT 数据交互流程

说了这么多理论,咱们来看一个实际流程。假设手机 App 要读取手环的电池电量:

  1. GAP 阶段:手机扫描到手环,发起连接。
  2. 服务发现:手机(Client)发送请求,发现手环(Server)上的所有 Service。
  3. 特征发现:手机找到电池服务(0x180F),然后发现该服务下的电池电量 Characteristic(0x2A19)。
  4. 读取操作:手机发送 Read Request,手环返回当前电量值(比如 85%)。
  5. (可选)启用通知:如果手机想实时获取电量变化,就向 CCCD 写入 0x0001,启用通知。
  6. 数据推送:当电量变化时,手环主动发送 Notification 给手机。

我的建议: 刚开始做 GATT 开发时,先用手机上的 nRF Connect 或 LightBlue 工具手动走一遍这个流程。亲眼看到 Service、Characteristic、Descriptor 的层级结构,比看十遍文档都管用。

1.7 本章小结

咱们这一章把 GATT 的骨架搭起来了:

  • BLE 协议栈是分层的,GATT 在最上面,直接面向应用。
  • GAP 管连接,GATT 管数据,各司其职。
  • GATT 有 Server 和 Client 两种角色,Server 提供数据,Client 消费数据。
  • Service、Characteristic、Descriptor 是 GATT 的三大核心概念,它们组成树形结构。