蓝牙mesh之心跳包

mesh心跳包用来干嘛的?

Heartbeat is used to monitor nodes on a network and discover how far nodes are apart from each other.

The Heartbeat message can be used for two main functions. The first function is the determination that a node is still active within a mesh network . The second function is the determination of how far a node is away.

可以确定节点是否在网,还可以根据心跳消息的到达时间,来判断网络状况是否良好。

心跳包操作的Model模型

The Heartbeat message is sent periodically, as configured by the Configuration Server model。 每个节点必须包含Configuration Server model,模型ID: 0x0000。

六个指令

操作心跳功能的前提是节点绑定了AppKey。因为心跳功能属于基础模型中配置模型的功能,所以不需要给模型绑定APPKey的步骤。

1.先获取节点的初始心跳订阅和发布信息,通过ConfigHeartbeatPublicationGetConfigHeartbeatSubscriptionGet这两个消息获取。

  • 发送ConfigHeartbeatPublicationGet(opcode: 0x8038, parameters: 0x),收到ConfigHeartbeatPublicationStatus(opcode: 0x 6, parameters: 0x00000000000000000000)
  • 发送ConfigHeartbeatSubscriptionGet(opcode: 0x803A, parameters: 0x),收到ConfigHeartbeatSubscriptionStatus(opcode: 0x803C, parameters: 0x000000000000000000)

2.配置订阅ConfigHeartbeatSubscriptionSet(source: 1, destination: 6, periodLog: 4)

返回确认消息ConfigHeartbeatSubscriptionStatus(status: Success, source: 1, destination: 6, periodLog: 4, countLog: 0, minHops: 127, maxHops: 0)

  • 配置客户端发送Config Heartbeat Subscription Get/Set消息来获取/设置节点的心跳订阅状态。
  • 节点收到后响应Config Heartbeat Subscription Status。
  • 配置客户端发送的Get消息的Opcode为0x803A。
  • 配置客户端发送的Set消息的Opcode为0x803B。
  • 配置端响应的Status消息的Opcode为0x803C

3.启动发布ConfigHeartbeatPublicationSet(networkKeyIndex: 0, destination: 1, countLog: 4, periodLog: 4, ttl: 5, features: [])

返回确认消息ConfigHeartbeatPublicationStatus(status: Success, networkKeyIndex: 0, destination: 1, countLog: 4, periodLog: 4, ttl: 5, features: [])

  • 心跳发布的Get/Set消息的Opcode是2个字节,而响应的Status消息是1个字节。
  • 配置客户端发送的Get消息的Opcode为0x8038。
  • 配置客户端发送的Set消息的Opcode为0x8039。
  • 配置端响应的Status消息的Opcode为0x06

心跳发布Publication参数

表一

count与count log之间的关系,传递的参数是count log。

count log值 实际count值
0x01 0x0001
0x02 0x0002 ~ 0x0003
0x03 0x0004 ~ 0x0007
0x04 0x0008 ~ 0x000F
0x05 0x0010 ~ 0x001F
0x06 0x0020 ~ 0x003F
0x07 0x0040 ~ 0x007F
0x08 0x0080 ~ 0x00FF
0x09 0x0100 ~ 0x01FF
0x0A 0x0200 ~ 0x03FF
0x0B 0x0400 ~ 0x07FF
0x0C 0x0800 ~ 0x0FFF
0x0D 0x1000 ~ 0x1FFF
0x0E 0x2000 ~ 0x3FFF
0x0F 0x4000 ~ 0x7FFF
0x10 0x8000 ~ 0xFFFF

心跳发布目标地址

Heartbeat Publication Destination是一个 16 位值,决定 Heartbeat 消息的目标地址。

  • 未分配的地址:说明目标地址无效,这个心跳消息不会发送。
  • 单播地址:除操作节点的其他所有节点。节点给自己发布心跳没有意义。
  • 组地址:所有组。
  • 特殊地址的表示方式:All Proxys : 0xFFFC,All Friends:0xFFFD,All Relays:0xFFFE,All Nodes:0xFFFF,一共四种。
  • 所有其他值都是禁止的。

