写一份赏心悦目的工程文档,是很困难的事情。若想写得完善,不仅得用对工具(use the right tools),注重文笔,还得投入大把时间,真心是一件难度颇高的事情。但,若是真写好了,也是善莫大焉:既可让人明白「为何如此设计」,即「知其然更知其所以然」;也能剥离一些琐碎的细节,让更多没那么多时间与精力、或者背景知识不足的朋友,对核心方法和思路,多一点理解,即,给人提供一种「纲举目张提纲挈领抽丝剥茧」的可能性。

机缘巧合,俺今天就决定抛砖引玉,写一篇不那么好的工程文档。也期望对本文话题感兴趣的朋友,将其扩展或者重构成一篇优秀的工程文档。

背景

Z-Stack 是德州仪器(Texas Instrument)的半开源 Zigbee 协议栈。Z-Stack 2.5.1a 是其发布的最后一个独立发行版;所谓「独立发行版」,即,提供的版本里,既包括了诸如智能家居的 Home Automation profile 相关内容,也包括了诸如集中抄表的 Smart Energy profile 相关内容,通俗地讲,大杂烩。往后的 Z-Stack,则是捆绑在单独的 profile 里,不再提供单独下载。(本信息参考链接,亦可参考 2.5.1a 的 release note)

为何采取这种策略?(以下是个人理解)还得先说说 Zigbee 诞生的初衷。

Zigbee 原本是为了解决低功耗局域网的互操作性问题,而诞生的一个基于 802.15.4 层的协议。假设,你家里的灯泡采取了 SmartBlub 协议(胡诌的名字),空气净化设备使用了私有 AirCleaner 协议,集中控制器则是 SmartController 协议,那么恭喜你,鸡同鸭讲的窘状,在你家的「智能家居」之间发生了。遇上如此窝火的事情,你当然会把这群「智能设备」骂一遍,毕竟阿Q说过,虫豸才不骂人。
为了避免高素质的你受委屈,Zigbee 诞生了:灯泡们,空调们,大伙都使用 Zigbee 协议,大家好才是真的好!

然而,世界是复杂的。除了智能家居,还有很多需要互联互通互操作的领域,如能源管理,医疗,建筑自动化。你会问,如此多迥异的应用场景,彼此也会有不同的拓扑和通信需求,同一套 Zigbee 协议,可以满足全部的场景吗?Zigbee 响亮地吼道「五大受损一个对策」!只可惜,Zigbee 不是欧莱雅,为了应对这些不同的场景,其不得不折腾了若干不同的 profile(配置),比如,上文你看到的 Home Automation 和 Smart Energy。

So,聪明的你已经看完了开头,也应该料到了结局:profile 的分化,是任何试图「不完全开源(槽点)但提供完善服务(优点)」的 Zigbee 协议栈供应商不得不面临的结局……

问题

以下部分,都是针对 Z-Stack 2.5.1a 版本。

Zigbee 角色中,有 Coordinator / Router / End-Device 三种角色。end-device 设备主要是做为传感节点,将采集到的数据信息,以及平时的控制信息(如维持连接的心跳数据包,命令控制数据包,等等)发送到 router / coordinator 设备上。下文称呼:节点 / 终端节点 / 传感节点,都是指代 End-device;路由 / 中继,都是指代 Router;协调器 / AP /  Zigbee 网关,都是指代 Coordinator。

本文讲述的部分,主要是针对节点的入网控制部分。网上很多朋友遇到的问题,归纳起来,都类似如下两个典型问题:

1.  router / coordinator 不存在时,或者因为信号强度过低而链路断开时,从 sniffer 嗅探器里可观察到,end-device 频繁发送 beacon 导致传感节点的电池电量(往往传感节点都是电池供电)被消耗殆尽。何解?

2.  在只有一群 end-device 和一个 coordinator 的稳定运行网络里,更换 coordinator 后,节点无法再次入网。如何破?
注意:这里故意不牵涉任何关于 router 的问题,因为 router 和 coordinator 在 zigbee 网络里,角色行为有一定的重合度,会使得问题本身复杂化;考虑到本文的重点是 end-device 的入网行为上,故简化问题,去掉 router。

解决办法

对上述两个问题,给出一些解决问题的建议,抛砖引玉,以供参考。

1.  这个问题比较简单。仅从解决方法入手,只需修改两个配置即可:

# file: \Projects\zstack\Tools\CC2530DB\f8wConfig.cfg
-DBEACON_REQUEST_DELAY=3000 # from default 100 -> 3000
-DBEACON_REQ_DELAY_MASK=0x0FFF # from default 0x00FF -> 0x0FFF

聪明的你一定知道 f8wConfig.cfg 文件的存在。这其中,涵盖了一众 Zigbee 协议栈的配置信息。

其中 –D 前缀表示预处理器的 #define 宏定义。而上述两个宏定义,前者,表示 beacon request 之间的延时 delay,后者,表示延时的掩码 mask(引入随机性),即真正的延时是 delay + rand() & mask。

