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.先获取节点的初始心跳订阅和发布信息,通过ConfigHeartbeatPublicationGet
和ConfigHeartbeatSubscriptionGet
这两个消息获取。
- 发送
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 Log
是Heartbeat 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阶段对心跳做处理。
心跳订阅与发布在节点的位置
访问层确认消息重发时间
需要确认的消息,会在访问层创建定时器监听消息,如果没有收到消息确认,在一段时间之后,将会重发。访问层的重发,数据不会改变。
访问层逻辑,重发时间间隔的计算,涉及如下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界面上。