心跳发布次数

Heartbeat Publication Count是一个 16 位的值,用于控制要发送的 Heartbeat 消息的数量。

描述
0x00 心跳消息无效,或者停止
0x01–0x11 心跳次数为2的(n-1)次幂
0x12-0xFE 禁止使用
0xFF 一直发心跳消息

心跳发布周期

Heartbeat Publication Period Log是一个 8 位值,用于控制周期性 Heartbeat 消息的间隔时间。

描述
0x00 心跳消息无效,或者停止
0x01–0x11 心跳间隔为2的(n-1)次幂秒
0x12-0xFE 禁止使用

心跳发布Features

Heartbeat Publication Features 状态决定了在更改时触发发送 Heartbeat 消息的功能。16位的值。

Bit Feature Notes
0 Relay Relay特性改变触发心跳消息: 0 = False, 1 = True
1 Proxy Proxy特性改变触发心跳消息: 0 = False, 1 = True
2 Friend Proxy特性改变触发心跳消息: 0 = False, 1 = True
3 Low Power Low Power特性改变触发心跳消息: 0 = False, 1 = True
4~15 RFU 保留

在心跳订阅时,至多只能选择一种进行订阅。

在心跳发布时,可以不选或者选择4种全部触发。

熟悉的参数

Heartbeat Publication NetKey Index状态决定了用于发送 Heartbeat 消息的 NetKey 的全局 NetKey Index。16位的值。

Heartbeat Publication TTL心跳消息的TTL。8位的值。

心跳订阅Subscription参数

心跳订阅源地址

Heartbeat Subscription Source心跳订阅源地址确定节点应处理的心跳消息的源地址。应为未分配地址或单播地址,所有其他值均禁止。2字节。

  • 两种情况:未分配地址或单播地址。
  • 订阅地址这里没有包含组地址。因为只允许给节点某个模型设置订阅消息,节点就是来源,节点地址就是源地址。

如果 Heartbeat Subscription Source 设置为未分配的地址,则不处理 Heartbeat 消息。

注意:原则上单播地址不包含正在操作的当前节点,因为没有意义。在程序中,获取的是除本节点外的其他节点,包括配网器节点,取节点的单播地址。

心跳订阅目标地址

Heartbeat Subscription Destination 决定 Heartbeat 消息的目标地址。2字节。

  • Heartbeat Subscription Destination 应该是未分配的地址、节点的单播地址或组地址,所有其他值都是禁止的。
  • 如果 Heartbeat Subscription Destination 设置为未分配的地址,则不会处理 Heartbeat 消息。

节点可以使用它来配置代理过滤器,以允许它们接收心跳消息,例如,使用 GATT 承载或友谊连接的节点。

特别注意:

1.目标地址的单播地址,给哪个节点添加订阅,限定只能选当前节点的单播地址。(The Heartbeat Subscription Destination shall be the unassigned address, the primary unicast address of the node, or a group address, all other values are Prohibited.)

2.还有一类特殊地址,一共是4种:All Proxys,All Friends,All Relays,All Nodes。

单播地址和组地址直接可以获取。特殊地址的表示方式:

All Proxys : 0xFFFC

All Friends:0xFFFD

All Relays:0xFFFE

All Nodes:0xFFFF

心跳订阅周期与周期计数

  • Heartbeat Subscription Count是一个 16 位计数器。
  • Heartbeat Subscription Count Log是一个 8 位计数器。1字节,实际传输数据。
  • 用于控制自最后一次执行 Config Heartbeat Subscription Set 消息后,接收到的 Heartbeat 消息的数量。
  • 计数器在 0xFFFF 处停止计数。
  • Heartbeat Subscription Count LogHeartbeat Subscription Count值的表示。Count Log的 0xFF与Count的0xFFFF等价。
