概述
在本课程中,我们将更深入地探讨通用属性配置文件(GATT)及其底层属性协议(ATT)。我们将学习如何通过不同的GATT操作来在两个相互连接的蓝牙LE设备之间表示并交换数据。同时,我们还将考察蓝牙LE中的重要概念,如服务及特性。
在本课的理论部分,我们将深入探讨GATT和ATT、属性表结构以及可用于数据交换的GATT操作(如读取和写入)。
我们将构建一个完整的蓝牙LE外围设备配置文件,该配置文件同时使用标准及自定义蓝牙LE服务特征。在本课程的第1个练习中,我们将学习如何从零开始构建LED按钮服务,并着重于GATT的读写操作。第2个练习中,我们将完成LBS服务,并涵盖GATT的通知与指示功能。最后一个练习将涉及北欧UART服务(NUS)以及如何添加由标准蓝牙LE定义的服务。
目标
- 理解通用属性配置文件(GATT)和属性协议(ATT)的作用
- 学习GATT如何用于在蓝牙LE连接中表示和交换数据
- 检查蓝牙低功耗(BluetoothLE)外设设备的属性表
- 掌握如何创建GATT服务和特征值
- 了解nRF Connect SDK中可用的标准蓝牙LE服务
- 通过动手练习创建和添加自定义及标准的BluetoothLE服务和特征值
- 通过使用GATTAPI(读取、写入、无响应写入、通知、指示)进行实践练习
GATT operations
正如我们所看到的,GATT层定义了由属性构成的服务和功能特性,这些属性被存储在GATT服务器中。在本课程中,我们将探讨蓝牙LE中的数据交换过程,即指服务器与客户端之间为获取属性信息并依照属性权限规则交换其值而执行的操作。
服务器既可以直接将数据发送给客户端,也可以由客户端从服务器获取数据。然而,为了让客户端知晓应向服务器提出何种请求,它必须了解GATT服务器所提供的服务与特性。因此,客户端需在连接初期进行服务发现操作,以了解服务器所提供的服务与特性,而后再执行任何旨在获取这些服务的操作。
定义
服务发现:GATT客户端在GATT服务器托管的属性表中发现服务和特征的过程。
数据访问
请记住,这里的通信是基于一种客户机-服务器架构的。服务器持有数据,既可以将其直接发送给客户端;也可以由客户端从服务器获取数据。因此,GATT操作被划分为客户端发起的操作和服务器发起的操作。
客户端发起的操作
由客户端发起的操作属于GATT协议中的操作类型,即客户端向GATT服务器请求数据。客户端可以请求读取或写入某个属性,而在进行写入操作时,客户端还可选择是否接收来自服务器的确认信息。我们将在本课的练习1中更详细地研究这些操作。
Read
如果客户端希望读取GATT服务器上某个属性中存储的特定值,则客户端向服务器发送一个读取请求。服务器通过返回该属性的值来响应此请求。
Write
如果客户端希望将某个值写入某个属性,它便会发送一个写入请求,并同时提供与目标属性格式相匹配的数据。如果服务器接受这一写入操作,便会以确认信息作为回应。
Write without response
如果此操作被启用,客户端便可在无需等待服务器确认的情况下向某个属性写入数据。这是一种非确认性写入操作,适用于需要快速数据交换的场景。
Server-initiated operations
GATT操作的另一种类型是服务器发起的操作,即服务器直接向客户端发送信息,而无需首先接收请求。
在此情况下,服务器可以采取通知或提示的方式。
我们将在本课的练习2中更详细地探讨服务器发起的操作。
Notify
通知操作被服务器用于自动将某个属性的值推送给客户端,而无需客户端主动请求。例如,这可用于向客户端更新最近已发生变化的某个传感器读数信息。
通知不需要客户端的确认回复。
Indicate
与"通知"操作类似,"指示"功能也会直接将属性值推送至客户端。但在此情况下,需要得到客户端的确认。由于存在确认要求,因此每个连接时段内你只能发送一次"指示"消息,这意味着"指示"的发送速度比通知"要慢
尽管这些操作是由服务器发起的,但客户端必须首先通过订阅该特性并启用通知或指示功能来使其生效。这一点将在后续主题中进一步阐述,届时我们将讨论客户端特性配置描述符的相关内容。
Services and characteristics
正如在前述主题中讨论到的那样,ATT层定义了属性以及数据如何在客户端和服务器之间进行暴露的方式。因此,GATT的主要功能之一便是对存储在GATT服务器中的属性进行层级结构化的处理,将其转化为标准化的实体(服务项和特性),从而在不同蓝牙LE设备之间实现无缝的互操作性。
Attributes
ATT层定义了数据在服务器数据库中如何被存储和访问的方式。数据以被称为属性(Attributes)的数据结构形式进行存储。属性是ATT和GATT层所共同依赖的核心数据单元。属性既包含用户数据,也包含描述属性本身、其类型、安全权限等元数据。发生在ATT服务器与客户端之间,或GATT服务器与客户端之间的数据交换均以属性的形式进行。当仅讨论属性时,它们通常被表述为存储在ATT服务器中。然而,正如我们将在本课程中进一步探讨的那样,当我们开始将属性分类为服务特征时,我们便将这种数据结构称为GATT服务器
一个属性由4个数据块组成:

- Handle::由栈分配的用于标识属性表中特定属性的一项16位唯一索引。属性可通过其处理项进行寻址。可以将其视为属性表中的行号,尽管处理项并不一定具有顺序性。
- Type (UUID):通用唯一标识符(UUID),它表明了属性的类型。例如,如果该属性声明了一项特性,那么这一信息将反映在其类型字段中,因为该字段将包含一个专门用于表示声明一项特性的UUID。
- Permissions:处理该属性所需的保护级别(加密和/或授权),并注明其是否为可读和/或可写
- Value:
- 实际用户数据(例如传感器读数)被存储在属性中。此字段可接受任何数据类型。它能够容纳心率监测器值(每分钟心跳数)、温度读数甚至字符串等信息。
- 它还可以存储关于另一个属性的信息(元数据),正如本课程稍后将要讲解的那样。
Universally unique ID (UUID)
UUID是你在蓝牙低功耗(BluetoothLE)世界中会经常看到的一个缩写。它是一个用于标识属性并告诉我们其重要性的唯一编号。UUID有两种类型