学习记录:基于Z-Stack 3.0.1的Zigbee智能插座实现
引言
本文记录了笔者基于Z-Stack 3.0.1协议栈,通过学习Zigbee通信协议,实现一个简单的智能插座控制过程。通过这个过程,笔者对Zigbee网络的形成、设备间的通信以及低功耗设计有了更深入的理解。
工程代码链接:
链接:https://pan.baidu.com/s/1J58LBN3H_sZg5ZD6UY6t7A?pwd=zwha
提取码:zwha
这里推荐一个学习zigbee 3.0的网址:
其中笔者也是在这个地方学习的zigbee相关知识
Zigbee 3.0 开发指南
一、前期准备
1.1 下载与安装
首先,从官方渠道下载Z-Stack 3.0.1协议栈,并进行安装。Z-Stack是TI公司提供的Zigbee协议栈,它包含了构建Zigbee网络所需的所有基础组件和示例代码。
1.2 学习基础
在学习实现智能插座之前,笔者已经对Zigbee的基础知识有了一定的了解,包括网络拓扑结构、设备类型(协调器、路由器、终端)以及ZCL(Zigbee Cluster Library)等。特别是ZCL,它是Zigbee设备间通信的标准语言,通过发送和接收ZCL命令,可以实现设备间的控制和数据交换。
二、实现过程
2.1 打开示例工程
在Z-Stack 3.0.1中,提供了多个示例工程,本文选择GenericApp
作为起点。这个示例工程展示了如何使用Zigbee协议进行基本的设备通信。
2.2 初始化与网络形成
协调器是Zigbee网络的中心节点,负责创建和管理网络。在zclGenericApp_Init
函数中,通过宏定义ZDO_COORDINATOR
来区分协调器和其他设备(路由器或终端)。协调器的初始化过程包括注册ZDO消息、启动网络形成过程以及允许设备加入网络。
1.在APP组 -> zcl_genericapp.c -> void zclGenericApp_Init( byte task_id )的末尾添加
#ifdef ZDO_COORDINATOR
ZDO_RegisterForZDOMsg ( zclGenericApp_TaskID, Device_annce );
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_FORMATION |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
NLME_PermitJoiningRequest(255);
#else
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_STEERING |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
zclGenericapp_DeviceAnnce();
#endif
2.3 网络加入状态处理
在路由器或终端中,需要处理网络加入的状态。如果加入成功,则加快LED闪烁频率;如果加入失败,则减慢LED闪烁频率,并尝试重新加入网络。
需要定义,初始化500ms:
static uint16 zclGenericapp_ONOFF_TEST_PERIOD = 500;
在这里初始化了一个任务:在void zclGenericApp_Init( byte task_id )中
osal_start_timerEx( zclGenericApp_TaskID, GENERICAPP_EVT_1, zclGenericapp_ONOFF_TEST_PERIOD );
在uint16 zclGenericApp_event_loop( uint8 task_id, uint16 events ) 中
if ( events & GENERICAPP_EVT_1 )
{
// toggle LED 2 state, start another timer for 500ms
HalLedSet ( HAL_LED_2, HAL_LED_MODE_TOGGLE );
osal_start_timerEx( zclGenericApp_TaskID, GENERICAPP_EVT_1, zclGenericapp_ONOFF_TEST_PERIOD );
return ( events ^ GENERICAPP_EVT_1 );
}
2.4 设备发现与通信
2.4.1 设备广播
路由器或终端在加入网络后,会通过ZDP_DeviceAnnce
函数广播自己的设备信息,包括网络地址、物理地址等。协调器接收到这些信息后,会保存设备的网络地址,以便后续通信。
static void zclGenericapp_DeviceAnnce( void )
{
ZDP_DeviceAnnce(
NLME_GetShortAddr(),//获取本设备的网络地址(短地址)
NLME_GetExtAddr(),//获取本设备的物理地址(通常就是MAC地址)
ZDO_Config_Node_Descriptor.CapabilityFlags,//暂不展开简介,可忽略
0//暂不展开讲解,可忽略
);
}
2.4.2 协调器处理设备广播
协调器在接收到设备广播后,会自动调用zclGenericapp_processZDOMgs
函数处理这些信息,并保存设备的网络地址。
这里的zclGenericapp_processZDOMgs
函数需要在zclGenericApp_event_loop函数中调用:
#ifdef ZDO_COORDINATOR
case ZDO_CB_MSG:
zclGenericapp_processZDOMgs( (zdoIncomingMsg_t *)MSGpkt );
break;
#endif
static void zclGenericapp_processZDOMgs(zdoIncomingMsg_t *pMsg)
{
switch ( pMsg->clusterID )
{
case Device_annce:
{
zclGenericapp_OnOffTestAddr = pMsg->srcAddr.addr.shortAddr;
zclGenericapp_ONOFF_TEST_PERIOD = 200;
}
break;
default:
break;
}
}
2.4.3 绑定命令回调
路由器或终端在初始化时,会绑定一系列ZCL命令的回调函数。当接收到相应的命令时,会调用对应的回调函数进行处理。例如,接收到开关命令时,会调用zclGenericapp_OnOffCB
函数控制LED的开关。
在zclGenericApp_Init函数初始化:
#ifdef ZDO_COORDINATOR
NULL,
#else
zclGenericapp_OnOffCB, // On/Off cluster commands
#endif
这里需要绑定函数,接收到开关命令时,会调用zclGenericapp_OnOffCB
函数控制插座的开关。
static void zclGenericapp_OnOffCB( uint8 cmd )
{
if(cmd == COMMAND_ON) // 命令为ON时
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_ON); // 开启所有LED
}
else if(cmd == COMMAND_OFF) // 命令为OFF时
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_OFF); // 关闭所有LED
}
}
2.5 发送与控制
协调器通过zclGenericapp_OnOffTest
函数发送开关命令给路由器或终端。这个函数会根据当前状态发送开或关的命令,并切换状态。
在这里是通过按键来出发这个函数的
static void zclGenericapp_OnOffTest(void)
{
afAddrType_t destAddr;
static uint8 txID = 0;
static bool on = true;
destAddr.endPoint = GENERICAPP_ENDPOINT;
destAddr.addrMode = Addr16Bit;
destAddr.addr.shortAddr = zclGenericapp_OnOffTestAddr;
if(on)
{
zclGeneral_SendOnOff_CmdOn(GENERICAPP_ENDPOINT, &destAddr, TRUE, txID++);
}
else
{
zclGeneral_SendOnOff_CmdOff(GENERICAPP_ENDPOINT, &destAddr, TRUE, txID++);
}
on = !on;
}
2.6 按键控制
在协调器的代码中,通过按键来控制发送开关命令。当按下指定按键时,会调用zclGenericapp_OnOffTest
函数发送命令。
static void zclGenericApp_HandleKeys( byte shift, byte keys )
{
if ( keys & HAL_KEY_SW_6 )
{
#ifdef ZDO_COORDINATOR
zclGenericapp_OnOffTest();
#else
#endif
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE);
}
}
2.7 开启宏设置
在路由器或者终端设备编写代码时,为了确保代码正确运行,使用“开关命令收发”时需要开启对应的宏开关
ZCL_ON_OFF
三、问题与思考
3.1 网络重连问题
目前,如果协调器与路由器或终端断开连接,需要复位设备才能重新连接。这在实际应用中是不方便的。未来可以考虑实现更健壮的网络重连机制,如定时尝试重连、保存网络配置等。
3.2 命令确认与反馈
目前,协调器发送命令后,没有收到路由器或终端的确认反馈。这可能导致命令丢失或执行失败而不知情。未来可以实现命令确认机制,确保命令的可靠传输和执行。
3.3 低功耗设计
Zigbee设备的一个重要特点是低功耗。目前,本实现还没有进行低功耗设计。未来可以通过优化代码、使用低功耗硬件、设置休眠模式等方式来降低设备功耗。
3.4 工程裁剪与优化
Z-Stack 3.0.1协议栈包含了大量的功能和示例代码,对于特定的应用来说,可能有很多不需要的部分。未来可以根据实际需求对工程进行裁剪和优化,去除不需要的功能和代码,减少资源占用和功耗。
四、总结与展望
通过本次学习实践,笔者成功实现了基于Z-Stack 3.0.1的Zigbee智能插座控制。这个过程不仅加深了对Zigbee协议的理解,还提高了编程实践和问题解决的能力。未来,笔者将继续深入探索Zigbee技术的更多应用场景和优化方法,为物联网技术的发展贡献自己的力量。