Count Log值(1字节) 描述
0x00 不会处理心跳消息
0x01–0x11 根据2的(n-1)次幂,对照在上方表一
0x12-0xFF 禁止使用

心跳订阅Min Hops与Max Hops

Heartbeat Subscription Min Hops 是自 Config Heartbeat Subscription Set 消息最后一次修改后,在接收 Heartbeat 消息时注册的最小跳数。

Heartbeat Subscription Max Hops指最大跳数。

  • 0x00 指没收到心跳消息。
  • 0x80–0xFF 禁止使用。
  • 0x01–0x7F 有效的范围。

目前程序中设置 maxHops 为 0;minHops为0x7F。实际计算中,会用

//与接收的heartbeat消息中的跳数做计算
minHops = min(minHops, heartbeat.hops)
maxHops = max(maxHops, heartbeat.hops)

所以这个最大值和最小值跟我们日常理解的相反。

mesh心跳业务逻辑

心跳是基于订阅和发布来实现。与发布和订阅强关联的是目标地址。若一个模型所在的元素订阅了目标地址,则该元素可以收到任何发送到目标地址的消息。若一个模型配置了状态发布,则该元素需要把模型状态变化的消息发布到对应的目标地址。

心跳与模型订阅有一些区别,比如:

  • 模型数据是保存在Element的Model数据中,但心跳的订阅和发布是直接保存在节点的数据中。
  • 模型的可以订阅多个组,但是心跳订阅只能订阅一个。

一个新的节点没有关于心跳订阅和发布的数据,因为在配网时没有对这个部分做处理。这也就意味着,如果有需要一直检测设备是否在线的需求,可以在设备配网后的config阶段对心跳做处理。

心跳订阅与发布在节点的位置

心跳订阅与发布在节点的位置.jpeg

访问层确认消息重发时间

需要确认的消息,会在访问层创建定时器监听消息,如果没有收到消息确认,在一段时间之后,将会重发。访问层的重发,数据不会改变。

访问层逻辑,重发时间间隔的计算,涉及如下3个参数:

  • 程序中可以设置确认消息时间间隔acknowledgmentMessageInterval
  • 节点的TTL或者网络的TTL。
  • 确认消息的段数segmentsCount

计算公式:(max是取两个数中的最大值)

int firstTime = max( acknowledgmentMessageInterval, 2.0) + ttl*0.05 + segmentCount*0.05

如上:第一次重发的时间是firstTime,单位是秒。

后一次重发时间间隔是上一次的2倍。

比如第二次的时间间隔是第一次发送数据后的2*firstTime秒。第三次是第二次发送数据后的4*firstTime秒。

定时器超时会取消监听,默认超时时间是30秒,可以设置更长时间。

新增一个心跳订阅

使用ConfigHeartbeatSubscriptionSet消息进行心跳订阅。

  • 属于需要确认的消息类型。
  • 属于config消息类型。
  • 如下例,当前操作节点0x0007,在模型层是两个参数message和node,无论消息内容是什么,该消息会发送到0x0007节点进行处理。

例:这个消息的3个参数:Source,Destination,PeriodLog。分别选择:

Source:0x000A

Destination:0x0007

PeriodLog:4

模型层:ConfigHeartbeatSubscriptionSet(source: 10, destination: 7, periodLog: 4)

经过访问层PDU处理后:Access PDU (opcode: 0x803B, parameters: 0x0A00070004)(iOS是小端模式)

经过上层传输层处理后:transportPdu :"2FC6B857B3AFCE07EFE77B" (无需分包)

经过底层传输层处理后,封装成Access Message (akf: 0, szmic: 0, data: 0x2FC6B857B3AFCE07EFE77B)

