大家好,欢迎来到IT知识分享网。
【BLE进阶日记】专栏目录
第一章 BLE介绍
第二章 BLE协议基础知识
第三章 BLE–GAP (Advertising and Connections)
第四章 BLE–GATT (Services and Characteristics)
第五章 BLE硬件平台
第六章 BLE调试工具
第七章 嵌入式应用程序开发
文章目录
前言
本专栏旨在提供对低功耗蓝牙的一个坚实的、实用的、高水平的理解:数据是如何组织的,设备之间如何相互通信,以及协议设计团队所 做出的关键设计决策和权衡。 1. 第1~4章提供了低功耗蓝牙技术的高级概述,解释了数据是如何组织的以及它的关键限制,同时也介绍了所有你在使用BLE时可能遇到 的关键概念。 2. 第5~6章介绍用于开发或对支持ble的应用程序或设备进行测试的有用工具(包括硬件和软件)。这些章节集中在低成本,易于访问的 工具,以帮助你开始低成本开发。 3. 第7章介绍了您可能为BLE使用的主要开发平台(用于产品设计和嵌入式硬件工程师的各种嵌入式电子平台)。 本专栏提供了示例代码,您可以免费在程序和文档中使用它。
本章概述了通用属性配置文件(GATT),该配置文件建立了在BLE中用于表示和操作数据的层次结构和格式。它介绍了服务和特征的基本概念,以及允许连接设备彼此交换数据的过程。
一、GATT简介
通用属性配置文件(GATT)详细规定了如何在BLE连接中交换所有配置文件和用户数据。与定义与设备进行低级交互的GAP相反,GATT仅处理实际的数据传输过程和格式。
GATT还为所有基于GATT的配置文件提供参考框架,这些配置文件涵盖精确的用例并确保来自不同供应商的设备之间的互操作性。因此,所有标准的BLE配置文件都是基于GATT的,并且必须符合它才能正常运行。这使得GATT成为BLE规范的关键部分,因为与应用程序和用户相关的每个数据项必须根据其规则进行格式化、打包和发送。
GATT使用属性协议作为其设备之间交换数据的传输协议。这些数据以services的形式按层次结构组织,并且services将概念上相关的用户数据片段称为characteristics。这决定了本章讨论的GATT的许多基本方面。
二、Roles
与蓝牙规范中的其他协议或配置文件一样,GATT从定义相互交互设备可以采用的角色开始:
- Client:
GATT客户端对应于“属性协议(ATT)”中讨论的ATT客户端,它向服务器发送请求并接收其响应(以及服务器发起的更新)。GATT客户端事先不知道服务器的属性情况,因此必须通过执行服务发现来了解属性的存在和性质。完成服务发现后,它可以开始读取和写入服务器中找到的属性,并接收服务器发起的更新。 - Server:
GATT服务器对应于“属性协议(ATT)”中讨论的ATT服务器,它从客户端接收请求并发送响应。它还在配置为这样做时发送服务器发起的更新,负责存储并提供用户数据给客户端,以属性组织。每个出售的BLE设备都必须包含至少一个基本的GATT服务器,即使只能返回错误响应也可以。
值得再次提到的是,GATT角色既与GAP角色完全独立,同时又相互兼容。这意味着GAP中央和GAP外围设备都可以作为GATT客户端或服务器,甚至可以同时充当两者角色。
三、UUIDs
通用唯一标识符(UUID)是一个128位(16字节)的数字,被保证是全球唯一的(或具有很高的概率)。UUIDs 在许多协议和应用程序中使用,除了蓝牙之外。它们的格式、用法和生成规范在ITU-T Rec. X.667中详细规定,也被称为ISO/IEC 9834-8:2005。
为了提高效率,因为16字节会占用链路层27字节数据负载长度的大部分,蓝牙低功耗(BLE)规范添加了两种额外的 UUID 格式:16位和32位的 UUIDs。这些缩短的格式只能用于在蓝牙规范中定义的 UUIDs(即,在蓝牙SIG列为标准蓝牙 UUIDs的 UUIDs)。
要从缩短的版本中恢复到完整的128位 UUID,将16位或32位的短值(由xxxxxxxx表示,包括前导零)插入到蓝牙基本UUID中:
xxxxxxxx-0000-1000-8000-00805F9B34FB
SIG 为其定义和规定的所有类型、服务和配置文件提供了(缩短的)UUIDs。但如果您的应用程序需要自己的 UUIDs,要么是因为 SIG 提供的 UUIDs 不满足您的要求,要么是因为您希望实现先前未考虑在配置文件规范中的新用例,您可以使用ITU的UUID生成页面来生成它们。
对于不是基于蓝牙基本UUID生成的UUIDs(通常称为特定供应商UUIDs),无法使用缩短。在这些情况下,您将始终需要使用完整的128位UUID值。
四、属性
属性是由GATT(和ATT)定义的最小数据实体。它们是可寻址的信息片段,可以包含有关服务器中不同属性的结构和分组的相关用户数据(或元数据)。GATT和ATT只能使用属性进行工作,因此为了使客户端和服务器进行交互,所有信息都必须以这种形式组织。
从概念上讲,属性始终位于服务器上,并由客户端访问(并可能被修改)。规范仅在概念上定义属性,并不强制ATT和GATT的实现使用特定的内部存储格式或机制。因为属性既包含不变的静态定义,又包含随时间快速变化的实际用户(通常是传感器)数据,所以属性通常存储在非易失性存储器和RAM的混合中。
每个属性都包含有关属性本身以及在以下部分描述的实际数据的信息。
1、句柄
属性句柄是特定GATT服务器上每个属性的唯一16位标识符。它是使属性可寻址的部分,并且在事务之间保证不会更改,对于已配对设备来说,即使在连接之间也不会更改。由于值0x0000表示无效的句柄,每个GATT服务器可用句柄的数量为0xFFFE(65535),尽管实际上,服务器中的属性数量通常接近几十个。
2、类型
属性类型无非是UUID。它可以是16位、32位或128位的UUID,分别占用2、4或16字节。属性类型决定了属性值中存在的数据类型,并提供了根据类型发现属性的机制。
虽然属性类型始终是UUID,但可以使用多种类型的UUID来填充类型。它们可以是标准UUID,用于确定GATT服务器的属性层次结构的布局,例如服务或特征UUID,也可以是指定属性中包含的数据类型的配置文件UUID,例如心率测量或温度,甚至可以是专有的、供应商特定的UUID,其含义由供应商分配并取决于实现。
3、权限
权限是元数据,用于指定可以在每个特定属性上执行哪些ATT操作以及具体的安全要求。
ATT和GATT定义了以下权限:
- 访问权限:
类似于文件权限,访问权限确定客户端是否可以读取或写入(或二者皆可以)属性值。每个属性可以具有以下访问权限之一:
①None:
客户端既不能读取也不能写入该属性。
②可读:
客户端可以读取该属性。
③可写:
客户端可以写入该属性。
④可读可写:
客户端可以读取和写入该属性。 - 加密:
确定访问该属性是否需要某个级别的加密。以下是GATT定义的允许的加密权限:
①不需要加密(安全模式1,级别1):
该属性可以在明文、非加密的连接上进行访问。
②需要非认证加密(安全模式1,级别2):
连接必须加密才能访问该属性,但加密密钥不需要经过认证(尽管可以经过认证)。
③需要认证加密(安全模式1,级别3):
连接必须使用经过认证的密钥加密才能访问该属性。 - 授权:
确定是否需要用户权限以访问该属性。属性只能选择是否需要授权:
①不需要授权:
访问该属性不需要授权。
②需要授权:
访问该属性需要授权。
所有权限彼此独立,服务器可以自由地将它们组合在一起,并将它们存储在每个属性的基础上。
4、值
属性值保存属性的实际数据内容。它的数据类型没有限制(可以将其视为非类型化的缓冲区,根据属性类型将其转换为实际类型),但其最大长度根据规范被限制为512字节。
根据属性类型,属性值可以包含关于属性本身或实际的、有用的用户定义的应用程序数据的附加信息。这是客户端可以自由访问的属性部分(在适当的权限允许下进行读写)。所有其他实体组成了属性的结构,客户端不能直接修改或访问这些实体(尽管客户端在与服务器的大多数交互中间接使用句柄和UUID)。
您可以将GATT服务器中包含的整个属性集想象为一个表格(例如下表),其中每一行代表一个单独的属性,每一列代表实际构成属性的不同部分。
在这个虚构的GATT服务器中,它包含的属性被表示为一个简单表格的行。这个特定的GATT服务器只包含五个属性(与真实设备相比较,这个数量相对较低)。需要注意的是,正如在本章前面提到的,不同属性的句柄不需要立即连续,但是顺序必须递增,就像这个例子一样。
表格的Value列旨在反映在不同基于GATT的配置文件中属性值可能包含的各种格式的多样性。具有句柄0x0201、0x0202和0x031A的属性在它们各自的值字段中包含16位整数。具有句柄0x0215的属性包含一个UTF-8字符串,0x030C包含一个4字节缓冲区,而0x030D则在其值字段中保存一个IEEE-754 64位浮点数。
五、属性和数据层次结构
虽然蓝牙规范在ATT部分中定义了属性,但ATT只涵盖到这一点。ATT以属性术语运作,以提供一系列精确的协议数据单元(PDUs,通常称为数据包),允许客户端访问服务器上的属性。
GATT更进一步,建立了一个严格的层次结构,以可重用和实用的方式组织属性,允许客户端和服务器之间的信息访问和检索遵循一套简明规则,这些规则共同构成了所有基于GATT的配置文件使用的框架。
在GATT服务器中,属性被分组为服务,每个服务可以包含零个或多个特征。而这些特征又可以包含零个或多个描述符。对于声称具有GATT兼容性的任何设备(基本上,所有出售的BLE设备),这种层次结构是严格执行的,这意味着GATT服务器中的所有属性都属于这三个类别之一,没有例外。没有悬空的属性可以存在于这种层次结构之外,因为在BLE设备之间交换数据取决于此。
在GATT层次结构中,对于大多数类型的数据,区分其定义(由其组成的所有属性组)和声明非常重要。声明是一个单独的属性,始终位于定义中的第一个位置(按照增加的句柄顺序),并且引入了有关随后的数据的大部分元数据。所有声明都具有只读权限,不需要安全性,因为它们不能包含敏感数据。它们仅是结构属性,允许客户端了解和发现服务器上属性的布局和性质。
1、服务
GATT服务组在GATT服务器的属性信息集中概念上相关的属性放在一个共同的部分中。规范将单个服务内的所有属性称为服务定义。因此,GATT服务器的属性实际上是一系列服务定义,每个定义都以标记服务开始的单个属性(称为服务声明)开头。该属性的类型和值格式在GATT中严格规定,如下表所示。
UUIDprimary service (0x2800)和UUIDsecondary service (0x2801)是指用作引入服务的互斥类型的标准、由SIG指定的UUID。它们自然是16位UUID(因为它们是规范定义的基本UUID之一)。
重要的是要注意主服务和次要服务之间的区别。主服务是包括GATT服务器提供的相关标准功能的标准类型的GATT服务。而次要服务则意味着它只能包含在其他主要服务中,并且只有作为其修饰符时才有意义,在自身上没有实际意义。实际上,次要服务很少被使用。
服务声明属性本身的值包含一个UUID(如“值”部分所述,属性的值可以是任何数据类型),这次对应着该声明引入的实际服务的UUID。
虽然服务声明必须始终是服务中的第一个属性,但在下一个服务声明之前,通常可以添加其他许多属性,通常以特征和描述符的形式。
从概念上讲,你可以将GATT服务看作是任何现代面向对象语言中的一个类,完整包括实例化,因为一个服务可以在单个GATT服务器中被多次实例化(但这并不常见,因此大多数服务都类似于单例)。
在服务定义(也就是在服务内部),您可以添加对其他服务的一个或多个引用,使用include定义。include定义由一个包含所有客户端引用所需详细信息的单个属性(include声明)组成。
包含的服务可以帮助避免在GATT服务器中重复数据。如果一个服务将被其他服务引用,您可以使用这个机制来节省内存并简化GATT服务器的布局。在前面与类和对象的类比中,您可以将include定义视为指向现有对象实例的指针或引用。
同样,UUIDinclude (0x2802)是一个在include声明中独占使用的特殊SIG指定的UUID,而此时的值字段包含了包含服务的起始和结束句柄,以及其UUID。
2、特征
您可以将特征理解为用户数据的容器。它们至少包括两个属性:特征声明(提供有关实际用户数据的元数据)和特征值(一个完整的属性,其值字段中包含用户数据)。
此外,特征值后面可以跟随描述符,进一步扩展特征声明中包含的元数据。声明、值和任何描述符共同构成特征定义,即构成单个特征的属性束。如下表是特征声明和特征值属性的示例:
所有GATT特性都始终是服务的一部分,因此始终可以在其中找到。
2.1 特征声明属性
再次强调,特征声明属性的类型UUID(0x2803)是一个经过标准化且唯一的UUID,专门用于表示特征的开始。与所有其他声明(如服务和包含)一样,该属性具有只读权限,因为客户端只能获取其值,而不能修改。
如下表列出了特征声明的属性值中连接的不同项目。
这个特征声明属性值中包含了这三个字段:
- 特征属性:
这个8位的位域,连同扩展属性描述符中额外的两位,包含了可以与这个特征一起使用的操作和过程。这些10个属性中的每一个都被编码为下表中显示的位域上的一个单独的位。
客户端可以读取这些属性来了解它能执行哪些操作特征。这对于Notify和Indicate两个属性尤为重要,因为这些操作由服务器发起,但需要客户端先通过“客户端特征配置描述符”来启用它们。 - 特征值句柄:
这两个字节包含包含特征实际值的属性句柄。虽然这通常是如此,但你不应该假设该句柄与包含声明的句柄是连续的(即,0xNNNN+1)。 - 特征UUID:
特定特征的UUID,可以是SIG认可的UUID(当使用标准配置文件中包含的几十种特征类型时),否则可以是128位供应商特定的UUID。
继续采用类和面向对象的类比,特征就像类中的个别字段或属性,配置文件就像一个针对特定需求或目的利用一个或多个类的应用程序。
2.2 特征描述词
特征值属性包含实际的用户数据,客户端可以读取和写入以进行实际信息交换。该属性的类型始终与特征声明值字段中的UUID相同。因此,特征值属性不再具有服务或特征类型,而是具体的、特定的UUID,可以引用传感器的读数或键盘上的按键。
特征值属性的值可以包含任何类型的数据,从摄氏温度到按键扫描码到显示字符串到英里每小时的速度,任何可以在两个BLE设备之间有效传输的内容都可以填充该值的内容。
3、特征描述符
GATT特征描述符(通常简称为描述符)主要用于向客户端提供特征的元数据(有关特征及其值的附加信息)。它们始终位于特征定义中,并位于特征值属性之后。描述符始终由单个属性组成,即特征描述符声明,其UUID始终是描述符类型,并且其值包含特定描述符类型定义的内容。
在不同的GATT特征中,您可以找到两种类型的描述符:
- GATT定义的描述符:
这些是基础的、广泛使用的描述符类型,仅添加有关特征的元信息。以下几节描述了最常见的一些描述符。 - 配置文件或供应商定义的描述符:
无论配置文件是由SIG还是由特定供应商指定和发布,这些描述符都可以包含各种类型的数据,包括有关特征值的附加信息,例如从传感器获取值时使用的编码或补充读数本身的其他细节。
以下几节描述了GATT定义的一些最常用的描述符。
3.1扩展属性描述符
当存在时,该描述符只包含两个额外的属性位。
3.2特征用户描述符
顾名思义,该描述符包含了所放置特征的可供用户阅读的描述。这是一个UTF-8编码的字符串,例如可以是“客厅温度”。
3.3客户端特征配置描述符
这种描述符类型通常简称为CCCD,无疑是最重要和最常用的,对于大多数配置文件和用例来说都是至关重要的。它的功能很简单:作为一个开关,用于启用或禁用服务器发起的更新,但仅适用于所包含的特征。
为什么需要提供通知开关? 正如之前讨论的那样,客户端对服务器的属性事先一无所知,因此需要进行发现以了解服务器 上存在哪些服务、特征和描述符。服务器在每当特征值发生变化时会异步发送服务器主动更新, 该更新以一个只包含属性句柄和带有值的字节数组的数据包格式进行。 如果客户端尚未发现服务器上的所有特征和描述符句柄,那么可能无法将这些通知和指示中接 收到的数据与特定类型关联起来,使得这些无线电通信事务变得无用。此外,即使客户端可以 识别所有句柄及其对应的服务和特征,也可能有时候(可能是因为应用程序不可见,可能是因 为客户端仅使用服务器中的众多服务和特征之一),客户端并不希望接收更新。这就是CCCDs 的作用,允许对支持通知和指示的所有特征进行精细化的启用和禁用。
一个CCCD的值只是一个两位的位字段,其中一个位对应通知,另一个位对应指示。客户端可以随时设置和清除这些位,而服务器会在封装它们的特征值每次变化并且可能需要进行无线更新时检查它们。
每当客户端想要为支持它们的特征启用通知或指示时,只需使用写请求ATT数据包将相应的位设置为1。然后服务器会回复写响应,并在需要提醒客户端某个值发生变化时开始发送适当的数据包。
此外,CCCD还具有两个与其他属性不同的特殊属性:
- 它们的值在每个连接中都是唯一的:
在多连接场景中,即中心设备同时连接多个外围设备并作为GATT服务器的情况下,每个外围设备在使用ATT读取CCCD时都会收到自己的值副本。 - 它们的值在与绑定设备的连接中保持不变:
“属性缓存”一节详细讨论了属性缓存,但只涉及属性句柄。值通常不会按设备存储,并且GATT服务器可以在连接之间重置它们。但CCCD在绑定设备之间不会发生这种情况:在服务器上由客户端写入CCCD的最后一个值在重新连接时将被恢复,无论连接之间经过多长时间。
许多协议堆栈都具有处理CCCD的特殊机制,从客户端和服务器的角度来看,因为它们对于正确的操作和确保及时的数据更新至关重要。
3.4特征呈现格式描述符
如果存在,此描述符类型在其七字节的属性值中包含封装特征值的实际格式。可用的格式列表包括布尔值、字符串、整数、浮点数,甚至是通用的无类型缓冲区。
4、示例服务
本节介绍了当今许多商业产品中的一项特定服务的示例。心率服务(HRS)向监测设备公开了用户的心率。
如下图展示了虚构服务器上的一个HRS实例。这不是服务器中唯一的服务,所以您可以将其看作客户端可以访问的完整属性集的部分切片。
下面是对上图中HRS服务的逐个描述:
- 句柄0x0021:
该属性包含心率服务的服务声明。该服务声明属性的字段如下:
①UUID:
UUID是表示主服务声明的标准16位UUID,是primary service (0x2800)。
②值:
该值是心率服务的16位UUID,由SIG分配(0x180D)。 - 句柄0x0024
该属性包含心率测量特征的特征声明。该特征声明属性的字段如下:
①UUID:
UUID是表示特征声明的标准16位UUID,是characteristic (0x2803)。
②值:
该特征的特征属性是只通知,特征值句柄是0x0027,特征值UUID是Heart Rate Measurement的UUID(0x2A37)。 - 句柄0x0027:
该属性包含特征值,在这种情况下是心率测量本身。该特征值属性的字段如下:
①UUID:
与特征定义属性值的最后两个字节相同的UUID。
②权限:
该属性的值既不能读取也不能写入:客户端只能通过服务器发送的通知来获取其值。
③值:
实际的心率测量值(为了清晰起见,以每分钟的心跳虚构表示)。 - 句柄0x0028:
该属性包含CCCD。该CCCD属性的字段如下:
①UUID:
任何CCCD的UUID始终是标准的16位UUIDCCCD (0x2902)。
②权限:
CCCD必须始终是可读和可写的。执行这些操作所需的安全级别由配置文件或应用程序定义。
③值:
如前所述,CCCD的值是一个位域,在这种情况下为0x0001,表示启用了对特定HRM特征的通知。 - 句柄0x002A:
该属性包含另一个特征声明,这次是为了身体传感器位置特征。该特征声明属性的字段如下:
①UUID:
UUID是表示特征声明的标准16位UUID,是characteristic (0x2803)。
②值:
该特征的特征属性是只读,特征值句柄是0x002C,特征值UUID是Body Sensor Location的UUID (0x2A38)。 - 句柄0x002C:
该属性包含特征值,在这种情况下是身体传感器位置。该特征值属性的字段如下:
①UUID:
与特征定义属性值的最后两个字节相同的UUID。
②权限:
该属性的值只能读取:客户端只能查询传感器的位置,而不能修改其位置(由服务器决定)。
③值:
实际的身体传感器位置。
六、高级属性概念
这篇文章介绍了与属性相关的额外概念,这些概念的理解经常在许多类型的BLE应用中需要。属性缓存是其中一个概念。
1、属性缓存
属性句柄允许客户端单独访问服务器上的所有可用属性。发现可用句柄的列表以及它们各自属性的内容可能是一个耗时(和耗电)的过程。但是暂时来说,这部分描述了客户端在重新连接到服务器时可以避免执行发现过程的方法,以及在什么情况下可以这样做。
服务器通常倾向于维护一组稳定的属性,并且在服务器设备的生命周期中,它们的基本结构在大多数情况下不会发生变化。但是在这方面,实施并没有强制性的限制,服务器完全可以完全修改其属性,甚至在任何时间(通过固件更新或者在服务器设备上安装应用程序等)用一个全新的属性集来替换它们。因此,必须有一定的规则和约束,以使客户端可以依赖于先前发现的句柄的有效性,而不会因为它们在服务器上已经被修改而不再有效。
作为一般规则,规范建议客户端对其感兴趣的属性的句柄进行缓存(即存储用于后续事务甚至连接)。属性值,特别是对于与实际用户数据对应的情况,是非常易变的,因此通常没有必要在客户端本地存储它们以供将来使用。
规范提供了“Service Changed”特征用于服务器向客户端传达其属性信息内容的任何潜在更改。这是一个可选的特征,因此它在服务器上的存在已经作为有关可能发生的结构性属性更改的警告。
通过观察以下条件,客户端可以确定发现的结果是否可以缓存以供将来使用:
- 服务器上没有“Service Changed”特征:
客户端可以自由且永久地缓存所有找到的句柄,没有任何限制。服务器保证这些句柄在设备的生命周期内不会发生变化。 - 服务器上存在“Service Changed”特征:
在这种情况下,客户端需要订阅服务器-initiated的更新,通过写入包含在“Service Changed”特征中的相应CCCD。这将允许服务器通知客户端任何结构性更改。
2、广告数据包中的GATT属性数据
如下表所示,要能够广播服务数据,GATT服务器必须在广告数据包的服务数据部分中包含两个不同的字段。
服务数据字段的内容可以与相应服务中的特定特性或描述符的完整或部分值对应。每个配置文件规范决定哪些数据最相关以进行广播,因为只有配置文件才具备足够的知识来决定。
七、功能特点
GATT功能特点是严格定义的过程,允许基于GATT的数据交换发生。它们都基于ATT提供的不同操作。
在某种程度上,本章列出的大多数功能在大多数GATT API中以某种方式公开。GATT服务器API添加了将实际服务器填充到属性的能力,但这严重依赖于实现,并超出了本章的范围。
1、交换MTU
这个简洁的过程使用两个数据包,让每个ATT对等体了解对方的缓冲区能够容纳的最大传输单元(MTU,或者说是最大数据包长度)。
只有在客户端或服务器(或两者)能够处理比默认的ATT_MTU(23字节)更长的MTU,并且希望通知对方可以发送比规范要求的默认值更长的数据包时,才会使用此过程。L2CAP然后会将这些更大的数据包分割成小的链路层数据包,并从小的链路层数据包重新组合它们。
2、服务和特征发现
- 发现所有主服务:
使用此功能,客户端可以从远程服务器检索到所有主要服务的完整列表(无论服务UUID如何)。当客户端支持多个服务并且想要了解服务器端的完整服务支持时,通常会使用此功能。因为客户端在发出所需请求时可以指定一个句柄范围,所以必须将0x0001-0xFFFF设置为句柄范围以实现此功能,覆盖服务器的全部属性范围。 - 通过服务UUID发现主服务:
只要客户端知道自己要寻找的服务(通常是因为它自身仅支持该单个服务),它就可以使用此功能来查找特定服务的所有实例,同时要求将句柄范围设置为0x0001-0xFFFF。
每个过程都产生指向属于单个服务的属性的句柄范围。发现所有主服务功能还可以获取各个服务的UUID。
当客户端已经在服务器上找到服务时,可以使用以下功能执行关系发现(即查找包含的服务):
- 查找包含的服务:
这允许客户端查询服务中包含的任何服务。查询中提供的句柄范围指的是先前使用服务发现获得的现有服务的边界。与服务发现一样,当适用时,客户端还会收到一组句柄范围和UUID。
在特征发现方面,GATT提供以下选项:
- 发现服务的所有特征:
一旦客户端获得了可能感兴趣的服务的范围,就可以继续检索其所有特征的完整列表。唯一的输入是句柄范围,服务器则返回该服务中包含的所有特征声明属性的句柄和值。 - 按UUID发现特征:
此过程与之前的过程相同,只是客户端会丢弃与其目标特定特征UUID不匹配的所有响应。
一旦确定了目标特征的边界(以句柄为基准),客户端就可以进行特征描述符发现:
- 发现所有特征描述符:
现在,客户端拥有某个服务中一些或所有特征的句柄范围和UUID,可以使用此功能来检索特定特征中的所有描述符。服务器会回复一个UUID和句柄对的列表,对应不同的描述符声明。
本节中的所有功能均可在开放的、未加密的连接上执行,因为所有客户端都可以进行发现,没有任何限制。
3、阅读特征和描述符
为了获取特征值或描述符的当前值,客户端有以下选择:
- 读取特征值或描述符:
使用句柄可以简单地读取特征值或描述符的内容。只能读取内容的前ATT_MTU-1字节,因为这是响应数据包中能够容纳的最大字节数(1个字节被保留为ATT操作码)。 - 读取长特征值或描述符:
如果值过长,不能通过前一个功能进行读取,该功能在请求中包含了句柄以及偏移量,使得可以分段读取特征值或描述符的内容。根据要读取的属性值的长度,可能需要多个请求/响应对。
此外,仅适用于特征值的以下功能也可用:
- 使用特征UUID读取特征值:
当客户端不知道感兴趣的特征的具体句柄时,可以读取特定类型的所有特征的值。客户端只需提供句柄范围和UUID,并接收包含在该范围内的特征值数组。 - 读取多个特征值:
相反,如果客户端已经拥有一组要获取值的特征的句柄,可以发送带有这组句柄的请求,然后接收所有相应特征的值。
阅读特征和描述符受安全权限限制,如果连接的安全级别与已建立的要求不匹配,服务器可能会拒绝权限。
阅读特征和描述受安全权限限制,如果连接的安全级别不符合设定的要求,服务器可以拒绝权限。
4、写特征值和描述符的特点和属性
要写入特征值或描述符的值,客户端有以下选择:
- 写特征值或描述符:
这个功能用于写入特征值或描述符。客户端提供一个句柄和值的内容(最多ATT_MTU-3字节,因为句柄和ATT操作码包含在数据包中),服务器会用响应确认写操作。 - 写长特征值或描述符:
与读长特征值或描述符功能类似,允许客户端将多于ATT_MTU-3字节的数据写入服务器的特征值或描述符。它通过排队多个准备写操作来工作,每个操作都包括偏移量和数据本身,然后最后通过执行写操作将它们原子化地写入。
此外,仅适用于特征值的以下功能可用:
- 无应答写入:
这个功能相当于通知的相反情况,它使用写命令数据包。写命令是未确认的数据包,包括一个句柄和一个值,可以在任何时间发送任意数量的写命令数据包,而无需任何流控机制介入(当然,除了原生链路层流控,因为所有流量都受其约束)。如果服务器无法处理或属性权限不允许接受写操作,服务器可以自由地静默丢弃这些数据包。客户端将永远不会知道,但这是相互同意的。客户端唯一可以知道是否成功写入值的方法是在事后进行读操作。 - 可靠写入:
类似于读取多个特征值的功能,当客户端想要排队写操作到多个特征值时,它会发送最后一个数据包来提交待定的写操作并执行它们。
写特征值和描述符受安全权限限制,如果连接的安全级别与建立的要求不匹配,服务器可以拒绝权限。
5、服务器发起的更新
服务器发起的更新是唯一可以从服务器流向客户端的异步(即不作为对客户端请求的回应)数据包。这些更新会及时地提醒特征值的变化,而无需客户端定期轮询,从而节省功耗和带宽。
有两种类型的服务器发起的更新:
- 特征值通知:
通知是包含特征值属性的句柄及其当前值的数据包。客户端接收它们后可以选择采取行动,但不需要向服务器发送确认接收的回复。
除了不需要回应的写入,这是ATT中唯一不符合标准请求/响应流控机制的其他数据包,因为服务器可以随时发送任意数量的这些通知。此功能使用句柄值通知(HVN)ATT数据包。 - 特征值指示:
指示与通知使用相同的句柄/值格式,但需要客户端显式确认的回复。请注意,尽管服务器在收到客户端的确认之前无法发送进一步的指示(即使是针对不同的特征值),因为这与通常请求/响应配对的流向相反,但未决的确认不会影响客户端在此期间可能发送的请求。
此功能使用句柄值指示(HVI)和句柄值确认(HVC)ATT数据包。
在服务器开始发送它们之前,客户端必须通过写入相应的CCCD启用这两种类型的服务器发起的更新。
八、安全
GAP认证过程通过Security Manager和GAP提供的不同手段从一种安全模式过渡到更高、更安全的模式。GATT事务可以作为这种认证过程的触发器。GATT服务器中的每个属性都有细粒度的独立权限,用于读取和写入,这些权限在ATT级别上强制执行。
一般来说,声明为attributes的属性无需特殊的安全措施即可访问。这对于服务和特征声明都是正确的,但对于描述符声明则不适用,描述符声明直接包含相关数据,而不是在一个独立的属性中。这样做是为了使尚未与服务器配对或绑定的客户端至少能够执行基本的服务和特征发现,而无需进行安全程序。服务器的属性布局和数据层次结构不被视为敏感信息,因此对所有客户端都是可自由获取的。
然而,当访问特征值或描述符声明(也称为服务请求)时,客户端可能会收到一个错误的ATT数据包,指示连接的当前安全级别不足以执行请求。以下两个错误代码通常用于此目的,并放置在错误的响应数据包中:
- 身份验证不足:
表示链路未加密,服务器没有可用于加密链路的长期密钥(LTK),或者链路确实加密,但用于执行加密过程的LTK未经身份验证,而需要权限进行经过身份验证的加密。 - 加密不足:
表示链路未加密,但可用适当的LTK。
GAP和GATT角色没有任何关联,可自由混合和匹配,但安全程序始终由GAP中心启动。因此,根据中心和外设的不同,可以由GATT客户端或GATT服务器来发起配对、绑定或加密的过程,以提高连接的安全级别。一旦安全级别符合属性的权限要求,客户端可以重新发送请求以在服务器上执行。
九、GATT 服务
就像GAP有其自己的SIG规定的服务,对于所有设备而言都是强制性的,GATT也有自己的服务(最多含有一个特征),必须在所有GATT服务器中包含。可选的服务改变特征,不能被读取或写入,其值只能通过特征值指示向客户端通信。
如下图所示,该值仅由一个句柄范围组成,该范围限定了服务器中的特定属性区域。这是由结构变化所影响并需要客户端重新发现的区域。客户端将需要在该区域执行服务和特征发现,因为它可能无法继续使用其缓存的属性。
在进行其他任何操作之前,客户端必须在相应的CCCD(Client Characteristic Configuration Descriptor)上启用指示,以便对服务器的属性结构变化有所了解。
如果服务器在属性布局上发生结构性变化,它会立即向客户端发送一个句柄值指示,并等待相应的确认。通过这种方式,它可以确保客户端知道该范围内的缓存属性句柄可能不再有效。如果属性在与绑定设备的连接寿命之外发生更改,服务器将在连接建立后立即发送指示,以便客户端有机会重新发现受影响的区域。
总结
本章概述了通用属性配置文件(GATT),该配置文件建立了在BLE中用于表示和操作数据的层次结构和格式。它介绍了服务和特征的基本概念,以及允许连接设备彼此交换数据的过程。
关注公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/151381.html