文章目录
- 一、简单描述符
- 1.1 简单介绍
- 1.2 简单描述结构体介绍
- 1.3 结构体中的簇
- 1.4 应用场景
- 二、AF通信原理
- 2.1 通信过程
- 2.2 端点号分类
- 2.3 通信方式
- 2.4 注册简单描述符
- 三、数据发送API简介
- 3.1 AF层数据发送API
- 3.2 基于AF层封装的通信API
- 3.2.1 点对点通信API
- 3.2.2 广播通信API
- 3.2.3 组播通信API
- 四、实战
一、简单描述符
1.1 简单介绍
在ZigBee中,简单描述符(Simple Descriptor)是一种数据结构,它提供了关于某个设备的端点(EndPoint)的基本信息。简单描述符包括端点的ID,设备的类型,以及设备支持的簇(Cluster)。
如果设备是一个智能灯泡,那么它可能有一个简单描述符,其中的设备类型就是"智能灯泡",支持的簇可能包括"开关控制",“亮度控制"等等。这样,当其他设备(比如一个开关或者调光器)想与这个智能灯泡交互时,就可以通过查看这个简单描述符来了解该设备的功能以及如何与其交互。
比如说,一个智能开关在连接一个智能灯泡时,它可以查看灯泡的简单描述符,发现这个灯泡支持"开关控制"和"亮度控制”,那么,这个开关就知道它可以通过发送特定的信号来控制灯泡的开关和亮度。
所以,可以说简单描述符在ZigBee网络中起到了"设备说明书"的作用,它描述了设备的功能以及设备之间如何交互,帮助设备之间的正常通信。
1.2 简单描述结构体介绍
简单描述符结构体
typedef struct{
uint8 EndPoint;//端点号
uint16 AppProfId;//描述所在应用场景,例如家居自动化
uint16 AppDeviceId;//设备ID
uint8 AppDevVer:4;//由开发者自定义,表示版本号
uint8 Reserved:4;//保留字段
uint8 AppNumInClusters;//端点支持的输入簇个数
cId_t *pAppInClusterList;//指向输入簇列表的指针
uint8 AppNumOutClusters;//端点支持的输出簇个数
cId_t *pAppOutClusterList;//指向输出簇列表的指针
}SimpleDescriptionFormat_t;
- 端点号:EndPoint,可以理解为简单描述符的编号,取值范围是0~255。在同一个ZigBee设备中,每一个简单描述符都有一个不同的端点号,可以利用端点号来找到对应的简单描述符。
- AppProfId:Profile ID,表示这个简单描述符所属的应用场景。这个应用场景可以是家居自动化、智能照明和智慧零售等。ZigBee联盟为不同的场景定义了对应的ID值,称为Profile ID。
- AppDeviceId:Device ID,表示这个简单描述符所属的设备类型。这个设备类型可以是插座、灯或者传感器等。类似地,ZigBee联盟为不同类型的设备定义了对应的ID值,称为Device ID。
- AppDevVer:这个值可以由开发者自定义,用来表示版本号。
Reserved:保留字段,暂时可以忽略。 - Cluster:簇,或者集群,可以划分为输入簇(In Cluseter)和输出簇(Out Cluster),用来描述这个服务的具体内容。一个简单描述符中可以包含多个Cluster,这些Cluster共同描述了这个服务的具体内容,后续章节将会详细讲解。
1.3 结构体中的簇
在ZigBee中,"簇"或"集群"是指一组基于某种特定功能组合在一起的设备。这些设备能够按照固定规则进行交互或执行特定任务。
比如,我们可以把灯泡、开关和调光器看作一个“簇”。开关负责控制灯泡的开启或关闭,调光器则可以调节灯泡的亮度。它们三个围绕着"控制照明"这个功能组成了一个簇。
在这个簇中,当你使用开关,开关会发送一个信号给灯泡,告诉它要开或者关。如果你用调光器,调光器会发送一个信号给灯泡,告诉它调到什么样的亮度。这就是开关、调光器和灯泡在这个“簇”中的交互方式。
所以,通过设置簇,我们可以更好地将相关设备组织在一起,更有效地实现环境控制、智能家居等复杂应用。不同的簇可以包含不同类型的设备,以满足不同场景的需求。
1.4 应用场景
简单描述符一般定义在App层,如下图所示:
SimpleDescriptionFormat_t zclSampleSw_SimpleDesc =
{SAMPLESW_ENDPOINT, // 端点号ZCL_HA_PROFILE_ID, // 设备应用领域IDZCL_HA_DEVICEID_ON_OFF_LIGHT_SWITCH,// 设备类型IDSAMPLESW_DEVICE_VERSION, // 版本号SAMPLESW_FLAGS, // 保留字段ZCLSAMPLESW_MAX_INCLUSTERS, // 最大输入簇数量(cId_t *)zclSampleSw_InClusterList, // 表示输入簇列表ZCLSAMPLESW_MAX_OUTCLUSTERS, // 最大输出簇数量(cId_t *)zclSampleSw_OutClusterList // 表示输出簇列表
};
二、AF通信原理
2.1 通信过程
AF的全称是Application Framework,中文意思是应用框架,AF层也就是应用框架层,它在ZigBee协议层次中位置如图所示。开发者可以基于这个层次编写代码,实现ZigBee设备之间的数据通信。
图片来源于:善学坊
需要知道的是ZigBee在很多情况下是基于服务的,也就是单单依靠网络地址是不够的,还需要端点号来确认通信的最终目的。
处于网络中的ZigBee设备都会被分配一个网络地址,用于标识设备所处的位置。通过这个网络地址就可以找到对应的设备了。我们知道ZigBee 3.0是利用简单描述符来描述特定的服务的,并且设备上的每个简单描述符都配有一个唯一的端点号。
图片来源于:善学坊
假设现在ZigBee网络中有一个协调器和一个智能插座终端,这个智能插座的网络地址为0x0EF1,并且配有一个简单描述符描述这个插座具备的一些功能,这个简单描述符的端点号是8。
如果协调器要向这个插座发送一个打开指令,不能简单地使用网络地址0x0EF1来说明把命令发送给这个插座,还需要利用端点号8来说明把命令发送给端点号8对应的服务。
2.2 端点号分类
端点号的分类
端点号的取值范围是0~255,其中:
- 端点号0被分配给了ZDO(ZigBee Device Object)。
- 端点号255是广播,也就是向这个端点号发送数据时,设备的所有服务(简单描述符)都会收到这个数据。
- 端点号241~254是暂时保留起来的端点号,暂时不能使用。
- 剩下的端点号1~240可以供开发者自由地使用,例如读者一直学习的SampleSwitch这个工程所使用的端点号可以在工程中找到,如图所示。
2.3 通信方式
ZigBee支持3种通信方式,分别是点对点、广播和组播。
- 点对点通信(P2P,Peer to Peer):顾名思义,就是两个设备之间一对一通信。
- 广播:给其他的所有设备发送数据。
- 组播:给一部分的设备发送数据,这里的“一部分”说的就是一个特定的组。
2.4 注册简单描述符
在使用AF通信前,需要先注册简单描述符并且让对应的端点号生效,或者说让这个简单描述符对应的服务生效。这个流程是:
(1)创建一个简单描述符。在讲解简单描述符的章节中,我们已经看到相关的定义了。
(2)注册这个简单描述符。打开配套的工程,在zcl_samplesw.c文件中的zclSampleSw_Init()这个函数中可以找到以下代码,如图所示。
在应用层初始化函数zclSampleSw_Init()中,有两种注册简单描述符的方法:
(1)可以调用bdb_RegisterSimpleDescriptor()来在BDB中注册简单描述符。
(2)也可以直接调用afRegister()直接在AF中注册简单描述符。
在注册完简单描述符之后,就可以使用AF层的通信API来收发数据了。
三、数据发送API简介
3.1 AF层数据发送API
数据发送API
在AF.c这个文件中可以找到一个名为AF_DataRequest()的数据发送API,如图所示。
参数说明
/*
* @param dstAddr 目标设备地址,包含网络地址和端点号
* @param srcEP 发送设备的简单描述符
* @param cID Cluster ID,后续章节将会详细讲解
* @param len 待发送数据的长度
* @param buf 待发送的数据
* @param transID 传输ID,可以用来给每一次发送的数据包编一个号
* @param options 附加选项,可以用来给这次数据发送添加一些说明
* @param radius 最大的路由跳转级数
*/
afStatus_t AF_DataRequest(afAddrType_t *dstAddr, endPointDesc_t *srcEP,uint16 cID,uint16 len, uint8 *buf,uint8 *transID,uint8 options,uint8 radius );
协议栈里面的SampleSwitch这个例程基于这个API封装出了点对点通信API、广播通信API和组播通信API。
3.2 基于AF层封装的通信API
3.2.1 点对点通信API
/*
* @param destNwkAddr 目标设备的网络地址
* @param cid Cluster ID,后续课程将会详细讲解
* @param len 数据长度
* @param data 数据内容
*/
static void zclSampleSw_AF_P2P(uint16 destNwkAddr, uint16 cid, uint8 len, uint8 *data)
{ afAddrType_t dstAddr; //寻址信息配置 static uint8 transferId = 0;//传输ID,是数据包的标识符/* Destination */ dstAddr.addrMode = afAddr16Bit;// 设置目标地址模式为16位网络地址,表示使用P2P的通信方式dstAddr.addr.shortAddr = destNwkAddr;//目标设备的网络地址 dstAddr.endPoint = SAMPLESW_ENDPOINT;//目标设备的端点号transferId++; AF_DataRequest(&dstAddr, &sampleSw_TestEp,//已经创建好的简单描述符cid, len, data, &transferId,AF_DISCV_ROUTE,//进行路由扫描操作,用于建立发送数据报文的通信路径。关于这个参数,暂时保持例程默认的代码就可以了AF_DEFAULT_RADIUS);//指定最大的路由跳转级数
}
这个API中使用了一个afAddrType_t类型变量,该变量类型被定义在AF.h中,用于配置寻址信息。这个变量定义如下:
typedef struct
{union{uint16 shortAddr;ZLongAddr_t extAddr;} addr;afAddrMode_t addrMode;uint8 endPoint;uint16 panId; // used for the INTER_PAN feature
} afAddrType_t;
这个API中也使用了一个afAddrMode_t变量,表示地址模式(类型),用于说明使用的是点对点、广播还是组播的通信方式。同样地,可以查看其定义,代码如下:
可以看到,afAddrMode_t是一个枚举类型变量。
在zclSampleSw_AF_P2P()函数的的最后,可以看到最终就是调用AF_DataRequest()来发送数据的。开发者也可以基于AF_DataRequest()来编写自己的点对点通信API,而不使用这个工程中的默认的这个zclSampleSw_AF_P2P()。
3.2.2 广播通信API
在zcl_samplesw.c文件中可以找到广播通信API,代码如下:
/*
* @param cid Cluster ID
* @param len 待发送数据的长度
* @param *data 待发送数据的内容
*/
static void zclSampleSw_AF_Broadcast(uint16 cid,uint8 len,uint8 *data)
{afAddrType_t dstAddr; static uint8 transferId = 0; /* Destination */ dstAddr.addrMode = afAddrBroadcast; // 使用广播模式dstAddr.addr.shortAddr = 0xFFFF; // 广播地址dstAddr.endPoint = SAMPLESW_ENDPOINT; // 目标设备的端点号/* Transfer id */ transferId++; /* Send */ AF_DataRequest(&dstAddr, &sampleSw_TestEp,//已经创建好的简单描述符cid, len, data, &transferId,AF_TX_OPTIONS_NONE,AF_DEFAULT_RADIUS );//指定了最大的路由跳转级数
}
从广播通信API的定义可以发现,它和点对点通信API是相似的。广播通信API中使用了网络地址0xFFFF,这表示把数据发送到该网络上的所有设备中。但其实ZigBee的广播地址一共有3个,如下所示:
0xFFFF:广播给该网络中的所有设备
0xFFFD:只广播给该网络中打开收听功能的设备
0xFFFC:只广播给该网络中的协调器和路由设备
同样地,开发者也可以基于AF_DataRequest()来编写自己的广播通信API,而不使用这个工程中的默认的这个zclSampleSw_AF_Broadcast()
。
3.2.3 组播通信API
使用组播通信API,可以只给指定的一组设备发送数据。在zcl_samplesw.c文件中可以找到组播通信API,代码如下:
/*
* @param groupId 组ID
* @param cid ClusterID,后续章节将会详细讲解
* @param len 待发送数据的长度
* @param data 待发送数据的内容
*/
static void zclSampleSw_AF_Groupcast(uint16 groupId,uint16 cid,uint8 len,uint8 *data)
{ afAddrType_t dstAddr; static uint8 transferId = 0; /* Destination */ dstAddr.addrMode = afAddrGroup;//使用组播通信模式dstAddr.addr.shortAddr = groupId;dstAddr.endPoint = SAMPLESW_ENDPOINT;//组中的设备的端点号/* Transfer id */ transferId++; /* Send */ AF_DataRequest(&dstAddr, &sampleSw_TestEp,//已经创建好的简单描述符cid, len, data, &transferId, AF_TX_OPTIONS_NONE,AF_DEFAULT_RADIUS );//指定最大的路由跳转级数,暂时可忽略
}
四、实战
在学习完AF层通信API后,本章将以实验的方式讲解如何使用这些API,实验设备包含一个协调器和一个路由器(或终端),内容是:
- 路由器(或终端)定时地通过点对点的方式向协调器发送数据。
- 让路由器(或终端)加入到一个组中。
- 协调器定时地发送广播数据和组播数据。
这部分根据善学坊教程:3.4 ZigBee 3.0 通信实验