Discovering the GATT database of a remote device every time a connection is made takes a lot of time and energy. To avoid this, most Bluetooth devices use attribute caching, i.e. once they discovered the GATT database of a remote device, they store the discovered attribute handles for future use (in other words they store a local copy of the remote database structure, or at least parts of it). This works well as long as the remote device keeps its GATT database structure unchanged. If the database structure changes - e.g. because of a firmware update that introduces new features - the stored attribute handles become invalid.
There are two methods to notify peer devices about the change:
using Service Change Indication
using GATT Caching feature
Service Change Indication was introduced in Bluetooth 4.0. This solution is discussed in the following article: KBA_BT_0312: Service Change Indications. The main disadvantage of this method is, that it works only if the peer devices are bonded.
GATT caching feature was introduced in Bluetooth 5.1 to overcome the bonding problem. Using GATT caching, every device can make sure, if it stores the latest version of the GATT database structure. This is achieved by:
Adding two new characteristics to the Generic Attribute service (Database Hash and Client Supported Features)
Adding a new GATT error code (database out-of-sync)
Database Hash characteristic
The Database Hash characteristic stores a 128bit value, which is a AES-CMAC hash calculated from the database structure. Any little change in the database structure will result in a different hash value. After discovering the database once, the client should store this value. Next time, when the client connects to the server, it should only check if the Database Hash has changed. If it hasn't, it is safe to assume, that the database structure in unchanged, and the cached attribute handles are still valid. If it has changed, the client need to rediscover the GATT database.
Note: The Database Hash must be read using GATT Read Using Characteristic UUID sub-procedure gecko_cmd_gatt_read_characteristic_value_by_uuid()
Database out-of-sync error code
If the client enables Robust Caching, by setting the Robust Caching bit to 1 in the Client Supported Features characteristic, then the server can send a database out-of-sync error code as a response to a GATT operation whenever it assumes, that the client has an out-of-date database.
If the client and the server are bonded, then the server checks if the database has changed since the last connection and notifies the client about the change via Service Change Indication - just like before introducing GATT caching. The only difference is, that if the client initiates a GATT operation before the indication is received and confirmed, the server can send a database out-of-sync error code. This helps avoid race condition if the client would like to initiate a GATT operation too quickly after connection establishment.
If the client and the server are not bonded, then the server assumes, that the client checks the Database Hash and/or discovers the GATT database upon each connection before initiating any GATT operation. I.e. the server assumes that the client is aware of database changes after the connection was established. If there is a change in the database during connection, the client gets change-unaware. At the next GATT operation the server sends a database out-of-sync error code to the client to signal this (except if the client reads the Database Hash meanwhile). From the next GATT operation the server assumes that the client is change-aware again.
Implementation
Server
To enable GATT caching functionality on the server side you have to enable Generic Attribute Service in the GATT database. To do so
Open the GATT Configurator by opening the .isc file in you Bluetooth project
Click on the Profile (by default it is Custom BLE GATT) in the GATT database structure
In the GATT settings tick the Generic Attribute Service checkbox. You will not see the service in the GATT database, but it will be added to generated gatt_db.c
Press Generate
Client
To enable GATT caching on the client side, you have to
Read the Database Hash characteristic upon each connection and compare its value with the last stored value:
Check for database out-of-sync error after each GATT operation
if (evt->data.evt_gatt_procedure_completed.result == bg_err_att_out_of_sync)
printLog("database has changed!\r\n");
Example
In the attached example we implement a server that can change its GATT database structure runtime, and a client that can recognize the database change.
The server uses the polymorphic GATT feature, i.e. it can enable and disable some if its services / characteristics runtime. Here we define two custom services in the database, from which only one is enabled at a time.
From the client side we always want to write to the "My Custom Characteristic" characteristic, but its characteristic handle will change with the version of the database. So we have to detect the version of the database, and write to the proper handle. Also, we have to recognize if the database changes, and change the handle according to it.
To run the example:
Create 2 new soc-empty projects, one for the server, and one for the client
import gatt_server.xml into the server project. Open the GATT Configurator, click the import button on the right side, select the file, and after successful import, press Generate.
copy app.h into both projects (overwriting the existing app.h)
remove app.c from both projects
copy app_server.c into the server project
copy app_client.c into the client project
copy C:\SiliconLabs\SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v2.x\platform\emdrv\gpiointerrupt\src\gpiointerrupt.c into the server project
build and flash the two projects to two WSTKs with a radio board
open two terminals on your PC and connect to the virtual COM ports to see the logs
reset both WSTKs
press PB0 / PB1 on the server side WSTK to switch between the two GATT database versions
Periodic advertising is a Bluetooth 5.0 feature based on extended advertisements. It allows non-connectable advertisements to be sent at fixed interval of time where advertising data can change between those intervals. One or more scanners can then listen for such advertisements. It is a form of multicast.
Some points to know about Periodic Advertisements:
Although extended advertiser does not need to be enabled when periodic advertising is enabled, the only way to get the pointer to the Periodic Advertisement train is through the SyncInfo field of the extended advertiser hence starting extended advertisement is mandatory.
At least one advertisement packet needs to be sent for periodic advertisements to be enabled.
It will use the same PHY as the auxiliary packet. Each periodic advertiser has same parameters as in connection. Additionally, the channel is also determined in the same way as in connection using the Channel Selection Algorithm #2.
Since it is based on extended advertisements, it uses data channels as opposed to advertisements channels. The overall process is shown in this figure.
Periodic advertisement modes
Advertiser
Advertising device is sending periodic advertisement train.
Scanner
User will obtain address and SID (advertising set ID) values by scanning. Device will synchronize with periodic advertisement train with user specifies address and SID. Synchronization will only happen when scanning is enabled. When device has received at least one AUX_SYNC_IND packet it is in synchronized mode. Device will then report received AUX_SYNC_IND packets. After device has synchronized, scanning can be stopped.
Note: Periodic advertising is a new feature; it's not yet implemented in Phones yet. Hence, periodic advertisement packets would not be visible in mobile platforms and in Bluetooth scanning apps.
Periodic advertisement mode is indicated with the ADV_EXT_IND packets, which point to AUX_ADV_IND packet, containing the actual information about the periodic advertisement mode, such as interval, hopping sequence, advertiser address, and so on. The advertiser will also send AUX_SYNC_IND packets at the identified interval containing the actual periodic advertisement data. The AUX_SYNC_IND along with AUX_CHAIN_IND make up a sequence of advertisements forming a periodic advertisement train. The advertiser will periodically send new AUX_ADV_IND packets, so that new scanners can synchronize to the data stream or existing scanners can resume a lost sync. The Bluetooth stack sends AUX_ADV_IND packet before every periodic advertisement so that new scanners will be able to synchronize quickly.
To add periodic advertising functionality to your application use the commands listed below. Refer the sample application attached to this document for implementation details. For details of each command please refer to the Bluetooth Software API Reference Manual.
Advertiser
max_advertisers in the Bluetooth configuration structure also configures the maximum number of periodic advertisers. Usually the structure is found in main.c file.
#else
.sleep.flags = 0,
#endif // LFXO
.bluetooth.max_connections = MAX_CONNECTIONS, /* Maximum number of simultaneous connections */
.bluetooth.max_advertisers = MAX_ADVERTISERS, /* Maximum number of advertisement sets */
.bluetooth.heap = bluetooth_stack_heap, /* Bluetooth stack memory for connection management */
The MAX_ADVERTISERS can be overwritten in app.c using
void appMain(gecko_configuration_t *pconfig)
{
#if DISABLE_SLEEP > 0
pconfig->sleep.flags = 0;
#endif
/* Set maximum number of periodic advertisers */
pconfig->bluetooth.max_advertisers = 1;
Feature Initialization
On the advertiser side, you can initiate using the periodic advertisement using this command. Please note this command should be called after gecko_init().
gecko_init_periodic_advertising()
Enable/Disable Periodic advertising
This command can be used to start periodic advertisement on the given advertisement set. The stack will enable the advertisement set automatically if the set has not been enabled and the set can advertise using extended advertising PDUs.
This command sets the pointers for the data to be sent in Periodic advertisement. Make sure to execute this command every time you want to change the periodic advertisement data. If periodic advertising is currently disabled for the specified advertising set, the data shall be kept by the Controller and used once periodic advertising is enabled for that set. The data shall be discarded when the advertising set is removed.
The value of scan_rsp for Periodic advertisements is 8.
The maximum data length is 191 bytes for extended advertising.
Periodic advertisement has the highest priority in the current implementation (BLE SDK 2.11.1.0). For collisions, Periodic advertisements is always overriding other advertisements. If you see collisions, try increasing the interval of advertisements.
Scanner
max_periodic_sync in the Bluetooth config is used to configure the maximum number of synchronizations the Bluetooth stack needs to support. Usually the config is mentioned in main.c however, MAX_PERIODIC_SYNC can be overwritten in app.c using
void appMain(gecko_configuration_t *pconfig)
{
#if DISABLE_SLEEP > 0
pconfig->sleep.flags = 0;
#endif
/* Set maximum number of periodic advertisers */
pconfig->bluetooth.max_periodic_sync = 1;
Feature Initialization
To enable Periodic Advertisement Synchronization in the Bluetooth stack, the following initialization function must be called after the generic gecko_init function
void gecko_bgapi_class_sync_init()
Enable extended scan response
This command can be used to enable or disable the extended scan response event.
This command can be used to establish a synchronization with periodic advertisement from the specified advertiser and begin receiving periodic advertisement packets. More details here.
The adv_sid parameter specifies the value that must match the Advertising SID subfield in the ADI (Advertise Data Info) field of the received advertisement for it to be used to synchronize. It can be obtained from the event gecko_evt_le_gap_extended_scan_response_id
The skip parameter specifies the maximum number of consecutive periodic advertisement events that the receiver may skip after successfully receiving a periodic advertisement packet. The timeout parameter specifies the maximum permitted time between successful receives. If this time is exceeded, synchronization is lost.
The advertiser address_type, and advertiser address parameters specify the periodic advertising device to listen to. It can be obtained from the event gecko_evt_le_gap_extended_scan_response_id
Successfully synchronization leads to the generation of this event:
gecko_evt_sync_opened_id
Whenever a periodic advertisement packet is received, this event is generated:
gecko_evt_sync_data_id
Close synchronization
This command can be used to close a synchronization with periodic advertisement or cancel an ongoing synchronization establishment procedure.
gecko_cmd_sync_close(uint8 sync)
where sync is the Periodic advertisement synchronization handle.
When the synchronization is closed, lost or when an ongoing sync process is cancelled this event is generated:
gecko_evt_sync_closed_id
Test setup
We will use 2 devices (WSTK radio boards) to demonstrate this feature. This feature works with any Silicon Labs radio chip which supports Bluetooth 5. This functionality outlined in this KBA was implemented using
Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of advertiser_app.c file attached to this KBA. This program generates a sequence of random numbers every second as the data for periodic advertisement. This data is also visible on the console.
Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of scanner_app.c file attached to this KBA. This program looks for the synchronizeSync service in the scanned advertisement data.
Set DEBUG_LEVEL to 1 in app.h to enable printing on the console on both devices. Observe the data broadcasting on the advertiser and being reflected on the scanner.
Observations
Use the energy profiler in Simplicity studio to evaluate the current consumption. The scanner goes into energy saving mode and wakes up every 200ms to receive sync packets from the advertiser. The advertiser sleeps when not advertising as shown in the figure below.
Code assembly instructions:
As mentioned above, we will use 2 devices (WSTK radio boards) to demonstrate this feature.
Advertiser: Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of advertiser_app.c file attached to this KBA. In the .isc file click Custom BLE GATT Profile and click import icon on the right to import the attached gatt.xml file. This program generates a sequence of random numbers every second as the data for periodic advertisement. This data is also visible on the console.
Scanner: Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of scanner_app.c file attached to this KBA. This program looks for the Health Thermometer service in the scanned advertisement data.
Set DEBUG_LEVEL to 1 in app.h to enable printing on the console on both devices. Observe the data broadcasting on the advertiser and being reflected on the scanner.
Bluetooth Knowledge Base
KBA_BT_0313: GATT Caching
Introduction
Discovering the GATT database of a remote device every time a connection is made takes a lot of time and energy. To avoid this, most Bluetooth devices use attribute caching, i.e. once they discovered the GATT database of a remote device, they store the discovered attribute handles for future use (in other words they store a local copy of the remote database structure, or at least parts of it). This works well as long as the remote device keeps its GATT database structure unchanged. If the database structure changes - e.g. because of a firmware update that introduces new features - the stored attribute handles become invalid.
There are two methods to notify peer devices about the change:
Service Change Indication was introduced in Bluetooth 4.0. This solution is discussed in the following article: KBA_BT_0312: Service Change Indications. The main disadvantage of this method is, that it works only if the peer devices are bonded.
GATT caching feature was introduced in Bluetooth 5.1 to overcome the bonding problem. Using GATT caching, every device can make sure, if it stores the latest version of the GATT database structure. This is achieved by:
Database Hash characteristic
The Database Hash characteristic stores a 128bit value, which is a AES-CMAC hash calculated from the database structure. Any little change in the database structure will result in a different hash value. After discovering the database once, the client should store this value. Next time, when the client connects to the server, it should only check if the Database Hash has changed. If it hasn't, it is safe to assume, that the database structure in unchanged, and the cached attribute handles are still valid. If it has changed, the client need to rediscover the GATT database.
Database out-of-sync error code
If the client enables Robust Caching, by setting the Robust Caching bit to 1 in the Client Supported Features characteristic, then the server can send a database out-of-sync error code as a response to a GATT operation whenever it assumes, that the client has an out-of-date database.
If the client and the server are bonded, then the server checks if the database has changed since the last connection and notifies the client about the change via Service Change Indication - just like before introducing GATT caching. The only difference is, that if the client initiates a GATT operation before the indication is received and confirmed, the server can send a database out-of-sync error code. This helps avoid race condition if the client would like to initiate a GATT operation too quickly after connection establishment.
If the client and the server are not bonded, then the server assumes, that the client checks the Database Hash and/or discovers the GATT database upon each connection before initiating any GATT operation. I.e. the server assumes that the client is aware of database changes after the connection was established. If there is a change in the database during connection, the client gets change-unaware. At the next GATT operation the server sends a database out-of-sync error code to the client to signal this (except if the client reads the Database Hash meanwhile). From the next GATT operation the server assumes that the client is change-aware again.
Implementation
Server
To enable GATT caching functionality on the server side you have to enable Generic Attribute Service in the GATT database. To do so
Client
To enable GATT caching on the client side, you have to
Example
In the attached example we implement a server that can change its GATT database structure runtime, and a client that can recognize the database change.
The server uses the polymorphic GATT feature, i.e. it can enable and disable some if its services / characteristics runtime. Here we define two custom services in the database, from which only one is enabled at a time.
From the client side we always want to write to the "My Custom Characteristic" characteristic, but its characteristic handle will change with the version of the database. So we have to detect the version of the database, and write to the proper handle. Also, we have to recognize if the database changes, and change the handle according to it.
To run the example:
Now you should observe something like this:
KBA_BT_0207: Periodic Advertising
Introduction
Periodic advertising is a Bluetooth 5.0 feature based on extended advertisements. It allows non-connectable advertisements to be sent at fixed interval of time where advertising data can change between those intervals. One or more scanners can then listen for such advertisements. It is a form of multicast.
Some points to know about Periodic Advertisements:
Although extended advertiser does not need to be enabled when periodic advertising is enabled, the only way to get the pointer to the Periodic Advertisement train is through the SyncInfo field of the extended advertiser hence starting extended advertisement is mandatory.
At least one advertisement packet needs to be sent for periodic advertisements to be enabled.
It will use the same PHY as the auxiliary packet. Each periodic advertiser has same parameters as in connection. Additionally, the channel is also determined in the same way as in connection using the Channel Selection Algorithm #2.
Since it is based on extended advertisements, it uses data channels as opposed to advertisements channels. The overall process is shown in this figure.
Periodic advertisement modes
Advertiser
Advertising device is sending periodic advertisement train.
Scanner
User will obtain address and SID (advertising set ID) values by scanning. Device will synchronize with periodic advertisement train with user specifies address and SID. Synchronization will only happen when scanning is enabled. When device has received at least one AUX_SYNC_IND packet it is in synchronized mode. Device will then report received AUX_SYNC_IND packets. After device has synchronized, scanning can be stopped.
Periodic advertisement mode is indicated with the ADV_EXT_IND packets, which point to AUX_ADV_IND packet, containing the actual information about the periodic advertisement mode, such as interval, hopping sequence, advertiser address, and so on. The advertiser will also send AUX_SYNC_IND packets at the identified interval containing the actual periodic advertisement data. The AUX_SYNC_IND along with AUX_CHAIN_IND make up a sequence of advertisements forming a periodic advertisement train. The advertiser will periodically send new AUX_ADV_IND packets, so that new scanners can synchronize to the data stream or existing scanners can resume a lost sync. The Bluetooth stack sends AUX_ADV_IND packet before every periodic advertisement so that new scanners will be able to synchronize quickly.
To add periodic advertising functionality to your application use the commands listed below. Refer the sample application attached to this document for implementation details. For details of each command please refer to the Bluetooth Software API Reference Manual.
Advertiser
max_advertisers in the Bluetooth configuration structure also configures the maximum number of periodic advertisers. Usually the structure is found in main.c file.
The MAX_ADVERTISERS can be overwritten in app.c using
Feature Initialization
On the advertiser side, you can initiate using the periodic advertisement using this command. Please note this command should be called after gecko_init().
Enable/Disable Periodic advertising
This command can be used to start periodic advertisement on the given advertisement set. The stack will enable the advertisement set automatically if the set has not been enabled and the set can advertise using extended advertising PDUs.
Set Periodic advertisement data
This command sets the pointers for the data to be sent in Periodic advertisement. Make sure to execute this command every time you want to change the periodic advertisement data. If periodic advertising is currently disabled for the specified advertising set, the data shall be kept by the Controller and used once periodic advertising is enabled for that set. The data shall be discarded when the advertising set is removed.
The value of scan_rsp for Periodic advertisements is 8.
The maximum data length is 191 bytes for extended advertising.
Scanner
max_periodic_sync in the Bluetooth config is used to configure the maximum number of synchronizations the Bluetooth stack needs to support. Usually the config is mentioned in main.c however, MAX_PERIODIC_SYNC can be overwritten in app.c using
Feature Initialization
To enable Periodic Advertisement Synchronization in the Bluetooth stack, the following initialization function must be called after the generic gecko_init function
Enable extended scan response
This command can be used to enable or disable the extended scan response event.
Set it 0 to disable and 1 to enable.
Establish synchronization
This command can be used to establish a synchronization with periodic advertisement from the specified advertiser and begin receiving periodic advertisement packets. More details here.
The adv_sid parameter specifies the value that must match the Advertising SID subfield in the ADI (Advertise Data Info) field of the received advertisement for it to be used to synchronize. It can be obtained from the event gecko_evt_le_gap_extended_scan_response_id
The skip parameter specifies the maximum number of consecutive periodic advertisement events that the receiver may skip after successfully receiving a periodic advertisement packet. The timeout parameter specifies the maximum permitted time between successful receives. If this time is exceeded, synchronization is lost.
The advertiser address_type, and advertiser address parameters specify the periodic advertising device to listen to. It can be obtained from the event gecko_evt_le_gap_extended_scan_response_id
Successfully synchronization leads to the generation of this event:
Whenever a periodic advertisement packet is received, this event is generated:
Close synchronization
This command can be used to close a synchronization with periodic advertisement or cancel an ongoing synchronization establishment procedure.
where sync is the Periodic advertisement synchronization handle.
When the synchronization is closed, lost or when an ongoing sync process is cancelled this event is generated:
Test setup
We will use 2 devices (WSTK radio boards) to demonstrate this feature. This feature works with any Silicon Labs radio chip which supports Bluetooth 5. This functionality outlined in this KBA was implemented using
Advertiser: BGM13S22
Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of advertiser_app.c file attached to this KBA. This program generates a sequence of random numbers every second as the data for periodic advertisement. This data is also visible on the console.
Scanner: BGM13P22
Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of scanner_app.c file attached to this KBA. This program looks for the synchronizeSync service in the scanned advertisement data.
Set DEBUG_LEVEL to 1 in app.h to enable printing on the console on both devices. Observe the data broadcasting on the advertiser and being reflected on the scanner.
Observations
Use the energy profiler in Simplicity studio to evaluate the current consumption. The scanner goes into energy saving mode and wakes up every 200ms to receive sync packets from the advertiser. The advertiser sleeps when not advertising as shown in the figure below.
Code assembly instructions:
As mentioned above, we will use 2 devices (WSTK radio boards) to demonstrate this feature.
Advertiser: Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of advertiser_app.c file attached to this KBA. In the .isc file click Custom BLE GATT Profile and click import icon on the right to import the attached gatt.xml file. This program generates a sequence of random numbers every second as the data for periodic advertisement. This data is also visible on the console.
Scanner: Create an SoC - Empty example for your radio board and overwrite the contents of app.c file with the contents of scanner_app.c file attached to this KBA. This program looks for the Health Thermometer service in the scanned advertisement data.
Set DEBUG_LEVEL to 1 in app.h to enable printing on the console on both devices. Observe the data broadcasting on the advertiser and being reflected on the scanner.