Connect Tutorial 2 - Communication Basics: Send and Receive
10/274/2019 | 03:55 PM
This is the second tutorial in a series that demonstrates "the essentials" of building applications based on Silicon Labs Connect. See Connect Tutorial Series: Table of Contents for an overview of topics addressed in the series, general pre-requisites to ensure you get the most out of the exercises, and definitive Connect references for when you're ready to dive deeper.
The first tutorial (Getting Started with Application Development) introduced the Connect application development construct, based on plugins, callbacks, and events. In this second tutorial, we expand on this Connect-based application concept to demonstrate basic wireless communication by implementing simple message transmit and receive operations.
To conduct the following exercise, you'll need two Wireless Starter Kit (WSTK) boards, each equipped with a (preferably identical) radio board.
Basic Application Example: Transmit / Receive
First, create a new "Empty" Flex SDK Project as described in tutorial 1 "Getting Started with Application Development". (Just create a new project for this tutorial 2 and give it an appropriate name - you don't need to actually repeat the tutorial 1 exercise.)
Radio Configuration
Radio configuration is a crucial part of successful communication between devices. Importantly, radio settings in the application must be in agreement with those in the hardware.
Configuring the EFR32 radio is not the focus of this tutorial (for more information on this subject, see AN971: EFR32 Radio Configurator Guide). For the purposes of this tutorial series, please take "on faith" the following steps and select a predefined radio configuration in AppBuilder/Radio Configurator to provide radio functionality in the exercises:
Open the radio configurator (open the project .isc file, and select the [Radio Configuration] tab in AppBuilder), then click on Protocol Configuration. Select Connect Profile from the available radio profiles. In the radio PHY drop down menu there are several predefined PHY settings that support the radio boards available in the Silicon Labs EFR32 portfolio. Select one of these that corresponds to your target hardware (especially the radio band).
By selecting a Connect profile, you ensure that a Connect-compatible packet format will be applied during transmit and receive operations.
If none of the predefined radio PHYs match your target hardware, select Custom settings from the radio PHY list and then apply any necessary adjustments to settings in the radio configuration fields. If the delta between a built-in PHY option and your hardware is small enough (like a minor change in frequency, bitrate, or deviation), select that closest predefined setting first, then switch to Custom setting and make your adjustments. However, be aware that radio configuration is a complex task, and even minimal changes can result in a non-functional radio PHY.
Select Plugins & Callbacks, Configure Buttons, and Generate Project Files
The default plugin configuration for the Connect (SoC): Empty Example provides all the features we'll use below, so we don't need to additionally enable or disable any plugins.
We will, however, need to activate some related callbacks (using AppBuilder / Callbacks, as described in "Getting Started with Application Development"), so that our application is notified when the corresponding events occur:
emberAfIncomingMessageCallback
emberAfMessageSentCallback
emberAfStackStatusCallback (for future use - does nothing in the current project)
Pushbutton configuration is managed using the Hardware Configurator in Simplicity Studio. As with the plugins, the default settings are suitable for our example, so we won't cover this tool in this tutorial. The buttons are initialized from the example Connect application's main() function.
You can now generate the project (by clicking the [Generate] button) in AppBuilder. Doing so will populate the Simplicity Studio project workspace with source files and linked resources to support the configuration specified in your AppBuilder (.isc) file. We'll write our application using some these files in the following sections - for example, when we implement the callbacks enabled above.
Address (Node ID) Selection
The two devices which will send/receive messages to/from each other must have unique node IDs on the network. So that we can run the same firmware on both devices, we'll use button PB0 on the WSTK to select the node ID for each device at startup. When the application begins, we will first retrieve the state of PB0, then set the local (self) and remote (the other device on the network that we'll communicate with) addresses corresponding to the button state.
We check the button state (and determine the correct assignment of device addresses) in emberAfMainInitCallback():
You can see here that we've also used LED0 to visibly indicate the address, which helps to distinguish the WSTK boards from each other. Note that this indication is only applicable at this point (shortly after power-on/reset), as LED0 is repurposed for a different use during the later message transmit and receive operations. (If you're not sure where to find emberAfMainInitCallback(), review the steps in tutorial 1 "Getting Started with Application Development" to enable callbacks within AppBuilder.)
Assigning Local Node ID
Note: A single Connect network consists of multiple devices using a single one of the three available Connect Modes. For this tutorial (and the next few in this series), we've chosen a Direct mode Connect application for our demonstrations, as it provides both a sufficiently general platform and the fastest ramp to competency for new Connect developers. The modes are not interoperable, and the APIs vary (often significantly) among them - which makes porting from one to another impractical - so determining the optimal mode for your application is a critical first step. Other Connect Modes will be explored in later tutorials in this series, but with the API call that follows we effectively designate this as a Direct mode device.
Later in emberAfMainInitCallback(), we join the network using the appropriate address by commissioning:
At this point the network parameters are set, and the device can communicate with other devices.
Button Event Handling
For this exercise, we want to send a message when button PB1 is pressed. Although we could periodically check (i.e. "poll") the button state, we will instead use an interrupt-based approach available with HAL support. The function halButtonIsr() will be called on the "press" or "release" of any configured button. It is a weak function that is empty by default, and the application must implement it to use it.
The following snippet shows one possible implementation:
Although it is possible to send a message directly from this ISR, blocking execution by conducting lengthy operations from ISR context is not recommended. Instead, we'll only activate a Connect event (to be acted upon later) and quickly exit the ISR.
To create a Connect event (as described in "Getting Started with Application Development"), we add it to AppBuilder / Other / Event Configuration and implement it in flex-callbacks.c:
Here, we've named the event "command" as buttonEvent, and the event "callback" as buttonHandler. Remember to generate the project in AppBuilder again, after you've modified the .isc file by adding this event.
Since we want to call the handler only once on each button press (as opposed to the blink event we created in the first tutorial, where the event was rescheduled repeatedly), the event must be inactivated in the handler.
We send a message from this handler using the emberMessageSend() function, where the argument nodeIdRemote specifies the node ID of the device to which we are addressing the message. The endpoint is a freely chosen value (the concept is similar to a port number in TCP/IP) which can be used to filter incoming messages.
Implementing Callbacks
Previously, we enabled in the AppBuilder / Callbacks tab some callbacks we intend to use in our application. We'll now implement them using the stubs that were created in flex-callbacks.c during project generation.
The Connect stack informs the application that a message has been sent using the following callback:
In the implementation above, we only toggle the state of LED0. Note, however, that this callback will fire even if the message did not actually send or the remote device did not successfully receive the message. The variable status will indicate this result.
Handling incoming messages is accomplished through a callback as well:
The above callback fires when an incoming message is available. It checks whether the endpoint matches and compares the received message payload with the one we previously loaded to messageBuffer (this example sends the same message in both directions). There are several message properties stored in the message structure which is of type EmberIncomingMessage. See the Connect API Documentation documentation for details.
Usage and Testing
Compile and download the firmware onto both devices. Start (reset) one of them without pressing PB0 (it will assume node ID 0x0000 and LED0 will be off) and do the same on the other board but hold PB0 down during start/reset (its node ID will become 0x0001 and LED0 will be on).
After completing the above steps, press PB1 on one of the boards. LED0 should toggle on that board and LED1 should change its state on the other board if the message is successfully received. The same sequence (press PB1 to transmit a message) should work in the other direction as well.
Notes
Keep in mind that it is always a good policy to handle errors/return values/statuses, and the Connect API provides this feedback in most cases. Hence, as a "best practice" we highly recommend implementing such evaluation routines. This tutorial example omitted these important features to keep it simple, easy to understand, and focused on fundamental topics.
Conclusion
Connect-based applications benefit from simple APIs that support radio transmit and receive operations. The Wireless Starter Kit (WSTK) platform facilitates application development for the EFR32 product families. Later tutorials in this series dive deeper into more sophisticated radio support APIs and flexible bring-up/debug/management interfaces available to Connect-based applications.
Connect Tutorial 2 - Communication Basics: Send and Receive
This is the second tutorial in a series that demonstrates "the essentials" of building applications based on Silicon Labs Connect. See Connect Tutorial Series: Table of Contents for an overview of topics addressed in the series, general pre-requisites to ensure you get the most out of the exercises, and definitive Connect references for when you're ready to dive deeper.
The first tutorial (Getting Started with Application Development) introduced the Connect application development construct, based on plugins, callbacks, and events. In this second tutorial, we expand on this Connect-based application concept to demonstrate basic wireless communication by implementing simple message transmit and receive operations.
To conduct the following exercise, you'll need two Wireless Starter Kit (WSTK) boards, each equipped with a (preferably identical) radio board.
Basic Application Example: Transmit / Receive
First, create a new "Empty" Flex SDK Project as described in tutorial 1 "Getting Started with Application Development". (Just create a new project for this tutorial 2 and give it an appropriate name - you don't need to actually repeat the tutorial 1 exercise.)
Radio Configuration
Radio configuration is a crucial part of successful communication between devices. Importantly, radio settings in the application must be in agreement with those in the hardware.
Configuring the EFR32 radio is not the focus of this tutorial (for more information on this subject, see AN971: EFR32 Radio Configurator Guide). For the purposes of this tutorial series, please take "on faith" the following steps and select a predefined radio configuration in AppBuilder/Radio Configurator to provide radio functionality in the exercises:
Open the radio configurator (open the project .isc file, and select the [Radio Configuration] tab in AppBuilder), then click on Protocol Configuration. Select Connect Profile from the available radio profiles. In the radio PHY drop down menu there are several predefined PHY settings that support the radio boards available in the Silicon Labs EFR32 portfolio. Select one of these that corresponds to your target hardware (especially the radio band).
By selecting a Connect profile, you ensure that a Connect-compatible packet format will be applied during transmit and receive operations.
If none of the predefined radio PHYs match your target hardware, select Custom settings from the radio PHY list and then apply any necessary adjustments to settings in the radio configuration fields. If the delta between a built-in PHY option and your hardware is small enough (like a minor change in frequency, bitrate, or deviation), select that closest predefined setting first, then switch to Custom setting and make your adjustments. However, be aware that radio configuration is a complex task, and even minimal changes can result in a non-functional radio PHY.
Select Plugins & Callbacks, Configure Buttons, and Generate Project Files
The default plugin configuration for the Connect (SoC): Empty Example provides all the features we'll use below, so we don't need to additionally enable or disable any plugins.
We will, however, need to activate some related callbacks (using AppBuilder / Callbacks, as described in "Getting Started with Application Development"), so that our application is notified when the corresponding events occur:
emberAfIncomingMessageCallback
emberAfMessageSentCallback
emberAfStackStatusCallback
(for future use - does nothing in the current project)Pushbutton configuration is managed using the Hardware Configurator in Simplicity Studio. As with the plugins, the default settings are suitable for our example, so we won't cover this tool in this tutorial. The buttons are initialized from the example Connect application's
main()
function.You can now generate the project (by clicking the [Generate] button) in AppBuilder. Doing so will populate the Simplicity Studio project workspace with source files and linked resources to support the configuration specified in your AppBuilder (.isc) file. We'll write our application using some these files in the following sections - for example, when we implement the callbacks enabled above.
Address (Node ID) Selection
The two devices which will send/receive messages to/from each other must have unique node IDs on the network. So that we can run the same firmware on both devices, we'll use button PB0 on the WSTK to select the node ID for each device at startup. When the application begins, we will first retrieve the state of PB0, then set the local (self) and remote (the other device on the network that we'll communicate with) addresses corresponding to the button state.
We check the button state (and determine the correct assignment of device addresses) in
emberAfMainInitCallback()
:You can see here that we've also used LED0 to visibly indicate the address, which helps to distinguish the WSTK boards from each other. Note that this indication is only applicable at this point (shortly after power-on/reset), as LED0 is repurposed for a different use during the later message transmit and receive operations. (If you're not sure where to find
emberAfMainInitCallback()
, review the steps in tutorial 1 "Getting Started with Application Development" to enable callbacks within AppBuilder.)Assigning Local Node ID
Note: A single Connect network consists of multiple devices using a single one of the three available Connect Modes. For this tutorial (and the next few in this series), we've chosen a Direct mode Connect application for our demonstrations, as it provides both a sufficiently general platform and the fastest ramp to competency for new Connect developers. The modes are not interoperable, and the APIs vary (often significantly) among them - which makes porting from one to another impractical - so determining the optimal mode for your application is a critical first step. Other Connect Modes will be explored in later tutorials in this series, but with the API call that follows we effectively designate this as a Direct mode device.
Later in
emberAfMainInitCallback()
, we join the network using the appropriate address by commissioning:At this point the network parameters are set, and the device can communicate with other devices.
Button Event Handling
For this exercise, we want to send a message when button PB1 is pressed. Although we could periodically check (i.e. "poll") the button state, we will instead use an interrupt-based approach available with HAL support. The function
halButtonIsr()
will be called on the "press" or "release" of any configured button. It is a weak function that is empty by default, and the application must implement it to use it.The following snippet shows one possible implementation:
Although it is possible to send a message directly from this ISR, blocking execution by conducting lengthy operations from ISR context is not recommended. Instead, we'll only activate a Connect event (to be acted upon later) and quickly exit the ISR.
To create a Connect event (as described in "Getting Started with Application Development"), we add it to AppBuilder / Other / Event Configuration and implement it in
flex-callbacks.c
:Here, we've named the event "command" as
buttonEvent
, and the event "callback" asbuttonHandler
. Remember to generate the project in AppBuilder again, after you've modified the .isc file by adding this event.Since we want to call the handler only once on each button press (as opposed to the blink event we created in the first tutorial, where the event was rescheduled repeatedly), the event must be inactivated in the handler.
We send a message from this handler using the
emberMessageSend()
function, where the argumentnodeIdRemote
specifies the node ID of the device to which we are addressing the message. Theendpoint
is a freely chosen value (the concept is similar to a port number in TCP/IP) which can be used to filter incoming messages.Implementing Callbacks
Previously, we enabled in the AppBuilder / Callbacks tab some callbacks we intend to use in our application. We'll now implement them using the stubs that were created in
flex-callbacks.c
during project generation.The Connect stack informs the application that a message has been sent using the following callback:
In the implementation above, we only toggle the state of LED0. Note, however, that this callback will fire even if the message did not actually send or the remote device did not successfully receive the message. The variable
status
will indicate this result.Handling incoming messages is accomplished through a callback as well:
The above callback fires when an incoming message is available. It checks whether the endpoint matches and compares the received message payload with the one we previously loaded to
messageBuffer
(this example sends the same message in both directions). There are several message properties stored in themessage
structure which is of typeEmberIncomingMessage
. See the Connect API Documentation documentation for details.Usage and Testing
Compile and download the firmware onto both devices. Start (reset) one of them without pressing PB0 (it will assume node ID 0x0000 and LED0 will be off) and do the same on the other board but hold PB0 down during start/reset (its node ID will become 0x0001 and LED0 will be on).
After completing the above steps, press PB1 on one of the boards. LED0 should toggle on that board and LED1 should change its state on the other board if the message is successfully received. The same sequence (press PB1 to transmit a message) should work in the other direction as well.
Notes
Keep in mind that it is always a good policy to handle errors/return values/statuses, and the Connect API provides this feedback in most cases. Hence, as a "best practice" we highly recommend implementing such evaluation routines. This tutorial example omitted these important features to keep it simple, easy to understand, and focused on fundamental topics.
Conclusion
Connect-based applications benefit from simple APIs that support radio transmit and receive operations. The Wireless Starter Kit (WSTK) platform facilitates application development for the EFR32 product families. Later tutorials in this series dive deeper into more sophisticated radio support APIs and flexible bring-up/debug/management interfaces available to Connect-based applications.
API References
Functions
emberAfIncomingMessageCallback()
emberAfMessageSentCallback()
emberAfStackStatusCallback()
emberAfMainInitCallback()
emberMessageSend
emberJoinCommissioned()
emberEventControlSetActive()
halButtonIsr()
halButtonPinState()
halClearLed()
halSetLed()
halToggleLed()
Type definitions
EmberStatus
EmberOutgoingMessage
EmberIncomingMessage
Defines
EMBER_DIRECT_DEVICE
EMBER_OPTIONS_NONE
BUTTON_PRESSED