经过网络层处理后,Network PDU (ivi: 0, nid: 0x1A, ctl: 0, ttl: 5, seq: 470, src: 0001, dst: 0007, transportPdu: 0xB3D7817FAAA58F951D7F2591, netMic: 0xA7197688)

交给承载器发送:0x001A1D91D598773CE3E472251D991283EDA904DEBBB1434F4EFB

订阅设置的响应指令

收到:0x001AB6FBDA9BEC28292A8C98894625C5EC61F299DE1D83DEC27F007DEC48

网络层识别:Network PDU (ivi: 0, nid: 0x1A, ctl: 0, ttl: 5, seq: 2066, src: 0007, dst: 0001, transportPdu: 0x8C98894625C5EC61F299DE1D83DEC27F, netMic: 0x007DEC48)

底层传输层识别为分段消息,Segmented Access Message (akf: 0, szmic: 0, seqZero: 2065, segO: 0, segN: 1, data: 0x36B682A4519BB663249A8F96)

一共两端消息(segN=1),收到的是第一段(segO: 0),发送消息确认Sending ACK (seqZero: 2065, blockAck: 0x00000001)

保存这段数据,等待消息完整后再做处理。

(期间有多条重复数据)

收到:0x001A1DAEDEB3F9745B8347ADEBED904699F63561FB

网络层识别:Network PDU (ivi: 0, nid: 0x1A, ctl: 0, ttl: 5, seq: 2067, src: 0007, dst: 0001, transportPdu: 0x47ADEBED904699, netMic: 0xF63561FB)

底层传输层识别为分端消息:Segmented Access Message (akf: 0, szmic: 0, seqZero: 2065, segO: 1, segN: 1, data: 0xDEFDA2)

一共两段消息,收到的是第二段。发送消息确认Sending ACK (seqZero: 2065, blockAck: 0x00000003)

底层传输层判断出消息完整,合并两端消息数据,得到完整访问消息:Access Message (akf: 0, szmic: 0, data: 0x36B682A4519BB663249A8F96DEFDA2)

消息向上层传输层传递,处理:Upper Transport PDU (encrypted data: 0x36B682A4519BB663249A8F, transMic: 0x96DEFDA2)

访问层生成访问PDU:Access PDU (opcode: 0x803C, parameters: 0x000A00070001007F00)

根据下方心跳订阅状态消息格式解析消息parameters字段。

模型层识别并解析出参数:ConfigHeartbeatSubscriptionStatus(status: Success, source: 10, destination: 7, periodLog: 1, countLog: 0, minHops: 127, maxHops: 0)

将心跳订阅参数保存到节点对应的本地网络数据。

为什么设置的periodLog无效 ???

响应:心跳订阅状态

Config Heartbeat Subscription Status 是一条非确认消息,用于报告节点的 Heartbeat Subscription 状态

Parameters Size (octets) Notes
Status 1 请求消息的状态码,0x00 表示成功,0x01 ~0x11为失败
Source 2 源地址
Destination 2 目标地址
PeriodLog 1 Remaining Period for processing Heartbeat messages
CountLog 1 Number of Heartbeat messages received
MinHops 1 Minimum hops when receiving Heartbeat messages
MaxHops 1 Maximum hops when receiving Heartbeat messages

新增一个心跳发布

使用ConfigHeartbeatPublicationSet消息进行心跳发布。

  • 属于需要确认的消息类型。
  • 属于config消息类型。
  • 如下例,当前操作节点0x0007,在模型层是两个参数message和node,无论消息内容是什么,该消息会发送到0x0007节点进行处理。

例:这个消息的6个参数:Destination,CountLog,PeriodLog,TTL,Features,NetKeyIndex。分别选择:

Destination:0x000A

CountLog:10

PeriodLog:7

TTL:5

Features:[]

NetKeyIndex:0

模型层ConfigHeartbeatPublicationSet(networkKeyIndex: 0, destination: 10, countLog: 10, periodLog: 7, ttl: 5, features: [])

