蓝牙mesh的协议栈

这里是关于第三篇蓝牙mesh相关的文章,该写什么?有三个东西可以写:1.蓝牙mesh中的节点 2.蓝牙mesh中的专业术语 3.蓝牙mesh协议栈。经过反复思考,基于对新了解蓝牙mesh网络朋友的角度出发,还是觉得应该写蓝牙mesh协议栈。因为上一篇讲的是蓝牙mesh的网络拓扑,主要是蓝牙mesh 相关的概念,在上一篇的基础上再讲这一篇,下一篇再讲蓝牙mesh中的专业术语,这个学习路径相对简单与轻松。

蓝牙mesh协议栈,准确来讲是由两个部分组成。配网协议和节点消息发送协议(七层网络系统架构)。

如果将蓝牙mesh协议栈,等同于7层网络协议,这是不准确的。为什么这么说?在前一篇中讲到,设备在未加入网络前叫未配网设备,加入网络后叫做节点。这个过程叫做配网流程。而实现这个配网流程的是配网协议,而不是7层网络架构。所以可以这么说,设备配网成功前使用的是配网协议,设备配网成功后,使用的是七层网络协议。我们在接触蓝牙mesh的第一个操作,那绝对是将设备配网,如果你不知道配网协议,那就悲剧了。

配网协议

首先,看一下配网协议,由3个层组成:1、承载层(Bearer) 2、传输层(Transport) 3、配网协议层(Provisioning Protocol)

配网协议.png

配网协议的承载层跟7层网络架构的承载层差不多是一样的,都是使用BLE的广播或者GATT连接进行数据交互,但是在服务和特性,以及消息数据报上有细微的区别。


服务特性对比.png

ProxyPDU在代码中通常放在Network Layer里面合并处理,所以有的书籍或者文章中,将配网协议的前两层用七层架构的前两层进行描述。但Provisioning Protocol在蓝牙mesh中是唯一,在代码中要单独处理。配网协议和配网流程的每个步骤,后面的文章中再做详细的解读。

代理功能:为了兼容旧的不支持蓝牙mesh广播包传播的设备(比如手机),具备代理功能的设备可以与旧设备建立低功耗蓝牙GATT连接,在mesh广播数据包和mesh GATT连接数据包之间转换,支持代理功能的节点被称为代理节点(Proxy Node)。(如果暂时对手机不支持蓝牙mesh广播包传输这句话不理解的朋友,我会在后面的文章中给出解释)

在上一篇中,有提到过这个问题。为什么手机不支持蓝牙mesh广播包?如果熟悉BLE的朋友肯定知道,手机既可以做主机搜索附近的设备,也可以做从机,向四周发送广播信号。但是这里说的广播包是指蓝牙mesh协议中的配网广播包,手机在出厂的时候并不会把mesh协议自带到系统里面。我们在把手机当成从机,向外广播时,是我们自己定义的广播内容,手机只是有这个通道。这也是为什么手机APP没法通过广播的形式对设备进行配网的原因,通道里面传什么内容,需要我们自己定义。

手机APP实现配网流程一定是走代理协议建立GATT连接的方式。因为在前一篇中说过,整个蓝牙mesh 是基于广播和GATT连接这两个核心,它只有这两种方式。对应的是广播承载器和GATT承载器,在后面讲到承载层的时候,还会再详细的讲这个部分。

蓝牙mesh协议栈的本质

从蓝牙1.0到蓝牙5.0,每一个版本的出现,只要手机支持,对应的全部功能就可以使用。但是蓝牙mesh不是,蓝牙mesh 是蓝牙4.0后蓝牙Sig发布的一套协议规范,并不是硬件能力相关的东西。所以,蓝牙mesh协议栈的本质是在蓝牙BLE4.0或者BLE5.0版本基础上建立的一个应用程序,应用层是蓝牙mesh,底层是BLE4.0,或者BLE5.0。

节点消息七层网络架构

这里面的每一层,都足够写一篇文章,这里也不详细的讲解。


七层网络架构.png

