Skip to content

Commit

Permalink
samples: Bluetooth: add concurrent scanner and initiator sample
Browse files Browse the repository at this point in the history
Add sample application that demonstrates faster connection
establishment using CONFIG_BT_SCAN_AND_INITIATE_IN_PARALLEL.

Signed-off-by: Henrik Lander <[email protected]>
  • Loading branch information
henrla committed Dec 2, 2024
1 parent 960fd99 commit d38bea7
Show file tree
Hide file tree
Showing 10 changed files with 797 additions and 14 deletions.
11 changes: 11 additions & 0 deletions samples/bluetooth/scanning_while_connecting/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(connection_establishment_benchmark)

target_sources(app PRIVATE src/main.c)
10 changes: 10 additions & 0 deletions samples/bluetooth/scanning_while_connecting/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

source "${ZEPHYR_BASE}/share/sysbuild/Kconfig"

config NRF_DEFAULT_IPC_RADIO
default y
228 changes: 228 additions & 0 deletions samples/bluetooth/scanning_while_connecting/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
.. _bt_scanning_while_connecting:

Bluetooth: Scanning while connecting
####################################

.. contents::
:local:
:depth: 2

The sample demonstrates how to reduce the time to establish connections to many devices, typically done when provisioning devices to a network.
The total connection establishment time is reduced by scanning while connecting and by using the filter accept list.

Requirements
************

The sample supports the following development kits:

.. table-from-sample-yaml::

The sample also requires at least one other development kit.
Out of the box, this sample can be used together with the :ref:`peripheral_with_multiple_identities`.
This sample filters out devices with a different name than a device running the :ref:`peripheral_with_multiple_identities` sample.
The :ref:`peripheral_with_multiple_identities` sample acts as multiple peripheral devices.

Overview
********

You can use this sample as a starting point to implement an application designed to provision a network of Bluetooth peripherals.
The approaches demonstrated in this sample will reduce the total time to connect to many devices.
A typical use-case is a gateway in a network of devices using Periodic Advertising with Responses.

To illustrate how connection establishment speed can be improved, it measures the time needed to connect to 16 devices with three different modes: sequential scanning and connection establishment, concurrent scanning while connecting, and concurrent scanning while connecting with the filter accept list.

Sequential scanning and connection establishment
================================================

This is the slowest and simplest approach.
Sequential scanning and connection establishment is recommended when the application only needs to connect a handful of devices.

The message sequence chart below illustrates the sequence of events.
After a device is discovered, the application stops scanning and attempts to connect to the device.
Once the connection is established, the application starts the scanner again to discover other connectable devices.

.. msc::
hscale = "1.3";
App, Stack, Peers;
App=>Stack [label="scan_start()"];
Peers=>Stack [label="ADV_IND(A)"];
Stack=>App [label="scan_recv(A)"];
App=>Stack [label="scan_stop()"];
App=>Stack [label="bt_conn_le_create(A)"];
Peers=>Stack [label="ADV_IND(A)"];
Stack=>Peers [label="CONNECT_IND(A)"];
Stack=>App [label="connected_cb(A)"];
App=>Stack [label="scan_start()"];
Peers=>Stack [label="ADV_IND(B)"];
Stack=>App [label="scan_recv(B)"];
App=>Stack [label="scan_stop()"];
App=>Stack [label="bt_conn_le_create(B)"];
Peers=>Stack [label="ADV_IND(B)"];
Stack=>Peers [label="CONNECT_IND(B)"];
Stack=>App [label="connected_cb(B)"];

Concurrent scanning while connecting
====================================

This mode requires the application to enable the :kconfig:option:`CONFIG_BT_SCAN_AND_INITIATE_IN_PARALLEL` Kconfig option.
In this mode, the scanner is not stopped when the application creates connections.
During a connection establishment procedure to a device, the application caches other devices it also wants to connect to.
Once the connection establishment procedure is complete, it can immediately initiate a new connection establishment procedure to the cached device.
When connecting to a cached device, the connection establishment procedure takes one less advertising interval compared to the sequential scanning and connection establishment mode.

