Simple Applications Examples

The purpose of these simple applications demonstrate how to robustly start the Module and send messages. These can be a simple starting point for building your own robust applications. When creating your own application you may want to review the Module library methods to see whether they meet your needs. For example, the library uses simple wait; loops when waiting for an expected message from the Module; you may want to alter this to meet your needs. In a low power application you might want to go to sleep while waiting; for a more advanced application you may want to return control to the main application or RTOS while waiting.

These examples assume that you are familiar with the other groups of examples and have them working first.

Note: These examples exceed the 4kB code limit of the IAR Kickstart version. If you would like to compile it then use the 30 day evaluation, or purchase the full edition. We have provided precompiled binary files of the Simple Applications examples for loading onto the microcontroller. These are located in the FactoryDefault directory of the firmware package. See Loading Binary Files into Stellaris and Loading Binary Files into MSP430 for instructions. Messages including the Info Message are sent with a common Message Header.

Overview
There are three separate projects for the Coordinator, Router, and End Device. The Coordinator is the simplest example; it starts the network and displays received messages. The Router and End Device examples initialize a sensor and periodically transmit sensor information to the Coordinator.

Application Flow
The applications use a fixed startup sequence, startModule to get on the network and then use a state machine. State machines are useful for these applications because they allow for easy debugging should your application get stuck in a state. In your debugger watch the variable  so that you can see what state you're in when using breakpoints or if the program terminates abnormally. In our state machine we process the highest priority events first. These are usually flags set by interrupt service routines to indicate a button was pressed or a message is waiting to be processes. Next we just use a switch statement to process the current state. Each state will typically perform an action and then transition to the next state or back to the idle state (e.g. ).

Coordinator
The Coordinator is similar to the basic communications coordinator example, displaying messages that arrive. If the received message is a valid Info Message then it parses the message and displays it. While all of our examples have the message flow from the Router or End Device to the Coordinator, the coordinator could send messages to the other devices. The challenge is how to know the short address (or long address, since you can get the short address based on the long address) of the device which you wish to send the message to. This is out of the scope of the Zigbee protocol and is typically set during network commissioning or using binding.