模型层定义了用于标准化典型用户场景操作的模型,这些模型在蓝牙mesh模型规范或其他更高的层规范中定义。更高层次模型规范的例子包括照明和传感器模型。
通俗点讲,就是协议中的应用层,你可以根据自己的业务需求,按蓝牙模型规范,制定符合自己业务需求的模型。

基础模型层定义配置和管理mesh网络所需的访问层状态、消息和模型。

访问层定义了高层应用程序如何使用上层传输层。它定义应用程序数据的格式;它定义和控制在上层传输层执行的应用程序数据加密和解密;并且在将传入的应用程序数据转发到更高的层之前,它会检查是否在正确的网络和应用程序键的上下文中接收到了传入的应用程序数据。

上层传输层对应用程序数据进行加密、解密和身份验证,并提供访问消息的机密性。它还定义了如何使用传输控制消息来管理节点之间的上层传输层,包括由Friend特性使用时。

底层传输层定义了如何将上层传输层的消息分割并重新组装成多个底层传输层的pdu,以将较大的上层传输层消息传递给其他节点。它还定义了一个单独的控制消息来管理分割和重组。

网络层定义如何向一个或多个元素处理传输消息。它定义了允许传输pdu由承载层传输的网络消息格式。网络层决定是否中继/转发消息,接受它们作进一步处理,或拒绝它们。它还定义了网络消息如何加密和身份验证。

承载层定义网络消息如何在节点之间传输。定义了两种承担者,广告承担者和关贸总协定承担者。将来可能会定义更多的承担者。

蓝牙mesh网络中有一个比较让人头疼的问题:数据大小端编码问题,什么是大小端?

大端编码
当多字节值定义为以“大端”发送时,适用本节中的约定。例如,值0x123456应以0x12、0x34和0x56(最高有效字节优先)的形式传输。
小端编码
当多字节值定义为以“小端”发送时,适用本节中的约定。例如,值0x123456应该以0x56、0x34和0x12的形式传输(最低有效字节优先)。

一个字节的时候,大小端都是一样。

蓝牙mesh 中的大小端规范
对于网络层、下层传输层、上层传输层、mesh beacons和Provisioning,所有多字节的数值都应以大端方式发送。
对于访问层和基础模型,所有的多八位数值应以小端序

大小端只是针对协议中的某个字段内部,当一个数据由多个字段组成时,多个字段的位置不适用大小端的规则。

2.1整个网络的本质

配网过程是比较无聊却又不得不做的一个事情。假设一种场景,一栋楼里面有很多个灯,管理员张三将网络配置好后,如何将网络共享给员工李四管理。这里就有一个网络分享的问题,在第一部分的初步认识部分提到过用JSON数据,这个功能对应到APP中就是网络的导入与导出。

将这个JSON数据从一台手机上导出后,能在另一台手机上操控整个网络,那么就说明了整个网络设置参数全部都在JSON数据中。从这个上帝视角上看,可以把Mesh网络分成3个部分:(1)用户的操作(2)JSON数据 (3)协议。如果将Mesh网络比作一个加工厂的话,用户的操作是原材料,那协议就是流水线,而JSON数据就是流水线上机器的运行参数。同样的参数放在同样机器上,运行结果是一样的,所以JSON数据从一台手机,共享到另外一台手机,对网络的控制是一样的。

那么JSON数据的内容是什么呢,具体内容看下图:

mesh网络的本地数据总览.png

对其中每个字段做一个简单的解释:

meshUUID:每个网络的唯一标识符,程序自动生成的加密随机数。在网络导入时,可能不是本网络的信息,所以需要通过这个唯一标识符进行过滤。

Provisioners:配网器。通常来说,弄一个配网器就够了。但配网器可以存在多个,比如建立多个配网器实现不同的地址分配方式、不同的地址区间。但是同一时间只能有一个配网器处于激活状态。

nodes:节点。网络中存在几个节点,Nodes中就有几个Object。

id:开发者给网络标记的标识符。假如我们的APP,不想接受其他开发者的网络数据,我们可以根据id对网络进行过滤,在网络导入时,只接收指定ID的数据。

groups:分组信息。将多个节点统一管理,可根据场景自行设置。比如将客厅的几个灯放在一组,实现同时开关灯。

