前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ZWave对CommandClass的处理流程

ZWave对CommandClass的处理流程

作者头像
IOT物联网小镇
发布2021-05-13 11:35:23
4770
发布2021-05-13 11:35:23
举报
文章被收录于专栏:IOT物联网小镇

文章主题

在开发一个 ZWave Device 的过程中,对 COMAND CLASS(单词太长了,后面就简写为 CC 啦) 的处理是最基本、最重要的工作。这篇文章以最最简单的 CC:COMMNAD_CLASS_BASIC 为例子,来拆解、分析应用层对它的处理流程。

内容导航

  • 接收指令
  • 处理指令
  • 发送数据

接收指令

1. 指令接口函数

我们就以一个最简单的场景为示例:网关 G 发送指令:COMMNAD_CLASS_BASIC 给设备 D。这个 CC 包含 3 个 COMMAND,分别是: BASIC_GET, BASIC_REPORT, BASIC_SET,官方提供的文档里对这 3 个命令有详细解释。(这里要说一句:官方的文档太 TMD 的多了,多的看不过来!)

先上图1:

是不是太乱了,有点接受不了啊?将就着看吧,我已经尽最大努力了,如果你会 PS,可以教教我。

也就是上图中标记0的地方,看起来是不是这些数据结构挺挺长、挺吓人的?!其实 C 语言开发中,我觉得核心就是数据结构和算法,其中数据结构又决定了算法。在分析 ZWave 代码的过程中,有很大一部分就是分析数据结构,当然了这是我的观点。

图中标记1是返回值的定义,它是一个枚举类型。

图中标记2是参数 rxOpt 的数据结构,它是一个结构体,这里结构体里面关于 NODE_ID 的解释可以看一下源代码,比较简单。

图中标记3是参数 pCmd 的数据结构,这里是重点:这是一个[共用体],简单的说,就是内存中的一块地址空间,你可以按照不同的组织方式去理解其中保存的内容。

2. 关于 C 语言的共用体/共同体/union

刚才的藐视似乎有点拗口,举个栗子,比如:有一块地址空间一共有 4 个字节的容量:

  • 你可以定义一个指针 char *p, p 指向这个地址空间的首地址 A,然后你就可以读取或者写入一 char 类型的数据;然后移动指针 p 到第二个字节的位置 A+1,读取或者写入另一 char 类型的数据;再然后是第三个字节的地址 A+2;再然后是第四个字节的地址 A+3。
  • 你也可以定义一个指针 int *p,指针 p 指向这个地址空间的首地址 A,此时,你可以直接读取/写入一个 4 字节的 int 型数据。

理解了上面的内容,那么对 ZWave 指令部分的处理可以说就理解一半了。回到参数 pCmd,当设备D 接收到不同的 CC 指令时,这个指针所指向的地址空间就保存了不同的数据(什么?这个地址空间是在哪里分配的?我也不知道。可以肯定的是协议层为我们准备好的,应用开发者不用操心这个事情)。至于如何解析这些指令,当然是参考官方提供的文档,其中详细说明了每一个 CC 所对应的指令中,每一个字节,每一个 bit 代表什么意思。

注意:在这个地址空间中,开头2个字节的意思一定是确定的,也就是图1中标记5列出的:cmdClass 和 cmd。

所以,在这个指令接收函数中,首先通过

pCmd->ZW_Common.cmdClass

来判断该指令是哪一个 COMMAND CLASS。也就是说,pCmd 理解成:这个指针指向了 ZW_COMMON_FRAME 这个结构体。所以,我们就知道了当前的指令是:COMMAND_CLASS_BASIC,从而进入了第一个 case 中调用处理函数:

handleCommandClassBasic()

处理指令

handleCommandClassBasic( ) 函数位于 ApplicationHandlers 文件夹下面,这里应该说是官方为了降低开发者的难度,把所有 CC 的处理逻辑的共性提取出来。

继续上图:图2。

好像比图1更乱了?!真的是尽力了。

这个函数首先判断指令是 COMMAND_CLASS_BASIC 中的哪一个 COMMAND: BASIC_SET or BASIC_GET。

1. BASIC_SET