可以通过简单的计算得知,起初 delay 默认是 100 毫秒,mask 默认是 0x00FF,延时的上下限分别是 [100, 355];修改成 3000 毫秒和 0x0FFF 掩码后,上下限则分别变成了 [3000, 7095]。取平均值 228ms 和 5048ms,可见平均延时增加了 20 倍左右。如果一直处于 beacon request 搜网过程里,原本能持续搜索 5 天的电池容量,一下子可以持续支撑 3 个月。很简单,对吧!

2.  根据假设,网络里只有一群 end-device 和 coordinator,并且已经稳定运行ing。突然你的小猫小狗跑过来,把协调器从桌上扔到了地上,不幸翘腿,以至于你不得不更换协调器。问题来了:节点依然在发送 beacon request,新的协调器也一直在回应 superframe(dev.Cap 也是 1,即还有剩余的 end-device capacity、允许节点入网),可节点就是没有 association request,更别提入网了…… 你感到很沮丧,把小猫小狗批评了一顿,尽管它们不懂你在说什么。

要解决这个问题,有两种全然不同的方法。

a)  第一种,简单粗暴,软件重启。

聪明的你肯定知道,sample application 应用的 SampleApp_ProcessEvent 函数中,有一个 ZDO_STATE_CHANGE 的系统消息,是 ZDO (Zigbee Device Object)  层发来的消息称「哥状态有变,兄弟们请根据新状态自行作出处理」。

啥情况下 SampleApp_ProcessEvent 会收到来自 ZDO 的 STATE_CHANGE 消息呢?

在上述问题情况下,由于 end-device 不知 coordinator 已经翘腿,所以一旦传感器检测到某种信息,它依然傻兮兮的给协调器发过去;自然,它无法收到来自协调器的 MAC layer ACK 即确认响应(confirmation acknowledgement);从 sniffer 上观察,你会发现收不到响应的 end-device 多次重传数据包。

多次重传数据包都失败(都没有收到 MAC layer ACK)后,end-device 就会认为已经同父节点(parent-node)失去了联系,遂敦促 ZDO 发送状态变更(先前是 DEV_END_DEVICE 状态,如今和父节点失联,成为了孤儿节点状态,即 DEV_NWK_ORPHAN)。

一旦 SampleApp_ProcessEvent 收到状态变更为孤儿,软件重启开始新一轮搜网入网过程即可。

b)  第一种方法过于简单粗暴,以至于你无法完成一些更完善的操作,以应对更复杂的现实环境。

不妨再假设,你的协调器被小猫小狗扔到地上后,并没有翘腿,而仅仅是电池脱落(暂时休克)。正在厕所里拉屎的你,听到响声后,虽心有余,而力不足,无法立刻冲出厕所,拯救暂时休克的协调器,只能眼睁睁看着 end-device 给协调器发数据而收不到 MAC layer ACK、从而自认为变成了孤儿……

根据第一种方法,end-device 的传感器或许检测到了某个重要的信息,而一旦重启,除非你将数据写入了 NV 即 non-volatile 区域,否则,信息将丢失……
其实,end-device 只需要再等两分钟,等你走出厕所,给协调器安上电池后,它就可以直接通过 orphan notification(而不是 beacon request),以更少的空中交互次数、更低的能量消耗完成再次入网…… 如何做到这一点?

聪明的你肯定可以搜索到 devStartModes_t 枚举类型,这个枚举状态,决定了节点入网的默认行为。

如,MODE_RESUME 对应于孤儿节点状态,即试图通过 orphan notification 入网;而 MODE_REJOIN 和 MODE_JOIN 虽然都是发送 beacon request 入网,但 REJOIN 希望看到 superframe.extended_PAN_ID 字段(扩展 PAN ID),等于其变成孤儿状态之前的网络的 extended PAN ID。

节点同父节点失联后,MAC / NWK 层自然是最先得知此消息,为了尽快告知其它层的兄弟们,MAC / NWK 通过 ZDO 层的回调函数 ZDO_SyncIndicationCB(),告知说「咱们已经同父节点失联,哥们请负责通知其他兄弟」。此函数遂发送 ZDO_NWK_JOIN_REQ 消息给 ZDApp,通知说「我们已经失联,请尽快处理重新入网事宜」。

接下来的事情,聪明的你应该可以通过阅读 Z-Stack 网络部分的源码自行搞定了。如果想要把 Zigbee primitive 即原语弄得很清楚,可以参考 Zigbee 2007 Specification,戳这里可以参考下俺之前阅读源码时的一些摘要。

整体来讲,理解清楚 request confirmation indication 的含义,应该就可以比较顺利的理解网络层代码了。比如,假设现在是 MODE_JOIN 入网模式,ZDO_StartDevice() 里的 NLME_NetworkDiscoveryRequest() 会请求「发送 beacon 获取周围的父节点们」,对于回应的 superframe(s) 数据帧,会通过 ZDO_beaconNotifyIndCB() 做处理,而 Network Discovery 的结果,则会通过 ZDO_NetworkDiscoveryConfirmCB() 来反馈,等等。这篇博客里,也在理论层面上,描述了整体的入网过程。

福利

