Introduction
Z-Stack is a component of the SimpleLink CC13x2 / CC26x2 Software Development Kit and is a complete Zigbee 3.0 software solution.
The intention of this lab is to help developers create their own custom Zigbee devices using Z-Stack. Topics that we will cover in this lab include:
- Choosing a certifiable Zigbee device type from the Zigbee device type specifications
- Gathering the required information about your chosen Zigbee device from the Zigbee documentation
- Modifying the sample applications provided in the SimpleLink SDK to suit the needs of your chosen Zigbee device
Note: This lab is intended to be a starting point for development, it does not go into details about how to test and verify the final product.
Technical support
For any questions you may have, please refer to the TI Zigbee & Thread E2E Forum.
Prerequisites
Background
- Familiarity with the Zigbee 3.0 specification
- More information about the changes in Zigbee 3.0 compared to older specifications can be found in SWRA615
- Basic CCS knowledge
- Some basic familiarity with embedded programming
Software
- Code Composer Studio v10.1 or later
- SimpleLink CC13x2 / CC26x2 SDK (4.20.xx)
Recommended Reading
Zigbee Documentation
- Zigbee Cluster Library v7 Specification (ZCL spec) - Zigbee Document number 07-5123-07
- Zigbee Lighting and Occupancy Device Specification (ZLO spec) - Zigbee Document number 15-0014-05
- Zigbee Home Automation Public Application Profile (ZHA spec) - Zigbee Document number 05-3520-29
Note
- Zigbee documentation can be obtained from the Zigbee Alliance website
Gathering information from Zigbee documentation
Before we jump into the software, our first task is to determine which type of Zigbee device we wish to create. Then we must determine which clusters that device must support and which attributes those clusters must support.
1. Choosing a Zigbee device type
There are currently two specifications we can refer to for choosing a Zigbee device type, the Zigbee Lighting and Occupancy Device Specification (ZLO spec) and the Zigbee Home Automation Public Application Profile (ZHA spec).
The first thing I would recommend doing is going to the "Device Descriptions" sections of both documents, Section 5 in the ZLO spec and Section 5.7 in the ZHA spec. At first glance you will notice that there is some overlap between these two specifications, namely Device ID 0x0100 through 0x0107. If you are creating one of these Zigbee Lighting devices, refer to the information in the ZLO spec instead of the ZHA spec. The ZLO spec is the newest device specification document that has been released by the Zigbee Alliance, so the information in this document is newer and supersedes anything regarding these devices types in the ZHA spec. For all other non-overlapping device types between these two documents, the current instructions from the Zigbee Alliance are to follow the guidelines in each corresponding document for each device type.
So, let's say we want to create a Smart Plug as our Zigbee device. In Section 5.7 of the ZHA spec, we can see in Table 5.1 below that this device has Device ID 0x0051.
2. Determining which clusters our device support
In the same document that we chose our Zigbee device out of, we can navigate to the section that defines what clusters our device supports. In our case, we need go to Section 7.4.16 of the ZHA spec for Smart Plug. From here we can determine which clusters our device supports under the subsection 'Supported Clusters'.
Here are all the important points from the images below:
Mandatory and Optional Clusters
- All Zigbee devices must implement their mandatory clusters to pass Zigbee device certification. Zigbee device manufacturers may choose to implement certain optional clusters for their own application needs.
Note
- If you choose to implement an optional cluster, even though it is marked as optional, you must still pass certification for that cluster if you wish to include it with your device.
- Server Side and Client Side cluster implementation
- Implementing the Server vs. the Client side of a cluster is very important because it determines which attributes you support.
- Implementing the Server vs. the Client side of a cluster is very important because it determines which attributes you support.
- Common Clusters for all Zigbee devices
- All Zigbee devices support a common set of mandatory and optional clusters, listed below in table 7.1. We can see in section 7.4.16.1 below that it mentions "In addition to those specified in Table 7.1"
Now, using the information from the tables above, let's make new table that shows what clusters our Smart Plug device is going to support. I will only select the mandatory clusters for this example.
Cluster ID | Cluster Name | Client/Server Side |
---|---|---|
0x0000 | Basic | Server |
0x0003 | Identify | Client + Server |
0x0702 | Metering | Server |
0x0006 | On/Off | Server |
3. Determining which attributes our clusters support
Now that we have a list of clusters, we can refer to the Zigbee Cluster Library v7 Specification (ZCL spec). The ZCL spec will tell us which attributes our selected clusters support. Each section of the ZCL spec will give us a cluster and tell us which attributes the Server Side and Client Side implementations of that cluster support.
As of the ZCL v6 spec, it is mandatory for every cluster to support the ClusterRevision attribute.
Let's start with the Basic Cluster. From the table we made in the previous section, we must support the Basic Cluster Server. Table 3-7 in Section 3.2.2.2 of the ZCL spec tells us which attributes the Basic Cluster Server supports.
Next we can find the Identify Cluster, for which we must support both Client and Server. Table 3-28 in section 3.5.2.2 of the ZCL spec tells us which attributes the Identify Cluster Server supports. Section 3.5.2.1 of the ZCL spec tells us that the Identify Cluster Client does not support any cluster specific attributes.
Next is the Metering Cluster, for which we must support the Metering Cluster Server. The Metering Cluster Server has a very elaborate list of attributes. In fact, so elaborate that it is broken up into 9 different attribute sets, which you can see in section 10.4.2.2 of the ZCL spec. For the purpose of this exercise, I am only going to include snippits from the tables in this section that include mandatory attributes for the Metering Cluster Server.
Lastly is the On/Off Cluster, for which we must support the On/Off Cluster Server. Table 3-45 in section 3.8.2.2 of the ZCL spec tells us which attributes the On/Off Cluster Server supports.
Once again, I will create a new table using the information from the tables above that shows us which attributes our Smart Plug device is going to support. I will only select mandatory attributes for this example.
Attribute | Cluster | Data Type | Access | Default Value |
---|---|---|---|---|
ZCLVersion | Basic | uint8 | Read | 2 |
PowerSource | Basic | enum8 | Read | 0 |
ClusterRevision | Basic | uint16 | Read | 1 |
IdentifyTime | Identify | uint16 | Read/Write | 0 |
ClusterRevision | Identify | uint16 | Read | 1 |
CurrentSummationDelivered | Metering | uint48 | Read | 0 |
Status | Metering | map8 | Read | 0 |
UnitofMeasure | Metering | enum8 | Read | 0 |
SummationFormatting | Metering | map8 | Read | 0 |
MeteringDeviceType | Metering | map8 | Read | 0 |
ClusterRevision | Metering | uint16 | Read | 1 |
OnOff | On/Off | bool | Read/Reportable | 0 |
ClusterRevision | On/Off | uint16 | Read | 1 |
Reportable Attributes
- If any of the attributes you are supporting have an access type of Reportable, it is mandatory for your device to support ZCL reporting which means your device will need to include the compile flag BDB_REPORTING which enables ZCL report sending capabilities. We will revisit this again later on in the lab.
Modifying the GenericApp project
It's finally time to start modifying the software to suit our needs. The first thing you will want to do is make a copy of the GenericApp project so we can preserve the original project. You can choose to do this with either a Coordinator, Router, or End Device project. For my example I will use a Router. You can do this by simply making a copy of the the project folder at this path:
C:\ti\simplelink_cc13x2_26x2_sdk_<version>\examples\rtos\<LaunchPad>\zstack\zr_genericapp
1. Importing the project into Code Composer Studio
Now we are ready to import the project into Code Composer Studio (CCS). Open CCS and go to File > Import > C/C++ Project > CCS Project
and then browse for the zr_genericapp project path under Select search-directory
:
At this point it is a good idea to try building the project just to make sure all the file paths resolve correctly, this will verify that we have a good starting point for our incoming code modifications.
2. Add new compile flags to the project
All of the ZCL source code files are included in the GenericApp project by default, but the functionality contained in each file is compiled out via compile flags. Using the table below, determine which compile flags you must add to your project based on the clusters listed in SLA Table 1.
Cluster ID | Cluster Name | Compile Flags |
---|---|---|
0x0000 | ZCL_CLUSTER_ID_GEN_BASIC | ZCL_BASIC |
0x0001 | ZCL_CLUSTER_ID_GEN_POWER_CFG | N/A |
0x0002 | ZCL_CLUSTER_ID_GEN_DEVICE_TEMP_CONFIG | N/A |
0x0003 | ZCL_CLUSTER_ID_GEN_IDENTIFY | ZCL_IDENTIFY |
0x0004 | ZCL_CLUSTER_ID_GEN_GROUPS | ZCL_GROUPS |
0x0005 | ZCL_CLUSTER_ID_GEN_SCENES | ZCL_SCENES |
0x0006 | ZCL_CLUSTER_ID_GEN_ON_OFF | ZCL_ON_OFF |
0x0007 | ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG | N/A |
0x0008 | ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL | ZCL_LEVEL_CTRL |
0x0009 | ZCL_CLUSTER_ID_GEN_ALARMS | ZCL_ALARMS |
0x000A | ZCL_CLUSTER_ID_GEN_TIME | N/A |
0x000B | ZCL_CLUSTER_ID_GEN_LOCATION | ZCL_LOCATION |
0x000C | ZCL_CLUSTER_ID_GEN_ANALOG_INPUT_BASIC | N/A |
0x000D | ZCL_CLUSTER_ID_GEN_ANALOG_OUTPUT_BASIC | N/A |
0x000E | ZCL_CLUSTER_ID_GEN_ANALOG_VALUE_BASIC | N/A |
0x000F | ZCL_CLUSTER_ID_GEN_BINARY_INPUT_BASIC | N/A |
0x0010 | ZCL_CLUSTER_ID_GEN_BINARY_OUTPUT_BASIC | N/A |
0x0011 | ZCL_CLUSTER_ID_GEN_BINARY_VALUE_BASIC | N/A |
0x0012 | ZCL_CLUSTER_ID_GEN_MULTISTATE_INPUT_BASIC | N/A |
0x0013 | ZCL_CLUSTER_ID_GEN_MULTISTATE_OUTPUT_BASIC | N/A |
0x0014 | ZCL_CLUSTER_ID_GEN_MULTISTATE_VALUE_BASIC | N/A |
0x0015 | ZCL_CLUSTER_ID_GEN_COMMISSIONING | N/A |
0x0016 | ZCL_CLUSTER_ID_GEN_PARTITION | N/A |
0x0019 | ZCL_CLUSTER_ID_OTA | OTA_CLIENT_CC26XX |
0x001A | ZCL_CLUSTER_ID_GEN_POWER_PROFILE | N/A |
0x001B | ZCL_CLUSTER_ID_GEN_APPLIANCE_CONTROL | N/A |
0x0020 | ZCL_CLUSTER_ID_GEN_POLL_CONTROL | N/A |
0x0021 | ZCL_CLUSTER_ID_GREEN_POWER | N/A |
0x0022 | ZCL_CLUSTER_ID_MOBILE_DEVICE_CONFIGURATION | N/A |
0x0023 | ZCL_CLUSTER_ID_NEIGHBOR_CLEANING | N/A |
0x0024 | ZCL_CLUSTER_ID_NEAREST_GATEWAY | N/A |
0x0100 | ZCL_CLUSTER_ID_CLOSURES_SHADE_CONFIG | N/A |
0x0101 | ZCL_CLUSTER_ID_CLOSURES_DOOR_LOCK | ZCL_DOORLOCK |
0x0102 | ZCL_CLUSTER_ID_CLOSURES_WINDOW_COVERING | ZCL_WINDOWCOVERING |
0x0200 | ZCL_CLUSTER_ID_HVAC_PUMP_CONFIG_CONTROL | ZCL_HVAC_CLUSTER |
0x0201 | ZCL_CLUSTER_ID_HVAC_THERMOSTAT | ZCL_HVAC_CLUSTER |
0x0202 | ZCL_CLUSTER_ID_HVAC_FAN_CONTROL | ZCL_HVAC_CLUSTER |
0x0203 | ZCL_CLUSTER_ID_HVAC_DIHUMIDIFICATION_CONTROL | ZCL_HVAC_CLUSTER |
0x0204 | ZCL_CLUSTER_ID_HVAC_USER_INTERFACE_CONFIG | ZCL_HVAC_CLUSTER |
0x0300 | ZCL_CLUSTER_ID_LIGHTING_COLOR_CONTROL | ZCL_LIGHT_LINK_ENHANCE |
0x0301 | ZCL_CLUSTER_ID_LIGHTING_BALLAST_CONFIG | ZCL_LIGHT_LINK_ENHANCE |
0x0400 | ZCL_CLUSTER_ID_MS_ILLUMINANCE_MEASUREMENT | N/A |
0x0401 | ZCL_CLUSTER_ID_MS_ILLUMINANCE_LEVEL_SENSING_CONFIG | N/A |
0x0402 | ZCL_CLUSTER_ID_MS_TEMPERATURE_MEASUREMENT | N/A |
0x0403 | ZCL_CLUSTER_ID_MS_PRESSURE_MEASUREMENT | N/A |
0x0404 | ZCL_CLUSTER_ID_MS_FLOW_MEASUREMENT | N/A |
0x0405 | ZCL_CLUSTER_ID_MS_RELATIVE_HUMIDITY | N/A |
0x0406 | ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING | N/A |
0x0500 | ZCL_CLUSTER_ID_SS_IAS_ZONE | ZCL_ZONE |
0x0501 | ZCL_CLUSTER_ID_SS_IAS_ACE | ZCL_ACE |
0x0502 | ZCL_CLUSTER_ID_SS_IAS_WD | ZCL_WD |
0x0600 | ZCL_CLUSTER_ID_PI_GENERIC_TUNNEL | N/A |
0x0601 | ZCL_CLUSTER_ID_PI_BACNET_PROTOCOL_TUNNEL | N/A |
0x0602 | ZCL_CLUSTER_ID_PI_ANALOG_INPUT_BACNET_REG | N/A |
0x0603 | ZCL_CLUSTER_ID_PI_ANALOG_INPUT_BACNET_EXT | N/A |
0x0604 | ZCL_CLUSTER_ID_PI_ANALOG_OUTPUT_BACNET_REG | N/A |
0x0605 | ZCL_CLUSTER_ID_PI_ANALOG_OUTPUT_BACNET_EXT | N/A |
0x0606 | ZCL_CLUSTER_ID_PI_ANALOG_VALUE_BACNET_REG | N/A |
0x0607 | ZCL_CLUSTER_ID_PI_ANALOG_VALUE_BACNET_EXT | N/A |
0x0608 | ZCL_CLUSTER_ID_PI_BINARY_INPUT_BACNET_REG | N/A |
0x0609 | ZCL_CLUSTER_ID_PI_BINARY_INPUT_BACNET_EXT | N/A |
0x060A | ZCL_CLUSTER_ID_PI_BINARY_OUTPUT_BACNET_REG | N/A |
0x060B | ZCL_CLUSTER_ID_PI_BINARY_OUTPUT_BACNET_EXT | N/A |
0x060C | ZCL_CLUSTER_ID_PI_BINARY_VALUE_BACNET_REG | N/A |
0x060D | ZCL_CLUSTER_ID_PI_BINARY_VALUE_BACNET_EXT | N/A |
0x060E | ZCL_CLUSTER_ID_PI_MULTISTATE_INPUT_BACNET_REG | N/A |
0x060F | ZCL_CLUSTER_ID_PI_MULTISTATE_INPUT_BACNET_EXT | N/A |
0x0610 | ZCL_CLUSTER_ID_PI_MULTISTATE_OUTPUT_BACNET_REG | N/A |
0x0611 | ZCL_CLUSTER_ID_PI_MULTISTATE_OUTPUT_BACNET_EXT | N/A |
0x0612 | ZCL_CLUSTER_ID_PI_MULTISTATE_VALUE_BACNET_REG | N/A |
0x0613 | ZCL_CLUSTER_ID_PI_MULTISTATE_VALUE_BACNET_EXT | N/A |
0x0614 | ZCL_CLUSTER_ID_PI_11073_PROTOCOL_TUNNEL | N/A |
0x0615 | ZCL_CLUSTER_ID_PI_ISO7818_PROTOCOL_TUNNEL | N/A |
0x0617 | ZCL_CLUSTER_ID_PI_RETAIL_TUNNEL | N/A |
0x0700 | ZCL_CLUSTER_ID_SE_PRICE | ZCL_SE_PRICE_SERVER, ZCL_SE_PRICE_CLIENT |
0x0701 | ZCL_CLUSTER_ID_SE_DRLC | ZCL_SE_DRLC_SERVER, ZCL_SE_DRLC_CLIENT |
0x0702 | ZCL_CLUSTER_ID_SE_METERING | ZCL_SE_METERING_SERVER, ZCL_SE_METERING_CLIENT |
0x0703 | ZCL_CLUSTER_ID_SE_MESSAGING | ZCL_SE_MESSAGING_SERVER, ZCL_SE_MESSAGING_CLIENT |
0x0704 | ZCL_CLUSTER_ID_SE_TUNNELING | ZCL_SE_TUNNELING_SERVER, ZCL_SE_TUNNELING_CLIENT |
0x0705 | ZCL_CLUSTER_ID_SE_PREPAYMENT | ZCL_SE_PREPAYMENT_SERVER, ZCL_SE_PREPAYMENT_CLIENT |
0x0706 | ZCL_CLUSTER_ID_SE_ENERGY_MGMT | ZCL_SE_ENERGY_MGMT_SERVER, ZCL_SE_ENERGY_MGMT_CLIENT |
0x0707 | ZCL_CLUSTER_ID_SE_CALENDAR | ZCL_SE_CALENDAR_SERVER, ZCL_SE_CALENDAR_CLIENT |
0x0708 | ZCL_CLUSTER_ID_SE_DEVICE_MGMT | ZCL_SE_DEVICE_MGMT_SERVER, ZCL_SE_DEVICE_MGMT_CLIENT |
0x0709 | ZCL_CLUSTER_ID_SE_EVENTS | ZCL_SE_EVENTS_SERVER, ZCL_SE_EVENTS_CLIENT |
0x070A | ZCL_CLUSTER_ID_SE_MDU_PAIRING | ZCL_SE_MDU_PAIRING_SERVER, ZCL_SE_MDU_PAIRING_CLIENT |
0x0800 | ZCL_CLUSTER_ID_SE_KEY_ESTABLISHMENT | ZCL_KEY_ESTABLISH |
0x0900 | ZCL_CLUSTER_ID_TELECOMMUNICATIONS_INFORMATION | N/A |
0x0904 | ZCL_CLUSTER_ID_TELECOMMUNICATIONS_CHATTING | N/A |
0x0905 | ZCL_CLUSTER_ID_TELECOMMUNICATIONS_VOICE_OVER_ZIGBEE | N/A |
0x0B00 | ZCL_CLUSTER_ID_HA_APPLIANCE_IDENTIFICATION | ZCL_APPLIANCE_IDENTIFICATION |
0x0B01 | ZCL_CLUSTER_ID_HA_METER_IDENTIFICATION | ZCL_METER_IDENTIFICATION |
0x0B02 | ZCL_CLUSTER_ID_HA_APPLIANCE_EVENTS_ALERTS | ZCL_APPLIANCE_EVENTS_ALERTS |
0x0B03 | ZCL_CLUSTER_ID_HA_APPLIANCE_STATISTICS | ZCL_APPLIANCE_STATISTICS |
0x0B04 | ZCL_CLUSTER_ID_HA_ELECTRICAL_MEASUREMENT | ZCL_ELECTRICAL_MEASUREMENT |
0x0B05 | ZCL_CLUSTER_ID_HA_DIAGNOSTIC | ZCL_DIAGNOSTIC |
0x1000 | ZCL_CLUSTER_ID_TOUCHLINK | N/A |
In addition to the compile flags in the table above, we must also consider if any of our attributes have an access type of 'Reportable', which is information that we recorded in SLA Table 2. If we have an attribute that is 'Reportable', we must include the compile flag BDB_REPORTING in our project.
For our Smart Plug device, we will need to add the compile flags ZCL_ON_OFF, ZCL_SE, ZCL_SE_METERING_SERVER, and BDB_REPORTING. We can add them to the compilation by selecting our project in CCS and going to Project > Properties > Build > ARM Compiler > Predefined Symbols > Pre-define NAME
and adding the the flags, like in the image below:
ZCL_ON_OFF
ZCL_SE
ZCL_SE_METERING_SERVER
BDB_REPORTING
zrgenericapp[DEVICE]_LAUNCHXL_tirtos_ccs.projectspec
3. Making code changes to suit our selected Zigbee device
Here is a summarized list of code changes that we need to make to GenericApp to have it suit our needs:
Add ZCL header file(s) to
zcl_genericapp_data.c
andzcl_genericapp.c
Include ZCL Smart Energy files in the project
Add attributes to
zclGenericApp_Attrs
inzcl_genericapp_data.c
Add Server Side clusters to
zclGenericApp_InClusterList
inzcl_genericapp_data.c
Add Client Side clusters to
zclGenericApp_OutClusterList
inzcl_genericapp_data.c
Update the Simple Descriptor with the appropriate Zigbee device type in
zcl_genericapp_data.c
Update the attribute reset function
zclGenericApp_ResetAttributesToDefaultValues
to include the new attributes inzcl_genericapp_data.c
Register for, declare, and implement ZCL command callback functions in
zcl_genericapp.c
Add ZCL header files
Add the header files for the ZCL files that we added in the step 1 of this section to both zcl_genericapp_data.c
and zcl_genericapp.c
#include "zcl_se.h"
zcl_genericapp_data.c and zcl_genericapp.c
Include ZCL Smart Energy files
Inside the Common/zcl
folder, right-click on zcl_se.c/h
and uncheck the Exclude from build
option to add the Smart Energy Zigbee Cluster Library to your project.
Add attributes
By default, all of the mandatory attributes for the Basic Server and Identify Server are included, and a number of optional attributes for the Basic Server are included as well. In this section we will only need to address the attributes for the Metering Server and On/Off Server.
First we will want to make new global variables for each of our attributes. Using SLA Table 2, we can make a new global variable in zcl_genericapp_data.c
for each of these attributes. Use your best judgement for the data types of each variable. For instance, if an attribute is a ZCL uint48, obviously this is not a real data type on our embedded system so we will need to round up to a uint64_t
.
Note
- Since
ClusterRevision
is a global cluster attribute, we only need one instance of the global attribute variable that every cluster can share.
#ifdef ZCL_SE_METERING_SERVER
uint64_t zclGenericApp_CurrentSummationDelivered;
uint8_t zclGenericApp_Metering_Status;
uint8_t zclGenericApp_UnitofMeasure;
uint8_t zclGenericApp_SummationFormatting;
uint8_t zclGenericApp_MeteringDeviceType;
#endif // ZCL_SE_METERING_SERVER
#ifdef ZCL_ON_OFF
uint8_t zclGenericApp_OnOff;
#endif // ZCL_ON_OFF
The attribute table zclGenericApp_Attrs
is located in zcl_genericapp_data.c
. Each attribute entry in zclGenericApp_Attrs
is formatted as such:
{
ZCL_CLUSTER_ID_GEN_BASIC, // Cluster IDs - defined in the foundation (ie. zcl.h)
{ // Attribute record
ATTRID_BASIC_HW_VERSION, // Attribute ID - Found in Cluster Library header (ie. zcl_general.h)
ZCL_DATATYPE_UINT8, // Data Type - found in zcl.h
ACCESS_CONTROL_READ, // Variable access control - found in zcl.h
(void *)&zclGenericApp_HWRevision // Pointer to attribute variable
}
},
zclGenericApp_Attrs[]
Using the information in SLA Table 2, we can create our own attribute entries in the attribute table. For this step, you will need to look through all the corresponding ZCL header files to locate the correct Cluster ID, Attribute ID, Data Type, and Variable Access Control Type for each attribute. The ZCL header files should already have all of this information defined, you just need to find it and utilize it.
const uint16_t zclSampleLight_metering_clusterRevision = 0x0001;
const uint16_t zclSampleLight_onoff_clusterRevision = 0x0001;
CONST zclAttrRec_t zclGenericApp_Attrs[] =
{
...
// new clusters go after what's already on the list
#ifdef ZCL_SE_METERING_SERVER
// *** Smart Energy Metering Server Cluster Attributes ***
{
ZCL_CLUSTER_ID_SE_METERING,
{ // Attribute record
ATTRID_SE_METERING_CURR_SUMM_DLVD,
ZCL_DATATYPE_UINT48,
ACCESS_CONTROL_READ,
(void *)&zclGenericApp_CurrentSummationDelivered
}
},
{
ZCL_CLUSTER_ID_SE_METERING,
{ // Attribute record
ATTRID_SE_METERING_STATUS,
ZCL_DATATYPE_BITMAP8,
ACCESS_CONTROL_READ,
(void *)&zclGenericApp_Metering_Status
}
},
{
ZCL_CLUSTER_ID_SE_METERING,
{ // Attribute record
ATTRID_SE_METERING_UOM,
ZCL_DATATYPE_ENUM8,
ACCESS_CONTROL_READ,
(void *)&zclGenericApp_UnitofMeasure
}
},
{
ZCL_CLUSTER_ID_SE_METERING,
{ // Attribute record
ATTRID_SE_METERING_SUMM_FMTG,
ZCL_DATATYPE_BITMAP8,
ACCESS_CONTROL_READ,
(void *)&zclGenericApp_SummationFormatting
}
},
{
ZCL_CLUSTER_ID_SE_METERING,
{ // Attribute record
ATTRID_SE_METERING_DEVICE_TYPE,
ZCL_DATATYPE_BITMAP8,
ACCESS_CONTROL_READ,
(void *)&zclGenericApp_MeteringDeviceType
}
},
{
ZCL_CLUSTER_ID_SE_METERING,
{ // Attribute record
ATTRID_CLUSTER_REVISION,
ZCL_DATATYPE_UINT16,
ACCESS_CONTROL_READ,
(void *)&zclSampleLight_metering_clusterRevision
}
},
#endif // ZCL_SE_METERING_SERVER
#ifdef ZCL_ON_OFF
// *** On/Off Cluster Attributes ***
{
ZCL_CLUSTER_ID_GEN_ON_OFF,
{ // Attribute record
ATTRID_ON_OFF,
ZCL_DATATYPE_BOOLEAN,
ACCESS_CONTROL_READ | ACCESS_REPORTABLE,
(void *)&zclGenericApp_OnOff
}
},
{
ZCL_CLUSTER_ID_GEN_ON_OFF,
{ // Attribute record
ATTRID_CLUSTER_REVISION,
ZCL_DATATYPE_UINT16,
ACCESS_CONTROL_READ,
(void *)&zclSampleLight_onoff_clusterRevision
}
},
#endif // ZCL_ON_OFF
};
zcl_genericapp_data.c
Add Server Side clusters
Next we must add the Server Side clusters we are implementing to the list zclGenericApp_InClusterList
. For our Smart Plug example, we must add the Metering cluster and the On/Off cluster.
const cId_t zclGenericApp_InClusterList[] =
{
ZCL_CLUSTER_ID_GEN_BASIC,
ZCL_CLUSTER_ID_GEN_IDENTIFY,
ZCL_CLUSTER_ID_SE_METERING,
ZCL_CLUSTER_ID_GEN_ON_OFF
};
zclGenericApp_InClusterList[]
Add Client Side clusters
Next we must add the Client Side clusters we are implementing to the list zclGenericApp_OutClusterList
. For our Smart Plug example, we will not need to add anything to this list since Basic and Identify are populated by default.
const cId_t zclGenericApp_OutClusterList[] =
{
ZCL_CLUSTER_ID_GEN_BASIC,
ZCL_CLUSTER_ID_GEN_IDENTIFY,
};
zclGenericApp_OutClusterList[]
Update the Simple Descriptor
The Simple Descriptor is used during network device commissioning to let other devices know what type of Zigbee device you are. You will need to update the Simple Descriptor zclGenericApp_SimpleDesc
in zcl_genericapp_data.c
with the appropriate Zigbee Device type.
SimpleDescriptionFormat_t zclGenericApp_SimpleDesc =
{
GENERICAPP_ENDPOINT, // int Endpoint;
ZCL_HA_PROFILE_ID, // uint16_t AppProfId;
// Replaced ZCL_HA_DEVICEID_TEST_DEVICE with application specific device ID
ZCL_HA_DEVICEID_SMART_PLUG, // uint16_t AppDeviceId;
GENERICAPP_DEVICE_VERSION, // int AppDevVer:4;
GENERICAPP_FLAGS, // int AppFlags:4;
ZCLGENERICAPP_MAX_INCLUSTERS, // byte AppNumInClusters;
(cId_t *)zclGenericApp_InClusterList, // byte *pAppInClusterList;
ZCLGENERICAPP_MAX_OUTCLUSTERS, // byte AppNumInClusters;
(cId_t *)zclGenericApp_OutClusterList // byte *pAppInClusterList;
};
zclGenericApp_SimpleDesc
Update the attribute reset function to include the new attributes
zclGenericApp_ResetAttributesToDefaultValues
is responsible for initializing and resetting all the applications attributes to their default values. The default values can be obtained from SLA Table 2.
#define DEFAULT_ON_OFF_STATE 0x00
#define DEFAULT_CURR_SUMM_DLVD_VALUE 0
#define DEFAULT_METERING_STATUS 0
#define DEFAULT_UOM_VALUE 0
#define DEFAULT_SUMM_FMTG_VALUE 0
#define DEFAULT_METERING_DEVICE_TYPE 0
void zclGenericApp_ResetAttributesToDefaultValues(void)
{
...
#ifdef ZCL_SE_METERING_SERVER
zclGenericApp_CurrentSummationDelivered = DEFAULT_CURR_SUMM_DLVD_VALUE;
zclGenericApp_Metering_Status = DEFAULT_METERING_STATUS;
zclGenericApp_UnitofMeasure = DEFAULT_UOM_VALUE;
zclGenericApp_SummationFormatting = DEFAULT_SUMM_FMTG_VALUE;
zclGenericApp_MeteringDeviceType = DEFAULT_METERING_DEVICE_TYPE;
#endif
#ifdef ZCL_ON_OFF
zclGenericApp_OnOff = DEFAULT_ON_OFF_STATE;
#endif
}
zclGenericApp_ResetAttributesToDefaultValues()
Register for ZCL command callback functions
ZCL command callback functions are where your hardware-specific actions are performed in relation to the state of your cluster attributes. For instance, if you receive a Zigbee command from another device that changes the state of your On/Off Cluster "OnOff" attribute, you can receive a callback to your application saying that something happened, and at this point you would update your global attribute variable with the value passed into the callback function as a parameter and perform your hardware-specific action such as toggling a GPIO pin to reflect the new state of your attribute.
To receive these callback functions in your application, you must register a list of function pointers with each corresponding ZCL module. By default, we already do this with the ZCL General module in zclGenericApp_Init of zcl_genericapp.c
, as shown below:
static void zclGenericApp_Init( void )
{
...
// Register the ZCL General Cluster Library callback functions
zclGeneral_RegisterCmdCallbacks( GENERICAPP_ENDPOINT, &zclGenericApp_CmdCallbacks );
...
}
zclGenericApp_Init()
The second parameter we pass into this function is the list of function pointers that we wish to register with the ZCL general module. For every function present on this list we will receive a callback in our application when the corresponding action has been triggered, and we can set any callback we do not wish to receive to NULL
. By default, the Basic Cluster Reset callback function is registered with the ZCL general module with the function zclGenericApp_BasicResetCB
.
To know which callback functions are available, you must look into the corresponding zcl<module>_AppCallbacks_t
struct in the ZCL header files. Since the Basic, Identify, and On/Off clusters are all part of the ZCL General group (i.e. check Chapter 3 of the ZCL spec), all of their callback function descriptions are in the struct zclGeneral_AppCallbacks_t
in zcl_general.h
. For the Metering cluster, this is part of the ZCL Smart Energy group, so it is in the struct zclSE_AppCallbacks_t
in zcl_se.h
. Each function in these structs has a typedef
associated with it so you can know how to implement the function prototype and definition in your application. For instance, for the Basic Cluster Reset callback, it has the type zclGCB_BasicReset_t
in the struct zclGeneral_AppCallbacks_t
, and we can see the typedef
definition is as below:
typedef void (*zclGCB_BasicReset_t)( void );
And the in our application we can implement this function as such:
void zclGenericApp_BasicResetCB( void )
For the purpose of this lab, I am going to implement two callback functions in my application to show an example of how to do it. I will implement the On/Off cluster "OnOff" callback function and the Metering cluster "Get Profile" callback function.
As stated before, the On/Off cluster is part of the ZCL General group, so its callback is part of the zclGeneral_AppCallbacks_t zclGenericApp_CmdCallbacks
, which I mentioned before is already defined in our application for the Basic Cluster Reset callback.
To support the On/Off cluster "OnOff" callback:
#ifdef ZCL_ON_OFF
static void zclGenericApp_OnOffCB( uint8_t cmd );
#endif
static zclGeneral_AppCallbacks_t zclGenericApp_CmdCallbacks =
{
zclGenericApp_BasicResetCB, // Basic Cluster Reset command
NULL, // Identfiy cmd
NULL, // Identify Query command
NULL, // Identify Query Response command
NULL, // Identify Trigger Effect command
#ifdef ZCL_ON_OFF
zclGenericApp_OnOffCB, // On/Off cluster commands
NULL, // On/Off cluster enhanced command Off with Effect
NULL, // On/Off cluster enhanced command On with Recall Global Scene
NULL, // On/Off cluster enhanced command On with Timed Off
#endif
#ifdef ZCL_LEVEL_CTRL
NULL, // Level Control Move to Level command
NULL, // Level Control Move command
NULL, // Level Control Step command
NULL, // Level Control Stop command
#endif
#ifdef ZCL_GROUPS
NULL, // Group Response commands
#endif
#ifdef ZCL_SCENES
NULL, // Scene Store Request command
NULL, // Scene Recall Request command
NULL, // Scene Response command
#endif
#ifdef ZCL_ALARMS
NULL, // Alarm (Response) commands
#endif
#ifdef SE_UK_EXT
NULL, // Get Event Log command
NULL, // Publish Event Log command
#endif
NULL, // RSSI Location command
NULL // RSSI Location Response command
};
#ifdef ZCL_ON_OFF
static void zclGenericApp_OnOffCB( uint8_t cmd )
{
// do some stuff
}
#endif // ZCL_ON_OFF
The Metering cluster is part of the ZCL Smart Energy group, so we must define a new callback struct in our application with the type of zclSE_AppCallbacks_t
. The ZCL Smart Energy cluster group is extremely elaborate, and because of this the zclSE_AppCallbacks_t
is a bit different than the other "AppCallbacks" structs. Looking at its implementation, it is actually a struct of more structs, and each one of those other structs is full of callback functions for which we can register for. In addition to implementing these new structs, we must also register with the ZCL Smart Energy module in our initialization function (as we did with the ZCL General module). So, to do all of this, complete the following:
#ifdef ZCL_SE_METERING_SERVER
static void zclGenericApp_MeteringGetProfileCB( zclIncoming_t *pInMsg,
zclSE_MeteringGetProfile_t *pCmd );
#endif
static const zclSE_MeteringServerCBs_t zclGenericApp_MeteringServerCBs =
{
zclGenericApp_MeteringGetProfileCB,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
static zclSE_AppCallbacks_t zclGenericApp_SECmdCallbacks =
{
NULL, // zclSE_DRLC_ServerCBs_t
NULL, // zclSE_DRLC_ClientCBs_t
&zclGenericApp_MeteringServerCBs, // zclSE_MeteringServerCBs_t
NULL, // zclSE_MeteringClientCBs_t
NULL, // zclSE_PriceServerCBs_t
NULL, // zclSE_PriceClientCBs_t
NULL, // zclSE_MessagingServerCBs_t
NULL, // zclSE_MessagingClientCBs_t
NULL, // zclSE_TunnelingServerCBs_t
NULL, // zclSE_TunnelingClientCBs_t
NULL, // zclSE_PrepaymentServerCBs_t
NULL, // zclSE_PrepaymentClientCBs_t
NULL, // zclSE_EnergyMgmtServerCBs_t
NULL, // zclSE_EnergyMgmtClientCBs_t
NULL, // zclSE_CalendarServerCBs_t
NULL, // zclSE_CalendarClientCBs_t
NULL, // zclSE_DeviceMgmtServerCBs_t
NULL, // zclSE_DeviceMgmtClientCBs_t
NULL, // zclSE_EventsServerCBs_t
NULL, // zclSE_EventsClientCBs_t
NULL, // zclSE_MDUPairingServerCBs_t
NULL // zclSE_MDUPairingClientCBs_t
};
static void zclGenericApp_Init( void )
{
...
#ifdef ZCL_SE_METERING_SERVER
zclSE_RegisterCmdCallbacks( GENERICAPP_ENDPOINT, &zclGenericApp_SECmdCallbacks );
#endif
...
}
#ifdef ZCL_SE_METERING_SERVER
static void zclGenericApp_MeteringGetProfileCB( zclIncoming_t *pInMsg,
zclSE_MeteringGetProfile_t *pCmd )
{
// do some stuff
}
#endif // ZCL_SE_METERING_SERVER
Implement the ZCL command callback functions
As mentioned before, the ZCL command callback functions are where you, the developer, must implement your hardware-specific functionality in your application code based on the high-level intended action of each ZCL command. This is the part of the code where, aside from provided HAL drivers, there is no specific framework for how you should implement your actions since it is highly dependent on your custom hardware design. One thing you must do, however, is update the state of your global attribute(s) in these callback functions (if applicable).
As an example, in the On/Off cluster "OnOff" Callback, you would do at least 2 things:
- Update the state of your global "OnOff" attribute based on the function parameter of the callback function.
- Perform a hardware-specific action that actually changes the state of your hardware light, such as toggling a GPIO pin.
References
What's New in Zigbee 3.0: http://ti.com/lit/pdf/swra615
CC13X2/CC26X2 TRM: http://ti.com/lit/pdf/swcu185
Zigbee Alliance website: http://zigbee.org/
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.