netKeys:网络密钥。应用于网络层的安全通信。

$schema:表示该JSON Schema文件遵循的规范。

version:版本号。

timestamp:最后一次修改网络的时间。

meshName:网络名称。

appKey:应用程序密钥。应用与上层传输层的安全通信。

总结,可以看出mesh网络数据主要由(1)配网器(2)节点 (3)分组 (4)密钥 ; 这4个部分组成。分别对应到(1)设备加入网络的过程(2)网络中所有节点的信息(3)分组控制功能(4)安全通信。

2.2 配网器与地址

这里是狭义上的配网器,代码中的配网器,内容如下:

配网器的数据结构

从配网器的JSON数据中,我们可以看到配网器由5部分组成:(1)配网器名称 (2)配网器唯一标识符UUID (3)可分配单播地址的范围 (4)可分配组播地址的范围 (5)可分配虚拟地址的范围。

配网器的主要功能就是分配地址,那在mesh网络中,地址是怎么定义的?

mesh中地址的范围

网络层定义了4种地址类型,地址长度为16位。 如下图: 单播地址范围从0x0001到0x7FFF,可以有32767个单播地址。 虚拟地址范围从0x8000到0xBFFF,可以有16384个虚拟地址。 组播地址范围从0xC000到0xFFFF,可以有16384个组播地址。组播地址包括256个固定组播地址和16128个可动态分配的组播地址。

总结,地址就是一个整数,配网器确定如何给节点分配。配网器只需要遍历网络中的节点,就知道哪些地址已经分配下去。如果需要重新分配一个地址,则分配一个没使用过的地址就行。

2.3 节点

节点的数据结构

节点的信息并不是完全一致,可以从配网器节点和蓝牙设备节点看出区别。左边是配网器节点,右边是设备节点。对所有属性做简单的介绍。

features:如下图,左边是配网器节点的Features,右边是设备节点的Features。proxy指代理功能,friend指朋友功能,relay指转发功能,lowPower指低功耗功能。4个功能都是3种状态,0表示未开启,1表示已开启,2表示不支持。举个例子,APP中设置节点打开或者关闭转发功能,实际上就是改变对应节点Feature特性中的relay字段。

节点中属性示例.png

secureNetworkBeacon:用于让蓝牙mesh节点来明确子网状态及自身的安全状态。在IV Index Update以及Key Refresh过程中使用。

unicastAddress:节点的单播地址。

configComplete:节点的成分数据是否配置完成。配网过程中,在未配网设备完成配网流程后,有一个将设备信息加入到网络的过程,会从设备上获取成分数据,并将一些网络信息配置到节点上。比如当前网络的NetworkKey和APPKey等。

vid:version id,节点版本号。

cid:Company Identifier,节点所用的蓝牙芯片公司,在Sig注册的标识符。比如:奉加微的CID是0x0504,对应的公司名是PHYPLUS Inc。

DefaultTTL:默认的转发次数。消息在网络中最多会被转发多少次。

UUID:节点的唯一标识符。

security:节点的安全等级,有低(low)和高(high)两种等级,直接体现在TransMIC(传输的消息完整性检查)是使用32位还是64位,相差4字节。在访问层PDU计算分包个数时,需要考虑到这个值到来的4字节的差异影响。

crpl:包含一个16位的值,表示设备中重放保护列表项的最小数量。

blacklisted:当节点正在被删除的过程中,该标志被设置为“true”,并且在密钥刷新过程中被排除在新的网络密钥分发之外;否则设置为false 。

pid:product id,产品ID,三元组之一,一机一号。

name:节点名称。

deviceKey:设备密钥,只有节点和配置客户端知道的访问层密钥。设备密钥应被绑定到节点已知的每个网络密钥。这些绑定不能被更改。设备密钥是三元组的元素之一Secret。设备密钥主要用于配网、加密交换网络密钥等信息,两者之间传输内容。

2.4 节点中的元素

elements:元素是节点中的可寻址实体。每个节点至少有一个元素,即主元素,还可以有一个或多个附加的辅助元素。元素的数量和结构是静态的,在节点的整个生命周期内不会改变(也就是说,只要节点是网络的一部分)。

