This article is to share an idea how to debug your project with enabling an easy way to log the stack events and check the return values of BGAPIs. Checking the return value is very important while developers always don’t pay enough attention to it.
This article will use the stuff introduced in Log System, which is recommended to start from if you haven’t read it before.
Hardware –Bluetooth compatible boards - EFR32xGyy (x= M, B, y=1, 12, 13, 14) based boards
Tools – J-Link RTT viewer or serial terminal, if using serial terminal, the parameters are as below:
Baud rate: 115200
Data bits: 8
Stop bit: 1
Flow control: False
Stack Event Logging
The idea for this part is to log out all the stack events received from gecko_wait_event() or gecko_peek_event() function. Users can decide how verbose the logging will be. In the provided example, there are 3 levels defined as below.
Critical
Important
Verbose
Color
Red
Yellow
White
Priority
Highest
Medium
Lowest
Critical –Users think as the most important needed to be highlighted. E.g. connection closed event.
Important – they may carry important information that users will probably be interested in. E.g. write request to a characteristic.
Verbose – Informative, could be disabled to make the log concise. E.g. Bootloader version in the boot event.
The complete implementation is in the log.c file. It doesn’t cover all the events since I don’t aim at giving a full example but sharing an idea how to do such things. You can modify it to meet your own requirements.
The payload of an event could have multiple fields, it’s the user to decide how to log out the events. E.g. I think the boot event is very critical flag while the fields carried are not that important. Then I use the critical level to log boot event and use verbose level to log out its details. I can set the logging level to be higher than verbose to make the logging concise. See the boot and connection opened events in figure 1.
Return Values Checking
The idea for this part is to provide an easy way to check the return values of the BGAPI calls and log the error message out if any. All the BGAPIs return a pointer to a specific structure and all the structures have a variable – result to indicate if the command is called successfully or not. SE_CALL(x) is a macro definition in the log.h, it can be used to finish below purpose.
If x is a BGAPI command, it will call the command and check its return value, log the error message if the result is not success.
If x is the pointer to the specific structure returned by the BGAPIs, it will check the value and log error message if the result is not success. This should be used if there are useful information carried in the return value.
Below are the examples covering the above corresponding situations.
Set advertising timing and start advertising, x is BGAPI command here.
The complete implementation is in the log.c file. It doesn’t cover all the errors because of the reason mentioned above. Users can add the missing errors to your own project.
The last half part of figure 1 shows how the error checking works. I intentionally made these mistakes to demonstrate it. Below is a simple explanation of why the errors happen.
Command contained invalid parameter – I called the API to read RSSI to a specific connection in disconnection state, so the connection handle is supposed to be invalid.
Device is in wrong state to receive command – I started a periodical indication in connection state and didn’t disable it when connection dropped. It’s impossible to send indication in disconnection state so this error happened.
How to Add to Your Project?
Follow the steps in Log System to set up logging.
Copy the attached files(log.c and log.h) to “log_system” folder which should be created in the first step.
Call “log_events(evt);” after gecko_wait_event() or gecko_peek_event(), this enables the stack event logging feature.
Surround the BGAPI commands or the pointer returned by the BGAPIs with SE_CALL(…) to enable error checking feature.
There are many symbols in the log.h to decide if enable or disable the corresponding features, if logging and error checking are not required, users can disable all of them so that it will consumes very little memory.
This article aims at helping those developers who want to use the vendor specific models in their Bluetooth Mesh products.
What are Models?
The concept ‘Model’ is defined in the section 2.3.6 of Mesh Profile Specification 1.0. It’s good to understand the concepts client and server model, in the following example, we will use this architecture.
“Models may be defined and adopted by Bluetooth SIG and may be defined by vendors. Models defined by Bluetooth SIG are known as SIG adopted models, and models defined by vendors are known as vendor models. Models are identified by unique identifiers, which can be either 16 bits, for SIG adopted models, or 32 bits, for vendor models.”
Before reading this article, it’s recommended to read the article - Log System first. The output information will be shown from the RTT or serial terminal as they were introduced in that article.
The example supports both being provisioned by a standard provisioner, or being provisioned by the node itself by the test class command. The symbol PROV_LOCALLY in the file my_model_def.h decides which way the node uses. If it is defined, the nodes will provisioned themselves at boot time when they are unprovisioned, and the server node will publish to the group address the client node subscribes from and subscribe from the group address that the client node publishes to, the same for client node. If the symbol is not defined, they will send out the unprovisioned device beacon after boot when they are unprovisioned, waiting for a provisioner to provision and configure them.
Server and Client Model
Before introducing the server and client model, there are 3 concepts I want to introduce firstly. See figure 1, figure 2 and figure 3. This are the standard behaviors that the client and server models follow, which means all the opcodes defined later with the name “xxx Get/Set/Set unacknowledged” will use the “Reliable Get/Reliable Set/Unreliable Set”.
Figure 1. Reliable Get
Figure 2. Reliable Set
Figure 3. Unreliable Set
To be simple, there is only one group created and the provisioner configures both the client and server model to publish and subscribe to this group. Below are the definitions of the Client and server models
Vendor ID - 0x1111, a 16-bit Company Identifier, use this value only for demo purpose.
Server Model ID – 0x1111
Client Model ID – 0x2222
States
Temperature – 4 bytes (uint32) to store the temperature value with the unit milliCelsius or milliFahrenheit.
Unit
0x1 – Celsius
0x2 – Fahrenheit
Update period – The period that the vendor server updates the temperature to the client periodically. The format of this value aligns to the section 4.2.2.2 of Mesh Profile Specification 1.0.
Opcodes
0x1 – Temperature Get – it’s an acknowledged message used to get the Temperature Status of the server side.
0x2 – Temperature Status – It can be the acknowledge to Temperature Get or an update to publishing group
0x3 – Unit Get – it’s an acknowledged message used to get the Unit Status of the server side.
0x4 – Unit Set – it’s an acknowledged message used to set the Unit Status of the server side.
0x5 – Unit Set Unacknowledged – it’s an unacknowledged message used to set the Unit Status of the server side.
0x6 – Unit Status – It can be the acknowledge to Unit Get or an update to publishing group
0x7 – Update Period Get – It’s an acknowledged message used to get the Update Period Status of the server side.
0x8 – Update Period Set – It’s an acknowledged message used to set the Update Period Status of the server side.
0x9 – Update Period Set Unacknowledged – It’s an unacknowledged message used to set the Update Period Status of the server side.
0xA – Update Period Status - It can be the acknowledge to Update Period Get or an update to publishing group
Operations
For both sides – Keep PB1 pressing while boot will trigger a factory reset, which will erase all the stored data including the network key, application key, publication address, subscription addresses etc. and result in forcing the device to unprovisioned state.
Client Model
Short Press PB0 – send the Temperature Get to publishing group
Long Press PB0 – send “Update Period Set Unacknowledge” with sequent parameters as below
300ms
Off
2s
Off
10s
Off
2min
Off
10min
Off
Short Press PB1 – send the Unit Get to publishing group
Long Press PB1 – send "Unit Set(Celsius)" and "Unit Set Unacknowledged(Fahrenheit)" back and forth.
Server Model
Press PB0 – Measure the current temperature and send Temperature Status to publishing group
Press PB1 – Send the Unit Status to the publishing group
Running the example
This example is originally designed to use SLWRB4104A as the mesh nodes and SLWRB4103A as the provisioner. If you don’t have them, you probably need to migrate the existing projects to fit your boards. Below steps assume that you have the desired boards, and the migration effort is not covered. Follow below steps to run this example.
Download the attachment and extract it. The provisioner project is available from https://github.com/KevinFuSilabs/vendor_model_provisioner, Import the provisioner project to your Simplicity Studio. Step 2, 6 shall be passed if using the self-provision method.
Program the provisioner project to a SLWRB4103A board, optionally, you can develop your own provisioner.
Create 2 projects in Simplicity Studio 4 named vmec (Vendor model example client) and vmes (Vendor model example server).
Copy the (ROLE)_app.c, (ROLE)_app.h, main.c, my_model_def.h and self_test.h to the corresponding project. Open the $(PROJECT_NAME).isc file and click the "+" on the right of Vendor models label to add a vendor model to the project, set the ID of server model to 0x11111111, and the client model to 0x22221111, then click generate button on the top right corner. Build and program to at least one client and one server boards.
Open corresponding number of RTT terminals or the serial terminals and connect them to your nodes. And Open a serial terminal connected to the provisioner.
The nodes should have started sending unprovisioned beacons, and you should get message like figure 4 shows in the provisioner serial terminal. Press PB1 to start provisioning and configuring if this is your device waiting to be provisioned and configured, whereas, PB0 to ignore.
Figure 4. Provisioner Serial Terminal
Make sure that all the nodes are properly provisioned and configured. If there is no problem with the provisioning and configuring, you should get the “configuration complete” message from the serial terminal, and it starts to scan for other unprovisioned beacons.
Press the keys to verify the processes. If everything goes well, you should be able to see the logging from RTT like below. Figure 5 and figure 6 are a pair of nodes in the same network, and they are the logging for the actions – Pressing PB1 on client -> PB0 on client -> PB1 on server -> PB0 on server.
Figure 5. Client RTT Terminal
Figure 6. Server RTT terminal
Words in the end
This article doesn't aim at introducing the soc provisioner, the reason why we use it is because the Bluetooth Mesh app doesn't support configuring the vendor models yet. Some codes in the provisioner project are hardcoded to fix value so it can't be used as a generic provisioner. I know my colleague has an updated version covering both the SIG and vendor models, please navigate to above link for more information.
This example implements the periodical status update feature in the application layer. This can also be implemented using the model publication state in configuration server, section 4.2.2 of Mesh Profile Specification 1.0. I prefer to implement it in the application layer since only the nodes implementing the configuration client can modify the period value in publication value if using the configuration server, however, the client nodes probably don't.
This article assumes that you are familiar with the Gecko Bootloader, and it explains how you can add metadata to your upgrade image files. Please note, that metadata parsing works with Gecko Bootloader v1.6 or later, so you may have to upgrade your bootloader first.
What is metadata?
Metadata can be anything that can be put into a byte array. You can add text, structures or binary data to your GBL files. Hence, when you update your device with a new firmware, you can also update other settings, e.g. a parameter set, that is stored in the persistent storage. Or you can send extended version information along with the firmware. Or you can send new security keys in a signed and encrypted GBL file. Or you can even send an extra firmware image as metadata intended to be sent to another device.
How to add metadata to the GBL file?
Metadata can be added when you create the GBL file with the „commander gbl create” command. Just add the --metadata switch, and the name of the file, that contains the metadata, e.g.
Note that the application uploader can be used only in a device with an internal flash larger than 256k, or with a device that has an external flash.
Reading metadata on-the-fly
When you upload a new firmware image, you will receive the image in chunks. This chunks are received by the application, and they can be written into any storage slot using the API of the Gecko Bootloader. At the same time, when you get a chunk of the uploaded image, you can run the GBL parser on it, using the API of the Gecko Bootloader. When the parser finds metadata in the GBL file, it will call a callback function and pass the metadata to this function. It is good to know, that
The metadata is sent in chunks to the callback function, so it may be called several times with small portions of the whole metadata
If the GBL file is encrypted, the parser will decrypt it for you, and the callback function gets the decrypted metadata
To initialize the GBL parser use the following code:
And finally define the callback function like this:
void metadataCallback(uint32_t address, uint8_t *data, size_t length, void *context)
{
//process the received chunk of metadata (data,length)
}
Reading metadata from a stored GBL file
A much easier solution – compared to on-the-fly parsing – is to retrieve the metadata from an already stored GBL file. To do so, first upload the GBL file – without decoding it – into a storage slot, defined by the bootloader. To retrieve the metadata from a GBL file stored in a storage slot, simply call
bootloader_verifyImage(slotID,metadataCallback);
and define the callback function like this:
void metadataCallback(uint32_t address, uint8_t *data, size_t length, void *context)
{
//process the received chunk of metadata (data,length)
}
It’s important to note, that although the whole GBL file is available in the storage slot, you will still get the metadata in chunks, because the parser has limited buffer. The maximum size of a chunk is 64bytes.
Implementing the callback function
The metadata callback function is called several times, and provides the metadata in chunks. The callback function receives 4 parameters:
Address: this is the offset address of the given chunk within the whole metadata
Data: the pointer to the chunk of data
Length: the length of the chunk. This varies between 1 and 64.
Context: the parser context. Usually this can be ignored.
To assembly the metadata from the chunks you can use the following code snippet:
ON_THE_FLY_PARSING: in this case the metadata callback function will be called during OTA image upload
AFTER_UPLOAD_PARSING: in this case the metadata callback will be called right after OTA image upload
STORED_GBL_PARSING: in this case you have to write the slot number into the “Read Metadata from Slot” characteristic, to initiate a metadata parsing in that storage slot
In this example the content of the metadata is copied to the Metadata Test Characteristic, so after parsing you can read this characteristic with your smartphone to check if parsing was successful.
To create a project with metadata parsing functionality follow these steps:
Create an Internal Storage Bootloader or a SPI Flash Storage Bootloader project in Simplicity Studio
Check the Storage Slot configuration in AppBuilder and modify it according to your needs.
Generate and build the project
Create a SoC – Empty project
Import the attached gatt.xml file with the Visual GATT Editor. Your GATT database should look like this:
Click Generate to build the GATT database
Copy the attached ota_dfu_multislot.c and ota_dfu_multislot.h files into the project
Copy btl_interface.c and btl_interface_storage.c from C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v2.3\platform\bootloader\api to your project
Go to Project Properties > C/C++ Build > Settings and add the following folders to the Includes:
"${StudioSdkPath}/platform/bootloader"
"${StudioSdkPath}/platform/bootloader/api"
or copy the .h files from these folders to your project
Add the followings to main.c
a) Add #include “ota_dfu_multislot.h” to the beginning of main.c
b) Add ota_dfu_handle_event(evt); after evt = gecko_wait_event();
c) Remove the code that implements resetting into DFU mode (case gecko_evt_gatt_server_user_write_request_id)
Create a new file with the content of the metadata, e.g. metadata.bin. The content can be anything, e.g. a text saying “this is my metadata”.
Copy this file into your project
Edit the create_bl_files.bat file. Add the “--metadata metadata.bin” phrase to all “commander gbl create” commands, or at least to those you know you will use.
Find the last characteristic within the penultimate service
Write 0x00 into this characteristic. This will trigger the parsing of Slot 0. Meanwhile parsing the metadata gets written into the Metadata Test Characteristic.
Find and read the characteristic within the last service. You should see your metadata written into this characteristic.
Bluetooth Knowledge Base
KBA_BT_1207: Event and Error Checking
This article is to share an idea how to debug your project with enabling an easy way to log the stack events and check the return values of BGAPIs. Checking the return value is very important while developers always don’t pay enough attention to it.
This article will use the stuff introduced in Log System, which is recommended to start from if you haven’t read it before.
Figure 1. Events and Errors Log
Environments
Stack Event Logging
The idea for this part is to log out all the stack events received from gecko_wait_event() or gecko_peek_event() function. Users can decide how verbose the logging will be. In the provided example, there are 3 levels defined as below.
Critical
Important
Verbose
Color
Red
Yellow
White
Priority
Highest
Medium
Lowest
The complete implementation is in the log.c file. It doesn’t cover all the events since I don’t aim at giving a full example but sharing an idea how to do such things. You can modify it to meet your own requirements.
The payload of an event could have multiple fields, it’s the user to decide how to log out the events. E.g. I think the boot event is very critical flag while the fields carried are not that important. Then I use the critical level to log boot event and use verbose level to log out its details. I can set the logging level to be higher than verbose to make the logging concise. See the boot and connection opened events in figure 1.
Return Values Checking
The idea for this part is to provide an easy way to check the return values of the BGAPI calls and log the error message out if any. All the BGAPIs return a pointer to a specific structure and all the structures have a variable – result to indicate if the command is called successfully or not. SE_CALL(x) is a macro definition in the log.h, it can be used to finish below purpose.
Below are the examples covering the above corresponding situations.
SE_CALL(gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable));
SE_CALL(ret);
SE_CALL(gecko_cmd_gatt_server_send_characteristic_notification(0xFF, gattdb_indicate, 1, ret->data.data));
The complete implementation is in the log.c file. It doesn’t cover all the errors because of the reason mentioned above. Users can add the missing errors to your own project.
The last half part of figure 1 shows how the error checking works. I intentionally made these mistakes to demonstrate it. Below is a simple explanation of why the errors happen.
Command contained invalid parameter – I called the API to read RSSI to a specific connection in disconnection state, so the connection handle is supposed to be invalid.
Device is in wrong state to receive command – I started a periodical indication in connection state and didn’t disable it when connection dropped. It’s impossible to send indication in disconnection state so this error happened.
How to Add to Your Project?
Words in The End
A working example is provided in Github - https://github.com/KevinFuSilabs/event_error_checking.git, which can be directly used on SLWRB4104A. It’s welcomed to contribute the missing events and errors to the repository, and I can update the attached file accordingly.
There are many symbols in the log.h to decide if enable or disable the corresponding features, if logging and error checking are not required, users can disable all of them so that it will consumes very little memory.
KBA_BT_0502: Bluetooth Mesh Vendor Specific Model Example
Introduction
This article aims at helping those developers who want to use the vendor specific models in their Bluetooth Mesh products.
What are Models?
The concept ‘Model’ is defined in the section 2.3.6 of Mesh Profile Specification 1.0. It’s good to understand the concepts client and server model, in the following example, we will use this architecture.
What are vendor models?
It’s also defined in the section 2.3.6 of Mesh Profile Specification 1.0.
Before reading this article, it’s recommended to read the article - Log System first. The output information will be shown from the RTT or serial terminal as they were introduced in that article.
Environments
Details about the example
There are 3 projects in the example.
Provisioning
The example supports both being provisioned by a standard provisioner, or being provisioned by the node itself by the test class command. The symbol PROV_LOCALLY in the file my_model_def.h decides which way the node uses. If it is defined, the nodes will provisioned themselves at boot time when they are unprovisioned, and the server node will publish to the group address the client node subscribes from and subscribe from the group address that the client node publishes to, the same for client node. If the symbol is not defined, they will send out the unprovisioned device beacon after boot when they are unprovisioned, waiting for a provisioner to provision and configure them.
Server and Client Model
Before introducing the server and client model, there are 3 concepts I want to introduce firstly. See figure 1, figure 2 and figure 3. This are the standard behaviors that the client and server models follow, which means all the opcodes defined later with the name “xxx Get/Set/Set unacknowledged” will use the “Reliable Get/Reliable Set/Unreliable Set”.
Figure 1. Reliable Get
Figure 2. Reliable Set
Figure 3. Unreliable Set
To be simple, there is only one group created and the provisioner configures both the client and server model to publish and subscribe to this group. Below are the definitions of the Client and server models
Running the example
This example is originally designed to use SLWRB4104A as the mesh nodes and SLWRB4103A as the provisioner. If you don’t have them, you probably need to migrate the existing projects to fit your boards. Below steps assume that you have the desired boards, and the migration effort is not covered. Follow below steps to run this example.
Figure 4. Provisioner Serial Terminal
Figure 5. Client RTT Terminal
Figure 6. Server RTT terminal
Words in the end
This article doesn't aim at introducing the soc provisioner, the reason why we use it is because the Bluetooth Mesh app doesn't support configuring the vendor models yet. Some codes in the provisioner project are hardcoded to fix value so it can't be used as a generic provisioner. I know my colleague has an updated version covering both the SIG and vendor models, please navigate to above link for more information.
This example implements the periodical status update feature in the application layer. This can also be implemented using the model publication state in configuration server, section 4.2.2 of Mesh Profile Specification 1.0. I prefer to implement it in the application layer since only the nodes implementing the configuration client can modify the period value in publication value if using the configuration server, however, the client nodes probably don't.
KBA_BT_0607: Adding metadata to GBL files
This article assumes that you are familiar with the Gecko Bootloader, and it explains how you can add metadata to your upgrade image files. Please note, that metadata parsing works with Gecko Bootloader v1.6 or later, so you may have to upgrade your bootloader first.
What is metadata?
Metadata can be anything that can be put into a byte array. You can add text, structures or binary data to your GBL files. Hence, when you update your device with a new firmware, you can also update other settings, e.g. a parameter set, that is stored in the persistent storage. Or you can send extended version information along with the firmware. Or you can send new security keys in a signed and encrypted GBL file. Or you can even send an extra firmware image as metadata intended to be sent to another device.
How to add metadata to the GBL file?
Metadata can be added when you create the GBL file with the „commander gbl create” command. Just add the --metadata switch, and the name of the file, that contains the metadata, e.g.
If you use the create_bl_files.bat file to create GBL files, then simply edit the file and extend the commands with the --metadata switch.
How to read metadata from the uploaded files?
There are two ways to read the content of the metadata that was put into the GBL file:
Note: neither version is supported by the AppLoader of the Bluetooth stack, because AppLoader decodes the GBL file on-the-fly, it does not store the full GBL file anywhere, and it does not allow you to access the metadata. So you have to implement an application uploader, as described in this article: https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/2017/09/20/uploading_imagesto-DXxD
Note that the application uploader can be used only in a device with an internal flash larger than 256k, or with a device that has an external flash.
Reading metadata on-the-fly
When you upload a new firmware image, you will receive the image in chunks. This chunks are received by the application, and they can be written into any storage slot using the API of the Gecko Bootloader. At the same time, when you get a chunk of the uploaded image, you can run the GBL parser on it, using the API of the Gecko Bootloader. When the parser finds metadata in the GBL file, it will call a callback function and pass the metadata to this function. It is good to know, that
To initialize the GBL parser use the following code:
Note: the parser context size must be at least 384byte!
To run the parser on a received chunk of data (data,len) use:
And finally define the callback function like this:
Reading metadata from a stored GBL file
A much easier solution – compared to on-the-fly parsing – is to retrieve the metadata from an already stored GBL file. To do so, first upload the GBL file – without decoding it – into a storage slot, defined by the bootloader. To retrieve the metadata from a GBL file stored in a storage slot, simply call
and define the callback function like this:
It’s important to note, that although the whole GBL file is available in the storage slot, you will still get the metadata in chunks, because the parser has limited buffer. The maximum size of a chunk is 64bytes.
Implementing the callback function
The metadata callback function is called several times, and provides the metadata in chunks. The callback function receives 4 parameters:
To assembly the metadata from the chunks you can use the following code snippet:
Example
The attached example code demonstrates how to extend the application uploader (https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/2017/09/20/uploading_imagesto-DXxD) with metadata parsing functionality. You can choose from 3 modes with the #defines at the beginning of ota_dfu_multislot.c
In this example the content of the metadata is copied to the Metadata Test Characteristic, so after parsing you can read this characteristic with your smartphone to check if parsing was successful.
To create a project with metadata parsing functionality follow these steps:
"${StudioSdkPath}/platform/bootloader"
"${StudioSdkPath}/platform/bootloader/api"
or copy the .h files from these folders to your project
a) Add #include “ota_dfu_multislot.h” to the beginning of main.c
b) Add ota_dfu_handle_event(evt); after evt = gecko_wait_event();
c) Remove the code that implements resetting into DFU mode (case gecko_evt_gatt_server_user_write_request_id)
To use the example code do the followings: