This article describes the format of monochrome (1-bit, black and white) pixmap that can be used with various LCDs including the one attached to BRD4001A WSTK.
In case of monochrome images one pixel need one bit to store thus on one byte 8 pixels can be stored. Due to the limited flash memory of the MCUs it is straightforward to optimize memory occupation of a picture and minimize the wasted space. To achieve this, GLIB packs the bits of an image to eliminate unused memory spots.
The following example will use a 9 column wide and 9 row tall picture to show how the pixel data transformed to GLIB pixmap format. The picture we will transform to GLIB pixmap is a smiley:
In a GLIB pixmap the pixels are black and white however, the image can be normal or inverted so it may better to refer the pixel's state as "on" and "off" where "off" means the pixel matches with the background color while "on" means pixel has foreground color. In the picture above column 0 / row 0 spot is "off" and column 2 / row 0 is "on". Bit value "0" corresponds to "off" state, "1" represents "on".
The (0;0) coordinate of the pixmap is in the upper-left corner. The example image is 9 bit wide so, a row obviously does not fit into 1 byte (8 bits) thus the last pixel of the first row is pushed to the next byte. The pixels of the next rows are shifted to fill the space in the consecutive bytes as the figure below shows.
This way the 9-bit row patterns are arranged into 8-bit bytes. The whole image needs 81 bits but this does not exactly fit into integer number of bytes hence, there will be unused bits in the last byte (seven unused bits marked with "X" in the left side figure) - their values are irrelevant.
Pixel (0;0) of the picture is bit 0 of byte 0 of the image data, (1;0) is byte 0 / bit 1, (9;0) is byte 1 / bit 0, (0;1) is byte 1 / bit 1, and so on. Based on this the data bytes are:
GLIB is a graphics library that can be used to draw pixels, common shapes, text or bitmaps to a display connected to an MCU. GLIB treats the display as a matrix of pixels which is a model of the physical display. This matrix is exposed to GLIB via an API called DMD (Dot Matrix Display). To draw something on a physical display, the user application needs to provide GLIB with a single implementation of the DMD interface. Sample DMD implementations are provided for the displays that are connected to the Silicon Labs Starter Kits and the Silicon Labs Development Kits.
References
Detailed description is available in the Software documentation of your device. See the same for the Giant Gecko here for your reference.
For an extensive feature showcase, please see MCU example “SLSTKxxxxx_glib” for your MCU development kit in Simplicity Studio.
Initialization
First, glib needs several libraries to be included to work, such as display.h for display specific settings, dmd.h for the Dot Matrix Display library and glib.h for the graphics library itself. The graphics library uses a context for each display. This context must be defined before using glib with
static GLIB_Context_t glibContext;
All the previously listed libraries must be initialized to enable drawing on the screen, by calling the followings:
EMSTATUS status;
/* Initialize the display module. */
status = DISPLAY_Init();
if (DISPLAY_EMSTATUS_OK != status) {
while (1) ;
}
/* Initialize the DMD module for the DISPLAY device driver. */
status = DMD_init(0);
if (DMD_OK != status) {
while (1) ;
}
/* Initialize the glib context */
status = GLIB_contextInit(&glibContext);
if (GLIB_OK != status) {
while (1) ;
}
Drawing on the screen
Drawing usually consist of three steps. First, if we do not want to overlap the previously displayed content and/or are not partially rewriting the display, we should clear it first by calling GLIB_clear(&glibContext). The next step is to set the display content in the context by drawing something on the screen with GLIB_drawXXXX() where XXXX can stand for String, Bitmap, Circle, etc. After the content is set, the last step is to update the display using the DMD driver using DMD_updateDisplay().
In the above example a welcome text is written on the screen. First, we set the back- and foreground colors in the glibContext to white and back respectively. Then, as mentioned before, a screen clear is performed. Before calling the GLIB_drawString() function, the font is set to normal in the context and the string is put together into one character array. The GLIB_drawString() is called with the context, string and its length, along with the X and Y coordinate of the text and with the opaque setting last. The example displays the following on a Giant Gecko STK board:
Updating parts of the screen
When not all the information is outdated on the screen, partial update can be used. In this case do not clear the screen, as then all screen content will be lost. GLIB_clearRegion should be used instead, after setting the clipping region with the GLIB_setClippingRegion() function. This function needs a rectangle as an input, this will be the area the GLIB_clearRegion will clear out. After the clear is complete, you can draw in this territory by providing the appropriate X and Y coordinates to the draw function.
if (refresh) {
GLIB_setClippingRegion(&glibContext, &upperArea);
GLIB_clearRegion(&glibContext);
msgYOffset = 2;
} else {
GLIB_setClippingRegion(&glibContext, &lowerArea);
GLIB_clearRegion(&glibContext);
msgYOffset = 7;
}
/* Update only the current message region using msgYOffset */
GLIB_resetClippingRegion(&glibContext); //reset the clipping region to default
GLIB_applyClippingRegion(&glibContext); //application is not included in the resetting
sprintf(text, "Message from %d,\nRSSI: %d dBm", message->source, message->rssi);
GLIB_drawString(&glibContext, text, strlen(text), X_BORDER,
Y_BORDER + (glibContext.font.fontHeight + glibContext.font.lineSpacing) * msgYOffset, 0);
//print to the upper or lower area using msgYOffset
/* Update display */
DMD_updateDisplay();
refresh = !refresh;
The above example shows a way to display incoming messages alternately in the upper and the lower part of the screen. A previously defined upper and lower area clipping region is cleared and the upper or lower message is displayed using the predefined screen borders and the available font height and line spacing in the glib context.
Note: This application uses glib and graphics functionalities. To better understand how to use them, please refer to Enable LCD screen on WSTK KBA about how to setup graphics easily (used on sensor) and this KBA for a better knowledge on the glib library usage (used on sink).
Introduction
In this article we will learn about the Connect API and create a simple sensor-sink network. The purpose is to understand the basic functionalities of Connect through a simple RF sensor application and to integrate LCD screen printing into Connect, which is not presented yet in any example.
Specification
To create a somewhat real network, three wireless starter kits should be used – one sink and two sensors – however, it will work with two WSTKs as well. We will use Connect to create the network with encryption enabled, using a 2.4 GHz 802.15.4 radio profile.
The sensors will be sleepy-end devices (configurable), running one “task” (called Event in Connect) after the network is up, which sends out the sensor information to the sink every second. The sensor should print the sent values and basic information on its screen.
The sink upon receiving the packets should update its screen accordingly, while also should show basics network status info.
Quick introduction to Connect
Connect is a network stack on RAIL's 15.4 specific API. It does not allow accessing lower layers, every message should go through the upmost network layer, which is the Connect messaging API. It is a production-quality wireless networking stack and development environment for broad based proprietary applications, full-featured, easily customizable wireless connectivity solution for the Sub-GHz and 2.4 GHz proprietary market. It is based on 802.15.4 like MAC. By taking care of the low-level details of network formation and radio configuration, the customer can focus on application development.
The main functionality of a Connect application is written in the Callbacks source file. There are several built-in events – like message received, child has joined etc. – named “Callbacks”, and optionally create-able, user customizable Events. The Callback functions will run when their tied trigger occur (e.g. a message was received), and the so called “Event handlers” will run whenever we start or schedule their Events. These Events can be considered as tasks in an embedded operating system. They are round-robin scheduled without pre-emption, therefore their handler functions will run until not stopped (or interrupted).
In this stack the main.c source file is part of the SDK, it should not be edited, user defined code should only go into the callbacks.c file. In Connect, setting up a network is rather easy, it is done by writing a few Callbacks. We must initialize the network and set the security keys in the MainInitCallback function – which will run only once upon init – then using an appropriate Callback let children join or start the joining process (depends on the node type). With this the network should be up, from this point we can modify the application behavior with the rest of the Callback functions or Event handlers.
Setting up the projects
There are several examples available for Connect. Our application will be very similar to the sensor-sink examples, but much simpler. Let’s start and build the application therefore from a blank flex project. The AppBuilder will guide us through the project setup and will be able to generate the application frame, even if we start from scratch. Our job will be to find a simple way to configure Connect to achieve the basic functionality, and to – fulfilling our specifications – include the graphics handling.
Sensor project
Create a new blank application for the Flex SDK. After the project was created, the AppBuilder should come up with the General tab open. Make sure the architecture listed here is correct, especially if the toolchain is set. As you can see on the picture below, we are using a Mighty Gecko board, SDK version 2.4.0.0 and the GNU ARM compiler for this example.
General settings
Under the “Radio Configuration” tab select the Connect 2.4 GHz 802154 radio profile. You can also select any of the profiles compatible with your board, however in this example we are using the 802154 one.
Radio configuration
Under the “Printing” tab disable the Command line interface. We won't use the CLI, we would like to rely on the LCD screen of the WSTKs and automatic network buildup in this example.
Printing setup
On the “Plugins” tab, make sure to set the tick next to the following options only:
Connect Common
Main
Connect Debug
Debug Print
Diagnostic Stub
Stack Packet Counters Stub
Connect HAL
HAL Library
Simulated EEPROM version 1 Library
Connect I/O
Serial
WSTK Sensors (sensor project only)
Connect PHY
PHY Library
Connect Stack
Form and Join
Frequency Hopping stub
MAC Packet Queue
Parent Support
Security AES
Security XXTEA Stub
Stack Common
RAIL
RAIL Library
RAIL Utility
Application Graphics
After you enable Application Graphics, a warning will pop up that this action will also enable the SPIDISPLAY. This is something that we want, so go ahead and click OK.
Disabling EXTCOMIN pin
If you click on the Application Graphics plugin, the Hardware Configurator Dependent Module Options will show on the right. It's important to turn off setting "Use EXTCOMIN pin for polarity inversion" here, as this option was reported to cause issues with Connect. We will take care of this polarity inversion from our code instead.
The next step is to configure the Callbacks under the Callbacks tab. As mentioned before, these are the built-in events for which we can determine a functionality through a Callback function. If we do not want to use a specific Callback, here we can disable them. From the user configurable Callbacks, we will only need the MainInitCallback and the StackStatusCallback callbacks for the sensor application. Turn them on by checking the box next to them under the "Is used?" column.
Callbacks configuration - sensor
To finish the AppBuilder configuration, go to the “Other” tab. Under this tab you can configure additional macros and includes, but most importantly for a Connect application, here you can configure the Events, the user-defined “tasks”. Add a new Event in the "Event Configuration" and edit its command, by clicking on the automatically generated name, to “reportControl” and its callback function to “reportHandler”. This Event will be responsible for the sensor data reporting. Add another one with “connectControl” and “connectCallback” command and callback names, respectively. This Event will be responsible for reconnecting whenever the network connection is lost.
Event configuration - sensor
Save the *.isc file and generate the project.
Unfortunately the WSTK Sensors plugin does not enable the Sensor in the hardware configurator, therefore it has to be done manually. Open up the *.hwconf file in the project and under the DefaultMode Peripherals make sure to enable the I2C Sensor.
Hardware configurator
Sink project
Create another blank project for the Sink. Make sure to set up everything the same as you did for the sensor on the “Radio Configuration”, “Printing” and “Plugins” tabs. One exception is under the “Plugins” tab: “WSTK Sensors” under “Connect I/O” should not be enabled, as for the Sink we won’t need the sensors on the WSTK.
Under the Callbacks tab we will need the following to be enabled: MainInitCallback for the initializations, StackStatusCallback and ChildJoinCallback for creating the network, and IncomingMessageCallback so we can process and show the data on the display.
Callbacks configuration - sink
As no Event will be used for the sink application – we can handle everything from the Callbacks – we've got no configuration to change on the "Other" tab.
Save the *.isc file and generate the project.
Callbacks and Events – Sensor
It’s time to write the main functionality of the sensor application – in the callbacks source file as mentioned before.
Please note, not all necessary code snippets will be presented here. You can find the rest of the code, such as definitions and includes in the attached source files.
For the sensor we turned two callbacks on, let’s start with one of those.
MainInitCallback
void emberAfMainInitCallback(void) {
//Start with one-time configurations
emberSetSecurityKey(&securityKey);
//try to restore the networks state from NVM
EmberStatus initStatus = emberNetworkInit();
if (initStatus != EMBER_SUCCESS) {
//start the connectControl event ASAP
emberEventControlSetActive(connectControl);
}
//Initializes graphics
GRAPHICS_Init();
}
In the MainInitCallback function we first set the security key, then try to restore the network state from the Non-Volatile Memory. As the child table is not deleted after a device reset, this can be a quick way to initialize the network. If the device was just set up or the table is not available, we start our connectControl Event to initialize the network connections from scratch. In the end of this Callback, the graphics is initialized, as all kind of one-time-run initialization like this should be put in this Callback.
connectHandler
Since it was just mentioned it, let's continue with the connectControl Event's Handler, the connectHandler.
void connectHandler(void) {
// Put disconnected message on screen
...
// Set parameters and join the network
EmberNetworkParameters parameters;
parameters.radioTxPower = MAX_TX_POWER;
parameters.radioChannel = RADIO_CHANNEL;
parameters.panId = PAN_ID;
#ifdef SLEEPY_MODE
emberJoinNetwork(EMBER_STAR_SLEEPY_END_DEVICE, ¶meters);
#else
emberJoinNetwork(EMBER_STAR_END_DEVICE, ¶meters);
#endif
emberEventControlSetInactive(connectControl); //we'll re-enable it if join has failed
}
This Event puts the “disconnected” message on the screen, as it only runs when there’s no network connection. After setting necessary radio parameters it tries to connect to the network – as a sleepy end device or a regular device, based on the configuration. As a last step, we set this Event inactive; if we didn’t do this, the Event would run endlessly.
StackStatusCallback
The StackStatusCallback function will run whenever the stack status changes. This can mean a good thing, like the network is up, or something worse, like it got disconnected, or the join failed, etc. Based on the status it carries, we will start the reportControl Event – sending out the sensor data – or schedule the connectControl Event with a retry interval to try connecting again.
void emberAfStackStatusCallback(EmberStatus status) {
if (status == EMBER_NETWORK_UP) {
//network is up, start the reportControl event ASAP
emberEventControlSetActive(reportControl);
} else
if (status == EMBER_NETWORK_DOWN || status == EMBER_JOIN_FAILED
|| status == EMBER_NO_VALID_BEACONS) {
//network or joining failure, try to reconnect with delay
emberEventControlSetDelayMS(connectControl, CONNECT_RETRY_INTERVAL);
}
}
reportHandler
If the network is up, the reportHandler function will run immediately. The Handler consists of four main steps: first, we need to get the sensor data through calling PluginWstkSensorsGetSample(). With emberMessageSend() we can send the data. Using the sink’s node ID, it will be sent to the right node. We wouldn’t want this function to run as fast it can, but every 1000 ms, so we schedule the Event with the report interval. As a last step, the LCD screen is refreshed with the help of the graphics library.
void reportHandler(void) {
// Get sensor data
emberAfPluginWstkSensorsGetSample(&(txBuffer.humidity),
&(txBuffer.temperature));
//Send message
emberMessageSend(SINK_NODE_ID, DATA_ENDPOINT, 0, sizeof(dataMsg_t),
(uint8_t*) &txBuffer, EMBER_OPTIONS_ACK_REQUESTED);
//Schedule task run in 1000 ms
emberEventControlSetDelayMS(reportControl, REPORT_INTERVAL);
//Refresh sensor data on screen
...
}
With this the sensor should be able to connect to the network, send its sensor data to the sink and use the LCD screen to provide basic information of its state and the sensor information. To try it however, we will need the sink to work as well.
Callbacks and Events – Sink
The sink is the coordinator in the network. Its responsibilities are to create the network and receive the data from the nodes – the sensors in our case – and print it on its screen. Three Callbacks will be needed to create this simple functionality in the application. MainInitCallback and StackStatusCallback for inits and creating the network – just as we used these for the sensor – and additionally, the IncomingMessageCallback.
In the source file you can see an additional function implemented here, the ChildJoinCallback. It is used for diagnostic purposes only, therefore not mentioned here.With the help of an auxiliary function called printChildTable, upon every new node detect and connection, some information and the full child table – the list of devices connected to the coordinator – will be printed using this Callback and function to the USART console.
MainInitCallback
The main initialization Callback is very similar to the one that we saw during discussing the sensor application. First, we need to set the security key, then initialize the network with the appropriate parameters. As a last step, we call the FormNetwork() function to start the network forming. In this Callback the display and graphics initializations are also done but removed from this code snippet for better readability.
void emberAfMainInitCallback(void) {
//Start with one time configurations
emberSetSecurityKey(&securityKey);
//try to restore the networks state from NVM
EmberStatus initStatus = emberNetworkInit();
if (initStatus != EMBER_SUCCESS) {
EmberNetworkParameters parameters;
parameters.radioTxPower = MAX_TX_POWER;
parameters.radioChannel = RADIO_CHANNEL;
parameters.panId = PAN_ID;
emberFormNetwork(¶meters);
}
// ... display-specific functions were cut out
}
StackStatusCallback
After the initializations ran, we should have our network up. This status change will invoke the StackStatusCallback function. In this function all we need to do is to permit joining to the network. Anything else except this below is for diagnostic purposes only, as you can see we also print out the child table to the console – as it is stored in the non volatile memory, from earlier it actually can contain devices.
void emberAfStackStatusCallback(EmberStatus status) {
if (status == EMBER_NETWORK_UP) {
//network is up, enable joining indefinitely
emberPermitJoining(0xff);
emberAfAppPrintln("Network is up");
printChildTable();
}
}
IncomingMessageCallback
Any time a message was received, this Callback will be invoked. The passed parameter will contain all the information of the message we need, like where it came from, RSSI and the data of course. Using all this information, we process and print the sensor data on the console, then on the LCD screen.
Writing these three Callbacks should provide the minimal functionality for the sink to be operating. As you could see, this time we didn’t use any Events, relying on the Callbacks alone could resolve the main functionality of the coordinator.
With the project set up and callbacks written, we can test our Connect network. After flashing, the network should set itself up, however it might need some time to do so. If connection doesn't want to happen, try resetting the sensors and the sink as well.
Conclusion
The intent of this article was to show through a basic application the initial steps of development in Connect. We could see how easy and automatic it is to form a network from any number of nodes, then transmit messages between them. Using the Callbacks and Events it was demonstrated how to schedule tasks and transfer useful data over the air. We made the application independent from the console and the command line interface by automating the network forming and utilizing the display modules on the wireless starter kits under Connect.
The Connect stack support MAC mode. Although the Connect stack implements a full-featured network layer, including routing, supporting star and extended star topologies, network formation functionality (association, centralized address allocation), it supports MAC mode as well. In MAC mode, nodes can only communicate directly to each other and no routing is available.
The 802.15.4 MAC layer is used for basic message handling and congestion control and provides security functionality (scanning, authentication, encryption and replay attack protection). Packet size is up to a maximum of 127 bytes at the PHY layer. The MAC layer payload can vary depending on the security options and addressing type as illustrated in the figure below.
Simlpicity Studio provides example project for testing MAC mode. A MAC-device is able to send and receive standard 802.15.4 messages from other 802.15.4 devices in range. Such a device does not relay messages.
Open the following example from Simplicity Studio:
The AppBuilder automatically opens and and make it possible to change the configuration. The default settings are suitable to test MAC mode. Click on 'Generate', compile the project and run on two devices.
Open a terminal (ex.: TeraTerm) an check if the firmware works. After boot the following screen should be appear:
In case of MAC mode there are two modes to setup the devices:
directly commission the network parameters to the devices
setup a coordinator by commission network parameters and allow other devices to join
Before setup the devices it is a good practice to issue the leave command to clear network parameters.
To apply network parameters issue the commission command:
Directly commissioned network parameters to all devices
commissioned 6 0x0001 0xabcd 0 0 where the parameters in order are:
Node type: 6 (6: MAC mode device, 7: MAC mode sleepy device)
Node ID: 0x0001 (must be unique in one PAN)
PAN ID: 0xabcd
TX power: 0
Channel: 0
Issue the command above with different (ex: 0x0002) Node ID on the other device. To check the parameters issue the info that will list the following parameters:
At this point the devices can send messages to each other using the send command. This command has multiple forms based on the addressing modes on source and destination side The following example uses long address for both side:
The addressing format can be mixed, using long address for one of the nodes and short for the other. It is also possible to send message between different PANs (inter-PAN messages).
Setup a coordinator by commission network parameters and allow other devices to join
In this case one node must be coordinator and its network parameters have to commissioned just like in the case above:
commission 6 0x0000 0xf000 0 0
The other devices have to join to the network, however by default joining is not enabled issue the following command on the coordinator to permit nodes to join:
pjoin 255
The parameter is the time in second during the joining is permitted. There are two special values:
0: joining is not permitted
255 joining is permitted without timeout
On the end-devices issue the join command:
join 6 0xabcd 0 0
The parameters of the join command are:
Node type: 6 (6: MAC mode device, 7: MAC mode sleepy device)
PAN ID: 0xabcd
TX power: 0
Channel: 0
The coordinator assigns random short address to the devices.
Sending messages works the same as in direct commissioned mode.
Join modes
Joining nodes can be MAC-level devices or Sleepy devices. Sleepy devices must poll the parent node for any messages waiting for them to be received. For sleepy nodes, the parent node should have the Parent Support plugin enabled.
Proprietary Knowledge Base
GLIB monochrome pixmap format
This article describes the format of monochrome (1-bit, black and white) pixmap that can be used with various LCDs including the one attached to BRD4001A WSTK.
In case of monochrome images one pixel need one bit to store thus on one byte 8 pixels can be stored. Due to the limited flash memory of the MCUs it is straightforward to optimize memory occupation of a picture and minimize the wasted space. To achieve this, GLIB packs the bits of an image to eliminate unused memory spots.
The following example will use a 9 column wide and 9 row tall picture to show how the pixel data transformed to GLIB pixmap format. The picture we will transform to GLIB pixmap is a smiley:
In a GLIB pixmap the pixels are black and white however, the image can be normal or inverted so it may better to refer the pixel's state as "on" and "off" where "off" means the pixel matches with the background color while "on" means pixel has foreground color. In the picture above column 0 / row 0 spot is "off" and column 2 / row 0 is "on". Bit value "0" corresponds to "off" state, "1" represents "on".
The (0;0) coordinate of the pixmap is in the upper-left corner. The example image is 9 bit wide so, a row obviously does not fit into 1 byte (8 bits) thus the last pixel of the first row is pushed to the next byte. The pixels of the next rows are shifted to fill the space in the consecutive bytes as the figure below shows.
This way the 9-bit row patterns are arranged into 8-bit bytes. The whole image needs 81 bits but this does not exactly fit into integer number of bytes hence, there will be unused bits in the last byte (seven unused bits marked with "X" in the left side figure) - their values are irrelevant.
Pixel (0;0) of the picture is bit 0 of byte 0 of the image data, (1;0) is byte 0 / bit 1, (9;0) is byte 1 / bit 0, (0;1) is byte 1 / bit 1, and so on. Based on this the data bytes are:
0b01111100
->0x7c
0b00000100
->0x04
0b00000101
->0x05
...
The whole data array should look like this:
To use the created pixmap issue the GLIB
GLIB_drawBitmap()
API call. Further reading on using GLIB can be found in Displaying on the LCD screen using glib.Article Creating monochrome bitmap files for LCD / GLIB using GIMP shows how to create monochrome GLIB pixmaps with GIMP using the plug-in developed for that purpose.
Displaying on the LCD screen using glib
Introduction
GLIB is a graphics library that can be used to draw pixels, common shapes, text or bitmaps to a display connected to an MCU. GLIB treats the display as a matrix of pixels which is a model of the physical display. This matrix is exposed to GLIB via an API called DMD (Dot Matrix Display). To draw something on a physical display, the user application needs to provide GLIB with a single implementation of the DMD interface. Sample DMD implementations are provided for the displays that are connected to the Silicon Labs Starter Kits and the Silicon Labs Development Kits.
References
Detailed description is available in the Software documentation of your device. See the same for the Giant Gecko here for your reference.
For an extensive feature showcase, please see MCU example “SLSTKxxxxx_glib” for your MCU development kit in Simplicity Studio.
Initialization
First, glib needs several libraries to be included to work, such as
display.h
for display specific settings,dmd.h
for the Dot Matrix Display library andglib.h
for the graphics library itself. The graphics library uses a context for each display. This context must be defined before using glib withAll the previously listed libraries must be initialized to enable drawing on the screen, by calling the followings:
Drawing on the screen
Drawing usually consist of three steps. First, if we do not want to overlap the previously displayed content and/or are not partially rewriting the display, we should clear it first by calling
GLIB_clear(&glibContext)
. The next step is to set the display content in the context by drawing something on the screen withGLIB_drawXXXX()
where XXXX can stand for String, Bitmap, Circle, etc. After the content is set, the last step is to update the display using the DMD driver usingDMD_updateDisplay()
.In the above example a welcome text is written on the screen. First, we set the back- and foreground colors in the
glibContext
to white and back respectively. Then, as mentioned before, a screen clear is performed. Before calling theGLIB_drawString()
function, the font is set to normal in the context and the string is put together into one character array. TheGLIB_drawString()
is called with the context, string and its length, along with the X and Y coordinate of the text and with the opaque setting last. The example displays the following on a Giant Gecko STK board:Updating parts of the screen
When not all the information is outdated on the screen, partial update can be used. In this case do not clear the screen, as then all screen content will be lost.
GLIB_clearRegion
should be used instead, after setting the clipping region with theGLIB_setClippingRegion()
function. This function needs a rectangle as an input, this will be the area theGLIB_clearRegion
will clear out. After the clear is complete, you can draw in this territory by providing the appropriate X and Y coordinates to the draw function.The above example shows a way to display incoming messages alternately in the upper and the lower part of the screen. A previously defined upper and lower area clipping region is cleared and the upper or lower message is displayed using the predefined screen borders and the available font height and line spacing in the glib context.
Simple wireless sensor application in Connect with LCD support
Introduction
In this article we will learn about the Connect API and create a simple sensor-sink network. The purpose is to understand the basic functionalities of Connect through a simple RF sensor application and to integrate LCD screen printing into Connect, which is not presented yet in any example.
Specification
To create a somewhat real network, three wireless starter kits should be used – one sink and two sensors – however, it will work with two WSTKs as well. We will use Connect to create the network with encryption enabled, using a 2.4 GHz 802.15.4 radio profile.
The sensors will be sleepy-end devices (configurable), running one “task” (called Event in Connect) after the network is up, which sends out the sensor information to the sink every second. The sensor should print the sent values and basic information on its screen.
The sink upon receiving the packets should update its screen accordingly, while also should show basics network status info.
Quick introduction to Connect
Connect is a network stack on RAIL's 15.4 specific API. It does not allow accessing lower layers, every message should go through the upmost network layer, which is the Connect messaging API. It is a production-quality wireless networking stack and development environment for broad based proprietary applications, full-featured, easily customizable wireless connectivity solution for the Sub-GHz and 2.4 GHz proprietary market. It is based on 802.15.4 like MAC. By taking care of the low-level details of network formation and radio configuration, the customer can focus on application development.
The main functionality of a Connect application is written in the Callbacks source file. There are several built-in events – like message received, child has joined etc. – named “Callbacks”, and optionally create-able, user customizable Events. The Callback functions will run when their tied trigger occur (e.g. a message was received), and the so called “Event handlers” will run whenever we start or schedule their Events. These Events can be considered as tasks in an embedded operating system. They are round-robin scheduled without pre-emption, therefore their handler functions will run until not stopped (or interrupted).
In this stack the main.c source file is part of the SDK, it should not be edited, user defined code should only go into the callbacks.c file. In Connect, setting up a network is rather easy, it is done by writing a few Callbacks. We must initialize the network and set the security keys in the MainInitCallback function – which will run only once upon init – then using an appropriate Callback let children join or start the joining process (depends on the node type). With this the network should be up, from this point we can modify the application behavior with the rest of the Callback functions or Event handlers.
Setting up the projects
There are several examples available for Connect. Our application will be very similar to the sensor-sink examples, but much simpler. Let’s start and build the application therefore from a blank flex project. The AppBuilder will guide us through the project setup and will be able to generate the application frame, even if we start from scratch. Our job will be to find a simple way to configure Connect to achieve the basic functionality, and to – fulfilling our specifications – include the graphics handling.
Sensor project
Create a new blank application for the Flex SDK. After the project was created, the AppBuilder should come up with the General tab open. Make sure the architecture listed here is correct, especially if the toolchain is set. As you can see on the picture below, we are using a Mighty Gecko board, SDK version 2.4.0.0 and the GNU ARM compiler for this example.
Under the “Radio Configuration” tab select the Connect 2.4 GHz 802154 radio profile. You can also select any of the profiles compatible with your board, however in this example we are using the 802154 one.
Under the “Printing” tab disable the Command line interface. We won't use the CLI, we would like to rely on the LCD screen of the WSTKs and automatic network buildup in this example.
On the “Plugins” tab, make sure to set the tick next to the following options only:
If you click on the Application Graphics plugin, the Hardware Configurator Dependent Module Options will show on the right. It's important to turn off setting "Use EXTCOMIN pin for polarity inversion" here, as this option was reported to cause issues with Connect. We will take care of this polarity inversion from our code instead.
The next step is to configure the Callbacks under the Callbacks tab. As mentioned before, these are the built-in events for which we can determine a functionality through a Callback function. If we do not want to use a specific Callback, here we can disable them. From the user configurable Callbacks, we will only need the MainInitCallback and the StackStatusCallback callbacks for the sensor application. Turn them on by checking the box next to them under the "Is used?" column.
To finish the AppBuilder configuration, go to the “Other” tab. Under this tab you can configure additional macros and includes, but most importantly for a Connect application, here you can configure the Events, the user-defined “tasks”. Add a new Event in the "Event Configuration" and edit its command, by clicking on the automatically generated name, to “reportControl” and its callback function to “reportHandler”. This Event will be responsible for the sensor data reporting. Add another one with “connectControl” and “connectCallback” command and callback names, respectively. This Event will be responsible for reconnecting whenever the network connection is lost.
Save the *.isc file and generate the project.
Unfortunately the WSTK Sensors plugin does not enable the Sensor in the hardware configurator, therefore it has to be done manually. Open up the *.hwconf file in the project and under the DefaultMode Peripherals make sure to enable the I2C Sensor.
Sink project
Create another blank project for the Sink. Make sure to set up everything the same as you did for the sensor on the “Radio Configuration”, “Printing” and “Plugins” tabs. One exception is under the “Plugins” tab: “WSTK Sensors” under “Connect I/O” should not be enabled, as for the Sink we won’t need the sensors on the WSTK.
Under the Callbacks tab we will need the following to be enabled: MainInitCallback for the initializations, StackStatusCallback and ChildJoinCallback for creating the network, and IncomingMessageCallback so we can process and show the data on the display.
As no Event will be used for the sink application – we can handle everything from the Callbacks – we've got no configuration to change on the "Other" tab.
Save the *.isc file and generate the project.
Callbacks and Events – Sensor
It’s time to write the main functionality of the sensor application – in the callbacks source file as mentioned before.
For the sensor we turned two callbacks on, let’s start with one of those.
MainInitCallback
In the MainInitCallback function we first set the security key, then try to restore the network state from the Non-Volatile Memory. As the child table is not deleted after a device reset, this can be a quick way to initialize the network. If the device was just set up or the table is not available, we start our connectControl Event to initialize the network connections from scratch. In the end of this Callback, the graphics is initialized, as all kind of one-time-run initialization like this should be put in this Callback.
connectHandler
Since it was just mentioned it, let's continue with the connectControl Event's Handler, the connectHandler.
This Event puts the “disconnected” message on the screen, as it only runs when there’s no network connection. After setting necessary radio parameters it tries to connect to the network – as a sleepy end device or a regular device, based on the configuration. As a last step, we set this Event inactive; if we didn’t do this, the Event would run endlessly.
StackStatusCallback
The StackStatusCallback function will run whenever the stack status changes. This can mean a good thing, like the network is up, or something worse, like it got disconnected, or the join failed, etc. Based on the status it carries, we will start the reportControl Event – sending out the sensor data – or schedule the connectControl Event with a retry interval to try connecting again.
reportHandler
If the network is up, the reportHandler function will run immediately. The Handler consists of four main steps: first, we need to get the sensor data through calling PluginWstkSensorsGetSample(). With emberMessageSend() we can send the data. Using the sink’s node ID, it will be sent to the right node. We wouldn’t want this function to run as fast it can, but every 1000 ms, so we schedule the Event with the report interval. As a last step, the LCD screen is refreshed with the help of the graphics library.
With this the sensor should be able to connect to the network, send its sensor data to the sink and use the LCD screen to provide basic information of its state and the sensor information. To try it however, we will need the sink to work as well.
Callbacks and Events – Sink
The sink is the coordinator in the network. Its responsibilities are to create the network and receive the data from the nodes – the sensors in our case – and print it on its screen. Three Callbacks will be needed to create this simple functionality in the application. MainInitCallback and StackStatusCallback for inits and creating the network – just as we used these for the sensor – and additionally, the IncomingMessageCallback.
MainInitCallback
The main initialization Callback is very similar to the one that we saw during discussing the sensor application. First, we need to set the security key, then initialize the network with the appropriate parameters. As a last step, we call the FormNetwork() function to start the network forming. In this Callback the display and graphics initializations are also done but removed from this code snippet for better readability.
StackStatusCallback
After the initializations ran, we should have our network up. This status change will invoke the StackStatusCallback function. In this function all we need to do is to permit joining to the network. Anything else except this below is for diagnostic purposes only, as you can see we also print out the child table to the console – as it is stored in the non volatile memory, from earlier it actually can contain devices.
IncomingMessageCallback
Any time a message was received, this Callback will be invoked. The passed parameter will contain all the information of the message we need, like where it came from, RSSI and the data of course. Using all this information, we process and print the sensor data on the console, then on the LCD screen.
Writing these three Callbacks should provide the minimal functionality for the sink to be operating. As you could see, this time we didn’t use any Events, relying on the Callbacks alone could resolve the main functionality of the coordinator.
With the project set up and callbacks written, we can test our Connect network. After flashing, the network should set itself up, however it might need some time to do so. If connection doesn't want to happen, try resetting the sensors and the sink as well.
Conclusion
The intent of this article was to show through a basic application the initial steps of development in Connect. We could see how easy and automatic it is to form a network from any number of nodes, then transmit messages between them. Using the Callbacks and Events it was demonstrated how to schedule tasks and transfer useful data over the air. We made the application independent from the console and the command line interface by automating the network forming and utilizing the display modules on the wireless starter kits under Connect.
如何在EFR32 sub_GHz参考匹配电路中插入SAW滤波器
EFR32无线Gecko系列芯片在sub-GHz频段内,现有的参考匹配电路使用所谓的直连拓扑结构,即收发通路不使用外部RF开关而直接连在一起。为了能在这种匹配结构中插入SAW滤波器,建议分开接收和发射通路,不建议在发射通路中引入SAW滤波器,因为:
- SAW滤波器有相当大的插损,会降低预期的功率效率,
- SAW滤波器通常为低功率设计的,这也将导致发射功率受限,
- SAW滤波器特别在射频谐波的高频段衰减比较小,因此始终推荐使用分立器件的LPF。
EFR32带SAW滤波器的推荐原理图结构如下:
- 发射通路的匹配可与现有的参考匹配电路一致,建议使用AN923里的元器件值或参考已有的参考设计。
- LPF阶数可以根据发射功率电平,谐波抑制要求来改变。
- 接收通路的匹配使用类似于AN643里面所说的标准4元件巴伦匹配网络。下表中列出了仿真的元器件值。
- RF开关可以用于分离连接到同一根天线的发射端和接收端的匹配电路。
- SAW滤波器的单独匹配网络 (LW1, LW2, CW1, CW2, CW3 and CW4)可能不需要,请参考SAW滤波器的规格书来决定。
英文原文: https://www.silabs.com/community/wireless/proprietary/knowledge-base.entry.html/2018/06/05/how_to_insert_a_saw-aBOx
基于connect的OTA升级
弃用注
这个KBA 的内容 UG235.06 已含盖.
前言
Connect提供两种OTA升级方式:
两种方式都依赖于single image模式的Gecko bootloader(内部或外部flash都一样).OTA通信本身是用开源插件实现的.这些插件是构建在connect stack之上, 是应用无关的.
应用项目配置
为生成一个能OTA升级的应用,首先要在appbuilder的HAL栏使能"Application" bootloader,然后使能下列插件:
广播升级方式:
单播升级方式:
大部分插件都有可配置选项,细节不需要都弄明白,但最好都看过一遍.
Bootloader工程配置
为了升级,很明显需要一个bootloader. Connect现在仅支持单个slot的bootloader, 内部或外部flash都一样. 我们大部分开发板的射频板上都有外部flash.
关于Gecko bootloader详细介绍,请参考Gecko Bootloader User Guide.
根据硬件资源, 创建下列任一Gecko Bootloader工程:
例程不支持内部存储空间小于或等于256KB的设备.当然, 这个空间属性是可配置的, 只是要保证存储空间的一半SIZE可以装载应用程序(这里的一半指的是除去用做NVM/simEEPROM后的一半).
创建升级示例工程
我们将通过sensor-sink工程演示Connect的bootloading. sink将是OTA的服务端, sensor将是OTA客户端. 本文档在上下文是connect网络中用sink/sensor相称, 在上下文是OTA过程中则称为服务端/客户端.
这个升级应该在所有支持connect的Silicon Labs的开发板都可以操作,这里推荐使用xG12/xG13 SOC 开发板,因为它们都含有内部和外部flash.如果用2.4GHz, 请确认在radio configurator里选2.4GHz, 如果用subGHz, 别忘了加天线.
先建bootloader,一般在bootloader工程什么都不用改.
再建sink工程,使能application bootloader在HAL栏,还有使能下列插件:
从插件可以看出,这就是我们OTA的服务端.
推荐在OTA Bootloader Test Common plugin中禁用"Test EBL file inclusion".
创建两个sensor工程,一个通过通用J-Link烧录,另一个我们会用做OTA升级.请确保两个工程有差别, 例如将其中一个的心跳LED闪烁周期改为12 (3秒).
关于bootloading部分, 要在HAL栏使能application bootloader, 并使能以下插件:
这将是OTA客户端,这里仍然使能了服务端插件,这是因为两个插件有共用的配置(也就是说网络中用的endpoint), 这个是定义在服务端插件的. 如果存储空间紧张,可以将这些配置保存在别的地方,即可去除服务端插件了.
生成然后编译这四个工程(bootloader, sink, 两个sensor).
烧录应用程序
为保证应用程序正常工作,先要烧录bootloader.最简单的方法就是用Simplicity commander来烧录. 确保用 something-combined.s37 这个文件,这才是完整的bootloader: Gecko bootloader分为两部分,有个很小的first stage bootloader,可以用来升级main bootloader(在这里不做演示).只有"combined.s37"文件包含first stage bootloader,其他的只有main bootloader,所以无法正常工作.
接着烧录sink还有其中一个sensor应用程序.你可以用任一种方法, 要注意不用使用bin文件, 它没有带地址信息, 烧写这个可能会覆盖bootloader (看你用的是哪一代的产品). 同样要注意不可将flash全擦除.
如果bootloader跟应用程序都准备好了,接下来应该检查板子是否正常工作(心跳LED闪烁,CLI可用).
烧录用于OTA传送的文件
要烧录另一个sensor的GBL文档到sink/OTA服务端bootloader的slot空间:
Connect不能通过串行通讯接收GBL文档, 但我们可以用simplicity commander对内部外部flash烧录, 其中外部flash烧录只适用于Silicon Labs的开发板.
烧录到内部flash
为了写内部flash,首先要知道slot的起始地址.你可以从bootloader工程isc文档中的storage栏获取. 比如我这边看到的是540672. Simplicity commander能识别GBL文档, 我们不期望从GBL中解析地址信息(因为它应该烧到地址0): 我们要把它当做二进制文件来处理, 只要改其后缀为.bin就可以了. 然后我们可以通过GUI写入(要设置起始地址),或从命令行写,如下:
84000是540642的16进制的值, 而sensor-unicast-b.bin是GBL文档改名得来的.
烧录到外部flash
这种方法只能工作在Silicon Labs的开发板, 并只通过CLI来实现:
这个指令会锁住MCU,所以完成后要reset板子.
升级准备
下一步是通过CLI指令设置设备.
存储Flash准备
Sink/OTA服务端
第二条指令检验映像文件,有如下提示(实际会有很多个点):
Sensor/OTA客户端
第二条指令是擦除flash,写之前要求擦除.有如下提示(实际会多很多个点):
准备网络
用通常用的建网加网指令.
Sink/OTA服务端
Sensor/OTA客户端
我一般还用下面指令减少信息输出量:
如果一切顺利你应该可以看到sink与sensor通讯数据每10秒输出一次.
我们还需要给sensor设一个标签,因为OTA升级数据包是广播出来的,加标签后,接收到不一样标签的数据包可以舍去.
下一步我们还需要sensor的nodeid,可以通过”info”指令获取:
单播升级
接下来单播升级和广播升级有点区别.
OTA服务端用下列指令:
这指令将OTA数据包传送目的设成了0x0001,就是上一步获取的nodeid.
用下一条指令就可以开始传送了:
115800是GBL映像的大小,0xaa是OTA客户端所设的标签.
这就开始升级了.在OTA服务端你应该可以看到多行” get segment” (从flash中读的segment), 应该以” image distribution completed, 0x00” 结束. 在OTA客户端可以看到”incoming segment”,应该以”Image download COMPLETED tag=0xAA size=115800”结束.
到这里,客户端输入”bootloader validate-image”应该要返回”valid”.
接着我们输入指令要求更新:
在OTA服务端用下列指令:
1000 是超时时间, 单位为ms,0xaa是标签.
广播升级
在OTA服务端用下列指令:
虽是广播升级,server还是需要知道客户端的nodeid. 因为映像广播完后, 有客户端可能会丢失某些数据包. 所以当服务端完成传送,会询问所有的客户端丢失什么包然后重发.最大的客户端数是50.
上述指令是设nodeid 0x0001的索引号为0.
用下一条指令就可以开始传送了:
118784是GBL映像的大小,0xaa是OTA客户端所设的标签,1是通过” set-target” 设的客户号.
这就开始升级了.在OTA服务端你应该可以看到多行” get segment ”(从flash中读的segment), 应该以” image distribution completed,0x00” 结束. 在OTA客户端可以看到多行”incoming segment”,应该以”Image download COMPLETED tag=0xAA size=118784”结束.
到这里,客户端输入”bootloader validate-image”应该要返回”valid”.
接着我们输入指令要求更新:
在OTA服务端用下列指令:
1000是超时时间, 单位为ms, 后两个参数与上述的” distribute” 相同.
升级完成
升级指令很快能收到并有” bootload request completed ”显示,接着OTA客户端开始升级.升级完后, sensor会重新加入网络, 因为网络信息是存在NVM里的.
英文原版
https://www.silabs.com/community/wireless/proprietary/knowledge-base.entry.html/2018/04/26/ota_bootloading_with-ql8G
Connect MAC-device
The Connect stack support MAC mode. Although the Connect stack implements a full-featured network layer, including routing, supporting star and extended star topologies, network formation functionality (association, centralized address allocation), it supports MAC mode as well. In MAC mode, nodes can only communicate directly to each other and no routing is available.
The 802.15.4 MAC layer is used for basic message handling and congestion control and provides security functionality (scanning, authentication, encryption and replay attack protection). Packet size is up to a maximum of 127 bytes at the PHY layer. The MAC layer payload can vary depending on the security options and addressing type as illustrated in the figure below.
Simlpicity Studio provides example project for testing MAC mode. A MAC-device is able to send and receive standard 802.15.4 messages from other 802.15.4 devices in range. Such a device does not relay messages.
Open the following example from Simplicity Studio:
The AppBuilder automatically opens and and make it possible to change the configuration. The default settings are suitable to test MAC mode. Click on 'Generate', compile the project and run on two devices.
Open a terminal (ex.: TeraTerm) an check if the firmware works. After boot the following screen should be appear:
In case of MAC mode there are two modes to setup the devices:
Before setup the devices it is a good practice to issue the
leave
command to clear network parameters.To apply network parameters issue the
commission
command:Directly commissioned network parameters to all devices
commissioned 6 0x0001 0xabcd 0 0
where the parameters in order are:Issue the command above with different (ex: 0x0002) Node ID on the other device. To check the parameters issue the
info
that will list the following parameters:At this point the devices can send messages to each other using the
send
command. This command has multiple forms based on the addressing modes on source and destination side The following example uses long address for both side:send 0x0033 0xffff {99 7c a2 fe ff 57 0b 00} 0xffff {6f 7c a2 fe ff 57 0b 00} 0xffff 0xffff {01234567}
On the transmitter side the following output should appear:
While the receiver side should print info about reception:
Since the long address is unique for every chip it must be replaced by the actual value to use the example.
The parameters of thesend
command are:The "nibble mask" specifies the addressing mode:
Example usage of short address + PAN ID:
send 0x1122 0x0001 {} 0x0002 {} 0xabcd 0xabcd {01234567}
The addressing format can be mixed, using long address for one of the nodes and short for the other. It is also possible to send message between different PANs (inter-PAN messages).
Setup a coordinator by commission network parameters and allow other devices to join
In this case one node must be coordinator and its network parameters have to commissioned just like in the case above:
commission 6 0x0000 0xf000 0 0
The other devices have to join to the network, however by default joining is not enabled issue the following command on the coordinator to permit nodes to join:
pjoin 255
The parameter is the time in second during the joining is permitted. There are two special values:
On the end-devices issue the
join
command:join 6 0xabcd 0 0
The parameters of the
join
command are:The coordinator assigns random short address to the devices.
Sending messages works the same as in direct commissioned mode.
Join modes
Joining nodes can be MAC-level devices or Sleepy devices. Sleepy devices must poll the parent node for any messages waiting for them to be received. For sleepy nodes, the parent node should have the Parent Support plugin enabled.