综合实验_基于Zigbee的智能家居系统实验
实验内容
- 通过ZigBee网络传输数据;
- 通过ZigBee网络对硬件进行控制。
实验目的
- 熟练使用ZigBee协议读数据及发送控制指令。
实验环境
实验所需硬件
序号 | 名称 | 数量 | 备注 |
---|---|---|---|
1 | 电脑 | 1台 | 系统Windows7及以上 |
2 | CC2530底座模块 | 3个 | · |
3 | 温湿度模块 | 1个 | · |
4 | LED模块 | 1个 | · |
5 | OLED模块 | 1个 | 7*5cm |
6 | ST-Link下载器 | 1个 | · |
7 | ST-Link下载器连接线 | 1根 | · |
8 | ZigBee智能家居实验代码 | 4份 | · |
实验所需软件
- IAR 驱动安装步骤
-
CC Debugger 驱动安装步骤
- Git 软件下载(可选)
CC Debugger 仿真器和连接线
USB线
实验原理
0
ZigBee基本通信方式
ZigBee的通讯方式主要有三种点播、组播、广播。点播顾名思义就是点对点通信,也是2个设备之间的通讯,不允许有第三个设备收到信息;组播就是把网络中的节点分组,每一个组员发出的信息只有相同组号的组员才能收到。广播最广泛的也就是1个设备上发出的信息所有设备都能接收到。这也是ZigBee通信的基本方式。
ZigBee网络地址类型
序号 | 地址类型 | 说明 |
---|---|---|
1 | AddrNotPresent | 依照绑定表 |
2 | Addr16Bit | 16位地址,常用于点播 |
3 | Addr64Bit | Addr64Bit |
4 | AddrGroup | 组播 |
5 | AddrBroadcast | 广播 |
以上的的地址类型均保存在afAddrMode_t。在实际使用过程中考虑功耗,常用使用16位地址,原因16位地址是通过路由算法计算出来的,在传输路径上通过的节点较少最大的节省了网络功耗。16位地址可分成如下几类如表:
序号 | 16位地址类型 | 说明 |
---|---|---|
1 | 0xFFFF | 对所有设备广播,包括睡眠 |
2 | 0xFFFE | 间接传输,通过绑定表寻找网络短地址 |
3 | 0xFFFD | 对没休眠的设备广播 |
4 | 0xFFFC | 给协调器和路由器广播 |
5 | 0x0000 | 给协调器通信 |
6 | 0x0001-0xFFFB | 用户自设定的目标地址 |
实验步骤
-
将温湿度模块、OLED模块、LED模块分别安装在CC2530底座上,确认各个节点,如下图所示,ST_LINK连接OLED节点连接。
-
CC Debugger连接PC机与OLED节点CC2530底座,轻按CC Debugger复位按键,指示灯变绿,表示连接正常。
-
打开
IAR Embedded Workbench
工程软件,点击工具栏:File
->Open
->Workspace
,选择工程文件:综合实验\ZigBee智能家居\Projects\zstack\Samples\SampleApp\CC2530DB\SampleApp.eww
并打开。 -
修改PANID或者信道防止与他人网络冲突,终端与协调器代码共用该配置文件如图:
-
设置工程配置为
Coordinator
。 -
点击
Make
按钮,编译程序,点击Download and Debug
按钮,将程序下载到底座中。 -
点击
X
,退出仿真模式。 -
将CC Debuger连接到温湿度节点,选择工程的配置为
EndDeviceEB-TempHumi
如图。 -
编译工程,下载程序,方法参考以上步骤6~7。
-
将CC Debuger连接到LED节点,选择工程的配置为
EndDeviceEB-LED
如图: -
编译工程,下载程序,方法参考以上步骤6~7。
-
移除CC Debugger,采用USB线供电,如图:
-
观察OLED屏温湿度数据,如图:
-
单击OLED上的按键,选择灯1、灯2、灯3、灯4(灯1、灯2、灯3、灯4指的是LED模块上的四个灯),选择选后双击OLED的按键进行控制操作:
代码讲解
LED节点
① 程序目录结构,如下图。代码中有大量ZigBee底层的代码,我们主要关心下图中标出的文件代码,ZigBee底层的代码会使用即可。
② EndDevice-LED.c->SampleApp_Init()函数是应用代码的入口函数,对LED模块初始化、初始化Point_To_Point_DstAddr结构,注册端点。
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id;
SampleApp_NwkState = DEV_INIT;
SampleApp_TransID = 0;
UartInit(HAL_UART_PORT_1,HAL_UART_BR_115200);//用于调试
LedMode_Init();//初始化LED模块
printf("i am end device\r\n");//串口打印
//点对点通讯定义
Point_To_Point_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;//点播
Point_To_Point_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
Point_To_Point_DstAddr.addr.shortAddr = 0x0000;//发给协调器
//填写端点
EndDevice_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
EndDevice_epDesc.task_id = &SampleApp_TaskID;
EndDevice_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&EndDevice_SimpleDesc;
EndDevice_epDesc.latencyReq = noLatencyReqs;
//注册端点
afRegister( &EndDevice_epDesc );
}
EndDevice.c->SampleApp_ProcessEvent()函数是任务处理函数。在该函数中调用SampleApp_MessageMSGCB()处理无线信道的指令。
// Received when a messages is received (OTA) for this endpoint
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
温湿度节点
① 程序目录结构,如下图。代码中有大量ZigBee底层的代码,我们只要主要关心下图中标出的文件代码,ZigBee底层的代码会使用即可。
② EndDevice_TempHumi.c->SampleApp_Init()函数是应用代码的入口函数,对温湿度模块初始化、初始化Point_To_Point_DstAddr结构,注册端点、启动传感器数据采集。
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id;
SampleApp_NwkState = DEV_INIT;
SampleApp_TransID = 0;
SHT2x_Init();//初始化温湿度传感器
UartInit(HAL_UART_PORT_1,HAL_UART_BR_115200);//初始化串口1,用于printf
printf("i am end device\r\n");//串口打印
//点对点通讯定义
Point_To_Point_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; //点播
Point_To_Point_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
Point_To_Point_DstAddr.addr.shortAddr = 0x0000;//发给协调器
//填写端点
EndDevice_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
EndDevice_epDesc.task_id = &SampleApp_TaskID;
EndDevice_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&EndDevice_SimpleDesc;
EndDevice_epDesc.latencyReq = noLatencyReqs;
//注册端点
afRegister( &EndDevice_epDesc );
//启动定时器,开始本地显示温湿度数值
osal_start_timerEx( SampleApp_TaskID, READ_TEMP_HUMI_MSG_EVT,1000);
}
③ EndDevice.c->SampleApp_ProcessEvent()函数是任务处理函数。在该函数中调用SHT2x_DisplayHumi()、SHT2x_DisplayTemp()函数读取传感器数据显示在数码管上。定时调用SendTemperatureHumityToCoordinator()函数向协调器发送传感器数据。
if(events & READ_TEMP_HUMI_MSG_EVT)
{
TempHumiShowSwitch = 1 - TempHumiShowSwitch;//滚动显示标志
/*读取温湿并显示*/
TempHumiShowSwitch?SHT2x_DisplayHumi():SHT2x_DisplayTemp();
/*1000ms 后触发READ_TEMP_HUMI_MSG_EVT事件*/
osal_start_timerEx( SampleApp_TaskID, READ_TEMP_HUMI_MSG_EVT,1000);
}
if(events & SEND_TEMP_HUMI_MSG_EVT)
{
SendTemperatureHumityToCoordinator();//发送温湿度到OLED节点
/*2000ms 后触发SEND_TEMP_HUMI_MSG_EVT事件*/
osal_start_timerEx( SampleApp_TaskID, SEND_TEMP_HUMI_MSG_EVT,2000);
}
OLED节点
① 程序目录结构,如下图。代码中有大量ZigBee底层的代码,我们只要主要关心下图中标出的文件代码,ZigBee底层的代码会使用即可。
② Coordinator.c->SampleApp_Init()函数是应用代码的入口函数,对OLED屏,注册端点。
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id;
SampleApp_NwkState = DEV_INIT;
SampleApp_TransID = 0;
UartInit(HAL_UART_PORT_1,HAL_UART_BR_115200);
printf("我是协议器\r\n");//串口打印
OLEDM_Init();//初始化OLED
OLED_PowerUpDisp();//开机启动界面
//点对点通讯定义,本次实验中协调器指向风扇节点发送数据,而不会向湿湿度发送,
//Point_To_Point_DstAddr 结构体用于向风扇节点发送指令使用
Point_To_Point_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;//点播
Point_To_Point_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
Point_To_Point_DstAddr.addr.shortAddr = 0;
//广播通信定义
Boardcast_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
Boardcast_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
Boardcast_DstAddr.addr.shortAddr = 0Xffff;
//填写端点
Coordinator_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
Coordinator_epDesc.task_id = &SampleApp_TaskID;
Coordinator_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&Coordinator_SimpleDesc;
Coordinator_epDesc.latencyReq = noLatencyReqs;
//注册端点
afRegister( &Coordinator_epDesc );
/*3000ms后触发 READ_KEY_MSG_EVT */
osal_start_timerEx( SampleApp_TaskID, READ_KEY_MSG_EVT,3000);
}
Coordinator.c ->SampleApp_ProcessEvent()函数是任务处理函数。当接收到温湿度数据时,进入SampleApp_MessageMSGCB()函数。
// Received when a messages is received (OTA) for this endpoint
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
SampleApp_MessageMSGCB()函数中将传感器的数据解析显示在OLED显示屏上。
case TEMP_HUMI_CLUSTERID:
Sensor_Temp = pkt->cmd.Data[0];
Sensor_Humi = pkt->cmd.Data[1];
OLED_Disp_Temp(0,0,Sensor_Temp);//更新温度
OLED_Disp_Humi(0,2,Sensor_Humi);//更新湿度
break;
Coordinator.c ->SampleApp_ProcessEvent()函数是任务处理函数,其中调用OLED_KeyPoll(),获取当前按键是单击还是双击。按键单击选择控制对象(灯1、灯2、灯3、灯4)。双击进行控制操作(亮/灭)。
switch(OLED_KeyPoll())//OLED_KeyPoll()检测按键
{
case KV_DOUBLE://双击按键
printf("KV_DOUBLE\r\n");
LEDCtrl = 1;
LEDCmd[0] = DevItem-1;//控制哪个灯。
LED_State[LEDCmd[0]] = 1 - LED_State[LEDCmd[0]];
LEDCmd[1] = LED_State[LEDCmd[0]];//控制动作(亮/灭)。
Send_LEDCtrl(LEDCmd[0],LEDCmd[1]);//发送LED节点
break;
case KV_SINGLE://单击按键
printf("KV_SINGLE\r\n");
DevItem++;
if(DevItem>4){//只有4个选项,灯1 灯2 灯3 灯4
DevItem = 1;
}
OLED_SelectDev(DevItem);//更新选项反显。
break;
}
常见问题
-
弹出警告窗口,不能下载程序。
- 请确认CC Debugger的驱动否安装。
- 轻按CC Debugger仿真器的按键,指示灯不是绿色表示连接有问题。
- CC Debugger仿真器是否正常接入到底座,参考实验步骤第一步。
-
下载代码后程序没观察到实验现象。
- 请重新上电,或者按下底座上的复位按键。
- 模块没有安装稳妥。
- 节点之间的PANID、信道是否相同。
实验思考
- 将继电器模块加入其中并实现对继电器的控制。