访问层Access PDU (opcode: 0x8039, parameters: 0x0A000A070500000000)

上层传输层Upper Transport PDU (encrypted data: 0x33348E9C6A8D31680EAC17, transMic: 0x3677F938)(无需分包)

底层传输层生成Access Message (akf: 0, szmic: 0, data: 0x33348E9C6A8D31680EAC173677F938)

网络层Network PDU (ivi: 0, nid: 0x1A, ctl: 0, ttl: 5, seq: 490, src: 0001, dst: 0007, transportPdu: 0x665E8B80589AC134417E43EA1235F2B8, netMic: 0xFDDFBA01)

发给承载器发送-> 0x001A4ACC7F19206993DF665E8B80589AC134417E43EA1235F2B8FDDFBA01

心跳发布的响应指令

ConfigHeartbeatPublicationStatus响应流程类似心跳订阅:

第一段数据0x001A2BE08892A34E66D783899B27DADC3F1A2F084F4C78BF7D225F34FB24

第二段数据0x001A4B2702D732AE9752E125C797B7C2B716E5C2EF

最终生成访问层消息Access PDU (opcode: 0x 6, parameters: 0x000A000A070500000000)

模型层解析ConfigHeartbeatPublicationStatus(status: Success, networkKeyIndex: 0, destination: 10, countLog: 10, periodLog: 7, ttl: 5, features: [])

将心跳发布参数保存到节点对应的本地网络数据。

参数 长度(字节) 注释
Status 1 Status Code for the requesting message
Destination 2 Destination address for Heartbeat messages
CountLog 1 Number of Heartbeat messages remaining to be sent
PeriodLog 1 Period for sending Heartbeat messages
TTL 1 TTL to be used when sending Heartbeat messages
Features 2 Bit field indicating features that trigger Heartbeat messages when changed
NetKeyIndex 2 NetKey Index

心跳消息

实例:目前网络中是3个节点加手机App配网器,3个节点和配网器均在一米范围内,配网器不确定连接的是哪个节点。

在开启心跳发布后,收到心跳消息,流程如下:

承载层收到0x001AD72A7656276E06C7270ED41C19086A5A95435DC6

网络层生成Network PDU (ivi: 0, nid: 0x1A, ctl: 1, ttl: 126, seq: 2654, src: 0007, dst: 0001, transportPdu: 0x270ED41C, netMic: 0x19086A5A95435DC6)

底层传输层识别Control Message (opCode: 0x0A, data: 0x7F0007)

上层传输层判断出是Heartbeat Message (initial TTL: 127, received TTL: 126, hops: 2, features: Relay, Proxy, Friend)

间隔一个心跳周期后,会收到下个包

承载层收到0x001A46FC18E205EE783BD698D2E37AE903084E061D75

网络层生成Network PDU (ivi: 0, nid: 0x1A, ctl: 1, ttl: 125, seq: 2655, src: 0007, dst: 0001, transportPdu: 0xD698D2E3, netMic: 0x7AE903084E061D75)

底层传输层识别Control Message (opCode: 0x0A, data: 0x7F0007)

上层传输层判断出是Heartbeat Message (initial TTL: 127, received TTL: 125, hops: 3, features: Relay, Proxy, Friend)

间隔一个心跳周期后,会收到下个包

承载层收到0x001A0D3A0FE5CE5A461F0ACE35ADD06F697603831682

网络层生成Network PDU (ivi: 0, nid: 0x1A, ctl: 1, ttl: 126, seq: 2656, src: 0007, dst: 0001, transportPdu: 0x0ACE35AD, netMic: 0xD06F697603831682)

底层传输层识别Control Message (opCode: 0x0A, data: 0x7F0007)

上层传输层Heartbeat Message (initial TTL: 127, received TTL: 126, hops: 2, features: Relay, Proxy, Friend)

