Bluetooth: ISO time synchronization
The Bluetooth® isochronous time synchronization sample showcases time-synchronized processing of isochronous data.
Requirements
The sample supports the following development kits:
Hardware platforms |
PCA |
Board name |
Board target |
---|---|---|---|
PCA10156 |
|
||
PCA10156 |
|
||
PCA10095 |
|
||
PCA10056 |
|
||
PCA10100 |
|
The sample also supports other development kits running SoftDevice Controller variants that support isochronous channels (see SoftDevice Controller feature support). To demonstrate time synchronization between the transmitter and a receiver, you need two development kits. To demonstrate time synchronization between the transmitter and multiple receivers, you need at least three development kits. You can use any combination of the development kits mentioned above, mixing different development kits.
Additionally, the sample requires a connection to a computer with a serial terminal for each development kit in use.
To observe the toggling of the LEDs, use a logic analyzer or an oscilloscope.
Overview
The sample demonstrates the following features:
How to send isochronous data on multiple streams so that the receiving devices and the sending device can process the data at the same time.
How the receivers of the isochronous data can process the received data synchronously.
How to provide the isochronous data to the controller right before it is sent on-air.
How to achieve this for either broadcast (BIS) or over connections (CIS).
This sample configures a single device as a transmitter of its Button 1 state. The transmitting and receiving devices toggle LED 2 synchronously with the accuracy of a few microseconds. When the CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE Kconfig option is enabled, LED 1 is toggled upon sending or receiving data. This allows you to measure the minimal end-to-end latency.
This sample configures a single device as a transmitter of its Button 0 state. The transmitting and receiving devices toggle LED 1 synchronously with the accuracy of a few microseconds. When the CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE Kconfig option is enabled, LED 0 is toggled upon sending or receiving data. This allows you to measure the minimal end-to-end latency.
Note
This sample requires less hardware resources when it is run on an nRF54L Series device compared to the nRF52 or nRF53 Series devices. On an nRF54L Series device, only one GRTC channel and PPI channel is needed to set up accurate toggling of an LED. On nRF52 and nRF53 Series devices, you also need one RTC peripheral, one TIMER peripheral, one EGU channel, three PPI channels, and one PPI group.
Configuration
See Configuring and building for information about how to permanently or temporarily change the configuration.
Configuration options
Check and configure the following Kconfig options:
- CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE
This configuration option enables immediate toggling of LED 1 (nRF52 and nRF53 DKs) or LED 0 (nRF54 DKs) when isochronous data is sent or received. It allows for measurement of the minimum end-to-end latency.
- CONFIG_SDU_INTERVAL_US
This configuration option determines how often the button value is transmitted to the receiving devices.
- CONFIG_TIMED_LED_PRESENTATION_DELAY_US
This configuration option represents the time from the SDU synchronization reference until the value is applied. The application needs to ensure it can process the received SDU within this amount of time. The end-to-end latency will be at least the sum of the configured transport latency and presentation delay.
Isochronous channels
The Bluetooth LE Isochronous Channels feature is a way of using Bluetooth LE to transfer time-bounded data between devices. It provides a mechanism that makes sure that multiple sink devices, receiving data from the same source, render it at the same time. Data has a time-limited validity period, at the end of which it is said to expire. Expired data that has not yet been transmitted, will be discarded. This means that receiver devices only ever receive data that is valid with respect to rules regarding its age and acceptable latency.
The key concepts of isochronous channels are the following:
- Topology
Isochronous channels can either be used for one-to-many (Broadcast) or one-to-one (Connected). For both topologies, data may be processed simultaneously or time-synchronized on all transmitting and receiving devices. Connected isochronous streams can either transfer data from central to peripheral, peripheral to central, or in both directions.
- SDU interval and size
The SDU interval specifies how often a service data unit (SDU) is transferred. The SDU interval and size as well as the number of streams are limited by the bandwidth of the link layer. In this sample, the SDU interval is configured with the CONFIG_SDU_INTERVAL_US Kconfig option.
- Maximum transport latency and retransmission count
Upon setting up an isochronous stream as a central or a broadcaster, the application provides a maximum transport latency and retransmission count. The Bluetooth controller uses the provided parameters to select link layer parameters that satisfy these constraints. This involves selecting an isochronous interval, PDU sizes, and isochronous parameters, such as burst number (BN), number of subevents (NSE), and flush timeout (FT) or pretransmission offset (PTO). For more details about how the SoftDevice Controller selects these parameters, see Parameter selection.
- Presentation delay
The receiver(s) of isochronous data may receive their data at different points in time, but need to render or present it synchronously. Presentation delay represents the amount of time that is added to the received timestamp to obtain the presentation time of the data. In this sample, the presentation delay is configured with the CONFIG_TIMED_LED_PRESENTATION_DELAY_US Kconfig option.
For more details about this feature, see Bluetooth Core Specification Version 5.2 Feature Overview.
This sample demonstrates the following:
Any of the possible isochronous topologies with unidirectional data transfer. The topology is selected when the application starts.
The transmitter of the data sends it over all configured and connected streams. The number of streams is configured with the
CONFIG_BT_ISO_MAX_CHAN
Kconfig option.The receiver of the isochronous data receives data from one transmitter only.
The maximum transport latency and retransmission count is configured when the application starts.
Sample structure
The sample code is divided into multiple source files, which makes it easier to compile in or out certain functionalities.
main.c
The main entry point of the sample allows you to select the role and parameters to be used.
bis_transmitter.c
This file implements a device that sets up a BIS transmitter with a configured number of streams. That is:
Configure and start an extended advertiser. The extended advertiser data contains the device name.
Configure and start a periodic advertiser.
Configure and start a broadcast isochronous group (BIG).
bis_receiver.c
This file implements a device that syncs to a BIS broadcaster with a given BIS index.
Scan for extended advertisers containing the device name.
Synchronize to the corresponding periodic advertiser.
Synchronize to the selected BIS.
When the ISO channel is disconnected, it tries to sync to it again.
cis_central.c
This file implements a device that acts as a CIS central. The central can be configured either as a transmitter or a receiver.
Configure a connected isochronous group (CIG).
Scan and connect to a device that contains the device name.
Set up a CIS to the connected device.
Continue to scanning for more peripherals, if there are more available isochronous streams.
cis_peripheral.c
This file implements a device that acts as a CIS peripheral. The peripheral can be configured either as a transmitter or a receiver.
Set up an isochronous server so that it can later on accept the setup or a CIS.
Start advertising its device name.
Once a device has connected, accept the incoming CIS request.
iso_rx.c
This file handles receiving of isochronous data. Once an isochronous stream is connected, the isochronous parameters are printed.
When a valid SDU is received, the following operations are performed:
If the CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE Kconfig option is enabled, LED 1 is toggled immediately. You can use this to observe that different receivers may receive the SDU at different points in time.
A timer trigger is configured to toggle LED 2 CONFIG_TIMED_LED_PRESENTATION_DELAY_US after the received timestamp. This ensures that all receivers and the transmitter toggle it at the same time.
iso_tx.c
This file handles time-synchronized transmitting of isochronous data on all established streams. Each SDU contains a counter and the current value of Button 1. The implementation ensures the following:
The SDU is transmitted right before it is supposed to be sent on air. This is achieved by setting up a timer to trigger right before the next TX timestamp.
The SDU is transmitted on all the established isochronous channels with the same timestamp. This ensures that all the receivers can toggle their corresponding LEDs at the same time. The very first SDU is provided without a timestamp, because the timestamp is not known at this point in time.
LED 2 is configured to toggle synchronously with the LED 2 on all the receivers. The toggle time is determined by the TX timestamp and defined relative to the synchronization reference on the receiver.
If the CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE Kconfig option is enabled, LED 1 is toggled right before sending the SDU. You can use this to observe the end-to-end latency.
timed_led_toggle.c
This file implements timed toggling a LED. The
GPIOTE
peripheral is used together withPPI
to achieve accurate timing.controller_time_<device>.c
The SDU timestamps sent to and received from the controller are based upon the controller clock. These files allow the application to read the current timestamp and set up a PPI trigger at a given point in time.
The implementation for nRF52 and nRF53 Series devices is implemented by shadowing an RTC peripheral combined with a timer peripheral. The implementation for nRF54L Series devices uses the GRTC and is simpler to use.
main.c
The main entry point of the sample allows you to select the role and parameters to be used.
bis_transmitter.c
This file implements a device that sets up a BIS transmitter with a configured number of streams. That is:
Configure and start an extended advertiser. The extended advertiser data contains the device name.
Configure and start a periodic advertiser.
Configure and start a broadcast isochronous group (BIG).
bis_receiver.c
This file implements a device that syncs to a BIS broadcaster with a given BIS index.
Scan for extended advertisers containing the device name.
Synchronize to the corresponding periodic advertiser.
Synchronize to the selected BIS.
When the ISO channel is disconnected, it tries to sync to it again.
cis_central.c
This file implements a device that acts as a CIS central. The central can be configured either as a transmitter or a receiver.
Configure a connected isochronous group (CIG).
Scan and connect to a device that contains the device name.
Set up a CIS to the connected device.
Continue to scanning for more peripherals, if there are more available isochronous streams.
cis_peripheral.c
This file implements a device that acts as a CIS peripheral. The peripheral can be configured either as a transmitter or a receiver.
Set up an isochronous server so that it can later on accept the setup or a CIS.
Start advertising its device name.
Once a device has connected, accept the incoming CIS request.
iso_rx.c
This file handles receiving of isochronous data. Once an isochronous stream is connected, the isochronous parameters are printed.
When a valid SDU is received, the following operations are performed:
If the CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE Kconfig option is enabled, LED 0 is toggled immediately. You can use this to observe that different receivers may receive the SDU at different points in time.
A timer trigger is configured to toggle LED 1 CONFIG_TIMED_LED_PRESENTATION_DELAY_US after the received timestamp. This ensures that all receivers and the transmitter toggle it at the same time.
iso_tx.c
This file handles time-synchronized transmitting of isochronous data on all established streams. Each SDU contains a counter and the current value of Button 0. The implementation ensures the following:
The SDU is transmitted right before it is supposed to be sent on air. This is achieved by setting up a timer to trigger right before the next TX timestamp.
The SDU is transmitted on all the established isochronous channels with the same timestamp. This ensures that all the receivers can toggle their corresponding LEDs at the same time. The very first SDU is provided without a timestamp, because the timestamp is not known at this point in time.
LED 1 is configured to toggle synchronously with the LED 1 on all the receivers. The toggle time is determined by the TX timestamp and defined relative to the synchronization reference on the receiver.
If the CONFIG_LED_TOGGLE_IMMEDIATELY_ON_SEND_OR_RECEIVE Kconfig option is enabled, LED 0 is toggled right before sending the SDU. You can use this to observe the end-to-end latency.
timed_led_toggle.c
This file implements timed toggling a LED. The
GPIOTE
peripheral is used together withPPI
to achieve accurate timing.controller_time_<device>.c
The SDU timestamps sent to and received from the controller are based upon the controller clock. These files allow the application to read the current timestamp and set up a PPI trigger at a given point in time.
The implementation for nRF52 and nRF53 Series devices is implemented by shadowing an RTC peripheral combined with a timer peripheral. The implementation for nRF54L Series devices uses the GRTC and is simpler to use.
Building and running
This sample can be found under samples/bluetooth/iso_time_sync
in the nRF Connect SDK folder structure.
To build the sample, follow the instructions in Building an application for your preferred building environment. See also Programming an application for programming steps and Testing and optimization for general information about testing and debugging in the nRF Connect SDK.
Note
When building repository applications in the SDK repositories, building with sysbuild is enabled by default.
If you work with out-of-tree freestanding applications, you need to manually pass the --sysbuild
parameter to every build command or configure west to always use it.
Testing
The sample can demonstrate broadcast isochronous streams or connected isochronous streams.
Testing broadcast isochronous streams
After programming the sample to the development kits, perform the following steps to test it:
Connect to the kit that runs this sample with a terminal emulator (for example, nRF Connect Serial Terminal). See Testing and optimization for the required settings and steps.
Reset the kits.
Configure one of the devices as an isochronous broadcaster by typing either
b
orc
in the terminal emulator.Configure the number of retransmissions and maximum transport latency.
Observe that the broadcaster starts and begins to transmit SDUs.
On the other terminal(s), type
r
to configure the device(s) as receivers of the broadcast isochronous stream.Select which BIS the receiver should synchronize to.
Observe that the device(s) synchronize to the broadcaster and start receiving isochronous data.
Press Button 1 on the broadcaster.
Observe that LED 2 toggles on both the broadcaster and the receivers.
Connect to the kit that runs this sample with a terminal emulator (for example, nRF Connect Serial Terminal). See Testing and optimization for the required settings and steps.
Reset the kits.
Configure one of the devices as an isochronous broadcaster by typing either
b
orc
in the terminal emulator.Configure the number of retransmissions and maximum transport latency.
Observe that the broadcaster starts and begins to transmit SDUs.
On the other terminal(s), type
r
to configure the device(s) as receivers of the broadcast isochronous stream.Select which BIS the receiver should synchronize to.
Observe that the device(s) synchronize to the broadcaster and start receiving isochronous data.
Press Button 0 on the broadcaster.
Observe that LED 1 toggles on both the broadcaster and the receivers.
Testing connected isochronous streams
After programming the sample to the development kits, perform the following steps to test it:
Connect to the kit that runs this sample with a terminal emulator (for example, nRF Connect Serial Terminal). See Testing and optimization for the required settings and steps.
Reset the kits.
Configure one of the devices as a connected isochronous stream central by typing
c
in the terminal emulator.Configure the number of retransmissions and the maximum transport latency.
Select data direction. If the central is configured for transmission, it connects to multiple peripherals.
On the other terminal(s), type
p
to configure the device(s) as connected isochronous stream peripheral(s).Select data direction.
Observe that the peripheral(s) connect to the central and start receiving isochronous data.
Press Button 1 on the central device.
Observe that LED 2 toggles on both the central and peripheral devices.
Connect to the kit that runs this sample with a terminal emulator (for example, nRF Connect Serial Terminal). See Testing and optimization for the required settings and steps.
Reset the kits.
Configure one of the devices as a connected isochronous stream central by typing
c
in the terminal emulator.Configure the number of retransmissions and the maximum transport latency.
Select data direction. If the central is configured for transmission, it connects to multiple peripherals.
On the other terminal(s), type
p
to configure the device(s) as connected isochronous stream peripheral(s).Select data direction.
Observe that the peripheral(s) connect to the central and start receiving isochronous data.
Press Button 0 on the central device.
Observe that LED 1 toggles on both the central and peripheral devices.
Observe time-synchronized ISO data processing
When you press Button 1 on the transmitting device, you can observe that LED 2 toggles simultaneously on all devices. To observe the accurate toggling, use a logic analyzer or an oscilloscope.
When you press Button 0 on the transmitting device, you can observe that LED 1 toggles simultaneously on all devices. To observe the accurate toggling, use a logic analyzer or an oscilloscope.
Sample output
The result should look similar to the following output:
For the isochronous broadcaster:
Bluetooth ISO Time Sync Demo Choose role - cis_central (c) / cis_peripheral (p) / bis_transmitter (b) / bis_receiver (r) : b Choose retransmission number [0..30] : 2 Choose max transport latency in ms [5..4000] : 20 Starting BIS transmitter with BIS indices [1..4], RTN: 2, max transport latency 20 ms BIS transmitter started ISO channel index 0 connected: interval: 10 ms, NSE: 3, BN: 1, IRC: 2, PTO: 1, transport latency: 12418 us ISO channel index 1 connected: interval: 10 ms, NSE: 3, BN: 1, IRC: 2, PTO: 1, transport latency: 12418 us ISO channel index 2 connected: interval: 10 ms, NSE: 3, BN: 1, IRC: 2, PTO: 1, transport latency: 12418 us Sent SDU counter 0 with timestamp 2329536 us, controller_time 2330444 us, btn_val: 0 LED will be set in 16510 us ISO channel index 3 connected: interval: 10 ms, NSE: 3, BN: 1, IRC: 2, PTO: 1, transport latency: 12418 us Sent SDU counter 100 with timestamp 3329536 us, controller_time 3331085 us, btn_val: 0 LED will be set in 15869 us Sent SDU counter 200 with timestamp 4329536 us, controller_time 4331085 us, btn_val: 0 LED will be set in 15869 us Sent SDU counter 300 with timestamp 5329536 us, controller_time 5331085 us, btn_val: 0 LED will be set in 15869 us Sent SDU counter 400 with timestamp 6329536 us, controller_time 6331085 us, btn_val: 0 LED will be set in 15869 us Sent SDU counter 500 with timestamp 7329536 us, controller_time 7331085 us, btn_val: 0 LED will be set in 15869 us
For the isochronous broadcast receiver:
Bluetooth ISO Time Sync Demo Choose role - cis_central (c) / cis_peripheral (p) / bis_transmitter (b) / bis_receiver (r) : r Choose bis index [1..31] : 2 Starting BIS receiver, BIS index 2 Scanning for periodic advertiser Waiting for BigInfo Synced to periodic advertiser BigInfo received Syncing to BIG index 2 ISO Channel connected: interval: 10 ms, NSE: 3, BN: 1, IRC: 2, PTO: 1, transport latency: 12418 us Recv SDU counter 1000 with timestamp 11851325 us, controller_time 11839813 us, btn_val: 0, LED will be set in 16512 us Recv SDU counter 1100 with timestamp 12851327 us, controller_time 12839813 us, btn_val: 0, LED will be set in 16514 us Recv SDU counter 1200 with timestamp 13851331 us, controller_time 13839813 us, btn_val: 0, LED will be set in 16518 us Recv SDU counter 1300 with timestamp 14851334 us, controller_time 14839813 us, btn_val: 0, LED will be set in 16521 us Recv SDU counter 1400 with timestamp 15851337 us, controller_time 15839813 us, btn_val: 0, LED will be set in 16524 us Recv SDU counter 1500 with timestamp 16851342 us, controller_time 16839843 us, btn_val: 0, LED will be set in 16499 us
For the connected isochronous stream central configured for transmission:
Bluetooth ISO Time Sync Demo Choose role - cis_central (c) / cis_peripheral (p) / bis_transmitter (b) / bis_receiver (r) : c Choose direction - TX (t) / RX (r) : t Choose retransmission number [0..30] : 3 Choose max transport latency in ms [5..4000] : 20 Starting CIS central, dir: tx, RTN: 3, max transport latency 20 ms CIS central started scanning for peripheral(s) Connected: FA:BB:79:57:D6:45 (random) Connecting ISO channel ISO channel index 0 connected: interval: 10 ms, NSE: 2, BN: 1, FT: 2, transport latency: 13536 us CIS Central started scanning Sent SDU counter 0 with timestamp 3072152 us, controller_time 3070617 us, btn_val: 0 LED will be set in 20071 us Connected: D2:52:1C:B7:DC:2A (random) Connecting ISO channel ISO channel index 1 connected: interval: 10 ms, NSE: 2, BN: 1, FT: 2, transport latency: 13536 us CIS Central started scanning Sent SDU counter 100 with timestamp 4072152 us, controller_time 4072753 us, btn_val: 0 LED will be set in 17935 us Sent SDU counter 200 with timestamp 5072152 us, controller_time 5072631 us, btn_val: 0 LED will be set in 18057 us Sent SDU counter 300 with timestamp 6072152 us, controller_time 6072784 us, btn_val: 0 LED will be set in 17904 us Sent SDU counter 400 with timestamp 7072152 us, controller_time 7072631 us, btn_val: 0 LED will be set in 18057 us Sent SDU counter 500 with timestamp 8072152 us, controller_time 8072937 us, btn_val: 0 LED will be set in 17751 us
For the connected isochronous stream peripheral configured for reception:
Bluetooth ISO Time Sync Demo Choose role - cis_central (c) / cis_peripheral (p) / bis_transmitter (b) / bis_receiver (r) : p Choose direction - TX (t) / RX (r) : r Starting CIS peripheral, dir: rx CIS peripheral started advertising Connected: E8:DC:8D:B3:47:69 (random) Incoming request from 0x20002440 ISO Channel connected: interval: 10 ms, NSE: 2, BN: 1, FT: 2, transport latency: 13696 us Recv SDU counter 100 with timestamp 3584552 us, controller_time 3573333 us, btn_val: 0, LED will be set in 16219 us Recv SDU counter 200 with timestamp 4584555 us, controller_time 4573333 us, btn_val: 0, LED will be set in 16222 us Recv SDU counter 300 with timestamp 5584559 us, controller_time 5573333 us, btn_val: 0, LED will be set in 16226 us Recv SDU counter 400 with timestamp 6584562 us, controller_time 6573333 us, btn_val: 0, LED will be set in 16229 us Recv SDU counter 500 with timestamp 7584567 us, controller_time 7573760 us, btn_val: 0, LED will be set in 15807 us
For the connected isochronous stream central configured for reception:
Bluetooth ISO Time Sync Demo Choose role - cis_central (c) / cis_peripheral (p) / bis_transmitter (b) / bis_receiver (r) : c Choose direction - TX (t) / RX (r) : r Choose retransmission number [0..30] : 3 Choose max transport latency in ms [5..4000] : 20 Starting CIS peripheral, dir: rx, RTN: 3, max transport latency 20 ms CIS central started scanning for peripheral(s) Connected: FA:BB:79:57:D6:45 (random) Connecting ISO channel ISO Channel connected: interval: 10 ms, NSE: 2, BN: 1, FT: 2, transport latency: 10884 us Recv SDU counter 0 with timestamp 2144246 us, controller_time 2144897 us, btn_val: 0, LED will be set in 4349 us Recv SDU counter 100 with timestamp 3144246 us, controller_time 3144897 us, btn_val: 0, LED will be set in 4349 us Recv SDU counter 200 with timestamp 4144246 us, controller_time 4144897 us, btn_val: 0, LED will be set in 4349 us Recv SDU counter 300 with timestamp 5144246 us, controller_time 5144897 us, btn_val: 0, LED will be set in 4349 us Recv SDU counter 400 with timestamp 6144246 us, controller_time 6144897 us, btn_val: 0, LED will be set in 4349 us Recv SDU counter 500 with timestamp 7144246 us, controller_time 7144897 us, btn_val: 0, LED will be set in 4349 us
For the connected isochronous stream peripheral configured for transmission:
Bluetooth ISO Time Sync Demo Choose role - cis_central (c) / cis_peripheral (p) / bis_transmitter (b) / bis_receiver (r) : p Choose direction - TX (t) / RX (r) : t Starting CIS peripheral, dir: tx CIS peripheral started advertising Connected: E8:DC:8D:B3:47:69 (random) Incoming request from 0x20002440 ISO channel index 0 connected: interval: 10 ms, NSE: 2, BN: 1, FT: 2, transport latency: 10884 us Sent SDU counter 0 with timestamp 2649643 us, controller_time 2640289 us, btn_val: 0 LED will be set in 14354 us Sent SDU counter 100 with timestamp 3649638 us, controller_time 3649627 us, btn_val: 0 LED will be set in 5011 us Sent SDU counter 200 with timestamp 4649635 us, controller_time 4649627 us, btn_val: 0 LED will be set in 5008 us Sent SDU counter 300 with timestamp 5649631 us, controller_time 5649627 us, btn_val: 0 LED will be set in 5004 us Sent SDU counter 400 with timestamp 6649626 us, controller_time 6649627 us, btn_val: 0 LED will be set in 4999 us Sent SDU counter 500 with timestamp 7649623 us, controller_time 7649627 us, btn_val: 0 LED will be set in 4996 us
Dependencies
This sample uses the following sdk-nrfxlib library:
In addition, it uses the following Zephyr libraries:
include/console.h
-
include/kernel.h
include/sys/printk.h
include/zephyr/types.h
API:
include/bluetooth/bluetooth.h
include/bluetooth/iso.h
include/bluetooth/conn.h
include/bluetooth/hci.h
include/bluetooth/scan.h
References
For more information about how to use isochronous channels with the SoftDevice Controller, see Isochronous channels.