你时间少,事情多,今天要陪人打牌,明天要陪人吃饭,后天得去旅游,实在是没时间阅读那么多的网络层源码,如何破?戳这里下载福利。这里设计的入网行为(参考链接里的《入网行为设计》),是相当宽泛的设定,应该可以满足绝大多数场景的需求,而且可简单定制(参考 ZDApp.c 中的 gl_zdo_prepare_init_cnt_timeout 二维数组)。

虽然 z-stack 本身是半开源且能够公开下载的,但考虑到这里对其做了一些裁剪和改动,为了防止和 TI 的发布协议之间有任何冲突,上述链接里的压缩包是加密的。

如果你希望获取解压密码,请给我发送邮件,表明自己使用 z-stack 正在做何种类型的项目(俺也顺便做点小调研,获得一些反馈)。一句话信息诸如「求密码」是会被直接无视的。博客左侧的链接即是邮件地址。谢绝骚扰。

「2014-5-31」Z-Stack - Modification of Zigbee Device Object for better network access management的更多相关文章

  1. Z-Stack - Modification of Zigbee Device Object for better network access management

    写一份赏心悦目的工程文档,是很困难的事情.若想写得完善,不仅得用对工具(use the right tools),注重文笔,还得投入大把时间,真心是一件难度颇高的事情.但,若是真写好了,也是善莫大焉: ...

  2. LibreOJ #6014. 「网络流 24 题」最长 k 可重区间集

    #6014. 「网络流 24 题」最长 k 可重区间集 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   ...

  3. [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?

    [译]聊聊C#中的泛型的使用(新手勿入)   写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发 ...

  4. loj #6014. 「网络流 24 题」最长 k 可重区间集

    #6014. 「网络流 24 题」最长 k 可重区间集 题目描述 给定实直线 L LL 上 n nn 个开区间组成的集合 I II,和一个正整数 k kk,试设计一个算法,从开区间集合 I II 中选 ...

  5. 迄今为止最硬核的「Java8时间系统」设计原理与使用方法

    为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...

  6. 企业运营对 DevOps 的「傲慢与偏见」

    摘要:出于各种原因,并非所有人都信任 DevOps .有些人觉得 DevOps 只不过给开发者改善产品提供了一个途径而已,还有的人觉得 DevOps 是一堆悦耳的空头支票,甚至有人认为 DevOps ...

  7. Contest - 2014 SWJTU ACM 手速测试赛(2014.10.31)

    题目列表: 2146 Problem A [手速]阔绰的Dim 2147 Problem B [手速]颓废的Dim 2148 Problem C [手速]我的滑板鞋 2149 Problem D [手 ...

  8. LOJ6003 - 「网络流 24 题」魔术球

    原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...

  9. 「NowCoder Contest 295」H. Playing games

    还是见的题太少了 「NowCoder Contest 295」H. Playing games 题意:选出尽量多的数使得异或和为$ 0$ $ Solution:$ 问题等价于选出尽量少的数使得异或和为 ...

随机推荐

  1. C++STL学习笔记_(1)deque双端数组知识

    #include<iostream> using namespace std; #include "deque" #include "algorithm&qu ...

  2. C++ 中数串互转、进制转换的类

    /******************************************************************** created: 2014/03/16 22:56 file ...

  3. 编辑器Ultraedit快捷键

    编辑器Ultraedit快捷键说到编辑器的快捷键,VIM是无与伦比的.要反对,也得是带脚踏板的EmaCS.UE还是有差距的,很大差距.注意:VIM是开源.免费的,而UE则需要注册.UE是Windows ...

  4. C# 将文件嵌入DLL 。Log4net 配置

    最近在弄使用Log4net记录日志. 将配置文件封装到的DLL中. 封装步骤: 1.将配置文件添加到类库中. 2.在配置文件上右键,选择属性. 3. 此时生成类库.DLL中就存在该配置文件啦.如图: ...

  5. css实现并列效果

    <a href="#" class="mj-picList"> <div class="mj-picList-pic" s ...

  6. [Notes] Timer Comparision when turn influence computing on/off

    Overall algorithm – bunny 关闭influence计算                                                             ...

  7. Nodejs Express下引入本地文件的方法

    Express的结构如下: |---node_modules------用于安装本地模块.     |---public------------用于存放用户可以下载到的文件,比如图片.脚本文件.样式表 ...

  8. C与C# socket 跨平台通讯传输结构体

    最近需要写一个C组成的服务器端与C#的客户端进行交互的软件,刚开始写的时候发现C#端解析时候出现了故障,经过仔细研究后发现原因是发送方传输太快,出现了所谓粘包的现象.也就是在C#端的Receive() ...

  9. 关于fast cgi和php-fpm的关系

    相关文档“https://segmentfault.com/q/1010000000256516%20” 一.什么是cgi cgi是一个协议,这个协议规定我们web服务器访问的时候,nginx和php ...

  10. vs2015 command prompt here

    网上搜的很多方法都不能用,比如:http://app.paraesthesia.com/CommandPromptHere/ 主要是都搞错了注册表路径,写成了: HKCR,Directory\Shel ...