Bluetooth 5 introduces several enhancements to advertising. This article discusses the concept of chained advertising.
Discussion
Chained advertising allows advertising packets to be linked or chained together to allow for larger advertisements, up to 1650 bytes, to be sent. A new packet type, AUX_CHAIN_IND, is used to transmit the individual portions of these advertisements. Chained advertising works together with extended advertising and periodic advertising. The following example allows a synchronizer, i.e. the receiver of a periodic advertisement, to synchronize to a chained periodic advertisement and begin receiving these advertisements.
Implementation
Advertiser
The maximum size of a BGAPI command is 255B, which does not allow to use one command to set 1650B of advertisement data. Therefore, version 2.12 of Silicon Labs Bluetooth SDK introduces a new API: gecko_cmd_system_data_buffer_write() to write up to 255 bytes of data to the system data buffer, in which the stacks assembles the full advertisement data. This API can be called multiple times when more than 255 bytes must be written to the buffer.
The sample code provided with this article implements a simple function, writeSystemDataBuffer(), to simplify writing up to 1650 bytes to the system data buffer. Once the desired advertising data has been written to the buffer the application starts advertising. The advertisement will include a service UUID, which the synchronizer will look for, and the sync info, on which the synchronizer can sync on. Next, the periodic advertisement is started. Finally, the data assembled in the system data buffer is transferred to the periodic advertisement by calling another new API: gecko_cmd_le_gap_set_long_advertising_data().
Scanner
The scanner, or synchronizer, begins by starting scanning for advertisements containing the ‘watchable’ service with the UUID f69dd7f9-340a-4693-9e46-c8630c898558. The first step is to request the extended scan response by calling gecko_cmd_le_gap_set_discovery_extended_scan_response(), this provides more information about the scanner than the basic scan response. Next, the scanner sets the discovery timing and type and starts discovery. Advertisements or scan responses are handled by gecko_evt_le_gap_extended_scan_response_id event handler. This event handler first filters out any packets which are not extended advertising packets. Next, the advertisement is searched for the service UUID mentioned above by calling findServiceInAdvertisement(). When an advertisement containing this UUID is found, the scanner syncs to this device by calling gecko_cmd_sync_open().
The sample code for the scanner/synchronizer includes event handlers for sync_open and sync_closed events. The sync_closed event is triggered when a sync timeout expires and is used to start scanning again. The gecko_evt_sync_opened_id event is purely informative and simply prints a message indicating that a sync has been opened. The gecko_evt_sync_data_id is triggered whenever sync data is received. This event handler handles three situations:
data received complete,
data received with more to follow and
data truncated.
The first situation occurs either when the advertisement fits in a single packet or when the last packet in a chain is received, in either case the data is saved. The second situation occurs when data is received, and more is expected. When this happens, event handler saves the data and begins reassembly the advertisement. If subsequent data is expected, but none is received, the status is set to data truncated. In this case, the sample application considers the data to be corrupt and discards it all by clearing it’s buffer.
Chained advertisements are buffered using the bluetooth_stack_heap found in main.c. The definition must be increased as shown below to provide sufficient space for the data.
The attached example requires two WSTK/radioboards.
Create a soc-empty project for your desired radio board.
Overwrite the app.c file in this project with app.c from the advertiser folder in the attached zip file.
Open main.c and modify the heap size uint8_t bluetooth_stack_heap[DEFAULT_BLUETOOTH_HEAP(MAX_CONNECTIONS)+1650];
Import gatt.xml from the advertiser folder using the ‘Import GATT’ icon in the GATT Configurator. When finished the result should look like this
Click generate.
Build and download to the first kit.
Create another soc-empty for the second kit.
Overwrite app.c in this project with app.c from the scanner folder in the attached zip file.
Open main.c and modify the heap size uint8_t bluetooth_stack_heap[DEFAULT_BLUETOOTH_HEAP(MAX_CONNECTIONS)+1650];
Open app.h and change the value of DEBUG_LEVEL from 0 to 1 to enable debug printing.
Build and download to the second board.
Open a terminal program, such as the SimplicityStudio serial console, to view the output from the device. The result should look similar to the followingShortly after starting up, the scanner discovers an advertiser that has come into range and synchronizes with it. Each gecko_evt_sync_data_id comes with a maximum of 250 bytes. The entire advertising packet contains 1650 bytes so we see the final event contains 150 bytes and displays a message indicating that the sync is complete. In the next chained advertisement, one of the advertising packet PDUs is not received, so the data is discarded. After one more successful chained advertisement, the sync is closed due to a timeout
Conclusion
Chained advertising provides a way of distributing larger amounts of data to multiple synchronizers without the need for connections. The focus of this article is chained advertising, for more information on periodic advertising in general please see our periodic advertising knowledgebase article
The sequence number, a 24-bit value contained in the sequence number field of the Network PDU, is primarily designed to protect against replay attacks. Elements within the same node may or may not share the sequence number space with each other. Having a different sequence number in each new Network PDU for every message source (identified by the unicast address contained in the SRC field) is critical for the security of the mesh network.
With a 24-bit sequence number, an element can transmit 16,777,216 messages before repeating a nonce. If an element transmits a message on average once every five seconds (representing a fairly high frequency message for known use cases), the element can transmit for 2.6 years before the nonce repeats.
Each element shall use strictly increasing sequence numbers for the Network PDUs it generates. Before the sequence number approaches the maximum value (0xFFFFFF), the element shall update the IV Index using the IV Update procedure (see Section 3.10.5). This is done to ensure that the sequence number will never wrap around.
Sequence number Storing Strategy
Sequence number should be non-volatile, software stacks should make sure that every time the device sends a valid packet, it should never use the sequence numbers which had been already used before, so it would be a good way to store it in the flash. However, writing flash once per each sequence number increment is probably too often so that the flash may easily wear out before the life time of the real product. To balance the impact, the stack will only write the sequence number to flash at a fixed interval when the sequence number increases by pstore_write_interval_elem_seq times, which is adjustable and located in the dcd.c file in your project. Given that there is a chance that the device may loss power unexpectedly before the interval, which means the real sequence number has not been written to flash, next time when the device boots, it will use the old value in the flash. To avoid the node uses any old sequence number, the node will always increase pstore_write_interval_elem_seq after reset. Besides, the stack will detect if there is valid sequence number increasing between the 2 times of reset, if no, the stack won't increase the sequence number. For more information, you can refer to the example in Figure 1.
NOTE: This is not standard but Silicon Labs' current solution and it may change in the future.
IV Index
The IV Index is a 32-bit value that is a shared network resource (i.e., all nodes in a mesh network share the same value of the IV Index and use it for all subnets they belong to).
The IV Index starts at 0x00000000 and is incremented during the IV Update procedure as described in Section 3.10.5. The timing when the value is incremented does not have to be exact, since the least significant bit is communicated within every Network PDU. Since the IV Index value is a 32-bit value, a mesh network can function approximately 5 trillion years before the IV Index will wrap.
The IV Index is shared within a network via Secure Network beacons (see Section 3.9.3). IV updates received on a subnet are processed and propagated to that subnet. The propagation happens by the device transmitting Secure Network beacons with the updated IV Index for that particular subnet. If a device on a primary subnet receives an update on the primary subnet, it shall propagate the IV update to all other subnets. If a device on a primary subnet receives an IV update on any other subnet, the update shall be ignored.
If a node is absent from a mesh network for a period of time, it can scan for Secure Network beacons (see Section 3.10.1) or use the IV Index Recovery procedure (see Section 3.10.6), and therefore set the IV Index value autonomously.
SDK – Bluetooth Mesh SDK 1.5.0 GA or newer, the latest version are always recommended.
Hardware – At least 2 Bluetooth Mesh compatible boards - EFR32xG12 or EFR32xG13 (x= M, B) based, SLWRB4104A and SLWRB4103A are recommended.
Provisioner - Bluetooth Mesh app on smartphones, or the Host Provisioner.
Tools – J-Link RTT viewer or serial terminal for print, if using serial terminal, the parameters are as below:
Baud rate: 115200
Data bits: 8
Stop bit: 1
Flow control: False
IV Update & Recovery Introduction
IV Update & Recovery Procedure
IV Update Procedure:
The sequence number is 24-bit length, as the example provided above, with 5 seconds cadence, the sequence number will repeat after 2.6 years. To avoid this, the node could start the IV update procedure to update the network to a new IV index so that the sequence number could be reset to 0. The IV update procedure can be initiated by any node in the primary subnet. Table 1 shows the summary of IV update procedure.
IV Index
IV Update Flag
IV Update Procedure State
IV Index Accepted
IV Index used when transmitting
n
0
Normal
n-1, n
n
m (m=n+1)
1
In Progress
m-1, m
m-1
m
0
Normal
m-1, m
m
Table 1: IV Update procedure summary
IV Recovery Procedure:
A node shall support the IV index recovery procedure because a node that is away from the network for a long time may miss IV Update procedures, in which case it can no longer communicate with the other nodes. In order to recover the IV Index, the node must listen for a Secure Network beacon, which contains the Network ID and the current IV Index.
IV Update & Recovery Limitations
There are some limitations on initiating IV update and recovery procedure, mentioned in chapters 3.10.5 and 3.10.6 of Mesh Profile Specification v1.0.1. Generally, as below:
If a node in Normal Operation receives a Secure Network beacon with an IV index less than the last known IV Index or greater than the last known IV Index + 42, the Secure Network beacon shall be ignored. Note: This above requirement allows a node to be away from the network for 48 weeks. A node that is away from a network for longer than 48 weeks must be reprovisioned.
A node shall not start an IV Update procedure more often when once every 192 hours.
The transition from Normal Operation state to IV Update in Progress state must occur at least 96 hours before the sequence numbers are exhausted.
After at least 96 hours and before 144 hours of operating in IV Update in Progress state, the node shall transition back to the IV Normal Operation state and not change the IV Index. At the point of transition, the node shall reset the sequence number to 0x000000.
The node shall not execute more than one IV Index Recovery within a period of 192 hours.
IV Test Mode
Because the limitations mentioned above, it's not easy to test the IV update & recovery procedure. So the IV test mode removes the 96 hours limit. All other behavior of the devices remains unchanged.
Secure Network Beacon
The secure network beacon must be enabled to do the IV update & recovery since it's the carrier of the information that a node is updating the IV index.
NOTE: the Bluetooth Mesh app may not support to configure the secure network beacon state, so you may need to enable it locally with the mesh test class commands. If using the Host Provisioner, then it's not a problem since it supports that.
Running The Example
The example contains two projects - iv_update and iv_recovery, they needs to work together to show how the IV update & recovery procedure works, as well as showing the sequence number storing strategy. Let's call the node flashed with iv_update firmware node U, and the node flashed with iv_recovery firmware node R.
BGAPI Commands & Events
Below is a list of BGAPI commands and events related to the IV update & recovery procedure, all of which are demonstrated in the example.
Commands:
gecko_cmd_mesh_node_request_ivupdate(...) - this command is used to initiate an IV update procedure.
gecko_cmd_mesh_node_set_ivrecovery_mode(...) - this command is used for enabling/disabling the IV recovery mode, this can typically be called when the stack reports it's found that the IV of the network is different than the IV of node itself.
gecko_cmd_mesh_test_set_iv_index(...) - this command is used to set the IV index of the node itself.
gecko_cmd_mesh_test_set_ivupdate_state(...) - this command is used to forcedly set the iv update procedure state
Events:
gecko_evt_mesh_node_changed_ivupdate_state_id - This event is generated whenever the node IV update state changes.
gecko_evt_mesh_node_ivrecovery_needed_id - this event is generated whenever the stack consider the found network IV index is too far to catch up automatically, so that the application will decide if to catch up or not.
Import The Projects
Download the attachment and extract it, create a soc-btmesh-switch example project and a soc-btmesh-light example and specify the names, e.g. iv_update and iv_recovery.
Replace the main.c file of the iv_update project with the main.c in the ${extract_folder}/iv_update folder.
Replace the app.c file of the iv_recovery project with the app.c in the ${extract_folder}/iv_recovery folder.
The example uses the Logging System, if you want to use printf instead, you can comment out the #define USE_LOG_MODULE in app.c.
Build both projects and flash them to 2 boards respectively.
Use Bluetooth Mesh app on smartphones or any other kinds of provisioner to provision the 2 boards to the same network and configure them properly.
Behaviors of The Example
As you can see from the last section that the examples are based on the light and switch examples, so they still keep the basic functionalities and behaviors as the original example. What is modified is listed below:
On the Node U side - iv_update project:
Very long press on PB0 - It will start the IV update procedure by increasing the IV index by MIN(TEST_IV_HOP, MAX_IV_HOP - 1) + 1 to demonstrate the IV index hop if the node is not in IV update state. Otherwise, it will terminate the IV update procedure if it's in IV update procedure.
Very long press on PB1 - It will start the IV update procedure by increasing the IV index by 1 to demonstrate the typical IV update procedure if the node is not in IV update state. Otherwise, it will terminate the IV update procedure if it's in IV update procedure.
Whenever the node sends a packet, it will print the current and remaining sequence number.
On the Node R side - iv_recovery project:
Press on PB0 - It will disable the IV recovery mode.
Press on PB1 - It will enable the IV recovery mode.
Test The Sequence Number Increasing
Every time the node U boots and if the node is provisioned, it will print the current and remaining sequence number. Every time any button is pressed to send a packet, the current and remaining sequence number will be printed. You can try to send some packets then reset the device to see what happens. How about if not send packets but reset the node directly? See the result in Figure 1, the pstore_write_interval_elem_seq here is 0x10000.
Figure 1. Sequence Number Increasing
System boots, node Sequence number is 0x30000.
Short press the PB0 once, the sequence number increments to 0x30001.
The same as step 2. After that, reset the device.
System boots, node sequence number is 0x40000. After that, directly reset the device without letting it send any packet.
System boots, node sequence number is still 0x40000.
Test IV Update & Recovery
Figure 2 shows the procedure of IV update and IV recovery, there are 2 nodes in the same network, as you can see from figure 1, on the left side, it's the print of the node which initiates IV update procedure, this is node U, on the right side, it's the print of the node which catches up the IV update, this is node R. There are 4 times of the IV update & recovery procedure, describing below:
From the print of node R you can see the IV recovery mode is disabled after reset. Both node U and R have IV index 158. Node U initiates the IV update from 158 to 159, which is the value of the current IV index of node R plus 1, at the same time, the IV update flag in the secure network beacon is set. So it catches up the IV update procedure automatically without application involved.
Manually set the IV index to 189, then starts IV update procedure. Because the current IV index of node R is 159, the IV index sent from node U is 190 which is not equal to 159 + 1, so the node R generates the event gecko_evt_mesh_node_ivrecovery_needed_id to let the application to decide if to catch up the IV update and start the IV recovery procedure. As you can see the IV recovery mode is enabled after it gets the event, so the node catches up to 190.
The IV index of node U is 156 and the flag in secure network beacon is not set, which means it's not in IV update procedure. The IV index of node R is 155, it generates the event gecko_evt_mesh_node_ivrecovery_needed_id to let the application to decide if to catch up the IV update and start the IV recovery procedure. The application didn't do it in this case, so the IV index didn't change after reset after a while.
The situation is the same as step 3, the only difference is the application enabled the IV recovery mode, so the node catches up the IV index to 156.
Bluetooth Knowledge Base
KBA_BT_0208: Chained Advertisements
Introduction
Bluetooth 5 introduces several enhancements to advertising. This article discusses the concept of chained advertising.
Discussion
Chained advertising allows advertising packets to be linked or chained together to allow for larger advertisements, up to 1650 bytes, to be sent. A new packet type, AUX_CHAIN_IND, is used to transmit the individual portions of these advertisements. Chained advertising works together with extended advertising and periodic advertising. The following example allows a synchronizer, i.e. the receiver of a periodic advertisement, to synchronize to a chained periodic advertisement and begin receiving these advertisements.
Implementation
Advertiser
The maximum size of a BGAPI command is 255B, which does not allow to use one command to set 1650B of advertisement data. Therefore, version 2.12 of Silicon Labs Bluetooth SDK introduces a new API: gecko_cmd_system_data_buffer_write() to write up to 255 bytes of data to the system data buffer, in which the stacks assembles the full advertisement data. This API can be called multiple times when more than 255 bytes must be written to the buffer.
The sample code provided with this article implements a simple function, writeSystemDataBuffer(), to simplify writing up to 1650 bytes to the system data buffer. Once the desired advertising data has been written to the buffer the application starts advertising. The advertisement will include a service UUID, which the synchronizer will look for, and the sync info, on which the synchronizer can sync on. Next, the periodic advertisement is started. Finally, the data assembled in the system data buffer is transferred to the periodic advertisement by calling another new API: gecko_cmd_le_gap_set_long_advertising_data().
Scanner
The scanner, or synchronizer, begins by starting scanning for advertisements containing the ‘watchable’ service with the UUID f69dd7f9-340a-4693-9e46-c8630c898558. The first step is to request the extended scan response by calling gecko_cmd_le_gap_set_discovery_extended_scan_response(), this provides more information about the scanner than the basic scan response. Next, the scanner sets the discovery timing and type and starts discovery. Advertisements or scan responses are handled by gecko_evt_le_gap_extended_scan_response_id event handler. This event handler first filters out any packets which are not extended advertising packets. Next, the advertisement is searched for the service UUID mentioned above by calling findServiceInAdvertisement(). When an advertisement containing this UUID is found, the scanner syncs to this device by calling gecko_cmd_sync_open().
The sample code for the scanner/synchronizer includes event handlers for sync_open and sync_closed events. The sync_closed event is triggered when a sync timeout expires and is used to start scanning again. The gecko_evt_sync_opened_id event is purely informative and simply prints a message indicating that a sync has been opened. The gecko_evt_sync_data_id is triggered whenever sync data is received. This event handler handles three situations:
data received complete,
data received with more to follow and
data truncated.
The first situation occurs either when the advertisement fits in a single packet or when the last packet in a chain is received, in either case the data is saved. The second situation occurs when data is received, and more is expected. When this happens, event handler saves the data and begins reassembly the advertisement. If subsequent data is expected, but none is received, the status is set to data truncated. In this case, the sample application considers the data to be corrupt and discards it all by clearing it’s buffer.
Chained advertisements are buffered using the bluetooth_stack_heap found in main.c. The definition must be increased as shown below to provide sufficient space for the data.
uint8_t bluetooth_stack_heap[DEFAULT_BLUETOOTH_HEAP(MAX_CONNECTIONS)+1650];
Running the Example
The attached example requires two WSTK/radioboards.
Create a soc-empty project for your desired radio board.
Overwrite the app.c file in this project with app.c from the advertiser folder in the attached zip file.
Open main.c and modify the heap size
uint8_t bluetooth_stack_heap[DEFAULT_BLUETOOTH_HEAP(MAX_CONNECTIONS)+1650];
Import gatt.xml from the advertiser folder using the ‘Import GATT’ icon in the GATT Configurator. When finished the result should look like this
Click generate.
Build and download to the first kit.
Create another soc-empty for the second kit.
Overwrite app.c in this project with app.c from the scanner folder in the attached zip file.
Open main.c and modify the heap size
uint8_t bluetooth_stack_heap[DEFAULT_BLUETOOTH_HEAP(MAX_CONNECTIONS)+1650];
Open app.h and change the value of DEBUG_LEVEL from 0 to 1 to enable debug printing.
Build and download to the second board.
Open a terminal program, such as the SimplicityStudio serial console, to view the output from the device. The result should look similar to the followingShortly after starting up, the scanner discovers an advertiser that has come into range and synchronizes with it. Each gecko_evt_sync_data_id comes with a maximum of 250 bytes. The entire advertising packet contains 1650 bytes so we see the final event contains 150 bytes and displays a message indicating that the sync is complete. In the next chained advertisement, one of the advertising packet PDUs is not received, so the data is discarded. After one more successful chained advertisement, the sync is closed due to a timeout
Conclusion
Chained advertising provides a way of distributing larger amounts of data to multiple synchronizers without the need for connections. The focus of this article is chained advertising, for more information on periodic advertising in general please see our periodic advertising knowledgebase article
KBA_BT_0513: IV Update & Recovery Procedure and Sequence Number Storing Strategy
IV Index & Sequence Number
Below is the definition of Sequence number and IV index from chapters 3.8.3 and 3.8.4 of Mesh Profile Specification v1.0.1.
Sequence Number
Sequence number Storing Strategy
Sequence number should be non-volatile, software stacks should make sure that every time the device sends a valid packet, it should never use the sequence numbers which had been already used before, so it would be a good way to store it in the flash. However, writing flash once per each sequence number increment is probably too often so that the flash may easily wear out before the life time of the real product. To balance the impact, the stack will only write the sequence number to flash at a fixed interval when the sequence number increases by pstore_write_interval_elem_seq times, which is adjustable and located in the dcd.c file in your project. Given that there is a chance that the device may loss power unexpectedly before the interval, which means the real sequence number has not been written to flash, next time when the device boots, it will use the old value in the flash. To avoid the node uses any old sequence number, the node will always increase pstore_write_interval_elem_seq after reset. Besides, the stack will detect if there is valid sequence number increasing between the 2 times of reset, if no, the stack won't increase the sequence number. For more information, you can refer to the example in Figure 1.
NOTE: This is not standard but Silicon Labs' current solution and it may change in the future.
IV Index
Environments
IV Update & Recovery Introduction
IV Update & Recovery Procedure
IV Update Procedure:
The sequence number is 24-bit length, as the example provided above, with 5 seconds cadence, the sequence number will repeat after 2.6 years. To avoid this, the node could start the IV update procedure to update the network to a new IV index so that the sequence number could be reset to 0. The IV update procedure can be initiated by any node in the primary subnet. Table 1 shows the summary of IV update procedure.
Table 1: IV Update procedure summary
IV Recovery Procedure:
A node shall support the IV index recovery procedure because a node that is away from the network for a long time may miss IV Update procedures, in which case it can no longer communicate with the other nodes. In order to recover the IV Index, the node must listen for a Secure Network beacon, which contains the Network ID and the current IV Index.
IV Update & Recovery Limitations
There are some limitations on initiating IV update and recovery procedure, mentioned in chapters 3.10.5 and 3.10.6 of Mesh Profile Specification v1.0.1. Generally, as below:
IV Test Mode
Because the limitations mentioned above, it's not easy to test the IV update & recovery procedure. So the IV test mode removes the 96 hours limit. All other behavior of the devices remains unchanged.
Secure Network Beacon
The secure network beacon must be enabled to do the IV update & recovery since it's the carrier of the information that a node is updating the IV index.
NOTE: the Bluetooth Mesh app may not support to configure the secure network beacon state, so you may need to enable it locally with the mesh test class commands. If using the Host Provisioner, then it's not a problem since it supports that.
Running The Example
The example contains two projects - iv_update and iv_recovery, they needs to work together to show how the IV update & recovery procedure works, as well as showing the sequence number storing strategy. Let's call the node flashed with iv_update firmware node U, and the node flashed with iv_recovery firmware node R.
BGAPI Commands & Events
Below is a list of BGAPI commands and events related to the IV update & recovery procedure, all of which are demonstrated in the example.
Commands:
Events:
Import The Projects
Behaviors of The Example
As you can see from the last section that the examples are based on the light and switch examples, so they still keep the basic functionalities and behaviors as the original example. What is modified is listed below:
On the Node U side - iv_update project:
On the Node R side - iv_recovery project:
Test The Sequence Number Increasing
Every time the node U boots and if the node is provisioned, it will print the current and remaining sequence number. Every time any button is pressed to send a packet, the current and remaining sequence number will be printed. You can try to send some packets then reset the device to see what happens. How about if not send packets but reset the node directly? See the result in Figure 1, the pstore_write_interval_elem_seq here is 0x10000.
Figure 1. Sequence Number Increasing
Test IV Update & Recovery
Figure 2 shows the procedure of IV update and IV recovery, there are 2 nodes in the same network, as you can see from figure 1, on the left side, it's the print of the node which initiates IV update procedure, this is node U, on the right side, it's the print of the node which catches up the IV update, this is node R. There are 4 times of the IV update & recovery procedure, describing below:
Figure 2. IV Update & Recovery Procedure