Note before you read this KBA: On SDK 2.4.0 a new API was introduced which allows changing the OTA device name in run-time. On SDK 2.4.2 a new feature was added to gecko_cmd_le_gap_set_adv_data which allows to manually set the content of the OTA advertisement packet. Both of these are described in this KBA. In practice this means that you can keep the gecko configuration structure as const and you can use the new API to set the device name instead of the structure fields.
Introduction
This article shows how you can read the Bluetooth (BT) address before initializing the stack, together with a use case example of how you can set it as the device name for OTA.
Typically the BT address can be read using the API command system_get_bt_address(), but it requires the stack to be initialized and boot event received. There are use cases where it might be required to read the BT address prior to initializing the stack which can be done by reading it straight from the underlying EFR32 registers.
Please note that this method does not work when the BT address is overwritten, it’s only usable for the default BT address
Where is the default Bluetooth address stored
The default BT address can be extracted from the device unique number that is in the Device Information page in flash, registers UNIQUEL and UNIQUEH (EFR32 reference manual, page 34). This is programmed in production and cannot be changed by the user so there is no risk of losing it, not even with a full flash erase.
The unique device number can be seen when you connect to a device using Simplicity Commander, in the lower right-hand corner, the last row of the “MCU Information”, Unique ID.
The unique number is 64-bits wide and if you exclude the 16 bits in the middle (0xfffe) you can see that it is the same as the BT address. In this example 00:0B:57:26:2F:32.
You can also run commander from command line, without the GUI. By passing commands device info as parameter, you can easily read the unique ID register as well as other information (such as the exact part number). See example usage below:
commander.exe device info
Reconfiguring debug connection with detected device part number: EFR32BG1B232F256GM48
Part Number : EFR32BG1B232F256GM48
Die Revision : A3
Production Ver : 147
Flash Size : 256 kB
SRAM Size : 32 kB
Unique ID : 000b57fffe2574c9
DONE
The em_system.h header file contains a function to read the unique device number, System_GetUnique().
Practical use case example - Using BT address as the device name in OTA
Attached to this KBA is an example which reads out the default BT address, converts it into an ascii string and sets it as the device name in OTA. The example is for the BGM111 module and it’s based on the SDK2.3.1 using GCC toolchain.
The gecko_configuration_t structure is typically declared as constant but this is not a requirement. The advantage of this approach is that the structure will be placed in Flash instead of RAM. If the structure is declared without the const qualifier then the ota.device_name_ptr and ota.device_name_len can be modified in run-time before calling gecko_init() which takes this structure as the input parameter.
Getting the BT address as the device name in OTA requires essentially 4 steps:
Read the Unique Device Number
Extract BT address out of the unique device number
Convert to ascii characters
Assign the new name to the ota field of the gecko configuration structure
For this we need to declare 3 variables, one to hold the unique device number, one to hold the BT address that will be extracted from the unique device number and one to hold the BT address string.
/* Variable for storing the 64-bit unique number */
uint64_t uniqueDeviceNumber;
/* BT address variable */
bd_addr myBTA;
/* Array to hold the BT address in string format - 12 characters + 5 ':' = 17 */
char myBTAstring[17];
Reading the unique device number
The unique device number can be read using the System_GetUnique() function mentioned earlier.
/* Get Unique Device Number */
uniqueDeviceNumber = SYSTEM_GetUnique();
Extract the BT address
To extract the BT address we need to shift the bytes from the unique number according to which address byte we want to read and ignore the middle section (0xfffe).
Once the address has been extracted it can be converted into an ascii string.
/* Convert to string format */
for(uint8_t i=0; i<6; i++) {
/* Convert to hexadecimal (capital letters) with minimum width of 2 and '0' stuffing
* More info on the sprintf parameters here: https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm
*/
sprintf(&(myBTAstring[i*3]), "%02X", myBTA.addr[i]);
/* Add the ':' character to overwrite the null terminator added by sprintf (except on the last iteration */
if(i<5) {
myBTAstring[i*3+2] = ':';
}
}
Assign string to OTA device name
The last part is to assign the new string pointer to the ota device name in the gecko configuration structure.
/* Use the new BT address string as device name for OTA */
config.ota.device_name_ptr = myBTAstring;
config.ota.device_name_len = 17;
Testing the example
Using the Blue Gecko app you can scan for the device and set into OTA mode by writing ‘0’ to the OTA control characteristic which will force the connection to drop from the slave side as the device goes into OTA DFU mode. Once the app goes back into scanning you’ll be able to see the BT address as the OTA device name.
The first stage is dummy. It can only start the second stage and it can overwrite the second stage with an already uploaded upgrade image. The first stage can only be flashed to the device and cannot be upgraded!
The second stage starts the application and it is able to upgrade the application in several ways, like using UART, SPI, Bluetooth, etc. It has other features as well, like handling multiple storage slots, verifying the images, etc. Unlike the first stage, the second stage is upgradable through the same channels as the application (UART, SPI, Bluetooth, etc.)
Upgrading via UART
This part discusses how you can upgrade the second stage via UART. For this you have to use the BGAPI UART DFU type Gecko Bootloader!
If you already have the bootloader flashed, the second stage can be upgraded in 3 steps:
Upload new second stage image in the device (it will be put to a predefined address)
Reset the device -> the first stage will overwrite the second stage with the new image
Upload the application again, since some part of it may have got corrupted when the second stage image was uploaded
In practice, follow these steps to upgrade your second stage:
Create a new bootloader project you want to upgrade to
Bootloader can only be upgraded and not downgraded, hence you need an increased version number with every upgrade. If you want to upload the same bootloader version with another configuration, then you have to increase BOOTLOADER_VERSION_MAIN_CUSTOMER. Find the "Other" tab in the AppBuilder of the Bootloader project (opening .isc file), and add BOOTLOADER_VERSION_MAIN_CUSTOMER to the defines. Define a value greater than 0, then increase this with every upgrade.
Generate and build your bootloader project
Create a bootloader upgrade file (.gbl) from the .s37 output file (*). E.g.
Upload the upgrade image. E.g. if the upgraded bootloader is an BGAPI UART DFU type bootloader, you can use the uart-dfu application (**) to do this:
uart-dfu COMn 115200 application.gbl
Now your bootloader is upgraded and your application is restored.
Note:
(*) commander.exe can be found under
C:\SiliconLabs\SimplicityStudio\vX\developer\adapter_packs\commander
(**) uart-dfu.exe can be found under
C:\SiliconLabs\SimplicityStudio\vX\developer\sdks\gecko_sdk_suite\<version>\app\bluetooth_<version>\examples_ncp_host\uart_dfu\exe
after make was run in
C:\SiliconLabs\SimplicityStudio\vX\developer\sdks\gecko_sdk_suite\<version>\app\bluetooth_<version>\examples_ncp_host\uart_dfu\
Upgrading OTA (over-the-air)
This part discusses how you can upgrade the second stage via Bluetooth connection (OTA – over the air). For this you have to use a Bluetooth in-place OTA DFU type Gecko Bootloader, and you have to upgrade to stack version 2.7 or later (If you already have an older stack version running on the device, it is not a problem, but upgrade is necessary.)
If you already has the bootloader flashed, the second stage can be upgraded in 4 steps:
Upload new second stage image along with an apploader image. Note: this will overwrite the current application image.
Reset the device -> the first stage will upgrade the second stage with the new bootloader image. Note: this process will overwrite some part of the current apploader image.
Reset the device -> the second stage will upgrade the corrupted apploader image with the newly uploaded apploader image.
Upload the application image again. This will upgrade the corrupted application image.
In practice, follow these steps to upgrade your second stage:
Bootloader can only be upgraded and not downgraded, hence you need an increased version number with every upgrade. If you want to upload the same bootloader version with another configuration, then you have to increase BOOTLOADER_VERSION_MAIN_CUSTOMER. Find the "Other" tab in the AppBuilder of the Bootloader project (opening .isc file), and add BOOTLOADER_VERSION_MAIN_CUSTOMER to the definitions. Define a value greater than 0, then increase this with every upgrade.
Generate and build your bootloader project
Open your Bluetooth application project or create a new one (SDK v2.7 or later)
Build your project if you haven’t done so yet
Copy the second stage bootloader image bootloader-storage-internal-ble.s37 (the .s37 file that does not end with –combined.s37) from your Bootloader project into your Bluetooth application project
Find the following line in create_bl_files.bat (which can be found in the Bluetooth project):
Repeat this for signed and encrypted images if you use signed/encrypted images
Run the create_bl_files.bat file, which creates the GBL files in the output_gbl folder
Do a full OTA update on your devices the same way as you do a full OTA for upgrading the stack/apploader and application, but now use these two files:
apploader+bootloader.gbl
application.gbl
During the OTA process the device resets itself twice, so there is no need for user interaction. After the OTA update, your bootloader is updated. You can check its version in the system_boot event of the Bluetooth stack or in the Gecko Bootloader version characteristic of the Silicon Labs OTA service (see AN1086)
This article provides a walkthrough on setting up the LCD screen on WSTK for a Bluetooth SDK project. This process may also suit projects which are using different SDKs under the Gecko SDK Suite but it has only been tested with the soc-empty from the Bluetooth SDK.
The process is slightly different depending on which Bluetooth SDK version you are using, 2.6.x or older and 2.7.x or newer. This relates to a change on SDK 2.7.0 where the SDK projects no longer link to files in the SDK but rather copy them onto the workspace project.
For the 2.7.x and newer SDKs you can use a script that automates the manual file copying making the process a lot faster and user friendly.
Adding LCD support for SDK 2.6.x or older
Attached you can find a soc-empty Bluetooth SDK example extended with LCD functionality. The example works on BRD4153A radio board, but the process of adding LCD support is very similar for every radio board.
To add LCD support to a project follow these steps:
1- Create lcd-graphics folder in your project.
2- Drag and drop the files below to lcd-graphics folder. It is wise to select the Link to files option and choose the STUDIO_SDK_LOC symbol as a root. This makes the project portable.
Include paths can be added on project properties dialog.
Adding LCD support for SDK 2.7.x or newer
1- Copy the attached script lcd_support.bat into your soc-empty project directory. The script assumes that the SDK is installed in the default studio path so make sure to change this in the script if the SDK path is different.
:: Note: This is the standard installation path for the GSDK, if it's different on your installation please update the variable below
SET SDKS_PATH=C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite
Run the script and you will find 2 additional folders in your project, lcd-graphics and dmd.
2- Add the following path to the include paths:
"${workspace_loc:/${ProjName}/lcd-graphics}"
Assuming you are using GCC this will be in project properties -> "C/C++ Build" -> Settings -> "GNU ARM C Compiler" -> Includes
3- Add this line to hal-config.h:
#define HAL_SPIDISPLAY_FREQUENCY (1000000)
Using the LCD
There is a set of basic LCD handling functions collected in graphics.h/.c . These are:
Note: The graphics.h is limited but easy to use. If you need features that graphics.h does not support than use glib graphics library directly. You can find the glib reference here.
Silicon Labs' A variant of modules typically require copper clearance area on the carrier board (together with a minimum size of suggested GND plane in order to achieve the possible best antenna performance). The recommended keep-out area dimensions and location for the on-module antenna are documented in the module's datasheet. Also, the A variant modules typically require to be placed at the edge of the carrier PCB.
However, there is an option to use these modules with external antennas as well. For this purpose the E variant of modules are also available.
Below are the layout recommendations in the case of using E module variants:
- There is no need for copper clearance area for the module on the carrier board.
- It is still recommended to ensure big enough continuous ground plane on the carrier board for the external monopole-type antenna. The minimum recommended GND plane size is quater-wavelength.
- 50 ohms RF port is typically accessible through an u.FL connector or a pad of the module. Via one of these connections a 50-ohm antenna can be attached, however it can also be suggested to place some placeholders for the external antenna matching components on the carrier board to be able to fine-tune the external antenna impedance to 50 ohms. A 3-element PI network can be suggested here.
- Connect the GND pads of the module directly to a well-stitched GND plane on the carrier board.
- In order to achieve the possible best antenna performance and avoid EMC immunity failures, it is also suggested to ensure continuous and large (and well-stitched) GND planes across the whole carrier board design. High density of GND via stitching is especially recommended around the RF sections and the carrier board edges.
- Also, try to get as many traces as possible buried between the top and bottom layers of the carrier board.
To migrate a project from Bluetooth SDK 2.1.1 to SDK 2.3.1. follow the steps described below.
1. Install SDK 2.3.1 via Simplicity Studio Package Manager.
2. Go to File->Properties then select C/C++ build-> Board/Part/SDK tab
3. Select the BLE SDK 2.3.1 then click on OK.
4. Remove the #include “bg_dfu.h” from main.c . Since SDK 2.3.0 the bg_dfu.h is not existing. See UG266: Silicon Labs Gecko Bootloader User Guide for the details.
5. OPTIONAL - Since SDK 2.3.0 the external flash drivers changed.
If your project using the external flash on the radio board, replace the
#include "flashpwr.h" from main.c with
#include "em_usart.h" and with #include "mx25flash_spi.h"
Additionally replace these lines from main.c
#ifdef FEATURE_SPI_FLASH
/* Put the SPI flash into Deep Power Down mode for those radio boards where it is available */
flashpwrInit();
flashpwrDeepPowerDown();
#endif
With these lines:
#ifdef FEATURE_SPI_FLASH
/* Put the SPI flash into Deep Power Down mode for those radio boards where it is available */
MX25_init();
MX25_DP();
/* We must disable SPI communication */
USART_Reset(USART1);
#endif /* FEATURE_SPI_FLASH */
6.OPTIONAL – Since SDK 2.3.0 the kit drivers moved to different folders. You may need to re-add these driver files to the project.
If the project using the Si7013 sensor on the WSTK add the si7013.c to your project.
9. The .isc files stores information about the used SDK. This information need to be updated.
Open the .isc file with text editor.
10. Overwrite the line starts with stackId:
stackId: com.silabs.sdk.stack.super
11. Overwrite the Plugin configuration lines to:
# Plugin configuration
appPlugin: bg_stack=true
appPlugin: bgapi=true
appPlugin: cmsis=true
appPlugin: command_interpreter=false
appPlugin: command_interpreter_handler=false
appPlugin: crc16_ccitt=false
appPlugin: device=true
appPlugin: emdrv=true
appPlugin: emlib=true
appPlugin: gecko_bootloader=false
appPlugin: glib=false
appPlugin: kit=false
appPlugin: kit_board=true
appPlugin: kit_flashpwr=true
appPlugin: kit_ioexpander=false
appPlugin: kit_lcd=false
appPlugin: legacy_ble_ota_bootloader=true
appPlugin: mpsi=false
appPlugin: mpsi-ipc=false
appPlugin: mpsi-storage=false
appPlugin: mpsi_ble_transport_server=false
appPlugin: rail=true
appPlugin: serial_vcom=false
appPlugin: slot-manager=false
appPlugin: stack_bridge=false
appPlugin: thunderboard_sense_drivers=false
12. Save the .isc file, close it then reopen it with double click.
13. Press Generate on VGATT editor
14. Now the project should build successfully.
15. OPTIONAL – Studio will exclude some files from the build. Typically the ones which are moved to different location since SDK 2.3.0. You may delete the excluded files from the project. These are grayed out by studio.
This example shows how to implement transparent serial data connection between two radio boards or modules.
The goal of this example is to provide a simple template for users that want to use SPP-like communication in their projects. To keep the code as short and simple as possible the features are quite minimal. Users are expected to customize the code as needed to match their project requirements.
The associated sample code is a single application that implements both the server and client roles (in their own C-files). The role is selected dynamically at power-up using pushbuttons, details are found later in the section Running the demo.
The client part of this example (spp_client_main) can be also used as a starting point for any generic BLE central implementation that needs to scan for devices and automatically connect to those devices that advertise some specific service UUID. The client performs service discovery after connection establishment and this process works similarly for any GATT based services. With small modifications the spp_client_main code can be converted into e.g. a “heart rate client” or “thermometer client”.
Project setup
This section guides you through setting up an SPP example application on your kit.
To run this example you need:
Two Wireless Starter Kits (WSTK)
Two radio boards or modules
Two terminal windows (such as TeraTerm or PuTTY) running on your PC
1. Create a new SoC - Empty application project with Bluetooth SDK using version 2.12 or newer.
The example sits on top of the basic SoC - Empty application. We will replace the app.c files with the files provided with this article.
2. Click on the .isc file in your project tree, select the Custom BLE GATT field on the right side of the configurator and finally select Import GATT from .bgproj file from the icons next to the field (the bottommost icon)
3. Select the provided gatt.xml, click Save and finally press Generate. You should now have a new SPP service and within it one characteristic.
4. Copy the following files to your project:
- app.c
- spp_utils.h
- spp_utils.c
- spp_client_main.c
- spp_server_main.c
5. Enable printing to console by setting DEBUG_LEVEL from 0 to 1 in app.h
That's it! When you program the application to your development kit, you should see this print at the serial output:
* SPP server mode *
Keeping button PB0 or PB1 pressed during reset will select client mode, in that case the print will be:
* SPP client mode *
SPP server
There is no standard SPP service in Bluetooth Low Energy and therefore we need to implement this as a custom service. The custom service is about as minimal as possible. There is only one characteristic that is used for both incoming and outgoing data. The service is defined in the gatt.xml file associated with this article and shown below.
<!-- Our custom service is declared here -->
<!-- UUID values generated with https://www.guidgenerator.com/ -->
<service uuid="4880c12c-fdcb-4077-8920-a450d7f9b907" advertise="true">
<description>SPP Service</description>
<characteristic uuid="fec26ec4-6d71-4442-9f81-55bc21d658d6" id="xgatt_spp_data">
<description>SPP Data</description>
<properties write_no_response="true" notify="true" />
<value variable_length="true" length="20" type="hex"></value>
</characteristic>
</service>
At boot event the server is put into advertisement mode. This example uses advertising packets that are automatically filled by the stack. In our custom SPP service definition (see above) we have set advertise=true, meaning that the stack will automatically add the 128-bit UUID in advertisement packets. The SPP client will use this information to recognize the SPP server among other BLE peripherals.
For incoming data (data sent by the SPP client and written to UART in SPP server) we use unacknowledged write transfers (write_no_response). This will give better performance than normal acknowledged writes because several write operations can be fitted into one connection interval.
For outgoing data (data received from UART and sent to SPP client) we use notifications with the command gatt_server_send_characteristic_notification. Notifications are unacknowledged and this will again allow several notifications to be fitted into one connection interval.
Note that the data transfers are unacknowledged at GATT level. This means that at application level there are no acknowledgements. However, at the lower protocol layers each packet is still acknowledged and retransmissions are used when needed to ensure that all packets are delivered.
The core of this SPP example implementation is a 256-byte FIFO buffer (data[] in send_spp_data function) that is used to manage outgoing data. Data is received from UART and it is pushed to the SPP client using notifications. Incoming data from client raises the gatt_server_attribute_value event. The received data will then be copied to UART.
Some simple overflow checking can be optionally included. If the number of bytes exceeds 256 then FIFO overflow happens. Depending on application the overflow situation may be handled differently. For some applications it could be best to just drop the bytes that do not fit in buffer. For other applications it may be better to immediately stop all data transfer to avoid any further damage. See comments in spp_utils.h for more information.
SPP client
In terms of incoming/outgoing UART data, the SPP client works the same way as the SPP server. A similar 256-byte FIFO buffer is used as in the SPP server. The only differences are that:
Data is received over the air by notifications (event gatt_characteristic_value).
Data is sent by calling gatt_write_characteristic_value_without_response.
The client is a bit more complex than the server because the client needs to be able to detect the SPP server by looking at the advertisement packets. Additionally, the client needs to do service discovery after connecting to the client to get the needed information about the remote GATT database.
To be able to use the SPP service the client needs to know the characteristic handle values. The handles are discovered dynamically so that there is no need to use any hard-coded values. This is essential if the SPP server needs to be ported to some other BLE module and the handle values are not known in advance.
At startup, the client starts discovery by calling le_gap_start_discovery. For each received advertisement packet the stack will raise event le_gap_scan_response. In order to recognize the SPP server the client scans through each advertisement packet and searches for the 128-bit service UUID that we assigned for our custom SPP service.
Scanning of advertisement packets is done in function process_scan_response. The advertising packets include one or more advertising data elements that are encoded as defined in the BT specification. Each advertising element begins with a length byte that indicates the length of that field. This makes it easy to scan through all the elements in a while-loop.
The second byte in advertising element is the AD type. The predefined types are listed in Bluetooth SIG website
In this case, we are interested in types 0x06 and 0x07 that indicate incomplete/complete list of 128-bit service values. If such AD type is found, then the AD payload is compared against the known 128-bit UUID of our SPP service to see if there is a match.
After finding a match in the advertising data the client will open a connection by calling le_gap_connect. When connection is opened, the next task is to discover services and figure out what is the handle value that corresponds to the SPP_data characteristic (see XML definition of our custom service attached).
The service discovery is implemented as a simple state machine and the sequence of operations after connection is opened is summarized below:
1) Call gatt_discover_primary_services_by_uuid to start service discovery
2) Call gatt_discover_characteristics to find the characteristics in the SPP service (in FIND_SERVICE state)
3) Call gatt_set_characteristic_notification to enable notifications for spp_data characteristic (in FIND_CHAR state)
Note that in step 1) above we want to discover only services that match the specific UUID we are looking for. Another option would be to call cmd_gatt_discover_primary_services that will return list of all services in the remote GATT database.
After each procedure in the above sequence is completed, the stack will raise event gatt_procedure_completed. The client uses a variable main_state that is used to keep track what is the current state. The gatt_procedure_completed event will trigger the state machine to move on to the next logical state.
When notifications have been enabled the application is in transparent SPP mode. This is indicated by writing string "SPP Mode ON" to UART. After this point, any data that is received from UART is sent to server using non-acknowledged write transfer. Similarly, all data received via notifications (event gatt_characteristic_value) is copied to the local UART.
Note that on the server application there is also "SPP Mode ON" string that is printed to console. On server side this is done when the remote client has enabled notifications for the spp_data characteristic.
The data is handled transparently, meaning that the program does not care at all what values are transmitted. It can be either ASCII strings or binary data, or mixture of these. The connection can be closed by pressing either of the pushbuttons on the client.
Running the demo
Simplest way to run the demo is to use two WSTKs with the same radio board (for example two BGM13S radio boards). In this case you can use the same binary for both boards.
When the application boots, it checks the state of pushbuttons PB0, PB1. If buttons are not pressed, the application starts in SPP server role.
By keeping either PB0 or PB1 pressed during reboot the application starts in SPP client mode.
In server mode, the device advertises the custom SPP service and waits for incoming connections.
In client mode, the device starts scanning and searches for the custom SPP UUID in the scan responses. If match is found, the client connects to the target, discovers the SPP service and characteristics and then enables notifications for the SPP_data characteristic. At this point, any data that is input in the client side is sent over the air to the server and printed on the remote UART. Similarly, any data input to the server UART is transmitted back to the client.
To connect to the kit using terminal program use the following UART settings: baud rate 115200, 8N1, no flow control.
NOTE: Make sure that you are using the same baud rate and flow control settings in your starter kit and radio board or module firmware as well as your terminal program. For WSTK, this can be checked in
The above screencapture illustrates what happens when the client and server boards are powered up. Client is on the left side and server on the right. Quickly after power up the client will find the server and open a connection automatically. When the connection is set up properly and notifications are enabled then both applications will output string "SPP Mode ON". This indicates that the transparent serial connection is open. From this point on, any data you type into the client terminal will appear on the server terminal and vice versa.
You can try pressing reset button on either of the boards and see what happens. The connection should be restored automatically when both units are back online. Pressing either of the buttons on the client board will close the connection and print the stats for the last session to the terminal.
Power management
USART peripheral is not accessible in EM2 sleep mode. For this reason, both the client and the server applications disable sleeping (EM2 mode) temporarily when the SPP mode is active. SPP mode in this context means that the client and server are connected and that the client has enabled notifications for the SPP_data characteristics.
When SPP mode is entered, the code calls SLEEP_SleepBlockBegin(sleepEM2) to temporarily disable sleeping. When connection is closed (or client disables notifications) then SLEEP_SleepBlockEnd(sleepEM2) is called to re-enable sleeping.
For more details on the power management details, see article here.
Known issues
This example implementation does not guarantee 100% reliable transfer. The implementation uses retargetserial driver for reading data from UART. The driver is found in <project_root>/hardware/kit/common/drivers/ . For incoming data, the driver uses a FIFO buffer whose size is defined using symbol RXBUFSIZE (default value 8).
To get more reliable operation, it is recommended to increase the RXBUFSIZE value. However, even with a large FIFO buffer, there is a chance that some data is lost if the data rate is very high. If the FIFO buffer in RAM becomes full, the driver will simply drop the bytes that do not fit in. More discussion about this topic is found in following forum thread: SPP-over-BLE C example for UART receive data is lost
Conclusion
This example is a simple C-based SPP implementation for BGMxxx / EFR32BG products. It is not optimized for performance or low power. The code has been kept as simple as possible.
UART input/output is handled using stdio retarget functions (see this article for details.)
This is not the most efficient way to handle UART input/output but the benefit is that it is portable and allows the sample code to be used easily on several radio boards without any modifications.
Bluetooth projects do not include Gecko Bootloader by default, so you have to add it separately!
In Bluetooth SDKs between v2.0 and v2.6:
legacy OTA bootloader is included in Software Example projects created for EFR32BG1 devices
no bootloader is included in Software Example projects created for other devices (EFR32xG12, EFR32xG13)
In Bluetooth SDK v2.7 or later
no bootloader is included in any Software Exmaple projects
however, when a Demo is flashed, both bootloader and application is flashed after each other
NCP - empty target Demo is flashed along with a BGAPI UART DFU type Gecko Bootloader
other Demos are flashed along with Bluetooth OTA type Gecko bootloader (config depends on part)
Devices are shipped with preprogrammed bootloaders:
EFR32BG1 devices are preprogrammed with legacy bootloader. The legacy bootloader is a one stage simple bootloader that has very limited capabilities compared to Gecko Bootloader. There are two types of the legacy bootloader: UART DFU and OTA DFU. UART DFU is able to upgrade the firmware via UART, OTA DFU is able to upgrade the firmware via Bluetooth connection. Devices are preprogrammed with UART DFU bootloader, but the projects in SDK v2.6 and older contains OTA DFU bootloader by default. Legacy bootloader is not supported since SDK v2.7, so you have to add Gecko Bootloader to your project to overwrite the legacy bootloader. Gecko Bootloader also has UART and OTA configuration, which is compatible with the legacy bootloaders.
Other devices are preprogrammed with dummy bootloader. This bootloader is only able to start the application, but cannot upgrade it, so it is essential to overwrite it with Gecko Bootloader.
To add a Gecko Bootloader to your Bluetooth project do the followings:
First method
Build your Bluetooth application
Flash your Bluetooth application (.s37 OR .hex OR .bin) to the device.
Create a new Gecko Bootloader project, e.g. Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader
(click New Project button in Simplicity Studio, select Gecko Bootloader, select Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader)
Generate and build it
Flash the .s37 file that ends with „–combined” (e.g. bootloader-uart-bgapi-s37) file to the device. This will overwrite the legacy bootloader in EFR32BG1 devices or the dummy bootloader in EFR32BG12 devices.
Note: bootloader-uart-bgapi-combined.s37 contains the first+second stage of the Gecko Bootloader, while bootloader-uart-bgapi.* contains only the second stage. The first+second stage is needed when flashing the bootloader the first time, while the second stage is needed when upgrading the bootloader (either by flashing or by DFU).
Second method
If you use a EFR32BG1 device with SDK v2.6 or older, remove the legacy bootloader from the Bluetooth application project (If you have other device or later SDK, then skip this):
Right click on your project -> properties
C/C++ build > Settings > IAR Linker for ARM > Library
or
C/C++ build > Settings > GNU ARM C Linker > Miscellaneous > Other objects
Remove binbootloader.o
Build your Bluetooth application
Create a new Gecko Bootloader project, e.g. Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader
(click New Project button in Simplicity Studio, select Gecko Bootloader, select Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader)
Generate and build it
Merge the (combined) bootloader and the application image:
If you use a EFR32BG1 device with SDK v2.6 or older, remove the legacy bootloader from the Bluetooth application project (If you have other device or later SDK, then skip this):
If the .isc file is open in your project, close it first.
Open the .isc file in your project with text editor (right click -> Open With -> Text Editor)
change appPlugin: gecko_bootloader=false to appPlugin: gecko_bootloader=true
change appPlugin: legacy_ble_ota_bootloader=true to appPlugin: legacy_ble_ota_bootloader=false
save and close the .isc file
open the .isc file with App Builder (right click -> Open With -> App Builder)
press Generate
Build your Bluetooth application
Create a new Gecko Bootloader project, e.g. Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader
(click New Project button in Simplicity Studio, select Gecko Bootloader, select Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader)
Generate and build it
Merge the (combined) bootloader and the application image:
flash the SoC - Empty demo to your device first. This will flash SoC - Empty application with Bluetooth in-place OTA DFU type Gecko Bootloader
OR: flash NCP target - Empty demo to your device. This will flash NCP target - Empty application with BGAPI UART DFU type Gecko Bootloader
Build your Bluetooth application
Flash the .hex or the .s37 file to your device. This will not overwrite the bootloader (unlike .bin)
Note: commander.exe can be found in: C:\SiliconLabs\SimplicityStudio\v4\developer\adapter_packs\commander\
Warning: for SDKs before v2.4.0 the second and third methods work only if you are using IAR as compiler. GCC is fully supported from v2.4.0.
Warning: in SDK v2.3.3 the postbuild step of the bootloader project may fail, and -combined.s37 file may be missing. In this case open the bootloader project, go to "Project > Properties > C/C++ Build > Settings > Build steps", and put the script path between quotes. Rebuild the bootloader project.
This article describes how to create an Eddystone beacon using Silicon Labs' BlueGecko-based SoCs and modules.
Eddystone Beacons
The Eddystone format defines four different packet types. This article describes how to implement the Eddystone-URL packet. The Eddystone-URL packet advertises a URL in compressed format.
URL Beacon Implementation
The first step in creating the beacon is to define our custom user data to be advertised:
#define EDDYSTONE_DATA_LEN (21)
static const uint8_t eddystone_data[EDDYSTONE_DATA_LEN] = {
0x03, // Length of service list
0x03, // Service list
0xAA, 0xFE, // Eddystone ID
0x10, // Length of service data
0x16, // Service data
0xAA, 0xFE, // Eddystone ID
0x10, // Frame type Eddystone-URL
0x00, // Tx power
0x00, // http://www., 0x01=https://www.
's','i','l','a','b','s','.','c','o','m'
};
to set this data to be advertised. The first two parameters are the advertising set handle and a value indicating that the data is to be used in advertising packets.
Then we are going to set the transmit power. The Eddystone-URL packet format includes the transmit power in the 10th byte. This data can be used by receivers of the beacon to determine distance so it is important that this data and the actual transmit power match.
gecko_cmd_system_set_tx_power(0); // Set global maximum TX power to 0 dBm
The following call sets the minimum and maximum advertising interval to 100 ms ( 625 us * 160). We use all three advertising channels by default (37, 38, 39).
This tells the stack to use the custom user data and to make the beacon non-connectable.
Running the Example
To setup this example, follow these steps:
Create a new SoC - Empty software example project in Simplicity Studio.
Replace the existing app.c file with the one attached here.
Build the project and use the Flash Programmer to write the firmware to your device.
The Eddystone beacon should now be broadcasting. You can verify this with a mobile app such as the BlueGecko App.
Conclusion
This article is a brief introduction to an implementation of the Eddystone-URL packet format, for more detailed information please see the links below.
Bluetooth Knowledge Base
KBA_BT_0805: Setting Bluetooth Address as device name in OTA
Note before you read this KBA: On SDK 2.4.0 a new API was introduced which allows changing the OTA device name in run-time. On SDK 2.4.2 a new feature was added to gecko_cmd_le_gap_set_adv_data which allows to manually set the content of the OTA advertisement packet. Both of these are described in this KBA. In practice this means that you can keep the gecko configuration structure as const and you can use the new API to set the device name instead of the structure fields.
Introduction
This article shows how you can read the Bluetooth (BT) address before initializing the stack, together with a use case example of how you can set it as the device name for OTA.
Typically the BT address can be read using the API command system_get_bt_address(), but it requires the stack to be initialized and boot event received. There are use cases where it might be required to read the BT address prior to initializing the stack which can be done by reading it straight from the underlying EFR32 registers.
Please note that this method does not work when the BT address is overwritten, it’s only usable for the default BT address
Where is the default Bluetooth address stored
The default BT address can be extracted from the device unique number that is in the Device Information page in flash, registers UNIQUEL and UNIQUEH (EFR32 reference manual, page 34). This is programmed in production and cannot be changed by the user so there is no risk of losing it, not even with a full flash erase.
The unique device number can be seen when you connect to a device using Simplicity Commander, in the lower right-hand corner, the last row of the “MCU Information”, Unique ID.
The unique number is 64-bits wide and if you exclude the 16 bits in the middle (0xfffe) you can see that it is the same as the BT address. In this example 00:0B:57:26:2F:32.
You can also run commander from command line, without the GUI. By passing commands device info as parameter, you can easily read the unique ID register as well as other information (such as the exact part number). See example usage below:
The em_system.h header file contains a function to read the unique device number, System_GetUnique().
Practical use case example - Using BT address as the device name in OTA
Attached to this KBA is an example which reads out the default BT address, converts it into an ascii string and sets it as the device name in OTA. The example is for the BGM111 module and it’s based on the SDK2.3.1 using GCC toolchain.
The gecko_configuration_t structure is typically declared as constant but this is not a requirement. The advantage of this approach is that the structure will be placed in Flash instead of RAM. If the structure is declared without the const qualifier then the ota.device_name_ptr and ota.device_name_len can be modified in run-time before calling gecko_init() which takes this structure as the input parameter.
Getting the BT address as the device name in OTA requires essentially 4 steps:
For this we need to declare 3 variables, one to hold the unique device number, one to hold the BT address that will be extracted from the unique device number and one to hold the BT address string.
Reading the unique device number
The unique device number can be read using the System_GetUnique() function mentioned earlier.
Extract the BT address
To extract the BT address we need to shift the bytes from the unique number according to which address byte we want to read and ignore the middle section (0xfffe).
Convert to ascii string
Once the address has been extracted it can be converted into an ascii string.
Assign string to OTA device name
The last part is to assign the new string pointer to the ota device name in the gecko configuration structure.
Testing the example
Using the Blue Gecko app you can scan for the device and set into OTA mode by writing ‘0’ to the OTA control characteristic which will force the connection to drop from the slave side as the device goes into OTA DFU mode. Once the app goes back into scanning you’ll be able to see the BT address as the OTA device name.
KBA_BT_0605: Upgrading Gecko Bootloader
Gecko bootloader consists of two stages.
Upgrading via UART
This part discusses how you can upgrade the second stage via UART. For this you have to use the BGAPI UART DFU type Gecko Bootloader!
As a first step you should flash both the first and the second stage of the bootloader to your device. To flash the first + second stage bootloader with a Bluetooth application, follow the steps detailed in http://community.silabs.com/t5/Bluetooth-Wi-Fi-Knowledge-Base/Adding-Gecko-Bootloader-to-Bluetooth-projects/ta-p/195432
If you already have the bootloader flashed, the second stage can be upgraded in 3 steps:
In practice, follow these steps to upgrade your second stage:
Upload the upgrade image. E.g. if the upgraded bootloader is an BGAPI UART DFU type bootloader, you can use the uart-dfu application (**) to do this:
Note:
(*) commander.exe can be found under
C:\SiliconLabs\SimplicityStudio\vX\developer\adapter_packs\commander
(**) uart-dfu.exe can be found under
C:\SiliconLabs\SimplicityStudio\vX\developer\sdks\gecko_sdk_suite\<version>\app\bluetooth_<version>\examples_ncp_host\uart_dfu\exe
after make was run in
C:\SiliconLabs\SimplicityStudio\vX\developer\sdks\gecko_sdk_suite\<version>\app\bluetooth_<version>\examples_ncp_host\uart_dfu\
Upgrading OTA (over-the-air)
This part discusses how you can upgrade the second stage via Bluetooth connection (OTA – over the air). For this you have to use a Bluetooth in-place OTA DFU type Gecko Bootloader, and you have to upgrade to stack version 2.7 or later (If you already have an older stack version running on the device, it is not a problem, but upgrade is necessary.)
As a first step you should flash both the first and the second stage of the bootloader to your device. To flash the first + second stage bootloader with a Bluetooth application, follow the steps detailed in http://community.silabs.com/t5/Bluetooth-Wi-Fi-Knowledge-Base/Adding-Gecko-Bootloader-to-Bluetooth-projects/ta-p/195432
If you already has the bootloader flashed, the second stage can be upgraded in 4 steps:
In practice, follow these steps to upgrade your second stage:
During the OTA process the device resets itself twice, so there is no need for user interaction. After the OTA update, your bootloader is updated. You can check its version in the system_boot event of the Bluetooth stack or in the Gecko Bootloader version characteristic of the Silicon Labs OTA service (see AN1086)
For more information please refer to UG266: Gecko Bootloader User Guide
KBA_BT_1007: Enable LCD screen on WSTK
Introduction
This article provides a walkthrough on setting up the LCD screen on WSTK for a Bluetooth SDK project. This process may also suit projects which are using different SDKs under the Gecko SDK Suite but it has only been tested with the soc-empty from the Bluetooth SDK.
The process is slightly different depending on which Bluetooth SDK version you are using, 2.6.x or older and 2.7.x or newer. This relates to a change on SDK 2.7.0 where the SDK projects no longer link to files in the SDK but rather copy them onto the workspace project.
For the 2.7.x and newer SDKs you can use a script that automates the manual file copying making the process a lot faster and user friendly.
Adding LCD support for SDK 2.6.x or older
Attached you can find a soc-empty Bluetooth SDK example extended with LCD functionality. The example works on BRD4153A radio board, but the process of adding LCD support is very similar for every radio board.
To add LCD support to a project follow these steps:
1- Create lcd-graphics folder in your project.
2- Drag and drop the files below to lcd-graphics folder. It is wise to select the Link to files option and choose the STUDIO_SDK_LOC symbol as a root. This makes the project portable.
Files needed for LCD handling:
C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\hardware\kit\common\drivers\display.c
C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\hardware\kit\common\drivers\ displayls013b7dh03.c
C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\hardware\kit\common\drivers\displaypalemlib.c
C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\platform\middleware\glib\dmd\display\dmd_display.c
c:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\util\silicon_labs\silabs_core\graphics\graphics.c
c:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\hardware\kit\common\drivers\udelay.c
Additionally, all the .c files needed from here:
*C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\platform\middleware\glib\glib*
Once every files added the lcd-graphics folder looks like this:
3- Add the following paths to the include paths
"${StudioSdkPath}/platform/middleware/glib"
"${StudioSdkPath}/platform/middleware/glib/glib"
"${StudioSdkPath}/platform/middleware/glib/dmd"
"${StudioSdkPath}/hardware/kit/ /config"
"${StudioSdkPath}/util/silicon_labs/silabs_core/graphics"
Include paths can be added on project properties dialog.
Adding LCD support for SDK 2.7.x or newer
1- Copy the attached script lcd_support.bat into your soc-empty project directory. The script assumes that the SDK is installed in the default studio path so make sure to change this in the script if the SDK path is different.
Run the script and you will find 2 additional folders in your project, lcd-graphics and dmd.
2- Add the following path to the include paths:
"${workspace_loc:/${ProjName}/lcd-graphics}"
Assuming you are using GCC this will be in project properties -> "C/C++ Build" -> Settings -> "GNU ARM C Compiler" -> Includes
3- Add this line to hal-config.h:
Using the LCD
There is a set of basic LCD handling functions collected in graphics.h/.c . These are:
To use these functions you need to include graphics.h.
LCD initialization
Before using the LCD it needs to be initialized by calling the GRAPHICS_Init function
Printing to the LCD
Note: The graphics.h is limited but easy to use. If you need features that graphics.h does not support than use glib graphics library directly. You can find the glib reference here.
GND plane shaping when using E variant of modules
KBA_BT_1403: Migrating Simplicity Studio projects from Bluetooth SDK 2.1.1 to 2.3.1
To migrate a project from Bluetooth SDK 2.1.1 to SDK 2.3.1. follow the steps described below.
1. Install SDK 2.3.1 via Simplicity Studio Package Manager.
2. Go to File->Properties then select C/C++ build-> Board/Part/SDK tab
3. Select the BLE SDK 2.3.1 then click on OK.
4. Remove the #include “bg_dfu.h” from main.c . Since SDK 2.3.0 the bg_dfu.h is not existing. See UG266: Silicon Labs Gecko Bootloader User Guide for the details.
5. OPTIONAL - Since SDK 2.3.0 the external flash drivers changed.
If your project using the external flash on the radio board, replace the
#include "flashpwr.h" from main.c with
#include "em_usart.h" and with #include "mx25flash_spi.h"
Additionally replace these lines from main.c
#ifdef FEATURE_SPI_FLASH
/* Put the SPI flash into Deep Power Down mode for those radio boards where it is available */
flashpwrInit();
flashpwrDeepPowerDown();
#endif
With these lines:
#ifdef FEATURE_SPI_FLASH
/* Put the SPI flash into Deep Power Down mode for those radio boards where it is available */
MX25_init();
MX25_DP();
/* We must disable SPI communication */
USART_Reset(USART1);
#endif /* FEATURE_SPI_FLASH */
6.OPTIONAL – Since SDK 2.3.0 the kit drivers moved to different folders. You may need to re-add these driver files to the project.
If the project using the Si7013 sensor on the WSTK add the si7013.c to your project.
It can be found in:
C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\hardware\kit\common\drivers
7. Since SDK 2.3.0 the linker files moved to different folders. They need to be added to the project. So create an IAR folder under the linker folder.
8. Copy the <your_target_mcu>.icf file to the <your_project_folder>\linker\IAR\ folder.
The .icf files can be found here:
c:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v1.0\protocol\bluetooth_2.3\ble_stack\linker\IAR\
9. The .isc files stores information about the used SDK. This information need to be updated.
Open the .isc file with text editor.
10. Overwrite the line starts with stackId:
stackId: com.silabs.sdk.stack.super
11. Overwrite the Plugin configuration lines to:
# Plugin configuration
appPlugin: bg_stack=true
appPlugin: bgapi=true
appPlugin: cmsis=true
appPlugin: command_interpreter=false
appPlugin: command_interpreter_handler=false
appPlugin: crc16_ccitt=false
appPlugin: device=true
appPlugin: emdrv=true
appPlugin: emlib=true
appPlugin: gecko_bootloader=false
appPlugin: glib=false
appPlugin: kit=false
appPlugin: kit_board=true
appPlugin: kit_flashpwr=true
appPlugin: kit_ioexpander=false
appPlugin: kit_lcd=false
appPlugin: legacy_ble_ota_bootloader=true
appPlugin: mpsi=false
appPlugin: mpsi-ipc=false
appPlugin: mpsi-storage=false
appPlugin: mpsi_ble_transport_server=false
appPlugin: rail=true
appPlugin: serial_vcom=false
appPlugin: slot-manager=false
appPlugin: stack_bridge=false
appPlugin: thunderboard_sense_drivers=false
12. Save the .isc file, close it then reopen it with double click.
13. Press Generate on VGATT editor
14. Now the project should build successfully.
15. OPTIONAL – Studio will exclude some files from the build. Typically the ones which are moved to different location since SDK 2.3.0. You may delete the excluded files from the project. These are grayed out by studio.
KBA_BT_0903: SPP-over-BLE example
Introduction
This example shows how to implement transparent serial data connection between two radio boards or modules.
The goal of this example is to provide a simple template for users that want to use SPP-like communication in their projects. To keep the code as short and simple as possible the features are quite minimal. Users are expected to customize the code as needed to match their project requirements.
The associated sample code is a single application that implements both the server and client roles (in their own C-files). The role is selected dynamically at power-up using pushbuttons, details are found later in the section Running the demo.
The client part of this example (spp_client_main) can be also used as a starting point for any generic BLE central implementation that needs to scan for devices and automatically connect to those devices that advertise some specific service UUID. The client performs service discovery after connection establishment and this process works similarly for any GATT based services. With small modifications the spp_client_main code can be converted into e.g. a “heart rate client” or “thermometer client”.
Project setup
This section guides you through setting up an SPP example application on your kit.
To run this example you need:
1. Create a new SoC - Empty application project with Bluetooth SDK using version 2.12 or newer.
The example sits on top of the basic SoC - Empty application. We will replace the app.c files with the files provided with this article.
2. Click on the .isc file in your project tree, select the Custom BLE GATT field on the right side of the configurator and finally select Import GATT from .bgproj file from the icons next to the field (the bottommost icon)
3. Select the provided gatt.xml, click Save and finally press Generate. You should now have a new SPP service and within it one characteristic.
4. Copy the following files to your project:
- app.c
- spp_utils.h
- spp_utils.c
- spp_client_main.c
- spp_server_main.c
5. Enable printing to console by setting DEBUG_LEVEL from 0 to 1 in app.h
That's it! When you program the application to your development kit, you should see this print at the serial output:
* SPP server mode *
Keeping button PB0 or PB1 pressed during reset will select client mode, in that case the print will be:
* SPP client mode *
SPP server
There is no standard SPP service in Bluetooth Low Energy and therefore we need to implement this as a custom service. The custom service is about as minimal as possible. There is only one characteristic that is used for both incoming and outgoing data. The service is defined in the gatt.xml file associated with this article and shown below.
At boot event the server is put into advertisement mode. This example uses advertising packets that are automatically filled by the stack. In our custom SPP service definition (see above) we have set advertise=true, meaning that the stack will automatically add the 128-bit UUID in advertisement packets. The SPP client will use this information to recognize the SPP server among other BLE peripherals.
For incoming data (data sent by the SPP client and written to UART in SPP server) we use unacknowledged write transfers (write_no_response). This will give better performance than normal acknowledged writes because several write operations can be fitted into one connection interval.
For outgoing data (data received from UART and sent to SPP client) we use notifications with the command gatt_server_send_characteristic_notification. Notifications are unacknowledged and this will again allow several notifications to be fitted into one connection interval.
Note that the data transfers are unacknowledged at GATT level. This means that at application level there are no acknowledgements. However, at the lower protocol layers each packet is still acknowledged and retransmissions are used when needed to ensure that all packets are delivered.
The core of this SPP example implementation is a 256-byte FIFO buffer (data[] in send_spp_data function) that is used to manage outgoing data. Data is received from UART and it is pushed to the SPP client using notifications. Incoming data from client raises the gatt_server_attribute_value event. The received data will then be copied to UART.
Some simple overflow checking can be optionally included. If the number of bytes exceeds 256 then FIFO overflow happens. Depending on application the overflow situation may be handled differently. For some applications it could be best to just drop the bytes that do not fit in buffer. For other applications it may be better to immediately stop all data transfer to avoid any further damage. See comments in spp_utils.h for more information.
SPP client
In terms of incoming/outgoing UART data, the SPP client works the same way as the SPP server. A similar 256-byte FIFO buffer is used as in the SPP server. The only differences are that:
Data is received over the air by notifications (event gatt_characteristic_value).
Data is sent by calling gatt_write_characteristic_value_without_response.
The client is a bit more complex than the server because the client needs to be able to detect the SPP server by looking at the advertisement packets. Additionally, the client needs to do service discovery after connecting to the client to get the needed information about the remote GATT database.
To be able to use the SPP service the client needs to know the characteristic handle values. The handles are discovered dynamically so that there is no need to use any hard-coded values. This is essential if the SPP server needs to be ported to some other BLE module and the handle values are not known in advance.
At startup, the client starts discovery by calling le_gap_start_discovery. For each received advertisement packet the stack will raise event le_gap_scan_response. In order to recognize the SPP server the client scans through each advertisement packet and searches for the 128-bit service UUID that we assigned for our custom SPP service.
Scanning of advertisement packets is done in function process_scan_response. The advertising packets include one or more advertising data elements that are encoded as defined in the BT specification. Each advertising element begins with a length byte that indicates the length of that field. This makes it easy to scan through all the elements in a while-loop.
The second byte in advertising element is the AD type. The predefined types are listed in Bluetooth SIG website
In this case, we are interested in types 0x06 and 0x07 that indicate incomplete/complete list of 128-bit service values. If such AD type is found, then the AD payload is compared against the known 128-bit UUID of our SPP service to see if there is a match.
After finding a match in the advertising data the client will open a connection by calling le_gap_connect. When connection is opened, the next task is to discover services and figure out what is the handle value that corresponds to the SPP_data characteristic (see XML definition of our custom service attached).
The service discovery is implemented as a simple state machine and the sequence of operations after connection is opened is summarized below:
1) Call gatt_discover_primary_services_by_uuid to start service discovery
2) Call gatt_discover_characteristics to find the characteristics in the SPP service (in FIND_SERVICE state)
3) Call gatt_set_characteristic_notification to enable notifications for spp_data characteristic (in FIND_CHAR state)
Note that in step 1) above we want to discover only services that match the specific UUID we are looking for. Another option would be to call cmd_gatt_discover_primary_services that will return list of all services in the remote GATT database.
After each procedure in the above sequence is completed, the stack will raise event gatt_procedure_completed. The client uses a variable main_state that is used to keep track what is the current state. The gatt_procedure_completed event will trigger the state machine to move on to the next logical state.
When notifications have been enabled the application is in transparent SPP mode. This is indicated by writing string "SPP Mode ON" to UART. After this point, any data that is received from UART is sent to server using non-acknowledged write transfer. Similarly, all data received via notifications (event gatt_characteristic_value) is copied to the local UART.
Note that on the server application there is also "SPP Mode ON" string that is printed to console. On server side this is done when the remote client has enabled notifications for the spp_data characteristic.
The data is handled transparently, meaning that the program does not care at all what values are transmitted. It can be either ASCII strings or binary data, or mixture of these. The connection can be closed by pressing either of the pushbuttons on the client.
Running the demo
Simplest way to run the demo is to use two WSTKs with the same radio board (for example two BGM13S radio boards). In this case you can use the same binary for both boards.
When the application boots, it checks the state of pushbuttons PB0, PB1. If buttons are not pressed, the application starts in SPP server role.
By keeping either PB0 or PB1 pressed during reboot the application starts in SPP client mode.
In server mode, the device advertises the custom SPP service and waits for incoming connections.
In client mode, the device starts scanning and searches for the custom SPP UUID in the scan responses. If match is found, the client connects to the target, discovers the SPP service and characteristics and then enables notifications for the SPP_data characteristic. At this point, any data that is input in the client side is sent over the air to the server and printed on the remote UART. Similarly, any data input to the server UART is transmitted back to the client.
To connect to the kit using terminal program use the following UART settings: baud rate 115200, 8N1, no flow control.
NOTE: Make sure that you are using the same baud rate and flow control settings in your starter kit and radio board or module firmware as well as your terminal program. For WSTK, this can be checked in
The above screencapture illustrates what happens when the client and server boards are powered up. Client is on the left side and server on the right. Quickly after power up the client will find the server and open a connection automatically. When the connection is set up properly and notifications are enabled then both applications will output string "SPP Mode ON". This indicates that the transparent serial connection is open. From this point on, any data you type into the client terminal will appear on the server terminal and vice versa.
You can try pressing reset button on either of the boards and see what happens. The connection should be restored automatically when both units are back online. Pressing either of the buttons on the client board will close the connection and print the stats for the last session to the terminal.
Power management
USART peripheral is not accessible in EM2 sleep mode. For this reason, both the client and the server applications disable sleeping (EM2 mode) temporarily when the SPP mode is active. SPP mode in this context means that the client and server are connected and that the client has enabled notifications for the SPP_data characteristics.
When SPP mode is entered, the code calls SLEEP_SleepBlockBegin(sleepEM2) to temporarily disable sleeping. When connection is closed (or client disables notifications) then SLEEP_SleepBlockEnd(sleepEM2) is called to re-enable sleeping.
For more details on the power management details, see article here.
Known issues
This example implementation does not guarantee 100% reliable transfer. The implementation uses retargetserial driver for reading data from UART. The driver is found in <project_root>/hardware/kit/common/drivers/ . For incoming data, the driver uses a FIFO buffer whose size is defined using symbol RXBUFSIZE (default value 8).
To get more reliable operation, it is recommended to increase the RXBUFSIZE value. However, even with a large FIFO buffer, there is a chance that some data is lost if the data rate is very high. If the FIFO buffer in RAM becomes full, the driver will simply drop the bytes that do not fit in. More discussion about this topic is found in following forum thread: SPP-over-BLE C example for UART receive data is lost
Conclusion
This example is a simple C-based SPP implementation for BGMxxx / EFR32BG products. It is not optimized for performance or low power. The code has been kept as simple as possible.
UART input/output is handled using stdio retarget functions (see this article for details.)
This is not the most efficient way to handle UART input/output but the benefit is that it is portable and allows the sample code to be used easily on several radio boards without any modifications.
KBA_BT_0603: Adding Gecko Bootloader to Bluetooth projects
Bluetooth projects do not include Gecko Bootloader by default, so you have to add it separately!
In Bluetooth SDKs between v2.0 and v2.6:
In Bluetooth SDK v2.7 or later
Devices are shipped with preprogrammed bootloaders:
To add a Gecko Bootloader to your Bluetooth project do the followings:
First method
(click New Project button in Simplicity Studio, select Gecko Bootloader, select Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader)
Note: bootloader-uart-bgapi-combined.s37 contains the first+second stage of the Gecko Bootloader, while bootloader-uart-bgapi.* contains only the second stage. The first+second stage is needed when flashing the bootloader the first time, while the second stage is needed when upgrading the bootloader (either by flashing or by DFU).
Second method
or
C/C++ build > Settings > GNU ARM C Linker > Miscellaneous > Other objects
(click New Project button in Simplicity Studio, select Gecko Bootloader, select Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader)
Third method
(click New Project button in Simplicity Studio, select Gecko Bootloader, select Bluetooth in-place OTA DFU Bootloader or BGAPI UART DFU Bootloader)
Fourth method (If you have SDK v2.7 or later)
Note: commander.exe can be found in: C:\SiliconLabs\SimplicityStudio\v4\developer\adapter_packs\commander\
Warning: for SDKs before v2.4.0 the second and third methods work only if you are using IAR as compiler. GCC is fully supported from v2.4.0.
Warning: in SDK v2.3.3 the postbuild step of the bootloader project may fail, and -combined.s37 file may be missing. In this case open the bootloader project, go to "Project > Properties > C/C++ Build > Settings > Build steps", and put the script path between quotes. Rebuild the bootloader project.
KBA_BT_0904: Creating an Eddystone-URL Beacon
Introduction
This article describes how to create an Eddystone beacon using Silicon Labs' BlueGecko-based SoCs and modules.
Eddystone Beacons
The Eddystone format defines four different packet types. This article describes how to implement the Eddystone-URL packet. The Eddystone-URL packet advertises a URL in compressed format.
URL Beacon Implementation
The first step in creating the beacon is to define our custom user data to be advertised:
Now we need to call the API
to set this data to be advertised. The first two parameters are the advertising set handle and a value indicating that the data is to be used in advertising packets.
Then we are going to set the transmit power. The Eddystone-URL packet format includes the transmit power in the 10th byte. This data can be used by receivers of the beacon to determine distance so it is important that this data and the actual transmit power match.
The following call sets the minimum and maximum advertising interval to 100 ms ( 625 us * 160). We use all three advertising channels by default (37, 38, 39).
The device is now ready to start advertising the Eddystone-URL beacon data. This is accomplished by making the following call:
This tells the stack to use the custom user data and to make the beacon non-connectable.
Running the Example
To setup this example, follow these steps:
The Eddystone beacon should now be broadcasting. You can verify this with a mobile app such as the BlueGecko App.
Conclusion
This article is a brief introduction to an implementation of the Eddystone-URL packet format, for more detailed information please see the links below.
Additional Reading