显微镜下的 BLE (ATTы GATTы...)

显微镜下的 BLE (ATTы GATTы...)

显微镜下的 BLE (ATTы GATTы...)

第 1 部分,概述

自第一个蓝牙 4.0 规范发布以来,已经过去了相当长的一段时间。 而且,尽管 BLE 主题非常有趣,但由于其复杂性,它仍然让许多开发人员望而却步。 在我之前的文章中,我主要看的是最底层,链路层和物理层。 这使我们能够避免求助于属性协议 (ATT) 和通用属性配置文件 (GATT) 等复杂且令人困惑的概念。 然而,无处可去,不了解它们,就不可能开发出兼容的设备。 今天我想与大家分享这些知识。 在我的文章中我将依赖 教科书 来自 Nordic 网站的初学者。 那么让我们开始吧。

为什么一切都那么难?

在我看来,很明显通过智能手机管理设备是一个非常有前途且持久的话题。 因此,他们决定立即最大限度地构建它。 因此,各种设备的制造商不会提出自己的协议,从而导致不兼容。 因此困难就来了。 在第一阶段,他们就尝试将所有可能的内容都压缩到 BLE 协议中。 而且以后有没有用并不重要。 此外,它们还提供了未来扩展设备列表的可能性。

我们看一下绘制BLE协议图的图。 它由几层组成。 最低的物理层 (PHY) 负责设备的无线电信道。 链路层 (LL) 包含传输消息中的整个字节序列。 在之前的文章中我们正是研究了这一点。 如果控制器和主机在不同的芯片上实现,则主机控制器接口 (HCI) 是 BLE 层或芯片之间的交换协议。 逻辑链路控制和适应协议(L2CAP)负责数据包形成、成帧、错误控制和数据包组装。 安全管理器协议 (SMP) 负责加密数据包。 通用访问配置文件 (GAP) 负责设备之间的初始数据交换,以确定“谁是谁”。 它还包括扫描和广告。 在本文中,我将重点讨论协议的其余两个部分 - GATT 和 ATT。 GATT是ATT的上层建筑,两者紧密相连。

显微镜下的 BLE (ATTы GATTы...)

为了简化这个故事,我想打个比方。 我在某处听到过并且想支持它。 将 BLE 设备视为带有多个架子的书柜。 每个架子都是一个单独的主题。 例如,我们的书架上放着科幻小说、数学和百科全书。 每个书架上都有特定主题的书籍。 有些书甚至还有带有笔记的纸质书签。 此外,我们还有一个包含所有书籍的小型纸质目录。 如果您还记得的话,学校图书馆是一个装有纸卡的窄盒子。 以此类比,机柜就是我们设备的轮廓。 书架是服务,书籍是特性,目录是属性表。 书籍中的书签是描述符,我稍后也会更详细地讨论。

开发过设备的人都知道,很多项目都有类似的代码片段。 事实上,许多设备都具有类似的功能。 例如,如果设备由电池供电,那么充电和监控其电量的问题将是相同的。 传感器也是如此。 实际上,面向对象的编程方法 “提供了创建对象的能力,这些对象将属性和行为组合成一个独立的联合,然后可以重用”。 在我看来,BLE 尝试了类似的方法。 配置文件由蓝牙特别兴趣小组 (SIG) 开发。 来自不同制造商、具有相同配置文件的设备应该可以毫无困难地相互协作。 反过来,配置文件由服务和特征服务组成,并辅以描述符。 一般来说,它可能看起来像这样:

显微镜下的 BLE (ATTы GATTы...)

例如,考虑心率监测器(健身手环)的轮廓图。 它由两项服务和几个特征组成。 由此,配置文件层次结构立即变得清晰。 检查点特征将总卡路里消耗计数重置为零。

1、心率服务包含三个特征(0x180D):
    a) 强制心率特征(0x2A37)
    b) 可选的身体传感器位置特性 (0x2A38)
    c) 心率控制点的条件特征(0x2A39)
2、电池维护服务(0x180F):
    a) 强制电池充电水平特征 (0x2A19)

UUID

