Bluetooth 5 introduces several enhancements to advertising. This document discusses the concept of chained advertising.
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.
The maximum size of a BGAPI command is 255B, which does not support using 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. After 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().
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 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 data.
The attached example requires two WSTK/radioboards.
Create a soc-empty project for your desired radio board.
Copy attached advertiser_app.c file into this project and remove existing app.c
Open main.c and modify the heap size
Import advertiser_gatt.xml using the ‘Import GATT’ icon in the GATT Configurator. When finished the result should look like this:
- Click generate.
- Build and flash the project to the first kit.
- Create another soc-empty for the second kit.
- Copy attached scanner_app.c file into this project and remove existing app.c.
- Open main.c and modify the heap size
- Open app.h and change the value of DEBUG_LEVEL from 0 to 1 to enable debug printing.
- Build and flash the project to the second kit.
Open a terminal program, such as the Simplicity Studio serial console, to view the output from the device. The result should look similar to the following:
Shortly 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. 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.