This article includes a code example that implements a Bluetooth mesh provisioner that can be run on one of our Bluetooth capable development kits.
To understand how the provisioning and configuration of mesh nodes works, it is recommended that you first try out the mesh Android app as explained in the quickstart guide QSG148.
The example presented in this article can perform the same operations as the mobile app but it is more autonomous and requires only minimal input from the user. When the provisioner sees an unprovisioned beacon, it prints the UUID of this device to the debug console and then you can either:
- push button PB1 to provision and configure the device (*
- push button PB0 to ignore this beacon and continue scanning for other beacons (*
*) Alternatively, you can press key 'y' / 'n' on the terminal to provision or ignore the found device (starting from version v6)
The purpose of this example is to show how the Silicon Labs BT mesh stack can be used in provisioning mode. The main steps in provisioning and configuration are summarized below, along with a list of related API calls.
NOTE: This example is intended only for small scale experimental use. It is not meant to be used as a basis for any production use and the code.
The provisioning data is stored in the EFR32 internal flash and has limited capacity, the maximum number of nodes this example can support is around 10-15
the max number of SIG and vendor models is limited to 16 and 4, respectively (see definition of struct tsDCD)
This example is missing many important features that are needed in a real production quality provisioner, such as:
key refresh, blacklisting of devices
removing nodes from the network
The main steps included in provisioning a device into the network and configuring the node are summarized in the following table. This is just a compact list of what operations and API calls are involved in the process. More detailed information about provisioning and configuration in Bluetooth mesh can be found in:
Mesh Profile specification by Bluetooth SIG, rev 1.0 (Chapter 5 Provisioning)
|Step||Description||Related API calls and events|
|1. Initialize provisionier mode||Initialize the Mesh stack in provisioner role. (wait for event||gecko_cmd_mesh_prov_init, gecko_evt_mesh_prov_initialized_id|
|2. Create keys||Application and network keys need to be created (unless they already exist)||gecko_cmd_mesh_prov_create_network, gecko_cmd_mesh_prov_create_appkey|
|3. Scanning||Start scanning for unprovisioned beacons||gecko_cmd_mesh_prov_scan_unprov_beacons, gecko_evt_mesh_prov_unprov_beacon_id|
|4. Start provisioning||Start provisioning, using either PB-ADV or PB-GATT bearer *||gecko_cmd_mesh_prov_provision_device, gecko_cmd_mesh_prov_provision_gatt_device, gecko_evt_mesh_prov_device_provisioned_id|
|5. Read DCD||Read the device composition data (DCD) from the freshly provisioned node||gecko_cmd_mesh_config_client_get_dcd, gecko_evt_mesh_config_client_dcd_data_id, gecko_evt_mesh_config_client_dcd_data_end_id|
|6. Send appkey||Push an application key to the node.||gecko_cmd_mesh_config_client_add_appkey, gecko_evt_mesh_config_client_appkey_status_id|
|7. Bind appkey to model(s)||Bind a model to an application key.||gecko_cmd_mesh_config_client_bind_model, gecko_evt_mesh_config_client_binding_status_id|
|8. Publish settings||Set a model's publication address, key, and parameters.||gecko_cmd_mesh_config_client_set_model_pub, gecko_evt_mesh_config_client_model_pub_status_id|
|9. Subscribe settings||Add an address to a model's subscription list.||gecko_cmd_mesh_config_client_add_model_sub, gecko_evt_mesh_config_client_model_sub_status_id|
The sample code is attached in a zip file. The zip file includes a readme.md file that explains step-by-step how this code can be installed on top of one of the examples in the Bluetooth mesh SDK.
The Silicon Labs Bluetooth Mesh SDK performs consistency checks on the non-volatile storage areas, that hold information such as keys, at startup. These areas are not erased when flashing a new application. If the application on the EFR32 is changed from embedded provisioner to something else, like a light or switch node, or from such a node to embedded provisioner, the entire contents of flash must be erased to avoid errors during initialization. This can be accomplished with Simplicity Commander
(commander device recover)
|v8||December 20, 2019||update to app.c format|
|May 10th||Added call to gecko_bgapi_class_mesh_prov_init|
|v6||February 28, 2019||Example modified to use the new API calls (introduced in SDK 1.4.0) and all deprecated calls removed. Additionally some refactoring of the code + minor improvements.|
|v5||January 22, 2019||Modified to work with Mesh SDK 1.4.0. Max number of SIG models increased to 16. Some sanity checks added.|
|v4||September 14, 2018||Modified to work with Mesh SDK 1.3.x.|
|v3||June 11, 2018||Option to provision using PB-GATT, Option to use fixed application and network keys|
|v2||May 31, 2018||First draft (based on code package that was earlier posted under a discussion thread|
In order for the provisioner to configure the nodes there are a few requirements that must be met.
Firstly, the config client must be added to the device composition data (DCD) as shown
The memory configuration tab contains several settings which must be modified as well. These parameters should all be set to non-zero, the default values for each are zero.
max provisioned devices
max provisioned device netkeys
max foundation client commands
For detailed descriptions of these settings please refer to section 3 of UG366.
This is a tutorial on how to perform a Device Firmware Upgrade (DFU) with Bluetooth over-the-air (OTA) upgrade. The OTA upgrade follows the Silicon Labs OTA procedure that is described in application note AN1086. Most of the example applications provided in the Bluetooth SDK already have OTA support built into the code. In these examples, the DFU mode is triggered through the Silicon Labs OTA service that is included as part of the application’s GATT database. This can be checked and added in the BLE GATT Configurator in Simplicity Studio.
For demonstration purposes, we are going to upgrade the SoC - Empty SDK example application to the SoC - Smart Phone App example application. This allows us to easily verify that the functionality of the user application has changed as the smartphone example has the Health Thermometer service and uses the LCD for visual feedback.
Once in the app,
After the OTA process has finished, verify that the kit is now running the SoC - Smart Phone App example. You can find the kit in the Bluetooth Browser with a new name like "BGXXXXX". The name is also shown on the LCD.
To enable Bluetooth OTA upgrade, the target device must be programmed with Gecko Bootloader that is configured as Internal Storage Bootloader. This is an application bootloader and it requires that the new firmware image acquisition is managed by application. Running the "Demos" in Simplicity Studio will include the bootloader and user application. However, flashing a "Software Example" does not. If your OTA upload stops at 0 % and you get a message on Android saying "GATT CMD STARTED", then that might indicate a missing or incorrect bootloader. In this case do the following:
Bluetooth Low Energy is a powerful technology, but not always the easiest to understand and use effectively. It is not like classic Bluetooth where you have a predefined set of official profiles to choose from; although there are predefined (a.k.a. "adopted") profiles specified by the Bluetooth SIG, these are just the tip of the iceberg, a small subset of the kind of functionality you can achieve with BLE.
In many (or even most) cases, the best option is to create your own custom profile(s) for your application. This gives you ultimate flexibility, and it doesn't even cost anything. In fact, it can be even easier than using one of the adopted profiles, since you get to define exactly how everything works together rather than conforming your application into something that is already defined. Also, since there is no official generic "serial port profile" in the BLE world like SPP in classic Bluetooth, sometimes a custom implementation is the only option to do what you need.
In order to do this effectively, it is important to understand how a BLE connection works, what roles are played by the devices involved, and how exactly data is transferred from one device to the other over the air. Many terms are used, and they are usually not interchangeable: master, slave, central, peripheral, client, server, advertise, scan, read, write, notify, and indicate all mean different things. Knowing the difference will make it easier to describe and build your BLE application.
Here's what this article covers:
One important concept in BLE connectivity is the difference between a master device and a slave device. What do each of these terms imply? First, realize that they are not interchangeable with client/server, which will be explained below. Actually, in the BLE world, the master/slave difference is very easy to define and recognize:
These pairs of terms are the only ones in the list above which are actually interchangeable. References to a "master" or "central" device are describing the same thing, and references to a "slave" or "peripheral" device are also each describing the same thing. In the CoreBluetooth APIs provided by Apple for iOS development, the "Central" and "Peripheral" nomenclature is used; you will not generally see references to master or slave devices in this context.
The BLE specification does not limit the number of slaves a master may connect to, but there is always a practical limitation, especially on small embedded modules. Our 2.8.1 BLE stack can support up to 8 simultaneous connections device, when properly configured (the default is 4). Our stack supports dual-topology and multi-master connections (which are part of the Bluetooth 4.1 spec) which means that a device can be simultaneously a master and a slave and it can also connect to multiple masters a slave device. The connection limit applies to the total number of connections regardless of the role.
The connection role, whether a device is a master or slave, is defined the moment the connection is made. Our stack is capable of acting either as a master or as a slave device. If a device is operating as a slave, it needs to advertise (accomplished in our BLE stack with the gecko_cmd_le_gap_start_advertising command); if it is operating as a master, it will optionally scan for devices (accomplished in our stack with gecko_cmd_le_gap_start_discovery) and initiate a connection request to another device (accomplished in our stack with gecko_cmd_le_gap_connect).
Another important concept in a BLE design is the difference between a GATT server and a GATT client (where GATT means Generic ATTribute profile). These roles are not mutually exclusive, though typically your device will only be one or the other. Which role(s) your device takes depends on how you need it to work. Here is a basic summary of each kind of functionality:
Unlike the master/slave distinction defined previously, it is easy to see that one device might actually be both of these things at the same time, based on how your application defines the data structure and flow for each side of the connection. While it is most common for the slave (peripheral) device to be the GATT server and the master (center) device to be the GATT client, this is not required. The GATT functionality of a device is logically separate from the master/slave role. The master/slave roles control how the BLE radio connection is managed, and the client/server roles are dictated by the storage and flow of data.
Most of our example projects in the SDK archive and online implement slave (peripheral) devices designed to be GATT servers. These are easy to test with our Blue Gecko App (available for Android and iOS). However, there are also a few examples which implement the master (central) end of the connection, designed to be GATT clients.
Now that we have established the difference between a master and slave device, and a GATT client and GATT server, how do we actually define the GATT structure, and how do we use it to move data from Device A to Device B, whether that means server-to-client or client-to-server? Both directions are easily possible, of course.
In BLE projects built using our Bluetooth SDK, the GATT structure can be configured using the built-in tool from Simplicity Studio, called the Visual GATT Editor. This can be found on the General or on the Bluetooth Configurator tab of the Application Builder. The configuration file that stores the GATT structure beside other configurations can be found inside the project-folder denoted by .isc-extension. Once you press Generate in the Visual GATT Editor, the gatt_db.c/.h and the gatt.xml files are generated.
The structure and flow of data is always defined on the GATT server, and the client simply gets to make use of whatever is exposed by the server.
If you are using the IAR Embedded Workbench refer to Profile Toolkit Developer Guide.
A GATT database implements one or more profiles, and each profile is made up of one or more services, and each service is made up of one or more characteristics. For example, in outline form:
You can implement as many profiles, services, and characteristics as you need. These may be entirely customized, in which case they would use your own 128-bit UUIDs generated by the Visual GATT editor. Or, you can create a project which implements adopted specifications by referencing the Bluetooth SIG's online definitions of profiles, services, and characteristics. (If you do this, it usually makes the most sense to start at the profile level and drill down from there, since the full list of characteristics includes many things that will undoubtedly be irrelevant to your design.) You can combine these two as well, using some adopted profiles/services and some of your own proprietary ones.
Every BLE device acting as a GATT server must implement the official Generic Access service. This includes two mandatory characteristics: Device Name and Appearance. These are similar to the Friendly Name and Class of Device values used by classic Bluetooth. Here is an example definition of an absolutely minimal GATT definition as shown in the Visual GATT editor:
This alone is enough to work with, but it won't let you do anything useful other than tell the GATT client who and what you are. We still need to explore basic data transfer methods that we can use. First though, one minor clarification between attributes and characteristics.
You may occasionally hear or see the terms "attribute" and "characteristic" used interchangeably, and while this isn't entirely wrong, it isn't totally accurate and it can be confusing. Remember that a service is made up of one or more characteristics. However, one single characteristic--generally the most specific level down to which we define our GATT structure--may be comprised of many different attributes.
Each attribute is given a unique numerical handle which the GATT client may use to reference, access, and modify it. Every characteristic has one main attribute which allows access to the actual value stored in the database for that characteristic. When you read about "writing a value to this characteristic" or "reading that characteristic's value," the read/write operations are really being done to the main data attribute.
Some other related attributes are read-only (such as a Characteristic User Description attribute), some control the behavior of the characteristic (such as the Client Characteristic Configuration attribute which is used to enable notify or indicate operations). Our BLE stack and SDK tools generate these as necessary based on the settings configured in the Visual GATT Editor.
Every attribute has a UUID. These may be either 16 bits (e.g. "180a") or 128 bits (e.g. "e7add780-b042-4876-aae1-112855353cc1"). All 16-bit UUIDs are defined by the Bluetooth SIG and are known as adopted UUIDs. All 128-bit UUIDs are custom and may be used for any purpose without approval from the Bluetooth SIG. Two very common 16-bit UUIDs that you will see are 2901, the Characteristic User Description attribute (defined in the User description field of the GATT Editor), and 2902, the Client Characteristic Configuration attribute (created by our SDK when either "notify" or "indicate" is enabled on a characteristic).
One important note is that some attribute UUIDs do not technically need to be unique. Their handles are always unique, but the UUIDs occasionally overlap. For example, every Client Characteristic Configuration attribute has a UUID of 0x2902, even though there may be a dozen of them in a single GATT database. You should always give your own custom characteristics fully unique UUIDs, but don't be alarmed if you are testing out your prototype with BGTool, you perform a Descriptor Discovery operation, and suddenly see multiple instances of the same UUID for certain things. This is normal.
There are four basic operations for moving data in BLE: read, write, notify, and indicate. The BLE protocol specification allows that the maximum data payload size for these operations is 247 bytes, or in the case of read operations, 249 bytes. BLE is built for low power consumption, for infrequent short-burst data transmissions. Sending lots of data is possible, but usually ends up being less efficient than classic Bluetooth when trying to achieve maximum throughput. Here are a few general guidelines about what kind of data transfer you will need to use:
The above four BLE data transfer operations are described here. Commands which you must send are shown separately from the events which the stack generates. Complete API reference can be found for example through Simplicity Studio Launcher on the Documentation tab.
This operation is requested by the GATT client on a specific attribute exposed by the GATT server, and the server then responds with the requested value. In our BLE stack, these API methods are typically involved in read operations:
This operation is requested by the GATT client on a specific attribute exposed by the GATT server, and a new value to write is provided at the same time. The server then stores the new value and (optionally) acknowledges the write operation back to the client. In our BLE stack, these API methods are typically involved in write operations:
Here is an example of the flow of queueing writes:
This operation is initiated by the server when a new value is written to a notify-enabled characteristic. If the client has subscribed to notifications on that characteristic, then the new value is pushed to the client when it is written. Notifications are not acknowledged, hence you may send more than one notification in a single connection interval, which can be helpful maximizing throughput. Notifications cannot be enabled by the server; they must be enabled by the client in order for the actual data transmission to occur. In our BLE stack, these API methods are typically involved in notify operations:
An indicate operation is identical to a notify operation except that indications are acknowledged, while notifications are not. This increases reliability at the expense of speed. In our BLE stack, these API methods are typically involved in indicate operations:
Typically, the GATT server functionality is provided by one side and the client functionality is entirely on the other side, but it could be that both sides provide both kinds of functionality. This does not usually pose any advantages for a well-designed and efficient BLE project — it usually complicates the implementation needlessly — so we will not go into it here.