为了让我们能够唯一地访问配置文件元素(服务、特征和描述符),我们需要以某种方式对它们进行编号。 为此,引入了通用唯一ID(UUID)或通用唯一标识符等概念。 UUID 在每行的括号中指示。 这里有一个特点。 对于 UUID,我们决定使用长度为 16 和 128 位的代码。 你为什么问? 在BLE协议中,一切都是为了节能。 因此,16位的维度是相当合理的。 在不久的将来创建的数量不太可能超过 65 个。 独特的服务和特色。 目前,他们可以计算的所有内容(记住这来自哪里 - “他也计算了你”:-))编号元素 简介, 服务, 特点 и 描述符 你可以看看链接。

不过,我想大家都还记得互联网上4字节IP地址的故事。 起初我们认为这就足够了,但现在我们仍然无法切换到 6 字节地址。 为了不再重蹈覆辙,并让 DIYers 自由发挥,SIG 立即决定引入 128 位 UUID。 这让我个人想起了未经许可的 433 MHz 频段,该频段是从无线电频道分配给各种 Kulibins 的。 在我们的例子中,服务和特征的 128 位标识符被外包。 这意味着我们的服务和设备几乎可以使用任何 128 位值。 尽管如此,得出相同 UUID 的概率趋于零。

事实上,短 16 位 UUID 已扩展为 128 位值。 在规范中,此扩展称为蓝牙基本 UUID,其值为 00000000-0000-1000-8000-00805F9B34FB。 例如,如果 16 位属性 UUID 的值为 0x1234,则等效的 128 位 UUID 的值为 00001234-0000-1000-8000-00805F9B34FB。 甚至还给出了相应的公式:

                                128_bit_value = 16_bit_value * 2^96 + 蓝牙_Base_UUID

我不知道这个神奇的数字是从哪里来的。 如果有读者知道,请在评论中留言(昵称Sinopteek的用户已经这样做了,请参阅评论)。 至于提出128位UUID,原则上你可以使用特殊的 发电机谁会为你做这件事。

阿蒂·盖蒂...

事实上,然后有趣的事情就开始了。 让我提醒您,ATT 基于客户端-服务器关系。 现在我们正在研究服务器设备。 它包含传感器值、灯开关状态、位置数据等信息。 现在所有“游行参与者”都已编号,我们需要以某种方式将它们放入设备的内存中。 为此,我们将它们放入一个称为属性表的表中。 记住这一点。 这是 BLE 的核心。 这是我们将进一步考虑的。 现在我们将每一行称为一个属性。 该表位于堆栈深处,通常我们无法直接访问它。 我们初始化它并访问它,但内部发生的事情对我们来说是隐藏在七个封印后面的。

让我们看一下规范中的图片,但在此之前,我想立即提请注意术语(即描述符)中经常出现的混淆。 描述符的作用是补充特征的描述。 当需要扩展其功能时,就使用描述符。 它们也是属性,就像服务和特征一样,它们位于属性表中。 我们将在本文的第二部分详细研究它们。 然而,有时描述符引用属性表中的行号。 必须牢记这一点。 为了避免混淆,我们将使用术语“属性指针”来实现这些目的。
显微镜下的 BLE (ATTы GATTы...)

因此,属性是一个离散值,具有以下关联属性:
1、Attribute Handle是属性对应的表索引
2. Attribute Type是描述其类型的UUID
3. 属性值是属性指针索引的数据
4. 属性权限是属性的一部分,即权限,不能使用属性协议读取或写入

如何理解这一切呢? 相对来说,属性指针就是它在我们表中的编号。
它允许客户端在读或写请求中引用属性。 我们可以将行(属性)编号为 0x0001 到 0xFFFF。 在我们与书柜的关联中,这是纸质目录中的卡号。 类似地,就像在图书馆目录中一样,卡片按照编号递增的顺序排列。 后续每一行的编号必须大于前一行。 就像在图书馆一样,有时有些卡片会丢失,所以对于我们来说,行编号可能会存在间隙。 这是允许的。 最主要的是他们是循序渐进的。

属性类型决定了属性代表什么。 类比C语言,
其中有布尔值、数值变量和字符串,所以就在这里。 通过我们识别的属性类型
我们正在处理什么以及我们如何继续利用此属性。 下面我们将看看一些特定类型的属性。 例如,“服务声明”(0x2800)、“特性声明”(0x2803)、“描述符声明”(0x2902)。

属性的值就是它的实际含义,请原谅同义反复。 如果属性类型是字符串,则属性值可以是例如口号“Hello World!!!”。 如果属性类型是“服务声明”,那么它的值就是服务本身。 有时,这是有关在哪里可以找到其他属性及其属性的信息。

