This document describes how to add additional endpoints to the Router application in the AN12061-MKW41Z-AN-Zigbee-3-0-Base-Device Application Note.
The Router application's main endpoint acts as a light controlled by the On/Off cluster acting as a Server. The steps below describe how to add two new endpoints with On/Off clusters acting as clients.
Note that these changes only go as far as making the new endpoints discoverable, no functionality has been added to read inputs and transmit commands from the new endpoints.
The first step is to add the new endpoints (Switch1, Switch2) into ZCL configuration file.
/* Endpoints */ #define ROUTER_ZDO_ENDPOINT (0) #define ROUTER_APPLICATION_ENDPOINT (1) #define ROUTER_SWITCH1_ENDPOINT (2) #define ROUTER_SWITCH2_ENDPOINT (3)
The second step is to update the ZigBee Configuration file to increase the simple descriptor table size from 2 to 4, as it is the number of application endpoints (3 in our case) + 1 (ZDO endpoint). :
/*****************************************************************************/
/* ZPS AF Layer Configuration Parameters */
/*****************************************************************************/
#define AF_SIMPLE_DESCRIPTOR_TABLE_SIZE 4
The third step is to update the ZigBee cluster Configuration file to add the new endpoints (Switch1, Switch2) and their clusters to the Router application.
For that one need to change the Configured endpoint from 1 to 3 and also the Endpoint Map list present as below:
PUBLIC uint8 u8MaxZpsConfigEp = 3; PUBLIC uint8 au8EpMapPresent[3] = { ROUTER_APPLICATION_ENDPOINT,ROUTER_SWITCH1_ENDPOINT,ROUTER_SWITCH2_ENDPOINT };
The Switch 1 and Switch 2 contains Basic Cluster (0x0000) Server and Client, Identify Cluster (0x0003) Server and Client, OnOff Cluster (0x0006) Client, Group Cluster (0x004) Client. The clusters are added to the Input cluster list (Server side) and output cluster list (Client side) but made discoverable using DiscFlag only for the cluster list which is enabled. So, assuming you need to add OnOff cluster client, you would need to use add the cluster id (0x0006 for OnOff) into input cluster list (Server side of cluster) and output cluster list (Client side of the cluster) and make it discoverable for output cluster list as it is a client cluster.
PRIVATE const uint16 s_au16Endpoint2InputClusterList[5] = { HA_BASIC_CLUSTER_ID, HA_GROUPS_CLUSTER_ID, HA_IDENTIFY_CLUSTER_ID,\ HA_ONOFF_CLUSTER_ID, HA_DEFAULT_CLUSTER_ID, }; PRIVATE const PDUM_thAPdu s_ahEndpoint2InputClusterAPdus[5] = { apduZCL, apduZCL, apduZCL, apduZCL, apduZCL, }; PRIVATE uint8 s_au8Endpoint2InputClusterDiscFlags[1] = { 0x05 }; PRIVATE const uint16 s_au16Endpoint2OutputClusterList[4] = { HA_BASIC_CLUSTER_ID, HA_GROUPS_CLUSTER_ID, HA_IDENTIFY_CLUSTER_ID,\ HA_ONOFF_CLUSTER_ID, }; PRIVATE uint8 s_au8Endpoint2OutputClusterDiscFlags[1] = { 0x0f }; PRIVATE const uint16 s_au16Endpoint3InputClusterList[5] = { HA_BASIC_CLUSTER_ID, HA_GROUPS_CLUSTER_ID, HA_IDENTIFY_CLUSTER_ID,\ HA_ONOFF_CLUSTER_ID, HA_DEFAULT_CLUSTER_ID, }; PRIVATE const PDUM_thAPdu s_ahEndpoint3InputClusterAPdus[5] = { apduZCL, apduZCL, apduZCL, apduZCL, apduZCL, }; PRIVATE uint8 s_au8Endpoint3InputClusterDiscFlags[1] = { 0x05 }; PRIVATE const uint16 s_au16Endpoint3OutputClusterList[4] = { HA_BASIC_CLUSTER_ID, HA_GROUPS_CLUSTER_ID, HA_IDENTIFY_CLUSTER_ID,\ HA_ONOFF_CLUSTER_ID, }; PRIVATE uint8 s_au8Endpoint3OutputClusterDiscFlags[1] = { 0x0f };
Now add these newly added endpoints as part of Simple Descriptor structure and initialize the structure (see the declaration of zps_tsAplAfSimpleDescCont and ZPS_tsAplAfSimpleDescriptor structures to understand how to correctly fill the various parameters) correctly as below :
PUBLIC zps_tsAplAfSimpleDescCont s_asSimpleDescConts[AF_SIMPLE_DESCRIPTOR_TABLE_SIZE] = { { { 0x0000, 0, 0, 0, 84, 84, s_au16Endpoint0InputClusterList, s_au16Endpoint0OutputClusterList, s_au8Endpoint0InputClusterDiscFlags, s_au8Endpoint0OutputClusterDiscFlags, }, s_ahEndpoint0InputClusterAPdus, 1 }, { { 0x0104, 0, 1, 1, 5, 4, s_au16Endpoint1InputClusterList, s_au16Endpoint1OutputClusterList, s_au8Endpoint1InputClusterDiscFlags, s_au8Endpoint1OutputClusterDiscFlags, }, s_ahEndpoint1InputClusterAPdus, 1 }, { { 0x0104, 0, 1, 2, 5, 4, s_au16Endpoint2InputClusterList, s_au16Endpoint2OutputClusterList, s_au8Endpoint2InputClusterDiscFlags, s_au8Endpoint2OutputClusterDiscFlags, }, s_ahEndpoint2InputClusterAPdus, 1 }, { { 0x0104, 0, 1, 3, 5, 4, s_au16Endpoint3InputClusterList, s_au16Endpoint3OutputClusterList, s_au8Endpoint3InputClusterDiscFlags, s_au8Endpoint3OutputClusterDiscFlags, }, s_ahEndpoint3InputClusterAPdus, 1 }, };
This file is used to set the options used by the ZCL.
The number of endpoints is increased from 1 to 3:
/* Number of endpoints supported by this device */
#define ZCL_NUMBER_OF_ENDPOINTS 3
The client cluster functionality for the new endpoints is enabled:
/****************************************************************************/
/* Enable Cluster */
/* */
/* Add the following #define's to your zcl_options.h file to enable */
/* cluster and their client or server instances */
/****************************************************************************/
#define CLD_BASIC
#define BASIC_SERVER
#define BASIC_CLIENT
#define CLD_IDENTIFY
#define IDENTIFY_SERVER
#define IDENTIFY_CLIENT
#define CLD_GROUPS
#define GROUPS_SERVER
#define GROUPS_CLIENT
#define CLD_ONOFF
#define ONOFF_SERVER
#define ONOFF_CLIENT
The structures that store data for the new Base Devices associated with the new endpoints are created:
/****************************************************************************/
/*** Exported Variables ***/
/****************************************************************************/
tsZHA_BaseDevice sBaseDevice;
tsZHA_BaseDevice sBaseDeviceSwitch1;
tsZHA_BaseDevice sBaseDeviceSwitch2;
The two new Base Devices and their endpoints are registered with the stack to make them available:
if (eZCL_Status != E_ZCL_SUCCESS)
{
DBG_vPrintf(TRACE_ZCL, "Error: eZHA_RegisterBaseDeviceEndPoint(Light): %02x\r\n", eZCL_Status);
}
/* Register Switch1 EndPoint */
eZCL_Status = eZHA_RegisterBaseDeviceEndPoint(ROUTER_SWITCH1_ENDPOINT,
&APP_ZCL_cbEndpointCallback,
&sBaseDeviceSwitch1);
if (eZCL_Status != E_ZCL_SUCCESS)
{
DBG_vPrintf(TRACE_ZCL, "Error: eZHA_RegisterBaseDeviceEndPoint(Switch1): %02x\r\n", eZCL_Status);
}
/* Register Switch2 EndPoint */
eZCL_Status = eZHA_RegisterBaseDeviceEndPoint(ROUTER_SWITCH2_ENDPOINT,
&APP_ZCL_cbEndpointCallback,
&sBaseDeviceSwitch2);
if (eZCL_Status != E_ZCL_SUCCESS)
{
DBG_vPrintf(TRACE_ZCL, "Error: eZHA_RegisterBaseDeviceEndPoint(Switch2): %02x\r\n", eZCL_Status);
}
The two new Base Devices are factory reset by re-registering them when the Reset To Factory Defaults command is received by the Basic cluster server:
case GENERAL_CLUSTER_ID_BASIC:
{
tsCLD_BasicCallBackMessage *psCallBackMessage = (tsCLD_BasicCallBackMessage*)psEvent->uMessage.sClusterCustomMessage.pvCustomData;
if (psCallBackMessage->u8CommandId == E_CLD_BASIC_CMD_RESET_TO_FACTORY_DEFAULTS )
{
DBG_vPrintf(TRACE_ZCL, "Basic Factory Reset Received\n");
FLib_MemSet(&sBaseDevice,0,sizeof(tsZHA_BaseDevice));
APP_vZCL_DeviceSpecific_Init();
eZHA_RegisterBaseDeviceEndPoint(ROUTER_APPLICATION_ENDPOINT,
&APP_ZCL_cbEndpointCallback,
&sBaseDevice);
eZHA_RegisterBaseDeviceEndPoint(ROUTER_SWITCH1_ENDPOINT,
&APP_ZCL_cbEndpointCallback,
&sBaseDeviceSwitch1);
eZHA_RegisterBaseDeviceEndPoint(ROUTER_SWITCH2_ENDPOINT,
&APP_ZCL_cbEndpointCallback,
&sBaseDeviceSwitch2);
}
}
break;
The default attribute values for the Basic clusters are initialized:
sBaseDevice.sOnOffServerCluster.bOnOff = FALSE;
FLib_MemCpy(sBaseDevice.sBasicServerCluster.au8ManufacturerName, "NXP", CLD_BAS_MANUF_NAME_SIZE);
FLib_MemCpy(sBaseDevice.sBasicServerCluster.au8ModelIdentifier, "BDB-Router", CLD_BAS_MODEL_ID_SIZE);
FLib_MemCpy(sBaseDevice.sBasicServerCluster.au8DateCode, "20150212", CLD_BAS_DATE_SIZE);
FLib_MemCpy(sBaseDevice.sBasicServerCluster.au8SWBuildID, "1000-0001", CLD_BAS_SW_BUILD_SIZE);
sBaseDeviceSwitch1.sOnOffServerCluster.bOnOff = FALSE;
FLib_MemCpy(sBaseDeviceSwitch1.sBasicServerCluster.au8ManufacturerName, "NXP", CLD_BAS_MANUF_NAME_SIZE);
FLib_MemCpy(sBaseDeviceSwitch1.sBasicServerCluster.au8ModelIdentifier, "BDB-Sw1", CLD_BAS_MODEL_ID_SIZE);
FLib_MemCpy(sBaseDeviceSwitch1.sBasicServerCluster.au8DateCode, "20170310", CLD_BAS_DATE_SIZE);
FLib_MemCpy(sBaseDeviceSwitch1.sBasicServerCluster.au8SWBuildID, "1000-0001", CLD_BAS_SW_BUILD_SIZE);
sBaseDeviceSwitch2.sOnOffServerCluster.bOnOff = FALSE;
FLib_MemCpy(sBaseDeviceSwitch2.sBasicServerCluster.au8ManufacturerName, "NXP", CLD_BAS_MANUF_NAME_SIZE);
FLib_MemCpy(sBaseDeviceSwitch2.sBasicServerCluster.au8ModelIdentifier, "BDB-Sw2", CLD_BAS_MODEL_ID_SIZE);
FLib_MemCpy(sBaseDeviceSwitch2.sBasicServerCluster.au8DateCode, "20170310", CLD_BAS_DATE_SIZE);
FLib_MemCpy(sBaseDeviceSwitch2.sBasicServerCluster.au8SWBuildID, "1000-0001", CLD_BAS_SW_BUILD_SIZE);
The Base Device Data structures are made available to other modules:
/****************************************************************************/
/*** Exported Variables ***/
/****************************************************************************/
extern tsZHA_BaseDevice sBaseDevice;
extern tsZHA_BaseDevice sBaseDeviceSwitch1;
extern tsZHA_BaseDevice sBaseDeviceSwitch2;
Data messages addressed to the two new endpoints are passed to the ZCL for processing:
if (psZpsAfEvent->u8EndPoint == ROUTER_APPLICATION_ENDPOINT
|| psZpsAfEvent->u8EndPoint == ROUTER_SWITCH1_ENDPOINT
|| psZpsAfEvent->u8EndPoint == ROUTER_SWITCH2_ENDPOINT)
{
DBG_vPrintf(TRACE_APP, "Pass to ZCL\n");
if ((psZpsAfEvent->sStackEvent.eType == ZPS_EVENT_APS_DATA_INDICATION) ||
(psZpsAfEvent->sStackEvent.eType == ZPS_EVENT_APS_INTERPAN_DATA_INDICATION))
{
APP_ZCL_vEventHandler( &psZpsAfEvent->sStackEvent);
}
}