什么是zigbee
无线传感网络的无线通信技术可以采用zigbee,蓝牙,wifi和红外
Zigbee技术是一种短距离,低复杂度,低功耗,低成本,低数据速率的双向无线通信技术,是一组基于IEEE802.15.4无线标准研制开发的有组网,安全和应用软件方面的通信技术
Zigbee应用范围
智能家居 - 对家用电器的控制和家庭娱乐系统的管理
楼宇自动化 - 整合并集中管理照明,采暖,制冷和安全和采集周围环境
工业自动化 - 利用传感器采集工业生产过程中的数据并进行分析和处理,提高工业产生的安全性
医学领域 - 将借助于各种传感器和Zigbee网络,准确而且实时的检测每个病人的血压,体温和心跳速度等信息
Zigbee协议体系结构
我们在学习网络的时候了解到网络的软件架构是按照分层的思想设计的,每个层负责不同功能,如典型的有OSI协议参考模型以及TCP/IP协议模型
zigbee协议也是在OSI参考模型上,结合无线网络的特点,采用分层思想实现
Zigbee标准将网络节点按照功能分为 协调器 (Coordinator),路由器(Router), 终端设备(EndDevice)
协调器:
为每个设备分配一个唯一的网络地址
为整个网络选择一个唯一的16位的PAN ID(个域网标志符),通过这个ID,网络中的设备就可以通过网络地址来相互通信
初始化,终止,转发网络中的信息
路由器:
允许设备加入网络,扩展网络覆盖的物理范围和数据包路由的功能
终端设备
主要负责无线网络数据的采集
点亮LED的硬件电路
寄存器 :
程序 代码:
#include "iocc2530.h"
#define LED1 P1_0
#define LED2 P1_1
#define LED3 P1_4
#define LED_NO 0
#define LED_OFF 1
void led_init(void);
void delay(unsigned int time);
int mian(void)
{
led_init();
while(1)
{
LED1 = LED_ON;
delay(10);
LED1 = LED_OFF;
delay(10);
LED2 = LED_ON;
delay(10);
LED2 = LED_OFF;
delay(10);
LED3 = LED_ON;
delay(10);
LED3 = LED_OFF;
delay(10);
}
}
void led_init(void)
{
P1SEL &= ~(1 << 0); //普通I/O口
P1DIR |= (1 << 0); //输出模式
LED1 = LED_OFF; //关灯
P1SEL &= ~(1 << 1);
P1DIR |= (1 << 1);
LED2 = LED_OFF;
P1SEL &= ~(1 << 4);
P1DIR |= (1 <<4);
LED3 = LED_OFF;
}
void delay(unsigned int time)
{
unsigned int i,j;
for(i = 0; i < time; i++)
{
for(j = 0; j < 10000; j++)
{
}
}
}
串口通信:
添加头文件:
#include "MT_UART.H"
#include "string.h"
在 void SampleApp_Init(uint8 task_id)函数里添加
MT_UartInit();
在 void SampleApp_Init(uint8 task_id)函数里添加
HalUARTWrite(0,"",strlen())
协议栈
OSAL
Zigbee协议栈的实时操作系统
操作系统抽象层 - Operating System Abstraction Layer
作用: 任务调度,资源分配和管理
关键字:任务与事件
taskCnt ——任务个数
taskEvents —— 指向事件表首地址的指针
taskArry —— 数组,数组中的每个元素都指向事件处理函数的指针
协议栈运行机制
入口在Zmain文件下,Zmain.c的main函数中,其中包含了各种硬件和协议栈的初始化,直到调用osal_start_system()函数,协议栈才开始真正运行起来
int main(void)
{
//...
//...
osal_start_system(); //No Return from here
return 0; //Shouldn't get here
}
在 OSAL_SampleApp.c 中找 SampleApp_ProcessEvent
#define SYS_EVENT_MSG 0x8000
#define SAMPLEAPP_SEND_PERIODIC_MSG_EVT 0x0001
//如果这俩个事件同时发生,则events = 0x8001
//异或运算,同为0 , 异为 1
events ^ SYS_EVENT_MSG = 0x001 //则只剩下后一个事件
events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT = 0x8000 //只剩下前一个事件
if (events & SYS_EVENT_MSG)
{
//...
//...
}
设备间对话
功能要求
协调器组建 PAN 网络
协调器组网成功后会周期性广播“ I am coordinator device” 5s
终端设备节点加入该PAN网络,加入网络成功后周期性广播字节串“I am endpoint device” 5s
实现过程
void SampleApp_SendPeriodicMessage(void)
{
if(AF_DataRaquest(&SampleApp_Periodic_DstAddr,
&SampleApp_eqDesc,
SAMPLEAPP_PERIODIC_CLUSTERID,
1,
(uint8 *)&SampleAppPeriodicCounter,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS) == afStatus_SUCCESS)
{
}
else
{
}
}
发送数据函数
AF_DataRequest(afAddrType_t * dstAddr, //目标设备地址
endPointDesc_t * srcFP, //端口描述符
uint16 cID, //命令号
uint16 len, //数据长度
uint8 * buf, //数据
uint8 * transID, //发送数据包序列号
uint8 options, //有效位掩码的发送选项 一般设 AF_DISCV_ROUTE
uint8 radius) //传送跳数或半径 一般设 AF_DEFAULT_RADIUS
地址结构体
typedef struct
{
union
{
uint16 shortAddr; //网络地址,该地址是设备在加入网络时由协议栈分配的
ZLongAddr_t extAddr; //64位扩展地址,全球唯一
}addr;
afAddrMode_t addrMode; //地址模式(广播,组播,单播)
byte endPoint; //端口号,可供范围1 ~ 240
uint16 panId; //一个无线网络的网络号
}afAddrType_t;
发送模式结构体
typedef enum
{
afAddrNotPresent = AddrNotPresent, //当前地址不存在
afAddr16Bit = Addr16Bit, //用于单播
afAddr64Bit = Addr64Bit, //用于单播
afAddrGroup = AddrGroup, //用于组播
afAddrBroadcast = AddrBroadcast //用于广播
}afAddrMode_t;
端口描述符
typedef struct
{
byte endPoint; //端口号
byte * task_id; //指定哪一个任务
SimpleDescriptionFormat_t * simpleDesc; //设备简单描述符
afNetwordkLatencyReq_t latencyReq; //延时请求
}endPointDesc_t;
设备简单描述符
typedef struct
{
byte EndPoint; //端口号
uint16 AppProfld; //规范号,一个规范定义一个应用领域,
uint16 AppDeviceId; //设备类型ID
byte AppDevVer:4; //应用设备版本号
byte Reserved:4; //保留位
byte AppNumInClusters; //输入簇的列表
cId_t *pAppInClusterList; //输入簇的列表
byte AppNumOutClusters; //输出簇的个数
cId_t *pAppOutClusterList; //输出簇的列表
}SimpleDescriptionFormat_t;
规范
在Zigbee网络中进行数据收发都是建立在应用规范基础上,每个应用规范都有一个ID来标识,应用规范又可以分为公共规范和制造商特定规范,公共规范ID的范围是0x0000 ~ 0x7FFF,制造商特定规范ID的范围0xBF00 ~ 0xFFFF
簇(cluster)
簇是一个应用领域下的一个特定对象,例如:智能家居中有这个调光器需要一些命令,实现这些操作需要不同的命令,多个操作命令的集合叫簇
在设备简单描述符中需要填充输入簇和输出簇,填充时需要注意:
消息发送方需把命令放在输出簇里,那么消息接受方需要把同样的命令放在输入簇里,之前的设备间第一次通话,由于输入簇和输出簇是一样的,所以协调器和终端设备间也能正常通信
广播与单播组建
Zigbee 网络支持星型,树(簇)型,网状 三个网络拓补结构
功能需求:
协调器周期性以广播的形式向终端节点发送数据 “I am coordinate”
加入其网络的终端节点都会收到数据,终端节点分别单播给协调器 “I am endpoint device ”
实现过程:
复制样本工程
把串口的相关代码添加
SampleApp_Init()里将SampleApp_Flash_DstAddr 设置为单播模式,且地址设置为0x000,此地址是协调器的地址
当网络状态发生改变,触发 SAMPLEAPP_SEND_PERIODIC_MSG_EVT事件,当检测到这个事件时,首先判断设备的逻辑类型:
协调器则执行 SampleApp_SendPeriodicMessage();
终端则执行 SampleApp_SendFlashMessage();
// SampleApp.c
// 添加头文件
#include "mt_uart.h"
#include "string.h"
// SampleApp.c
void SampleApp_Init(uint8 task_id)
{
// ...
MT_UartInit(); // 串口初始化
HalUARTWrite(0, "uart0 is ok ",strlen("uart0 is ok")); // 0端口
// ...
}
// MT_UART.c
void MT_UartInit()
{
halUARTCfg_t uartConfig;
//...
uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE; // 波特率
uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW; // 流控制
//...
}
// mt_uart.h
#define MT_UART_DEFAULT_BAUDRATE HAL_UART_BR_115200
#define MT_UART_DEFAULT_OVERFLOW FALSE // 关闭
// SampleApp.c
// 协调器给终端发送
SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast; //广播模式
SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF; //广播地址
// 终端给协调器发送
SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; //单播
SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_Flash_DstAddr.addr.shortAddr = 0x0000: //协调器地址
// SampleApp.c
// 关闭组播
/*
SampleApp_Grop.ID = 0x0001;
osal_memcpy(SampleApp_Group.name, "Group 1", 7);
aps_AddGroup(SAMPLEAPP_ENDPOINT, &SampleApp_Group);
*/
// SampleApp.c
typedef struct
{
uint8 event;
uint8 status;
} osal_event_hdr_t;
typedef struct
{
osal_event_hdr_t hdr; /* OSAL Message header */
uint16 groupId; /* Message's group ID - 0 if not set */
uint16 clusterId; /* Message's cluster ID */
afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
it's an InterPAN message */
uint16 macDestAddr; /* MAC header destination short address */
uint8 endPoint; /* destination endpoint */
uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */
uint8 LinkQuality; /* The link quality of the received data frame */
uint8 correlation; /* The raw correlation value of the received data frame */
int8 rssi; /* The received RF power in units dBm */
uint8 SecurityUse; /* deprecated */
uint32 timestamp; /* receipt timestamp from MAC */
uint8 nwkSeqNum; /* network header frame sequence number */
afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;
uint16 SampleApp_ProcessEvent(uint8 task_id,uint16 events)
{
afIncomingMSGPacket_t *MSGPkt;
(void)task_id;
if(events & SYS_EVENT_MSG) // 检测事件
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(SampleApp_TaskID); // 获取信息结构体
while(MSGpkt)
{
switch(MSGpkt -> hdr.event)
{
case KEY_CHANGE:
SampleApp_HandleKeys(((keyChange_t *)MSGpkt) -> state,
((keyChange_t *)MSGpkt) -> keys);
break;
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB(MSGpkt);
break;
case ZDO_STATE_CHANGE: // 网络状态
SampleApp_NwkState = (devStates_t)(MSGpkt -> hdr.status);
}
osal_msg_deallocate((uint8 *)MSGpkt);
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(SampleApp_TaskID);
}
return ();
}
if(events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT)
{
if(zgDeviceLogicalType == ZG_DEVICETYPE_COORDINATOR) // 判断协调器
{
SampleApp_SendPeriodicMessage(); // 发送数据
}
else if(zgDeviceLogicalTyep == ZG_DEVICETYPE_ENDDEVICE) //判断终端
{
SampleApp_SendFlashMessage(0x0000);
}
osal_start_timerEx(SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT +
(osal_read() & 0x00FF)) );
return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
return 0;
}
//SampleApp.c
void SampleApp_SendFlashMessage(uint16 flashTime) // 终端发送给协调器
{
uint8 *buffer = "I am endpoint device\n";
if( AF_DataRequest( &SampleApp_Flash_DstAddr, // 单步
&SampleApp_epDesc,
SAMPLEAPP_FLASH_CLUSTERID, // 命令号
strlen(buffer), // 长度
buffer,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS) == afStatus_SUCCESS)
{
}
else
{
}
}
void SampleApp_SendPeriodicMessage(void) // 协调器
{
uint8 *buff = "I am coordinator device\n";
if(AF_DataRequest(&SampleApp_Periodic_DstAddr,
&SampleApp_epDesc,
SAMPLEAPP_PERIODIC_CLUSTERID, //命令号
strlen(buff),
buff,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS) == afStatus_SUCCESS)
{
}
else
{
}
}
void SampleApp_MessageMSGCB(afIncomingMSGPacker_t *pkt)
{
uint16 flashTime;
switch(pkt -> clusterId)
{
//协调器到终端
case SAMPLEAPP_PFRIODIC_CLUSTERID:
HalUARTWrite(0 , pkt ->cmd.data , pkt -> cmd.DataLength);
break;
//终端到协调器
case SAMPLEAPP_FLASH_CLUSTERID:
HalUARTWrite(0, pkt ->cmd.Data, pkt -> cmd.DataLength);
break;
}
}
组播通信
功能要求
协调器创建网络,并加入一组,并向组内成员组播数据“I am coordinator device”
终端1加入网络,并加入与协调器相同的组,收到协调器发送而来的数据
终端2加入网络,并加入另外一组,不能收到协调器发来的数据
网络管理
地址分配机制
Z-stack 采用分布式分配机制,整个网络架构由 3 个值决定
Lm :网络的最大深度
Cm :每个父设备拥有子设备数
Rm : 每个父设备拥有路由子设备数
d : 父设备所在的网络深度
则父设备下的路由器子设备间地址间隔:
当 Rm = 1
1 + Cm * (Lm - d - 1)
当 Rm != 1
Cskip(d)= (1 + Cm -Rm - Cm *(Rm) ^ (Lm - d -1))/(1 - Rm)
父设备给路由器分配的网络地址
Achild = Aparent + (n - 1) *Cskip(d) +1
父设备给终端分配的网络地址
Achild = Aparent + Rm * Cskip(d)+ n
对协调器来说
Cskip(d)= (1 + Cm - Rm - Cm * (Rm) ^ (Lm - d - 1)) / (1 - Rm)
= (1 + 5 - 3 - 5 * 3 ^ (3 - 0 - 1)) / (1 - 3)
= 21
路由器地址 = Aparent + (n - 1)* Cskip(d) + 1
路由器1 = 0x0000 + 1 = 0x0001
路由器2 = 0x0000 + 21 + 1 = 0x0016
路由器3 = 0x0000 + 2 * 21 +1 = 0x002b
终端地址 = Aparent + Rm * Cskip(d) + n
终端1 = 0x0000 + 3 * 21 +1 = 0x0040
终端2 = 0x0000 + 3 * 21 + 2 = 0x0041
对路由器1来说
Cskip (d)= (1 + Cm - Rm - Cm * (Rm)^ (Lm - d - 1))/ (1 - Rm)
= (1 + 5 - 3 - 5 * 3 ^ (3 - 1 - 1 )) / (1 - 3)
= 6
路由地址 = Aparent + (n + 1) * Cskip(d)+1
路由器1 = 0x0001 + 1 = 0x0002
路由器2 = 0x0001 + 1 + 6 = 0x0008
路由器3 = 0x0001 + 1 + 2 * 6 = 0x000E
终端地址 = Aparent + Rm* Cskip(d)+ n
终端1 = 0x0001 + 3 * 6 + 1 = 0x0014
终端2 = 0x0001 + 3 * 6 + 2 = 0x0015
网络管理函数
在NLMED.h 里有 4 个网络管理函数
//返回指向本设备的MAC地址的指针
byte * NLME_GetExtAddr(void);
//返回本设备的网络地址
uint16 NLME_GetShortAddr(void);
//返回父设备的网络地址函数
uint16 NLME_GetCoordShortAddr(void);
//参数是指向父设备的MAC地址的指针
void NLME_GetCoordExtAddr(byte * buf);
协调器控制多个终端
功能要求:
协调器通过串口向终端发送控制命令
发送1 则终端1 的D8灯亮 发送 2 则 终端2 的灯亮 发送 0 则 俩个终端都灭
实现过程