看到没?参数 pCmd 又指向了另一个数据结构,即图中标记1处的 ZW_BASIC_SET_V2_FRAME。如果这里没有明白,需要回到上面再重新理解一下。

首先检查了参数 value 的范围,BYTE 的类型是通过 typedef 定义的,本质上是 unsigned char。ZWave 对 Basic Value 允许的范围是 0x00~0x63, 以及 0xFF,反正是代表不同的意思了。

然后调用函数 handleBasicSetCommand。 这个函数是什么鬼?在哪里? 原来这个函数是需要开发者自己实现的,官方已经替你在 CommandClassBasic.h 中声明了这个函数是外部的,也就是需要开发者实现的。

可以看到,传递了2个参数:

  • pCmd->ZW_BasicSetFrame.value
  • rxOpt->destNode.endpoint

第一个参数是设置的 value,第二个参数是说这个value是用来设置哪一个 EndPoint 的,比如接收指令的设备是一个有4个插孔的排插,那么每一个插孔就是一个 EndPoint。当然,如果当设备就是一个灯泡,那么这个 EndPoint 就等于0。

2. BASIC_GET

从字面上理解:就是发出指令的网关G要从接收指令的设备D获取一些信息,比如灯泡的亮度,那么设备D就要把当前的亮度值发送给网关G。于是,这里的处理流程是:

2.1 申请地址空间

申请的这块地址空间赋值给 pTxBuf,待会需要发送的数据就往这个地址放,图中标记2的地方。先看一下文件 ZW-tx_mutex.c 中一个重要的结构体变量:

static MUTEX myMutex;

这个 MUTEX 结构体定义在图中标记3处,也就是说,在系统的数据区域,默认定义了一块内存地址,专门用于在发送数据时使用。正因为这块地址空间是共用的,所以每个时刻只能有一个人使用,所以使用互斥量来保护这块地址空间。

简言之:谁先抢到谁先用(上锁),用完之后要归还(解锁)。

2.2 设置发送者、接收者参数

RxToTxOptions(rxOpt, &pTxOptionsEx);这个函数的作用就是:设置接收数据的nodeId 好 endpoint等参数, 发送者(也就是当前设备自己)的 sourceEndpoint, 以及其他一些安全上属性,都是从 rxOpt中复制而来。此时,设备D变成了发送者,网关G就编程了接受者。

注意这个函数中的2个静态变量:txOptionsEx, destNode。

2.3 往第一步得到的内存地址空间填数据

至于需要填哪些数据,看文档!

COMMAND_CLASS_BASIC 的 BASIC_GET 需要返回3个数据:

  • currentValue
  • targetValue
  • duration 这3个数据都是由开发者定义的3个函数提供,至于这几个值代表什么意思,就需要开发者根据所开发的具体产品类型来决定了。拿灯泡做栗子,当前亮度是50,目标亮度是80,还需要5秒钟达到目标亮度值。

2.4 发送数据

所有的数据发送都是调用函数Transport_SendResponseEP,传递的几个参数格式都是固定的,如果继续跟进到这个函数里,又是一个天地,特别是涉及到 MultiChannel 部分,也是比较复杂,以后再单独拿出来分析。

别忘了,发送完成之后,调用了函数 FreeResponseBuffer,把申请的内存地址空间释放掉,这里并不是 free掉,而只是解锁一下,对系统说:谢谢,我已经使用结束了,现在别人可以申请使用了。

总结

到这里,COMMAND_CLASS_BASIC 的分析过程就结束了,其他的 COMMAND CLASS 执行流程是完全一样的,有区别的地方就在于不同 CC 携带了不同的数据结构,当然,最开头的2个字节永远是固定的:cmdClass 和 cmd。

请容忍我再啰嗦两句啊。

ZWave 的开发博大精深,文档更是数不胜数。我进入 ZWave 的开发时间不长,以上分析过程难免会存在一些理解上的错误,希望没让您误入歧途。另外,知识的学习都是螺旋式的,不能追求一下子把所有相关的东西都理解正确,只要能满足当前的开发需求就可以了,循序渐进的提高、进步,最后就一定能够得到真经。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IOT物联网小镇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档