zoukankan      html  css  js  c++  java
  • Z-Stack

    写一份赏心悦目的工程文档,是很困难的事情。若想写得完善,不仅得用对工具(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: ProjectszstackToolsCC2530DBf8wConfig.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 正在做何种类型的项目(俺也顺便做点小调研,获得一些反馈)。一句话信息诸如「求密码」是会被直接无视的。博客左侧的链接即是邮件地址。谢绝骚扰。

  • 相关阅读:
    Java 泛型 泛型的约束与局限性
    Java 泛型 泛型方法
    Java 泛型 泛型数组
    Java 泛型 协变性、逆变性
    Java 泛型 协变式覆盖和泛型重载
    Java 泛型 泛型代码和虚拟机
    Insertion Sort List
    Remove Duplicates from Sorted List II
    String to Integer (atoi)
    SpringMvc源码入门
  • 原文地址:https://www.cnblogs.com/jtuki/p/3762655.html
Copyright © 2011-2022 走看看