点击左上角的
“关注”
,定期更新Zigbee最新资讯,总有你想要的信息!
这篇文章主要想让大家了解Zigbee的无线传输机制。了解Z-Stack协议栈中如何发送数据,如何接受数据和处理数据。
当子设备(终端节点或路由器)加入协调器的网络后,它们之间便可以相互通信了。Zigbee设备通信的实质是Zigbee设备端口与另一个Zigbee设备端口之间的通信。只要知道目标设备的网络地址和端口号就可以调用数据发送函数
“AF_DataRequest()” 发送数据给目标设备的端口,再由该端口绑定的任务来处理该数据。如下图所示。
Zigbee设备提供给用户使用的端口为1~240号端口。若用户要使用某端口收发数据,则需要使用afRegister()
函数在设备上注册该端口,注册完成后便可以使用该端口收发数据。
接下来我们分析一下这个端口注册函数“afStatus_t afRegister( endPointDesc_t *epDesc )”
如何使用。
先看一下afRegister()
函数的形参端口描述符“epDesc”,它包含了该端口的所有信息。
typedef struct
{
uint8 endPoint; // 端口号
uint8 *task_id; // 该端口绑定的任务的任务ID,发送至此端口的数据都由此任务处理
SimpleDescriptionFormat_t *simpleDesc; // 简单描述符:存储该端口更多的信息
afNetworkLatencyReq_t latencyReq; // 固定为noLatencyReqs
} endPointDesc_t;
下面是简单描述符所包含的信息:
typedefstruct
{
byte EndPoint; // 端口号
uint16 AppProfId; // 应用规范ID
uint16 AppDeviceId; // 特定规范ID的设备类型
byte AppDevVer:4; // 特定规范ID的设备的版本
byte Reserved:4; // AF_V1_SUPPORTusesforAppFlags:4.
byte AppNumInClusters; // 输入簇ID的个数
cId_t *pAppInClusterList; // 输入簇ID的列表
byte AppNumOutClusters; // 输出簇ID的个数
cId_t *pAppOutClusterList; // 输出簇ID的列表
}SimpleDescriptionFormat_t;
我们首先在我们自己的“任务初始化函数”中注册一个端口用来通信,应用层代码是基于我们上一篇文章《7天快速入门Zigbee:如何在协议栈中从零建立自己的任务》写的代码。
----------------------------------------------------------- Gateway.c -----------------------------------------------------------
#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"
#include "GenericApp.h"
#include "DebugTrace.h"
#if !defined( WIN32 ) || defined( ZBIT )
#include "OnBoard.h"
#endif
#include "Gateway.h"
/*********************************************************************
* MACROS
*/
/*********************************************************************
* CONSTANTS
*/
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
// 任务ID
byte g_gateway_taskid = 0;
/* 新添加代码 START */
// 定义端口描述符
endPointDesc_t g_gateway_epdesc = {0};
// 定义简单描述符
const cId_t Gateway_InClusterList[] =
{
TRANSMISSION_CLUSTERID
};
#define GATEWAY_MAX_INCLUSTERS ( sizeof( Gateway_InClusterList )/
sizeof( Gateway_InClusterList[0] ))
const cId_t Gateway_OutClusterList[] =
{
TRANSMISSION_CLUSTERID
};
#define GATEWAY_MAX_OUTCLUSTERS ( sizeof( Gateway_OutClusterList )/
sizeof( Gateway_OutClusterList[0] ))
const SimpleDescriptionFormat_t g_gateway_simpledesc =
{
GATEWAY_ENDPOINT, // int Endpoint;
GATEWAY_PROFID, // uint16 AppProfId[2];
GATEWAY_DEVICEID, // uint16 AppDeviceId[2];
GATEWAY_DEVICE_VERSION, // int AppDevVer:4;
GATEWAY_FLAGS, // int AppFlags:4;
GATEWAY_MAX_INCLUSTERS, // byte AppNumInClusters;
(cId_t *)Gateway_InClusterList, // byte *pAppInClusterList;
GATEWAY_MAX_OUTCLUSTERS, // byte AppNumInClusters;
(cId_t *)Gateway_OutClusterList // byte *pAppInClusterList;
};
/* 新添加代码 END */
/*********************************************************************
* GLOBAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
/*********************************************************************
* LOCAL FUNCTIONS
*/
static void Init_IndicatorLight(void);
/*********************************************************************
* EXTERN VARIABLES
*/
/*********************************************************************
* EXTERN FUNCTIONS
*/
void Gateway_Init( uint8 task_id )
{
g_gateway_taskid = task_id;
/* 新添加代码 START */
// 填充端口描述符
g_gateway_epdesc.endPoint = GATEWAY_ENDPOINT;
g_gateway_epdesc.task_id = &g_gateway_taskid;
g_gateway_epdesc.simpleDesc = (SimpleDescriptionFormat_t *)&g_gateway_simpledesc;
g_gateway_epdesc.latencyReq = noLatencyReqs;
// 注册该端口
afRegister(&g_gateway_epdesc);
/* 新添加代码 END */
// 初始化LED灯
Init_IndicatorLight();
// 通知g_gateway_taskid任务有LED灯闪烁事件发生
osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
}
uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
if ( events & EVENT_FLASH_LED )
{
// 置反LED灯
if(P1_0==1)
{
P1_0 = 0;
}
else
{
P1_0 = 1;
}
// 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500);
return (events ^ EVENT_FLASH_LED);
}
return 0;
}
static void Init_IndicatorLight(void)
{
// P1_0,LED1,低电平亮,高电平灭
P1SEL &= ~(1<<0);
P1DIR |= (1<<0); // IO口方向输出
P1_0 = 1; // LED灯灭
}
----------------------------------------------------------- Gateway.h -----------------------------------------------------------
#ifndef __GATEWAY_H
#define __GATEWAY_H
#ifdef __cplusplus
extern "C"
{
#endif
/*********************************************************************
* MACROS
*/
// 自定义事件
#define EVENT_FLASH_LED 0x0001
/* 新添加代码 START */
// 简单描述符信息
#define GATEWAY_ENDPOINT 8
#define GATEWAY_PROFID 0x0F04
#define GATEWAY_DEVICEID 0x0001
#define GATEWAY_DEVICE_VERSION 0
#define GATEWAY_FLAGS 0
#define TRANSMISSION_CLUSTERID 0x01
/* 新添加代码 END */
/*********************************************************************
* CONSTANTS
*/
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* VARIABLES
*/
/*********************************************************************
* FUNCTIONS
*/
void Gateway_Init( uint8 task_id );
uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events );
#ifdef __cplusplus
}
#endif
#endif
至此我们就已经在Zigbee设备上成功注册了端口8作为我们的数据收发端口,端口绑定的任务是我们上一篇文章建立的任务。
上面我们已经注册了“端口8”作为我们数据的收发端口。那么接下来我们再来分析一下这个全协议栈唯一的数据发送函数“AF_DataRequest()”
。
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius )
dstAddr
:填充目标设备的网络地址和目标端口号。
srcEP
:本身发送端口的端口描述符
cID
:ClusterID,这个我们以后再分析
len
:要发送数据的长度
buf
:要发送的数据
transID
:此条发送命令的发送ID
options
:后面分析,默认AF_DISCV_ROUTE
radius
:后面分析,默认AF_DEFAULT_RADIUS
我们的终端设备可以利用这个函数发送数据给协调器,协调器的网络地址已知固定为0x0000,上面已注册端口8为数据收发端口。
为了方便我们以后的编程,从现在开始我们将3种设备类型分为3个协议栈分开编程。
整体的编程思路为终端节点每隔1秒发送一次字符串数据“Enddevice”给协调器,协调器收到数据后置反LED灯开关状态。
------------------------------------------------------------ Gateway.h ------------------------------------------------------------
……
// 定义周期发送数据事件
#define EVENT_PERIOD_SEND_DATA 0x0002
// Cluster
#define TRANSMISSION_CLUSTERID 0x0001
……
------------------------------------------------------------ Gateway.c ------------------------------------------------------------
void Gateway_Init( uint8 task_id )
{
……
// 通知g_gateway_taskid任务有LED灯闪烁事件发生
// osal_set_event(g_gateway_taskid, EVENT_FLASH_LED); // 关掉上一篇文章的LED灯闪烁功能
// 开始定期发送数据给协调器
osal_set_event(g_gateway_taskid, EVENT_PERIOD_SEND_DATA);
}
uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
……
// 处理周期性发送数据事件
if( events & EVENT_PERIOD_SEND_DATA )
{
uint8 data[] = "Enddevice";
// 目标地址为协调器
afAddrType_t dstaddr = {0};
dstaddr.addrMode = (afAddrMode_t)Addr16Bit; // 单播
dstaddr.addr.shortAddr = 0x0000; // 目标地址为协调器
dstaddr.endPoint = GATEWAY_ENDPOINT; // 目标端口为收发数据端口8
// 发送数据
AF_DataRequest(&dstaddr, // 目标设备地址和端口
&g_gateway_epdesc, // 发送设备的端口描述符
TRANSMISSION_CLUSTERID, // 数据传输Cluster
sizeof(data), // 要发送数据的大小
data, // 要发送的数据
&g_transid, // 此条发送命令的发送ID
AF_DISCV_ROUTE, // 后面分析,默认AF_DISCV_ROUTE
AF_DEFAULT_RADIUS); // 后面分析,默认AF_DEFAULT_RADIUS
// 每隔1秒发送一次字符串数据“Enddevice”给协调器
osal_start_timerEx(g_gateway_taskid, EVENT_PERIOD_SEND_DATA, 1000);
return (events ^ EVENT_PERIOD_SEND_DATA);
}
……
}
协调器设备要接收来自终端设备的数据。
------------------------------------------------------------ Gateway.c ------------------------------------------------------------
uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
// 接收无线数据事件
case AF_INCOMING_MSG_CMD:
// 处理无线数据函数
Gateway_MessageMSGCB(MSGpkt);
break;
default:
break;
}
// Release the memory
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
……
}
协调器设备接收到终端设备的无线数据后,置反一次LED灯的开关状态。
------------------------------------------------------------ Gateway.h ------------------------------------------------------------
……
// Cluster
#define TRANSMISSION_CLUSTERID 0x0001
……
------------------------------------------------------------ Gateway.c ------------------------------------------------------------
// 声明无线数据处理函数
static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt );
void Gateway_Init( uint8 task_id )
{
……
// 通知g_gateway_taskid任务有LED灯闪烁事件发生
// osal_set_event(g_gateway_taskid, EVENT_FLASH_LED); // 关掉上一篇文章的LED灯闪烁功能
}
uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
……
if ( events & EVENT_FLASH_LED )
{
……
// 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
// osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500); // 关闭自动闪烁LED功能
return (events ^ EVENT_FLASH_LED);
}
……
}
static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
switch ( pkt->clusterId )
{
case TRANSMISSION_CLUSTERID:
{
// 置反LED灯开关状态
osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
}
break;
}
}
分别按下终端设备和协调器设备工程的编译按钮,0错误0警告,然后我们将程序分别烧录到两个CC2530设备中,等待设备自动组网后,协调器就可以收到终端设备每秒钟发送过来的数据,然后置反LED灯开关状态,我们就可以观察协调器上的LED灯1秒钟闪烁一次了。
软件源码的下载地址
在下面的评论区有给出。
大家的支持就是我分享技术的动力,希望大家需转载时能附上原作者的博客:https://blog.csdn.net/u012993936 ,谢谢。
> Zigbee进阶:功能模块
> 免费的Zigbee抓包神器!比Ubiqua还好用!
文章都看完了,随手点个赞吧~
↓↓↓ ↓↓↓