Another function of the Coordinator in this simple application is to display received sensor data on the RGB LED. Since we only have one RGB LED but two sensors, we need a way to select which sensor is displayed. This is done by pressing the button. Pressing the button will cycle between three modes. The BoosterPack board's red LED D8 and yellow LED D9 display which mode is selected (assuming that S4-3 and S4-4 are both on). The button is debounced to prevent inadvertent mode changes.
 * Display Nothing (LED's D8 and D9 both off)
 * Display IR Temp Sensor (yellow LED D9 on) - displays the temperature: Green if nominal, red if hot, blue if cool
 * Display Color Sensor (red LED D8 on) - displays the received color on the RGB LED.

Cross-Platform Differences
Note: There are some minor differences in the operation of the Simple Application Coordinator Demo dependent on whether it is plugged into a MSP-430 LaunchPad or a Stellaris LaunchPad board.
 * When operating with the MSP-430 LaunchPad:
 * The Simple Application Example uses the RGB LED on the Anaren BoosterPack board.
 * Swithches S1 on the BoosterPack board and on the LaunchPad board are wired in parallel and are used to reset the MSP-430.
 * Switches S2 on the BoosterPack board and on the LaunchPad board are wired in parallel and are used to cycle between the three modes listed above.
 * When operating with the Stellaris LaunchPad:
 * The Simple Application Example uses the RGB LED on the Stellaris LaunchPad board.
 * Only Switch S1 on the BoosterPack board is used to reset the MSP-430.
 * Switch S2 on the BoosterPack board and switches S1 and S2 on the Stellaris LaunchPad board can be used to cycle between the three modes listed above.

Starting the Module
All of the simple applications use the same Basic Module Startup that we use throughout the examples. Here you can modify PANID, channel, etc. as needed for your application. For simplicity these examples don't use security but this can be added with a few lines of code.

Receiving Messages
The application waits for a message to be fully received. If using SPI this will occur when SRDY gets asserted. Once this happens, the application retrieves the pending message and then if the message is an AF_INCOMING_MESSAGE it displays the header contents. Next, we check to see if the Cluster is an Info Message and if it is then we use deserializeInfoMessage to convert the stream of bytes into an infoMessage structure. We can now work with the infoMessage structure instead of the raw bytes which makes life much easier. Next we display any received OIDs to the console and display the parameter on the RGB LED. Received messages or OIDs that we don't know are displayed too, which makes it easy to add your own OIDs. We are now done, and go back to waiting for the next message.

Router
The router simple example shows how to start the Module and robustly send messages. We use the same messages (Info Message) as the coordinator so that they can be parsed successfully.

Initialization
The Router simple application first configures a timer on the microcontroller. This will be used to tell us when to send messages. On the MSP430 this uses the Very Low Oscillator.

Next, we pre-populate the Message Header that we will be sending as part of the Info Message. If you are scarce on RAM or will be sending a lot of different types of messages then there's no point in preparing the info message ahead of time. Since that's the only thing our application does, we prepare as much of it as we can ahead of time for efficiency.

State Machine
Our state machine starts in the STATE_MODULE_STARTUP which starts the Module using the startModule method. Unlike the basic communications examples, if we are unable to start then we wait a few seconds and retry. We do this until we are able to Join the network. If implementing a device that can tolerate a Module startup failure then you may want to exit this loop after N times and alert the host OS. Once the Module has been initialized we query it for MAC Address and store it in our infoMessage header.

Next, we initialize the timer and sets it to generate an interrupt periodically. After the Module has started successfully we change state to the STATE_DISPLAY_NETWORK_INFORMATION. This state displays information about our network. It can be commented out to reduce code size but you may want to include it.

Sending the Info Message
Now we are ready to send our first Info Message. We increment the header sequence number and populate the other fields appropriately. We get the sensor values through getSensorValues which uses compile options to load the message with the KVPs that we support. We display the message for good measure and then serialize it into a stream of bytes and send it.

We now attempt to send the message, and if the message send fails then we restart the Module. If you want to make your application more tolerant then you could retry N times and then restart the Module. Once the message is sent then we go to the STATE_IDLE.

Interrupts
We follow the design principle of keeping ISRs very small. Any interrupts set flags which are checked in the main state machine. On timer interrupt we set a state flag, STATE_FLAG_SEND_INFO_MESSAGE. This is read in STATE_IDLE and if set then the state is changed to STATE_SEND_INFO_MESSAGE and the flag cleared.

End Device
This example shows how to start a Zigbee End Device and periodically send sensor data. The End Device Simple Application is very similar to the Router version, but with the following changes:
 * Starts the Module as an End Device (duh)
 * Sleeps when not sending a message

State Machine
The State Machine startup is very similar to that of the Router.

Sending the Info Message
In STATE_SEND_INFO_MESSAGE we retrieve the sensor values, send the message, and then we put the processor to sleep with HAL_SLEEP. When running the example you will see output like: $~im~ IR Temperature = 26.82C Info #26: V/F=03/00, mac=124b0001fe480f, Device Type=01, numParameters=1: The ~im indicates that we are in the infoMessage state. Next, we display the sensor data that we wish to send. The third line is the contents of the message that will be sent showing the header and lastly the parameters in the format .

Interrupts
When we initialized the timer, we set a variable with the argument WAKEUP_AFTER_TIMER in the initTimer method. This sets a flag in the hal file so that at the end of the timer ISR the macro HAL_WAKEUP is called to wake up the processor.

Temperature Sensing
Using the built-in temperature sensor in the MSP430 will require a one-time production calibration. If this is not possible then there are external digital temperature sensors like the TI TMP100

Remote Firmware Update
Inevitably the firmware on the device needs to be updated. This can be challenging with RF devices since we can't assume that the RF link will be up and running. There are several ways of doing this with the Module:
 * Bootloader establishes Zigbee network with Module; downloads the new firmware: In this approach you create a bootloader file that communicates with the Module to join the network and download the new firmware image in chunks, writing to Flash each time.
 * Pros: cheapest method
 * Cons: vulnerable to interruptions in the process; may be difficult to fit everything you need in bootloader.
 * Storing firmware in external NV memory: Using an external Serial Flash, you download and verify the entire flash image before writing it to application flash.
 * Pros: Very robust; if the process is interrupted then can recover gracefully
 * Cons: Requires external serial flash (~$1, depending on size of application firmware)
 * Bank Switching: In this approach you run code from one half of application flash, which will download the code into the other half
 * Pros: Very robust, no external components required
 * Cons: Requires using a microcontroller with twice as much flash as needed.

Note: these simple applications do NOT work with Sensor Monitor.