HA on/off switch and light
Use case: customization of an HA on/off switch to communicate with an HA light
Chip: EM260
Network: EmberZNet 3.3.1
Tools: ISD 2.0.6, IAR 4.30c
Step 1: Basic ZigBee Introduction
Download the Application Developer’s Guide
Skip to Chapter 4, “Home Automation Application”, which we’ll follow from here. You can refer back to the other pieces of the Application Developer’s Guide if there are concepts that you want to pursue in more depth. You can also refer to the other parts of the gentle guide.
Step 2: Hardware Configuration
There are two settings we will use in this walkthrough — the “bootload” setting, used for programming software (either the EM260 or the Atmel host processor) and the “CLI” setting, used for interacting with the serial port on the host processor.
The bootload setting
- Make sure the DEI cable is connected to the InSight Adapter and the Breakout Board
- Make sure the SIF cable is connected to the InSight Adapter and the Breakout Board
- Make sure SW2 is to the right (DEI)
The CLI setting
- Make sure the USB cable is connected (plug the other end into your PC)
- Make sure SW2 is to the left (USB)
Step 3: Create device applications and load onto the devices
Configure InSight Desktop to use the correct stack version — this only needs to be done once for each stack version, but if you change to other versions, or change from the EM260 to the EM250, you will need to go through this process again.
In File -> Preferences…

Create a New ZigBee Application

Activate Dynamic Help [optional]

This will provide context-sensitive help as you click on the values you can edit. This is useful when you start configuring your own devices or making more complex changes to the behavior of the standard devices.
Configure the Device Type and Device Name

Configure the Stack Options

- Turn on HA security
Configure the HAL Options

- Be sure to select “Full CLI” in the HAL Configuration tab. This is useful for demos or evaluation, but consumes flash, so once you have a basic understanding
of what the commands are doing, you will want to change this back to “Minimal CLI” to save flash space.
Click “Generate”

Step 4: Compile & load onto the Atmega32
Compile
[We do not cover this in detail at this point — please attach comments if you are having trouble with this step. The project file to open in IAR is shown in the generation complete window above.]
Load the EM260 Firmware
This should only need to be done once per EM260 board, or if you need to update the version.
- Verify that your hardware is connected in the “bootload” configuration
- Upload the EM260 firmware

The firmware files can be found in the

Load the Host Processor Application
- Verify that your hardware is connected in the “bootload” configuration
- Upload the host application

The firmware files can be found in the

Step 5: Repeat using the HA on/off output device type
If you are following along, we named in MyOnOffOutput.
Step 6: Connect with InSight Desktop for network analysis
Note: while this step is optional, if anything goes wrong the first place you should look is at InSight Desktop, to see what happened. It is also our primary way of working with you to debug an application (using the ISD capture files), so we recommend that everyone get this working properly.
- Configure the ZCL decoding options in ISD — otherwise, ISD will not understand the application-level data and may show unexpected / incorrect interpretations of the packets.

- Right-click “Connect” to all devices of interest – MyOnOff and MyOnOffOutput, in this walkthrough.
- Refer back to ISD as packets are sent to see updated information
Step 7: Testing devices with the CLI (command line interface)
- Make sure your boards are in the “CLI” hardware configuration*
- We are using HyperTerminal to connect over USB to the boards. You should configure HyperTerminal to use 19200 baud, N81 with NONE for Flow Control.
Forming Network and Joining Devices to the network
- Make sure both devices have left the previous network
==
== MyOnOff>network leave leave 0×00 MyOnOff>EMBER_NETWORK_DOWN (2041 ms)
- MyOnOffOutput becomes the coordinator — form the network
Form network by pressing button 0 on breakout board
You should see this on HyperTerminal screen:
==
== MyOnOffOutput>security init TC: 0×00 set state to: 0×0306 BUTTON0: nwk down: attempt form EMBER_NETWORK_UP (10351 ms)
- Read the network channel and PAN ID from MyOnOffOutput using the CLI
==
== MyOnOffOutput>info node [000D6F000019AD3B] chan [0×18] pwr [0×03] panID [0×5177] nodeID [0×0000] xpan [0xDE00397103207B37] security level [05] NCP version [3325] nodeType [01] security type [HA] device [enabled] network state [02] default endpoints: src [0×0A] dst [0×0A] profile [0×0104] deviceId [0×0002] deviceVersion [0×00] in(server) clusters [0×07] (Basic) (Power Configuration) (Device Temperature Configuration) (Identify) (Groups) (Scenes) (On/off) out(client) clusters [0×00]
From this we can see that the channel is 0×18 and the pan id is 0×5177. Write these down, we’ll use them in the next step.
- MyOnOffOutput allows other devices to join the network
==
== MyOnOffOutput>BUTTON1: nwk up: permit join for 60 sec: 0×00
- MyOnOff joins the network
Enter the following command through the CLI:
Command Syntax: network join [channel in decimal] [power in decimal] [panid as a 4 character hex string]
For our network, it was on channel 0×18, which is decimal 24. The PAN ID was 0×5177, which is always used in hex.
So we join the network on PAN ID 0×5177 on channel 24 with txpower 3
==
== MyOnOff>network join 24 3 5177 security init: 0×00 set state to: 0×0900 join 0×00 MyOnOff>EMBER_NETWORK_UP (391 ms)
Testing the on-off command
- MyOnOff: compose an “on” command
==
== MyOnOff>zcl on-off on
- MyOnOff: send to MyOnOffOutput (addr 0×0000)
==
== MyOnOff>send 0000 T00000341:TX (CLI) Ucast 00
- View the results on MyOnOffOutput CLI output
==
== T00000658:RX len 03, clus 0×0006 (On/off) FC 01 seq 00 cmd 01 payload[]
Verify results of the on-off command
- MyOnOff: compose a message for reading attribute
Command Syntax: zcl global read [cluster in decimal] [attrID as 4 character hex string]
We will read on-off cluster (cluster ID 0×6), and on-off attribute (attribute ID 0×0)
==
== MyOnOff>zcl global read 6 0
- send command
==
== MyOnOff>send 0 T000012ED:TX (CLI) Ucast 00
Output device will reply with the attribute value. You should see the following response on MyOnOff’s console:
==
== T000012ED:RX len 08, clus 0×0006 (On/off) FC 08 seq 03 cmd 01 payload[00 00 00 10 01 ]
Decoding the payload by hand is possible (in this case, the last byte is the attribute value we want — so we can see it is “on”). If you have been following along with InSight Desktop, you can decode this packet in the Event Detail window:

Step 8: Moving commands to the application source code
Now that we’ve tested the commands on the CLI, we will move them to the application source code.
Modify MyOnOffOutput to respond to the on/off command
Incoming cluster commands (e.g. on, off) are implemented as handler commands in the specific cluster file. For example, the “on” command of the OnOff cluster is implemented as zclUtilOnOffRxOffHandler() in zcl-util-on-off.c.
To customize this, we will open ha/zcl-util-on-off.c add code to the appropriate zcl*Handler() function. The default handler implementation will look like this:
==
== void zclUtilOnOffRxOnHandler(void) { ZCL_UTIL_DEBUG("RX on/off:ON\r\n"); zclUtilOnOffSetValue(ZCL_ON_COMMAND_ID, FALSE); }
Note: It is important that you don’t modify the commands already in this handler, but instead you add your own at the end. If you modify the commands, the related attributes may no longer function properly and it will be a hassle to track down why.
So if you want to call your own function when the “On” command is received, you might update the callback handler to look like this:
==
== void zclUtilOnOffRxOnHandler(void) { // Default Ember code — DON'T MODIFY THIS ZCL_UTIL_DEBUG("RX on/off:ON\r\n"); zclUtilOnOffSetValue(ZCL_ON_COMMAND_ID, FALSE); // Custom code below this point to turn on the light lightTurnOn(); }
Alternate style
Alternatively, you can check the value of the attribute in the main loop, and when that value is changed, you can take appropriate action. This works well for less complex commands that only set one attribute.
If you are using this style, the same code would be written like this:
==
== // … in the declarations area … int8u currentValue = 0, oldValue, dataType; boolean status; // … in appTick() … // read current on/off value status = zclUtilReadAttribute(ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, (int8u*) ¤tValue, 1, &dataType); if (status != ZCL_SUCCESS) { ZCL_UTIL_DEBUG("Err: can't read on/off data\r\n"); return; } else { if (currentValue != oldValue) { if (currentValue == 1) lightTurnOn(); } else { lightTurnOff(); } oldValue = currentValue; } }
Note: We use oldValue to make sure we don’t call lightTurnOn()/lightTurnOff() every time through the loop — ie, we only call it when the value has changed.
Modify MyOnOff to send the on-off command
For this walkthrough, we want to send a message to the output based on the state of a flag/variable (which will later be set by a button press).
This is a slightly more complicated process (note: this will be improved in a future release to abstract the necessary function calls instead of doing it manually).
- Create a function for building the OnOff : On message
Note: We put this function in ha-zcl-cli.c for convenience. You may want to move it later if you stop using the CLI entirely. In that case, be sure to define the variable declarations indicated in the comment below.
==
== /* ********************************************* This function composes the command buffer and the APS header, I put this function inside ha-zcl-cli.c, as it requires the data structures (zcl buffer, APS header) and helper functions (zclCliBufferSetup()), but you can define the some where else. Require the following in case this is not defined in ha-zcl-cli.c: int8u appZclBuffer[APP_ZCL_BUFFER_SIZE]; int8u* pAppZclBuffer = appZclBuffer; int8u appZclBufferLen; EmberApsFrame globalApsFrame; void zclCliBufferSetup(void); *********************************************** */ void zclUtilCmdOn(void) { //APS header zclCliBufferSetup(); //set up the basic header info globalApsFrame.clusterId = ZCL_ON_OFF_CLUSTER_ID; //set specific cluster //ZCL header appZclBuffer[0] = ZCL_CLUSTER_SPECIFIC_COMMAND; //either ZCL_CLUSTER_SPECIFIC_COMMAND or ZCL_PROFILE_WIDE_COMMAND appZclBuffer[1] = appGetNextZclSeqNum(); //sequence number appZclBuffer[2] = ZCL_ON_COMMAND_ID; //set header length appZclBufferLen=3; //3 is the basic header length, append if it's a longer command }
- Build and send the message from within appTick()
==
== if(buttonPressed) { zclUtilCmdOn(); // create the message into the zcl buffer // E.g. sending to a direct address 0×0000 (i.e. not using address table or binding table) zaAppSendUnicast(EMBER_OUTGOING_DIRECT, //send to direct address 0, // destination address 0×0000 &globalApsFrame, appZclBufferLen, pAppZclBuffer); }
Summary
Now you have built a basic HA on/off switch and output (like a light), tested the behavior using the simple Command Line Interface (CLI), and moved the tested commands into the source code so they can be linked to your hardware. If you’ve followed the InSight Desktop configuration as well, then you’ve seen the commands and responses as they go over the air.
Other HA devices can be created and modified in the same way. If you run into trouble along the way, you can refer back to this walkthrough. If you get stuck, the InSight Desktop will be the first line of inquiry to seeing where the error is —- is the device not sending the command? Is it the wrong command? Is it sent but not received? Is it received but not properly handled? ISD will show you just how far the activity gets before it bogs down.
- Login to post comments