.. msc::
hscale = "1.3";
App, Stack, Peers;
App=>Stack [label="scan_start()"];
Peers=>Stack [label="ADV_IND(A)"];
Stack=>App [label="scan_recv(A)"];
App=>Stack [label="bt_conn_le_create(A)"];
Peers=>Stack [label="ADV_IND(B)"];
App rbox App [label="Cache address B"];
Peers=>Stack [label="ADV_IND(A)"];
Stack=>Peers [label="CONNECT_IND(A)"];
Stack=>App [label="connected_cb(A)"];
App=>Stack [label="bt_conn_le_create(B)"];
Peers=>Stack [label="ADV_IND(B)"];
Stack=>Peers [label="CONNECT_IND(B)"];
Stack=>App [label="connected_cb(B)"];

Concurrent scanning while connecting with the filter accept list
================================================================

This mode requires the application to enable the :kconfig:option:`CONFIG_BT_FILTER_ACCEPT_LIST` Kconfig option in addition to :kconfig:option:`CONFIG_BT_SCAN_AND_INITIATE_IN_PARALLEL`.
When the application starts the connection establishment procedure with the filter accept list, it can connect to any of the previously cached devices.
This reduces the total connection setup time even more, because connection establishment is not relying on the on-air presence of only one of the cached devices.

.. msc::
hscale = "1.3";
App, Stack, Peers;
App=>Stack [label="scan_start()"];
Peers=>Stack [label="ADV_IND(A)"];
Stack=>App [label="scan_recv(A)"];
App=>Stack [label="bt_conn_le_create(A)"];
Peers=>Stack [label="ADV_IND(B)"];
Peers=>Stack [label="ADV_IND(C)"];
Peers=>Stack [label="ADV_IND(D)"];
App rbox App [label="Cache addresses B, C, D"];
Peers=>Stack [label="ADV_IND(A)"];
Stack=>Peers [label="CONNECT_IND(A)"];
Stack=>App [label="connected_cb(A)"];
App rbox App [label="Set filter accept list to\nB, C, D"];
App=>Stack [label="bt_conn_le_create_auto()"];
Stack rbox Stack [label="The stack will connect to the first present ADV_IND of\nthe peers B,C,D"];
Peers=>Stack [label="ADV_IND(C)"];
Stack=>Peers [label="CONNECT_IND(C)"];
Stack=>App [label="connected_cb(C)"];

.. note::
This sample application assumes it will never have to cache more devices than the maximum number of addresses that can be stored in the filter accept list.
For applications that cannot adhere to this simplification, the function :cfunc:`cache_peer_address` can be changed to not store more than defined by the :kconfig:option:`CONFIG_BT_CTLR_FAL_SIZE` Kconfig option.
Another simplification done in the sample application is storing duplicate devices in the filter accept list.

Configuration
*************

|config|

Building and running
********************

.. |sample path| replace:: :file:`samples/bluetooth/scanning_while_connecting`

.. include:: /includes/build_and_run.txt

Testing
=======

|test_sample|

1. |connect_kit|
#. |connect_terminal|
#. Observe that the sample connects and prints out how much time it takes to connect to all peripherals.

Sample output
=============

The result should look similar to the following output::

*** Booting nRF Connect SDK v2.8.99-1c63490f0539 ***
*** Using Zephyr OS v3.7.99-b9bc0846b926 ***
I: SoftDevice Controller build revision:
I: 49 40 e2 c0 6b e5 0d b3 |[email protected]...
I: ba a6 48 5e 49 a6 95 3d |..H^I..=
I: 65 35 b6 7c |e5.|
I: HW Platform: Nordic Semiconductor (0x0002)
I: HW Variant: nRF54Lx (0x0005)
I: Firmware: Standard Bluetooth controller (0x00) Version 73.57920 Build 233139136
I: Identity: F6:BF:24:7D:46:5D (random)
I: HCI: version 6.0 (0x0e) revision 0x3030, manufacturer 0x0059
I: LMP: version 6.0 (0x0e) subver 0x3030
I: Bluetooth initialized

I: SEQUENTIAL_SCAN_AND_CONNECT:
I: starting sample benchmark
I: Connected to FF:AB:68:0C:34:FD (random), number of connections 1
I: Connected to E2:12:BF:D3:FB:D5 (random), number of connections 2
I: Connected to EE:37:AA:62:A2:FB (random), number of connections 3
I: Connected to C7:9B:42:1B:48:F8 (random), number of connections 4
I: Connected to F0:27:5B:37:0F:4B (random), number of connections 5
I: Connected to C8:BA:1D:6F:95:2B (random), number of connections 6
I: Connected to DF:02:31:C3:0B:C2 (random), number of connections 7
I: Connected to C7:E1:60:7A:F1:E0 (random), number of connections 8
I: Connected to CA:89:50:33:AB:31 (random), number of connections 9
I: Connected to E9:47:6B:FA:2F:DE (random), number of connections 10
I: Connected to EF:92:DC:88:3B:B3 (random), number of connections 11
I: Connected to F4:9C:8C:24:F9:44 (random), number of connections 12
I: Connected to DD:84:44:64:5D:FB (random), number of connections 13
I: Connected to FB:92:1D:8E:8C:D8 (random), number of connections 14
I: Connected to D9:E5:51:E0:5E:24 (random), number of connections 15
I: Connected to CF:2F:99:89:A3:4D (random), number of connections 16
I: 12 seconds to create 16 connections
I: Disconnecting connections...
I: ---------------------------------------------------------------------
I: ---------------------------------------------------------------------
I: CONCURRENT_SCAN_AND_CONNECT:
I: starting sample benchmark
I: Connected to F0:27:5B:37:0F:4B (random), number of connections 1
I: Connected to EE:37:AA:62:A2:FB (random), number of connections 2
I: Connected to D1:3D:B1:AA:84:27 (random), number of connections 3
I: Connected to CA:89:50:33:AB:31 (random), number of connections 4
I: Connected to C0:38:F8:47:10:17 (random), number of connections 5
I: Connected to E9:47:6B:FA:2F:DE (random), number of connections 6
I: Connected to D9:E5:51:E0:5E:24 (random), number of connections 7
I: Connected to FB:92:1D:8E:8C:D8 (random), number of connections 8
I: Connected to C7:E1:60:7A:F1:E0 (random), number of connections 9
I: Connected to E2:6D:21:28:C7:DB (random), number of connections 10
I: Connected to DF:02:31:C3:0B:C2 (random), number of connections 11
I: Connected to F0:F2:2A:C1:F7:72 (random), number of connections 12
I: Connected to DD:84:44:64:5D:FB (random), number of connections 13
I: Connected to F4:9C:8C:24:F9:44 (random), number of connections 14
I: Connected to EF:92:DC:88:3B:B3 (random), number of connections 15
I: Connected to C8:BA:1D:6F:95:2B (random), number of connections 16
I: 9 seconds to create 16 connections
I: Disconnecting connections...
I: ---------------------------------------------------------------------
I: ---------------------------------------------------------------------
I: CONCURRENT_SCAN_AND_CONNECT_FILTER_ACCEPT_LIST:
I: starting sample benchmark
I: Connected to DD:84:44:64:5D:FB (random), number of connections 1
I: Connected to C7:E1:60:7A:F1:E0 (random), number of connections 2
I: Connected to C7:9B:42:1B:48:F8 (random), number of connections 3
I: Connected to E9:47:6B:FA:2F:DE (random), number of connections 4
I: Connected to E2:12:BF:D3:FB:D5 (random), number of connections 5
I: Connected to FB:92:1D:8E:8C:D8 (random), number of connections 6
I: Connected to F0:F2:2A:C1:F7:72 (random), number of connections 7
I: Connected to CA:89:50:33:AB:31 (random), number of connections 8
I: Connected to F4:9C:8C:24:F9:44 (random), number of connections 9
I: Connected to E2:6D:21:28:C7:DB (random), number of connections 10
I: Connected to FF:AB:68:0C:34:FD (random), number of connections 11
I: Connected to EE:37:AA:62:A2:FB (random), number of connections 12
I: Connected to D1:3D:B1:AA:84:27 (random), number of connections 13
I: Connected to CF:2F:99:89:A3:4D (random), number of connections 14
I: Connected to EF:92:DC:88:3B:B3 (random), number of connections 15
I: Connected to D9:E5:51:E0:5E:24 (random), number of connections 16
I: 4 seconds to create 16 connections
I: Disconnecting connections...
I: ---------------------------------------------------------------------
I: ---------------------------------------------------------------------
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
Expand Down
10 changes: 6 additions & 4 deletions samples/bluetooth/scanning_while_connecting/peripheral/prj.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2020 Nordic Semiconductor
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
Expand All @@ -10,7 +10,9 @@ CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CTLR_ADV_EXT=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n

CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
CONFIG_BT_MAX_CONN=2
CONFIG_BT_ID_MAX=2
CONFIG_BT_EXT_ADV_MAX_ADV_SET=20
CONFIG_BT_MAX_CONN=20
CONFIG_BT_ID_MAX=20
CONFIG_BT_BUF_ACL_RX_COUNT=21
22 changes: 13 additions & 9 deletions samples/bluetooth/scanning_while_connecting/peripheral/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,19 @@ static void start_connectable_advertiser(struct k_work *work)

static int setup_advertiser(uint8_t id_adv)
{
struct bt_le_adv_param adv_param =
BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_CONNECTABLE,
BT_GAP_ADV_SLOW_INT_MIN,
BT_GAP_ADV_SLOW_INT_MAX,
NULL);

struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
.sid = 0,
.secondary_max_skip = 0,
.options = BT_LE_ADV_OPT_CONNECTABLE, // | BT_LE_ADV_OPT_EXT_ADV,
.interval_min = 800, /* Minimum Advertising Interval (N * 0.625 milliseconds) */
.interval_max = 800, /* Maximum Advertising Interval (N * 0.625 milliseconds) */
.peer = NULL,
};

size_t id_count = 0xFF;
int err;

bt_id_get(NULL, &id_count);
if (id_adv == id_count) {
int id;
Expand All @@ -102,7 +106,7 @@ static int setup_advertiser(uint8_t id_adv)
printk("Using current id: %u\n", id_adv);
adv_param.id = id_adv;
advertisers[id_adv].id = id_adv;

err = bt_le_ext_adv_create(&adv_param, NULL, &advertisers[id_adv].adv);
if (err) {
printk("Failed to create advertiser set (err %d)\n", err);
Expand Down Expand Up @@ -136,7 +140,7 @@ int main(void)
}

printk("Bluetooth initialized\n");

printk("Starting %d advertisers\n", CONFIG_BT_EXT_ADV_MAX_ADV_SET);
for (uint8_t i = 0; i < CONFIG_BT_EXT_ADV_MAX_ADV_SET; i++)
{
Expand Down
23 changes: 23 additions & 0 deletions samples/bluetooth/scanning_while_connecting/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

CONFIG_NCS_SAMPLES_DEFAULTS=y

CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_FILTER_ACCEPT_LIST=y
CONFIG_BT_SCAN_AND_INITIATE_IN_PARALLEL=y
CONFIG_BT_CTLR_SDC_ALLOW_PARALLEL_SCANNING_AND_INITIATING=y

# A ring buffer is used as a device address cache
CONFIG_RING_BUFFER=y

CONFIG_BT_CTLR_FAL_SIZE=255

CONFIG_LOG=y

CONFIG_BT_MAX_CONN=16
CONFIG_BT_BUF_ACL_RX_COUNT=17
16 changes: 16 additions & 0 deletions samples/bluetooth/scanning_while_connecting/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
sample:
description: Bluetooth Low Energy fast connection establishment sample
name: Bluetooth LE central scanning while connecting
tests:
sample.bluetooth.scanning_while_connecting:
sysbuild: true
build_only: true
integration_platforms:
- nrf52840dk/nrf52840
- nrf5340dk/nrf5340/cpuapp
- nrf54l15dk/nrf54l15/cpuapp
platform_allow:
- nrf52840dk/nrf52840
- nrf5340dk/nrf5340/cpuapp
- nrf54l15dk/nrf54l15/cpuapp
tags: bluetooth ci_build sysbuild
Loading

0 comments on commit d38bea7

Please sign in to comment.