参数 长度(字节) 含义
source 2 心跳消息来源地
destination 2 心跳消息目的地
features 2 特征值位
initialTtl 1 心跳消息TTL初始值
receivedTtl 1 收到心跳消息时的TTL值
hops 1 跳数

TTL初始值,减去收到心跳消息时的TTL值,就能算出心跳消息经过了几次转发。

当源节点与目标节点直接通信时,receivedTtl还是127,hops为1。

当源节点与目标节点经过一次转发通信时,receivedTtl是126,hops为2。

当源节点与目标节点经过二次转发通信时,receivedTtl是125,hops为3。

总结

在App端,接收到心跳消息后,判断节点的目标地址是否是配网器节点。如果是,才会在App端做处理。

在APP端,接收到心跳订阅状态或者心跳发布状态消息后,判断节点的目标地址是否是配网器节点。如果不是,才会在App端本地数据中做修改。

在如果需要一直判断设备是否在线的场景下,可以通过这个配网器节点接收心跳包的时间间隔判断设备是否在线。根据hops值和网络的拓扑结构,能判断出网络通信状态是否正常。

清除一个心跳订阅

ConfigHeartbeatSubscriptionSet(source: 0, destination: 0, periodLog: 0)

新增或修改心跳订阅

ConfigHeartbeatSubscriptionSet(source: 1, destination: 15, periodLog: 3)

ConfigHeartbeatSubscriptionStatus(status: Success, source: 1, destination: 15, periodLog: 3, countLog: 0, minHops: 127, maxHops: 0)作用是什么?

获取心跳订阅

ConfigHeartbeatSubscriptionGet()

ConfigHeartbeatSubscriptionStatus(status: Success, source: 1, destination: 15, periodLog: 0, countLog: 0, minHops: 127, maxHops: 0) 此时心跳发布已经完成或者没有开始心跳发布。

新增一个心跳发布

ConfigHeartbeatPublicationSet(networkKeyIndex: 0, destination: 1, countLog: 1, periodLog: 8, ttl: 5, features: []) 发布一次 间隔2分8秒后

ConfigHeartbeatPublicationStatus(status: Success, networkKeyIndex: 0, destination: 1, countLog: 1, periodLog: 8, ttl: 5, features: [])

Heartbeat Message (initial TTL: 127, received TTL: 127, hops: 1, features: Relay, Proxy, Friend)

ConfigHeartbeatPublicationSet(networkKeyIndex: 0, destination: 1, countLog: 4, periodLog: 6, ttl: 5, features: []) 发布八次 间隔32秒。 (设备收到心跳发布后,开始定时器计时,第一次心跳发布在定时器启动32秒后。)

ConfigHeartbeatPublicationStatus(status: Success, networkKeyIndex: 0, destination: 1, countLog: 4, periodLog: 6, ttl: 5, features: [])

Heartbeat Message (initial TTL: 127, received TTL: 127, hops: 1, features: Relay, Proxy, Friend)

获取心跳发布消息

ConfigHeartbeatPublicationGet()

ConfigHeartbeatPublicationStatus(status: Success, networkKeyIndex: 0, destination: 1, countLog: 1, periodLog: 0, ttl: 5, features: []) 此时一次心跳发布已完毕。

ConfigHeartbeatPublicationStatus(status: Success, networkKeyIndex: 0, destination: 1, countLog: 4, periodLog: 0, ttl: 5, features: []) 发布过8次消息后countLog值发生变化。

手机APP要怎么看到心跳包?需要在配网器节点上,给指定地址发送一条订阅消息,不做的话,在收到心跳包之后也不会处理。在订阅时设置的时间参数表示,在发送指令起,Heartbeat Publication Period Log时间内会处理发给配网器节点的心跳包,超过时间后,就不会再处理。目前NRFSDK在收到心跳包后,只会将数据写入到本地,并不会传到界面上。所以需要修改NRFSDK的上层传输层代码,将心跳数据传到APP界面上。

你可能感兴趣的:(蓝牙mesh之心跳包)