属性权限允许服务器了解是否允许读或写访问。
请注意,这些权限仅适用于属性值,而不适用于指针、类型或权限字段本身。 那些。 如果允许属性记录,那么我们可以更改,例如“Hello World !!!”这一行到“早上好”这一行。 但我们不能禁止写入新行或更改属性类型并将该行指定为“服务声明”。 当客户端联系服务器时,客户端请求其属性。 这允许客户端知道服务器可以提供什么。 尽管没有必要读取和写入这些值。

它看起来像什么

GATT 的概念是以非常具体和逻辑的顺序将属性表中的属性分组在一起。 让我们仔细看看下面的心率曲线。 该表最左边的列是可选的。 它只是向我们描述了这条线(属性)是什么。 所有其他列我们都已经熟悉了。

显微镜下的 BLE (ATTы GATTы...)

在每个组的顶部,我们始终有一个服务声明属性。 它的类型始终是 0x2800,并且指针取决于表中已经存在的属性数量。 它的权限始终是只读的,没有任何身份验证或授权。 我们稍后会讨论这些概念。 该值是另一个 UUID,用于标识服务是什么。 表中值为0x180D,蓝牙SIG将其定义为心率服务。

发布服务后,接着发布特性。 它在形式上与服务声明类似。 其UUID始终为0x2803,其权限始终为只读,无需任何身份验证或授权。 让我们看一下“属性值”字段,其中包含一些数据。 它始终包含一个指针、一个 UUID 和一组属性。 这三个元素描述了特征值的后续声明。 该指针自然表示特征值声明在属性表中的位置。 UUID 描述了我们可以期望什么类型的信息或值。 例如,温度值、灯开关的状态或一些其他任意值。 最后是属性,它描述了如何与特征值交互。

另一个陷阱在这里等待着我们。 它与属性权限和特性属性相关联。 让我们看一下规范中位域属性的图片。

显微镜下的 BLE (ATTы GATTы...)

正如你所看到的,这里还有提供读写功能的字段。 您可能想知道为什么我们对属性和属性具有读/写权限
读/写特征值? 它们不应该总是一样吗? 事实上,特征值的属性实际上只是针对 GATT 和应用层中使用的客户端的建议。 这些只是关于客户端可能期望从特征声明属性中得到什么的提示。 让我们更详细地看看这个。 属性具有哪些类型的权限?

1、访问权限:
     - 读
     - 记录
     - 读和写
2、认证权限:
     - 需要身份验证
     - 无需身份验证
3、授权许可:
     - 需要授权
     - 无需授权

属性解析和特征属性之间的主要区别在于前者适用于服务器,后者适用于客户端。 可以允许服务器读取特征值,但可能需要认证或授权。 因此,当客户端请求该特征的属性时,我们将收到允许读取的消息。 但是当我们尝试读取时,会出现错误。 因此,我们可以放心地谈论权限相对于属性的优先级。 在客户端,我们无法了解属性具有哪些权限。

描述符

让我们回到我们的桌子。 声明特征值后,可以进行以下属性声明:
1.新的特征声明(一个服务可以有多个特征)
2.新服务声明(表中可能有很多)
3. 声明句柄

对于心率测量特征,在我们的表中,特征值的声明伴随着描述符的声明。 描述符是具有有关特征的附加信息的属性。 描述符有多种类型。 我们将在本文的第二部分详细讨论它们。 现在,我们只讨论客户端特征配置描述符 (CCCD)。 它的 UUID 等于 0x2902。 使用此描述符,客户端能够在服务器上启用指示或通知。 它们之间的差异很小,但仍然存在。 通知不需要客户确认收到。 指示要求这样做,尽管它发生在关贸总协定级别,但没有达到应用程序级别。 你问为什么会这样? 唉,我不知道这个。 我只想说北欧专家建议使用通知。 此外,在这两种情况下都会检查包的完整性(使用 CRC)。

结论

在文章的最后我想说一下这一点。 最后一张表有点令人困惑。 然而,我选择它是因为它给出了 文章,我所依赖的。 在文章的第二部分中,我打算更深入地研究蓝牙 4.0 规范。 更正确的图表和图画在那里等待着我们。 在第三部分中,我想解析使用 Wireshark 程序从其中一个小工具获取的日志,并“实时”查看我们正在研究的所有理论。

集团公司员工 “凯撒卫星”
弗拉基米尔·佩切尔斯基赫

来源: habr.com

添加评论