This is a guide is for creating, building, and running a Dotdot OTA Server and Client using available sample applications. The OTA Server will be on a Host application on the Raspberry Pi that has an NCP connected. This KBA assumes that the user has reviewed UG116 and is familiar with running a Raspberry Pi Host with NCP. (Please note, it is not a requirement for the OTA Server to also be a Border Router).
This KBA was written with the following software tools:
With the following recommended hardware:
Create projects for the following sample applications found in Gecko SDK version 2.5 through the Application Wizard. These can be found through the File menu going to File -> New -> Project -> Silicon Labs AppBuilder Project.
The Light and OTA Server are pre-commissioned and will be in the same network at startup, unless the devices were already in a previous network. Resetting the network parameters will coax the devices to enter the networking state-machine. The reset command is as follows:
ota-server> network-management reset
OTA Server Configuration
The OTA Server (Host) Sample application has all the plugins required to be an OTA Server. Most notable are the following:
In OTA Server Sample app we recommend making the following changes for testing:
Generate, transfer to Raspberry Pi, Compile the OTA server application. Also, compile ip-driver-app found under v2.x/protocol/thread/ with the following command:
$> make -f app/ip-ncp/ip-driver-app.mak
Light OTA Client Configuration
The OTA Client is based on the Light sample app. The following plugins are used to enable the OTA functionality. Here we assume the device you are using has external EEPROM:
In the OTA Bootload Client options set the following to maximize the recurrence of OTA Client/Server communication:
In the OTA Bootload Client Policy options note the following and/or set them to something unique. These will be used for creating the OTA image:
In the OTA Bootload Storage EEPROM plugin make sure the value of EEPROM OTA Storage End Address is one less than the value found in SPI Flash Storage Bootloader Slot 0 size (under the Storage tab). If the size does not match the download will fail.
We also recommend enabling the following plugins for testing:
As well as the following for debug output under the Printing tab:
Generate, Build, and Flash the Light application and bootloader to the EFR32.
Building OTA Image
Recall the values in OTA Bootload Client Policy plugin for the Light. In order for the Light application to flash an image it must verify if the image has met the criteria of the OTA policy. The Manufacturer ID, Image Type ID need to be the same, and the Firmware Version must be a higher value. We will need to create a second image with a higher version number. For example, the first image will be version 0x00000001 and the new image will have version 0x00000002. Go back to AppBuilder and update the project to have a higher version number by changing the setting, generating, and compiling. (Note: this will overwrite your old image. If it needs to be available store it in a different place or rename the GBL file.)
The Image Builder tool will wrap the GBL file in an OTA file. The tool can be found under the SDK directory tree ../gecko_sdk_suite/v2.5/protocol/thread/tool/image-builder/. The following command can be called in a terminal:
$> ./image-builder-linux -c 1234-5678-00000002-light.ota -m 0x1234 -i 0x5678 -v 0x00000002 -s 4 -t 0 --security-credentials=3 -f "light292BRD4163A_02.gbl"
More information can be found in AN716: Instructions for Using Image-Builder
Kicking off OTA upgrade
Copy the OTA image to the same directory where the Server Host application is called. Start the ip-driver-app followed by the ota-server.
$> sudo ./ip-driver-app -u /dev/ACM1 -t tun0 -m 4901 & $> sudo ./ota-server -m 4901
In ota-server call the following command to see if it has found the OTA image.
ota-server> ota-bootload-storage info
The output should be similar to the lines below:
ota-server: 1234-5678-00000002-light.ota: m=0x4X t=0x4X v=0x8X ota-server.nvm: Load files found 1 files OTA Bootload Storage Info: maximumFileSize = 1000000, fileCount = 1 File 0: m=0x1234 t=0x5678 v=0x00000002 size = 311919
On the Light side open the console and check the network parameters using the info command and make sure it is on the same network as the OTA Server. The results should be similar to the lines below:
light_01> info network status: 0x03 eui64: >000B57FFFE25FAFC macExtendedId: >776A3C93A3776E86 network id: precommissioned node type: 0x02 extended pan id: >4F8EC75FB4E1EFC6 pan id: 0x1075 channel: 19 radio tx power: 3 dBm ula prefix: fd01::/64 mesh-local: fd01::1f0e:750c:b36:7ce5 link-local: fe80::756a:3c93:a377:6e86
To kickoff the search for an OTA Server use the following command:
light_01> ota-bootload-client update
The following lines should output to console:
Sent discovery command: .well-known/core?rt=urn:zcl:c.2000.s& get 0x00 Using upgrade server discovery Discovered server: fd01::63e6:a84:377c:3305->5683
If the server is discovered then the OTA transfer will start in ~1 minute (as we configured in OTA Bootload Client plugin).
The ISA3 Utilities are a set of software tools for the Silicon Labs Ember ISA3 Debug Adapter. With these tools you can manipulate adapter firmware as well as program chips in the EM35x and EM35xx family of parts. The latest downloads of these parts can be found here:
For assistance using these utilities, please refer to this user guide:
If you are having the issue mentioned in the title after an update to EmberZnet 220.127.116.11 please consider updating to the Gecko Bootloader. If it is not an option for you the following is an explanation on why this problem occurs followed by a proposed solution.
Although support of Ember Standalone Bootloader for EFR32 based products has ended in 2017. There could still be some devices in the field that were initially manufactured with the Ember Standalone Bootloader and are now unable to upgrade to the Gecko Bootloader.
Since the 2.5 GSDK release modules are regarded as first class citizens and while the Gecko bootloader has been upgraded to facilitate this update the Ember Bootloader was not because it is no longer being developed.
This update causes the AAT header in .ebl files built during the post-build process in Simplicity Studio to be interpreted as corrupt by the Ember Standalone Bootloader.
The problem is that the value of byte 0x78 used to be 0x10 for EFR32MG1P or 0x11 EFR32MG1B and after the update it is set to 0x01 for MGM111.
If this is the issue the advised solution is to convert the created .s37 file to .ebl using the 'em3xx_convert' tool.
This should be located at "(StudioFolder)\developer\adapter_packs\em3xx\utils"
Open any command line tool at this location and you can convert a .s37 to .ebl with the correct AAT header using the following command.
"em3xx_convert.exe --chip EFR32 (path)/(filename).s37 (path)/(filename).ebl"
If your existing Host-NCP solution is lacking Green Power proxy support and you want to add it to the design these steps will walk you through the process of adding it.
First, go to the ZCL Clusters tab and find the Multiple endpoint configuration section at the top. Click the New button to add an endpoint to your device, this will create a second endpoint on the device with identifier 2. Now we need to convert this to a green power endpoint.
In the Configuration column, click the Primary name, which will bring up a button with 3 dots
Click the box to bring up the Endpoint type window
Select the option Create new endpoint type and name your new endpoint Green Power. Click OK.
Keeping the endpoint selected, go to the pull-down menu for ZCL Device Type and change the device to GP Proxy Basic (under GP devices). When this is selected, this will make the endpoint change to number 242. This should also enable Cluster 0x0021, the Green Power cluster, on the device.
Now go to the Plugins tab and on the search bar, enter Green – this will find 4 plugins in the Green Power section:
Check the box next to the Green Power Client and Green Power Common plugins, this will enable them. There isn't any need to change any of the default settings within the plugins, so you can leave those alone.
At this point you should be able to generate and build your host application with no issue and Green Power will be supported. However support for green power needs to be enabled within the NCP.
As you are configuring your NCP application, go to the plugins tab and enable the Green Power Stack Library plugin. Make sure the Green Power Stack Stub Library is disabled:
Since this is just a proxy, you can leave the settings as defaulted, proxy table size of 5 and sink table size of 0. However you will need to consider your potential network when configuring your table sizes. A larger proxy table might be needed for supporting all devices on the network.
This is all you need for NCP support. You can generate and build your NCP and combined with your new host code, green power proxy support should be ready to go.
What is the maximum allowed power for ZigBee applications under ETSI EN 300 328?
Although ETSI EN 300 328 allows 20dBm RF output power, there is a limitation for PSD (Power Spectral Density) for wide band modulations other than FHSS. This PSD limit restricts the maximum allowed power for ZigBee applications.
Section 18.104.22.168 in EN 300 328 V2.1.1 details the test method for PSD. The ZigBee modulated signal measured according to Option 2, Step 3 (with sweep time set to 10s):
CW signal with the same power level and spectrum analyzer settings:
The peak power difference between the ZigBee and CW signal measured with RMS detector is ~2.3dB. In order to meet the PSD limit of 10dBm per MHz, the maximum allowed power is 12.3dBm (EIRP).
Note: The 12.3dBm power limit is radiated (EIRP), which means that in case of an antenna with 0dBi, the maximum conducted power allowed is 12.3dBm. For antennas with lower gain than 0dBi, conducted power can be increased, while for antennas with higher gain than 0dBi, conducted power should be reduced.
Measurements were performed on an EFR32MG13 based reference board (BRD4158A Rev A01).
So now it’s time to make the physical interface for our light and switch. While being able to send and see messages go across the network is fun, since we are making a light and a switch, we should expect them to act like a light, turning a light source on and off and a switch, taking an input from a button press or similar signal.
First let’s turn our attention to the light, since a light turning on and off is an easier goal to observe. As we have noticed on our last step, we have been toggling one of our cluster attributes, specifically an on-off attribute. What we want is to have our device change based on this attribute’s value. One mechanism that we can use to handle this is through a callback.
A callback is a way that EmberZNet handles events throughout the stack code and application layer as well. If you click on the callbacks tab, you will see a number of calls that can be used throughout your code. These are generally broken down into application callbacks, plugin callbacks, stack callbacks, API callback and cluster callbacks. An in depth look at the callbacks is beyond the scope of this lesson.
For our light code, we are going to need to handle two events. First, at startup we need to enable our lights and configure them. Once that’s done we need to handle the toggling of the light based on the On/Off attribute.
To setup our light, we need to look at the section called “Non-cluster related.” All of these callbacks are in order. As you look down the list you will come across two callbacks Main Init and Main Start, both of these callbacks run at the start of our main() function. Main Init runs right after main executes, but before the network is setup, while Main Start runs right before our main loop executes, after the HAL is done configuring the hardware. In most cases it won’t matter where we set up our LED interface, so for this we will use Main Start, check the box for it.
Next, we need to figure out how to toggle our light on and off. Because this is tied to the on/off cluster, you want to go to the On/Off cluster under the General. Looking through the list, we can find the Server Attribute Changed callback. Since we are operating on the attribute value setting, this is perfect for our needs, check its box as well.
Before we leave this page, make sure at the bottom of your callbacks page you have the “Generate project-specific callbacks file” box checked. This will ensure your new callbacks file is generated for your application.
Before we click generate, we also need to configure all of our GPIOs to work as LEDs. The Thunderboard has 1 green LED, 1 red LED and 4 full color LEDs, for this project we are going to use the full color LEDs. If you read UG309 section 3.4.8, you will see all of the pin definitions, as well as an explanation of how the LEDs are setup.
Next, we need to launch Hardware Configurator (HWConf for short). If you look in your light project, you should see a file named brd4166a_efr32mg12p332f1024gl125.hwconf, this is your header file for your project. Double click on it and it should open in HWConf in the DefaultMode Port I/O. If it isn’t in that format, click that tab at the bottom of the DefaultMode Peripherals window. This view brings us to a graphical representation of the pins, it is where we will set our GPIOs to be available to us within our software.
Setting the GPIOs from within HWConf is fairly straight forward. First you need to go to the Outline view on the top right corner of Studio and open the Port I/O tree. Then click on the Port (A-K) you want to set, this will make those GPIOs become active on the graphical pin grid. Locate the pin you want to setup on the grid (be careful, the pin letters are easy to get confused with their grid identifiers) and click on them. You will then see the Properties of this pin on the middle right of Studio. If you click on the value area within Custom pin name, you will be able to give the pins a name, this will be how you identify the pin in your program.
For example: PJ14 is RGB_LED_ENABLE (note this is configured by default)
Do the same for the following pins – locations provided for easy locating:
Remember all of the names you give these pins.
Once this is done, save your HWConf file, this might take some time as it will generate some new hardware configurations. Then return to Simplicity IDE view and click on your MyLight.isc file. Click the Generate button. After some time you will get a validation window to appear. Make sure that all files on this window are checked, Studio will many times leave the *_callbacks.c file unchecked to not overwrite this. Once all the boxes are checked, click OK to continue.
Finally, it is time to put in some code to make our lights turn on and off. First, locate the callbacks file in your project list. It will be called PROJECTNAME_callbacks.c, in our case, this will be MyLight_callbacks.c, double click on it and the code in this file will appear. You will note that you should have 2 functions in the project: emberAfMainStartCallback and emberAfOnOffClusterServerAttributeChangedCallback.
First let's enable our lights. Since we need to enable our LEDs as our code starts, we will focus our attention to the emberAfMainStartCallback() function. As noted, this function is called right before our program initiates it's loop while it is running.
Now we will need a fuction to initialize our GPIOs, configuring them as output pins. You can look through docs.silabs.com and find the EMLIB section to discover a function to run this setup. To save you some time, the function you are looking for is:
void GPIO_PinModeSet (GPIO_Port_TypeDef port, unsigned int pin, GPIO_Mode_TypeDef mode, unsigned int out)
The function call for this is defined fully here. But we will step you through the function calls for easy reference.
The first two arguments are the port and pin that we want to configure. Because we have defined our pins in HWConf, you can use those names you assigned these pins in the GUI. They will be something like PIN_NAME_PORT and PIN_NAME_PIN, which correspond do the port letter and port number respectively. If you look in your project tree there is a folder called hal-config and in that folder you will find a file hal-config.h, this file contains the names of the ports and pins for easy reference, locate the section
// $[Custom pin names]
The next argument is how the pin is configured. Fortunately all of our GPIOs are set as outputs and our GPIO mode is going to be gpioModePushPull. A full list of pin options can be found here.
Finally because we are using these as output pins, the last argument is the desired initial state of the pin, either 0: off or 1: on.
So to set up RGB_LED_ENABLE, use the following:
GPIO_PinModeSet(RGB_LED_ENABLE_PORT, RGB_LED_ENABLE_PIN, gpioModePushPull, 1);
Do the same for all of your ports and pins. You should certainly make sure that RGB_LED_ENABLE is on at startup. If you want a particular color, the colored LEDs should be enabled as you want for the color you want to output, red, green or blue, or some combination of them all. LEDs 0-3 can be disabled or enabled, they are what we are going to use to turn on the LEDs.
One final warning the LEDs are quite bright, especially if you turn on all three for white, so be careful when looking at them. After doing so, you might decide you want to start with them off.
At this point you can re-compile your code and test to see if your lights come on. Once you have verified that your LEDs are working, you can then implement the on/off feature.
So now we will move down to the function emberAfOnOffClusterServerAttributeChangedCallback. This function is called anytime a server sided attribute on the light changes. It has two variables in its definition which are the endpoint that is being operated on by the change and the attribute itself which is being changed. In our case we want to change anytime we are seeing a call on our defined endpoint on the on/off attribute and change to the state which is set in that attribute. Despite only having a single endpoint in our project, you should wrap this call to make sure you are calling it on a particular endpoint, because you can then expand it to work for additional endpoint. Since our light functionality is our primary endpoint, you should compare the endpoint passed to this function against this. EmberZnet provides a means of getting this with the function emberAfPrimaryEndpoint(), this means we know that we are getting this called against the main light endpoint.
if (endpoint == emberAfPrimaryEndpoint())
Once we have done that, we need to make sure we are operating on the on/off attribute. First you should look at the file attribute-id.h. This file lists all of the attributes in your project and sets a number of defines for these attributes, using these your code will be much more readable. If you go to the section with the On/off cluster, you will see the ZCL_ON_OFF_ATTRIBUTE_ID, which matches that attribute ID. You can then do some comparison or switch statement and use that as the entry into your function.
if(attributeId == ZCL_ON_OFF_ATTRIBUTE_ID)
Now, we need a means of reading our attribute and turning the light on or off based on this attribute. Because this callback is made after the attribute has changed, we can read this attribute to get its state. To do this we will need to look through the documentation of EmberZNet. If you go to Docs.silabs.com, you can find the Ember Application framework API
On the left, if you click Ember Application Framework API Reference it will shows you the sections of the top-level API. At this point we just want the General Application Framework Interface. When you get this page, you will find the Attribute storage section listed at the very top. We are looking for some function to read an attribute, a little searching will find the function emberAfReadServerAttribute, a function that is doing exactly what we want, reading a server attribute, so copy that whole definition and paste it into your code.
EmberAfStatus emberAfReadServerAttribute (uint8_t endpoint, EmberAfClusterId cluster, EmberAfAttributeId attributeID, uint8_t *dataPtr, uint8_t readLength)
Because the function returns an EmberAfStatus, create one to catch the return value. We should only change our LED if the read was successful. You can return to the API guide and look up EmberAfStatus, you will find this is EMBER_ZCL_STATUS_SUCCESS.
if(readStatus == EMBER_ZCL_STATUS_SUCCESS)
Finally we need to make sure we properly read our attribute and change our LED, so go back to emberAfReadServerAttribute function call and let’s set it up properly. We know that our endpoint is the endpoint passed to our callback, so make sure this matches. The cluster ID is that for the on/off cluster. If you look at cluster-id.h, you will see that it is enumerated as ZCL_ON_OFF_CLUSTER_ID, so put that as the 2nd variable in the call. The attribute ID is again ZCL_ON_OFF_ATTRIBUTE_ID, so use that. Finally, we need something to catch the value of the data. I created a boolean type named onOff to catch this, the last two variables of the signature are a pointer to this variable and the size of that variable. In the end our full call looks something like this:
boolean onOff; EmberAfStatus readStatus; readStatus = emberAfReadServerAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, &onOff, sizeof(onOff));
Now we can just use an if-else called against onOff to turn on or turn off our LED. The checking EMLIB again, we see that the function calls for this are:
Make the appropriate calls to the LEDs you want on and off within your code block, save and recompile your code. You can then reflash it to your “light” Thunderboard and use the switch to toggle it from the CLI. If you are able to turn the lights on and off, it’s time to move on to our switch to get it to toggle from the buttons.
In this tutorial we are going to be demonstrating how to build two simple Zigbee 3.0 applications for a light and a switch using Simplicity Studio on the Thunderboard Sense 2.
This project will start by building up a basic set of applications using AppBuilder which will start out as the framework for our light and switch. Then we will add some custom hardware definitions as well as some custom code and expand the base projects provided to tie the software in with the hardware.
When you are finished you will have a light application that controls the states of LEDs based on software attributes and you will have a switch which reacts to a button to send ZCL messages over the air to update the attributes of the light. Below you will find some basic instructions which walk you along the next few lessons as well as code examples and hardware configuration files to get you started.
Some prerequisites you will need for this tutorial:
Simplicity Studio 4 with the latest updates
EmberZNet 6.4.0 or later
Two Thunderboard Sense 2 boards (BRD4166A)
Two WSTKs (BRD4001A)
Two Mini-Simplicity connectors (BRD8010A) and the included 10 pin ribbon cable
You will also need easy access to Simplicity Commander's CLI interface. While it isn't the only way to program your chips, there are a few steps, like token programming, that require Commander.
The videos that went with the older lesson can be found here:
ZigBee Home Automation Developer Tutorial: Light and Switch Overview
ZigBee Home Automation - Light and Switch - part 1
ZigBee Home Automation - Light and Switch - part 2
ZigBee Home Automation - Light and Switch - part 3
ZigBee Home Automation - Light and Switch - part 4
ZigBee Home Automation - Light and Switch - part 5
ZigBee Home Automation - Light and Switch - part 6
ZigBee Home Automation - Light and Switch - part 7
ZigBee Home Automation - Light and Switch - part 8
ZigBee Home Automation - Light and Switch - part 9
ZigBee Home Automation - Light and Switch - part 10
ZigBee Home Automation - Light and Switch - part 11
ZigBee Home Automation - Light and Switch - part 12
We are now in the home stretch and ready to build our switch to work interfacing a button. The first thing we need to do is put some hooks into our project to give us a place to put our code.
If you remember back when you made the switch project, we enabled the Button Interface plugin, as the name suggests, this gives you an interface for buttons within our callbacks section. If you look at the callbacks tab, you will see these callbacks under the Plugin-specific callbacks. There are callbacks for different button states as well as long and short presses. For this example, let’s pick button 0 as it is generally clear of the ribbon cable on the Thunderboard. We also don’t need much more than a single button press, so let’s make use of the Button0 Pressed Short callback, check the box next to that identifier. *
* Quick note – a short press is by default defined as less than 250 mSec, you can look through the plugin code and the button timeout definition within the plugin to see how this is defined.
Like the light, make sure at the bottom of your callbacks page you have the “Generate project-specific callbacks file” box checked. This will ensure your new callbacks file is generated for your application. You can then save your .ISC file and hit generate to add this new callback. You will get a box that talks about overwriting files. Make sure your Project_callsbacks.c file is checked, you should also create backups, just in case. Then hit OK.
When you see the Generation Successful window, you’re ready to move to the next step.
Locate your project callbacks file on the left view titled Project Explorer and open the file in your editor. You will see that your callbacks file now includes a new, blank function named emberAfPluginButtonInterfaceButton0PressedShortCallback(). This function is called anytime the button is pressed for a short time.
Now we need to take our CLI calls and use this function to make these calls happen within this function. Looking back at our configuration, we send two commands via the CLI to send a message:
zcl on-off toggle
and then either
send 0 1 1
which sent our message outright to a node and endpoint we specified, or
which used a binding table entry to remove our need to know the exact device we were sending a message to.
First let’s look at zcl on-off toggle. To do this, we need something that will construct our message for us. We can look in the Application Framework reference guide and then refer to the Ember Application Framework API Reference and then the Application Framework Command Buffer Loading Interface. In here you will find a number of commands that will help load command or response buffers which can be sent out with some form of a send command.
If you search for the word ‘toggle’ through this document, it should find the function
which is a basic command which constructs our command buffer for us automatically. The benefit of this command is that it handled much of the overhead by utilizing the command buffer, making sending the message much easier later. So, add a call to this function to your button callback function.
Now we need to find a command to send our buffer over the air for us. So let’s return to the Application Framework and click over to the General Application Framework Interface and search for functions that deal with sending messages. On this page, you can search for the word messaging, which locates the function that deal with sending messages within the application framework. If you look through this section, there are a lot of functions which you can call.
You could use a function like emberAfSendUnicast. But this has a lot of parameters. And as we noted, the benefit we had about using the a FillCommand function is that the command buffer overhead has been handled for us. If you look down the list, you will find a number of functions that start out emberAfSendCommand… these are the functions we want to look at. Since this is a specific command send directly to a single node, you can use emberAfSendCommandUnicast(). This takes two options, the first is a messages type and the second is some sort of integer argument that differs based on what your message type is.
emberAfSendCommandUnicast (EmberOutgoingMessageType type, uint16_t indexOrDestination)
If you follow the link for the EmberOutgoingMessageType, you will see a number of different message types. EMBER_OUTGOING_DIRECT would allow you to send a message directly to a node id. Alternatively, a few lines down, you will find EMBER_OUTGOING_VIA_BINDING, which would allow you to leverage a previously made binding to send your message. Let’s take a look at each of these and you can pick the way you want to send your toggle.
If you choose to send a direct unicast to your node ID, this is the same operation as using the command send 0 1 1 to send your message. If you take a look at the send command’s help you will see the pieces you are providing with the numbers:
send (args) short id of the device to send the message to The endpoint to send the message from The endpoint to send the message to
Looking at the emberAfSendCommandUnicast function, your two options are a type and destination. In the case of using type EMBER_OUTGOING_DIRECT, the destination is our short ID of our target. As we noted before, you can use 0x0000 since this is a short id.
emberAfSendCommandUnicast (EMBER_OUTGOING_DIRECT, 0x0000);
This takes care of your node address. But we set our endpoints in the send command, and these are not yet handled. We need some function to configure the endpoints. And we need to configure this before we send our message otherwise it will be too late. Let’s return to the top level of the application framework and look around at operations that manipulate endpoints. If you search for the word endpoints, you will eventually come across the command emberAfSetCommandEndpoints(), which requires a source and destination endpoint. In our case we know both end points are 1, so you can hard code that if you would like, If you want, you can use emberAfPrimaryEndpoint() as the first argument because it’s the only endpoint of our device. However, in the case of the destination endpoint, you can only specify 1 as you need to hard code this.
Now you can test this out. Save your callbacks file and compile your code. Put this new application on your “switch” Thunderboard and press button 0, you should see your light turn on and off.
Alternative to sending a direct message to your node, you can leverage the fact that you have already created a binding and send directly to that. The advantage of this is that it already takes care of all of your settings like node address and endpoints. However, now you must know your binding entry to where you can send this to the on/off cluster.
So go to your console and enter the command ‘option binding-table print’ to display your binding table.
# type nwk loc rem clus node eui 0: UNICA 0 0x01 0x01 0x0003 0x0000 (>)000B57FFFEDEA263 1: UNICA 0 0x01 0x01 0x0006 0x0000 (>)000B57FFFEDEA263
The on-off cluster is 0x0006, so we want to send this to binding table entry 1. You can place this in as the second argument to your function:
emberAfSendCommandUnicast (EMBER_OUTGOING_VIA_BINDING, 1);
Like in _DIRECT format, now test this out. Save your callbacks file and compile your code. Put this new application on your “switch” Thunderboard and press button 0, you should see your light turn on and off.
In the end, this isn’t the cleanest code possible. Because this function is hard coded on the button, we are always sending our message. In the included MySwitch_callbacks.c file we have included some code that you can use to check that your network is currently up. We also have print statements to display sensible messages when your light or switch are operating.
Now that we have formed a network, it’s time to verify that we can successfully send messages between our light and switch. To do this will be using simple ZCL commands to start and
First click on the serial console of the switch. At the prompt enter:
zcl on-off toggle
This builds a Zigbee Clusters Library command frame. You can tell it was constructed correctly because of the output:
Msg: clus 0x0006, cmd 0x02, len 3 buffer: 01 00 02
Cluster 0x0006 corresponds to the on-off cluster.
Command 0x02 is the toggle command and length 3 corresponds to the length of the command buffer.
The buffer is 01 for the frame control, 00 for the sequence number and 02 for the command id for toggle.
Now we want to send this command to our light. Because our light is the coordinator, we know it’s Zigbee node ID is 0x0000. Because we want to send these commands from end point 1 on the switch and to endpoint 1 on the light our command to send this would be:
send 0 1 1
Hitting enter you will see on the light that it received a message for it’s 0x0006 cluster with command 02.
T00000000:RX len 3, ep 01, clus 0x0006 (On/off) FC 01 seq 00 cmd 02 payload
On the switch, you will see it received a message as well.
T00000000:RX len 5, ep 01, clus 0x0006 (On/off) FC 08 seq 05 cmd 0B payload[02 00 ]
This is a default response as it is sent back from the light to the switch.
If we look at App Builder, you can see these messages sent in the transaction view, a ZCL: Toggle followed by a ZCL: DefaultResponse. You can look at details of each message as they are sent on the side panel. For example, if you look within the event details of the the DefaultResponse, you can see that the response is Status: SUCCESS, meaning that the message was sent and properly handled.
Because our light is our network coordinator, we know that it’s address is 0x0000. However, most of the time we can’t trust that this will be the case. We need some sort of functionality to get our switch to look for and find our light and then join with the light so that it can easily send messages to the light instead of needing to know the address of the light. Additionally, the switch and light need some means of discovering if they share common clusters, like the off-off cluster. To assist with this, Zigbee provides finding & binding as a standard means of performing just this action.
If you remember back when we created our light and switch, we added a Find and Bind plugin to each device. The Light utilized the Find and Bind Target plugin while the Switch utilized the Find and Bind Initiator plugin. With these plugins, it makes finding and binding easy. Instead of having our light looking for ZCL messages doing endpoint discovery and having to manually construct a full set of ZCL messages to be sent across our network, asking for nodes to identify themselves as well as their endpoints and clusters and then binding like clusters, the plugins will take care of it all for us.
What we will first do is make the light a find and bind target for its endpoints. This will make it monitor network traffic and look for devices which are doing Find and Bind Initiation. To do this we will enter the following command:
plugin find-and-bind target 1
This tells endpoint 1 to act as an endpoint target. You will know the command works because you will see:
Find and Bind Target: Start target: 0x00
Now on the switch we must start it as an initiator. This is a similar function, but instead of target, we use:
plugin find-and-bind initiator 1
When you enter on this, you will see traffic in both the serial window as well as the network capture window as well. The process will be complete when you see
Find and Bind Initiator: Complete: 0x00
In the serial console.
Looking at the network traffic, you can see:
1. Our switch sends out a broadcast ZCL: IdentifyQuery. This is responded to by our light.
2. Then the switch asks the light for its full EUI64 address, the light responds to this with its EUI64.
3. Then finally the switch sends a simple descriptor request to get cluster information from the light. The light responds with 5 clusters which it is server: 0, 3, 4, 5 and 6 and no clusters which it is a client.
From this information our switch with then creates binding with clusters that it matches. For the 5 server clusters on the light, it looks to see if it is a client cluster on any of those, for the switch those are clusters 3 (identify) and 6 (on-off). So if you go to the CLI of the switch and print the binding table, you will see these new bindings, which are bound to the EUI64, not just the short ID. To print the binding table, use the following command:
option binding-table print
The output will look like this:
# type nwk loc rem clus node eui 0: UNICA 0 0x01 0x01 0x0003 0x0000 (>)000B57FFFEDEA263 1: UNICA 0 0x01 0x01 0x0006 0x0000 (>)000B57FFFEDEA263
With these bindings we can then use a bsend command to tell our device to send commands to a binding for a particular endpoint.
zcl on-off toggle bsend 1
This sends a command to our bindings on endpoint 1.
With this information, we can get to the step of making our light and switch act like a light and switch. The light will turn on or off LEDs based on attribute state, while our switch will respond to a button press instead of sending commands due to CLI entries.