参考DEMO JN-AN-1218
Zigbee的网络通信功能如此强大,你按下遥控器,就可以改变一盏灯的状态。嗯,可以开关,可以改亮度,可以改颜色,可以定时。。。那么,这个过程是如何实现的呢?
简单说几个概念
PADID
网络ID,是一个16位数字(短地址)或64位数字(长地址),这个很像是路由器的地址或路由名字
NetAddr
网络地址同样分短地址(协调器分配的地址)和长地址(IEEE Addr),有点像互联网的IP地址
Device
设备,Doit
Endpoint
端口号,一个设备或一个节点可以有多个端口号,网络通信时不同设备之间可通过端口号访问,有点像互联网的端口号
Cluster
ON/OFF开关量是一个Cluster,温度是一个Cluster,OTA是一个Cluster。一个Cluster包含有属性和命令。
举个例子
例如,热水壶。
Profile = ZHA
Device = Thermostat_device
端口号=1,实际是APP指定的,与热水壶无直接关系
Cluster:TemperatureMeasurement,Time,Alarm,Poll,Scenes,Thermostat_UI,Basic,Identify,Power… 有很多,但并不是所有的Cluster都是必须的
遥控器如何工作
此处假定已完成组网、绑定,即遥控器按下时对应的灯会收到消息。
按键
按键按下,会触发一个按键事件,MCU会处理这个事件,参考JN-AN-1219,文件中的函数
PRIVATE void vSendDelayedCommands(te_TransitionCode eTransitionCode)
{
DBG_vPrintf(TRACE_SWITCH_STATE,"\nIn vSendDelayedCommands with TransitionCode = %d -> ",eTransitionCode);
switch(eTransitionCode)
{
case ON_PRESSED:
DBG_vPrintf(TRACE_SWITCH_STATE," E_CLD_ONOFF_CMD_ON \n");
vAppOnOff(E_CLD_ONOFF_CMD_ON);
break;
case OFF_PRESSED:
DBG_vPrintf(TRACE_SWITCH_STATE," E_CLD_ONOFF_CMD_OFF \n");
vAppOnOff(E_CLD_ONOFF_CMD_OFF);
break;
//...
default :
break;
}
vDQButtonPress();
}
代码中可以看到捕捉到按键后调用了函数vAppOnOff
,进而调用函数eCLD_OnOffCommandSend
。
PUBLIC void vAppOnOff(teCLD_OnOff_Command eCmd) {
uint8 u8Seq;
tsZCL_Address sAddress;
vSetAddress(&sAddress, FALSE, GENERAL_CLUSTER_ID_ONOFF);
if ((eCmd == E_CLD_ONOFF_CMD_ON) || (eCmd == E_CLD_ONOFF_CMD_OFF) || (eCmd
== E_CLD_ONOFF_CMD_TOGGLE)) {
eCLD_OnOffCommandSend(
u8MyEndpoint,
sDeviceInfo.sLightInfo[sDeviceInfo.u8Index].u8Ep,
&sAddress, &u8Seq, eCmd);
}
}
eCLD_OnOffCommandSend函数会把数据发送出去。
^_^, 按键的很简单吧。
灯泡
接收端整明白了也很简单,MCU底层会对接收到的数据进行处理,此处会修改灯泡上ON/OFF Cluster的状态和相应属性的值,并用事件的方式通知app。
还记得前面提到的Device和端口号的说明吧,其是通过注册函数的方式把不同Cluster的相关接口函数指针添加到协议栈[这里不确定描述是否正确]的,例如JN-AN-1218调用的是eApp_ZLO_RegisterEndpoint
函数
eApp_ZLO_RegisterEndpoint(&APP_ZCL_cbEndpointCallback);
重要的是这里的APP_ZCL_cbEndpointCallback
函数,底层发送事件后会在此函数中进行处理。
tsZLO_ColourTemperatureLightDevice sLight;
// 代码段 app_zcl_light_task.c line 518
PRIVATE void APP_ZCL_cbEndpointCallback(tsZCL_CallBackEvent *psEvent)
{
bool_t bUpdateBulb = FALSE;
switch (psEvent->eEventType)
{
// ...
case E_ZCL_CBET_CLUSTER_CUSTOM:
DBG_vPrintf(TRACE_ZCL, "\nEP EVT: Custom Cl %04x\n", psEvent->uMessage.sClusterCustomMessage.u16ClusterId);
switch(psEvent->uMessage.sClusterCustomMessage.u16ClusterId)
{
case GENERAL_CLUSTER_ID_ONOFF:
{
tsCLD_OnOffCallBackMessage *psCallBackMessage = (tsCLD_OnOffCallBackMessage*)psEvent->uMessage.sClusterCustomMessage.pvCustomData;
DBG_vPrintf(TRACE_ZCL, " CmdId=%d", psCallBackMessage->u8CommandId);
switch(psCallBackMessage->u8CommandId)
{
case E_CLD_ONOFF_CMD_OFF_EFFECT:
DBG_vPrintf(TRACE_ZCL, "\nOff with effect %d:%d", psCallBackMessage->uMessage.psOffWithEffectRequestPayload->u8EffectId,
psCallBackMessage->uMessage.psOffWithEffectRequestPayload->u8EffectVariant);
break;
}
if (sLight.sIdentifyServerCluster.u16IdentifyTime == 0)
{
/* only update bulb if not identifying */
bUpdateBulb = TRUE;
}
}
break;
// ...
if (bUpdateBulb)
{
vUpdateBulbFromZCL(FALSE);
}
请注意代码中的bUpdateBulb = TRUE;
会触发函数vUpdateBulbFromZCL
的执行。
PRIVATE void vUpdateBulbFromZCL(bool_t bResetInterpolation)
{
vApp_eCLD_ColourControl_GetRGB(&u8Red, &u8Green, &u8Blue);
。。。
vRGBLight_SetLevels(sLight.sOnOffServerCluster.bOnOff,
sLight.sLevelControlServerCluster.u8CurrentLevel,
u8Red,
u8Green,
u8Blue);
#elif (defined MONO_ON_OFF)
/*
* mono on off bulb
*/
DBG_vPrintf(TRACE_PATH, "\nJP on_off only bulb");
vSetBulbState( sLight.sOnOffServerCluster.bOnOff);
#endif
u8StateChangeTick = BULB_SAVE_DELAY_SEC;
}
函数中可以看到调用了vSetBulbState
函数,这个函数最终完成灯泡的开关控制。
简单总结
ZCL会对接收的数据进行处理,并通过E_ZCL_CBET_CLUSTER_CUSTOM
事件通知app,在回调函数APP_ZCL_cbEndpointCallback
被执行的时候处理此时间,并根据ClusterID完成不同的事件处理。即Cluster的Handle函数只改变Cluster的属性值,有Callback函数再来根据Cluster的值来操作硬件。
灯泡与遥控配对过程的闪烁也是通过此方法实现的,区别是在不同的回调函数中调用vSetBulbState
函数。