节点中元素示例

如上图,左边是配网器节点,包含两个元素。包含主元素和一个辅助元素。

右边是蓝牙设备节点,仅包含一个主元素的情况,未给元素命名,右元素少了一个name属性。

主元素使用在配网期间分配给节点的第一个单播地址来寻址。每个附加的辅助元素都使用连续的后续地址进行寻址。这些单播元素地址允许节点识别节点中的哪个元素正在发送或接收消息。

如果元素的数量和结构发生变化,例如由于固件更新,必须重新配置节点。当执行固件更新时改变了元素的数量或结构时,使用节点移除过程。

消息在模型中基于操作码和元素地址分派。

一个元素不允许包含以相同方式使用相同消息的模型的多个实例(例如,接收“On”消息)。当同一元素中的多个模型使用相同的消息时,这些模型被称为“重叠”。“为了在一个节点内实现多个重叠模型的实例(例如,控制多个可以打开和关闭的灯具),节点需要包含多个元素。

2.5 节点中元素的模型

models:模型定义了节点的基本功能。一个节点可以包含多个模型。模型定义了所需的状态,在这些状态上操作的消息,以及任何相关的行为。

mesh应用程序是使用客户机-服务器体系结构与发布-订阅范例通信来指定的。模型的ModelID决定这个模型对应什么功能。

下图是模型的结构,左边是有订阅分组,右边是未订阅

蓝牙Mesh节点中的模型Model

2.6 节点中的NetworkKeys和网络中的NetworkKeys

节点中的netKeys:与节点绑定的networkKeys。单个netKey对象包括key的索引和更新状态。

网络密钥在本地数据中的两种数据表示方式

右边网络中的NetKeys包含的是NetworkKey的完整信息。

phase:key刷新的阶段,3种取值,0表示处于正常使用中,1表示正在发布新key给所有节点,2表示在发送网络信标,确认所有节点是否都有新key。

minSecurity:与此网络密钥相关联的子网的最低安全级别。如果子网中与此网络密钥相关联的所有节点都已通过网络进行发放,则执行安全发放步骤,那么子网的此属性的值将设置为.high;否则,该值将被设置为.low,而子网则被认为不太安全。

key:在加密过程中实际使用的完整的密钥内容。

timestamp:最后一次修改的时间戳。

name:密钥名称。

index:密钥索引,在节点绑定的netKeys中,就是这个index。通过这个index,再找到完整的密钥内容进行加解密。

2.7 节点中的AppKey和网络中的AppKeys

AppKeys:与节点绑定的ApplicationKeys。单个ApplicationKey对象包括key的索引和更新状态。

应用密钥对比

appKeys必须绑定到NetworkKey上才能使用,AppKey是用于应用层数据加解密,应用层的数据必须依托网络层才能往下传输出去,所以在给节点绑定应用秘钥时,必须要确定节点是否绑定了网络密钥,然后确定将应用秘钥绑定到哪个网络密钥。那么应用密钥已经包含了网络密钥的信息,在模型中选择应用秘钥就可以知道,数据在整个消息发送过程中,是如何进行加解密操作。

boundNetKey:NetworkKey的索引。

2.8 NetworkTransmit和RelayRetransmit

NetworkTransmit:网络传输状态,用于控制节点发出网络层PDU重传的次数和时间点。这个结构体包含节点在网络层中发送消息的原始参数。

RelayRetransmit:网络中继重传状态,用于控制网络层收到消息后的中继行为。

  • count(UInt8):网络层PDU重复发送的次数,取值范围是1至8。这个count是指定时器重复发送的次数,每发一次,count自减一,count为0时定时器停止。

  • interval(UInt16):发布周期,单位为毫秒。定时器两次数据发送的时间间隔;发送周期 = 步长 x 步长时间单位。步长时间单位固定为10ms,所以发布周期的取值范围为10ms至320ms。

发送设置和转发设置

2.9分组

分组包含一个名称,分组地址和父节点地址。目前不支持多层级分组功能,即分组中包含分组是不支持的。所以parentAddress都是0x0000。

分组在本地数据结构的表示方式

你可能感兴趣的:(蓝牙mesh的协议栈)