From 07bf10d30b58b098cb1a1c54438c946840928cce Mon Sep 17 00:00:00 2001 From: Aleksandar Stanoev Date: Thu, 14 Nov 2024 12:40:50 +0000 Subject: [PATCH] samples: bluetooth: Add Channel Sounding Reflector with RRSP sample This sample can be used to set up a Channel Sounding Reflector with the GATT Ranging Responder service. A Channel Sounding Initiator with Ranging Requestor can connect to this sample to receive CS Ranging Data and perform distance estimation. Signed-off-by: Aleksandar Stanoev --- CODEOWNERS | 1 + .../reflector/CMakeLists.txt | 15 ++ .../channel_sounding_ras/reflector/README.rst | 79 +++++++++ .../channel_sounding_ras/reflector/prj.conf | 25 +++ .../reflector/sample.yaml | 12 ++ .../channel_sounding_ras/reflector/src/main.c | 167 ++++++++++++++++++ 6 files changed, 299 insertions(+) create mode 100644 samples/bluetooth/channel_sounding_ras/reflector/CMakeLists.txt create mode 100644 samples/bluetooth/channel_sounding_ras/reflector/README.rst create mode 100644 samples/bluetooth/channel_sounding_ras/reflector/prj.conf create mode 100644 samples/bluetooth/channel_sounding_ras/reflector/sample.yaml create mode 100644 samples/bluetooth/channel_sounding_ras/reflector/src/main.c diff --git a/CODEOWNERS b/CODEOWNERS index 35a62f07f93..dddf94cb7c5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -413,6 +413,7 @@ /samples/bluetooth/central_nfc_pairing/ @nrfconnect/ncs-si-muffin /samples/bluetooth/central_smp_client/ @nrfconnect/ncs-si-muffin /samples/bluetooth/central_uart/ @nrfconnect/ncs-si-muffin +/samples/bluetooth/channel_sounding_ras/ @nrfconnect/ncs-dragoon /samples/bluetooth/conn_time_sync/ @nrfconnect/ncs-dragoon /samples/bluetooth/direction_finding_central/ @nrfconnect/ncs-dragoon /samples/bluetooth/direction_finding_connectionless_rx/ @nrfconnect/ncs-dragoon diff --git a/samples/bluetooth/channel_sounding_ras/reflector/CMakeLists.txt b/samples/bluetooth/channel_sounding_ras/reflector/CMakeLists.txt new file mode 100644 index 00000000000..3a43fe30e48 --- /dev/null +++ b/samples/bluetooth/channel_sounding_ras/reflector/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(channel_sounding_ras_reflector) + +# NORDIC SDK APP START +target_sources(app PRIVATE + src/main.c +) +# NORDIC SDK APP END diff --git a/samples/bluetooth/channel_sounding_ras/reflector/README.rst b/samples/bluetooth/channel_sounding_ras/reflector/README.rst new file mode 100644 index 00000000000..a82fd889dd0 --- /dev/null +++ b/samples/bluetooth/channel_sounding_ras/reflector/README.rst @@ -0,0 +1,79 @@ +.. _channel_sounding_ras_reflector: + +Bluetooth: Channel Sounding Reflector with Ranging Responder +############################################################ + +.. contents:: + :local: + :depth: 2 + +This sample demonstrates how to use the ranging service to provide ranging data to a client. + +Requirements +************ + +The sample supports the following development kits: + +.. table-from-sample-yaml:: + +The sample also requires a device running a Channel Sounding Initiator with Ranging Requestor to connect to. + +Overview +******** + +The sample demonstrates a basic Bluetooth® Low Energy Peripheral role functionality that exposes the GATT Ranging Responder Service and configures the Channel Sounding reflector role. +When Channel Sounding Ranging Data is generated by the controller, it will be automatically stored by the Ranging Service, and can be queried at any time by the Ranging Requestor. +The Channel Sounding Ranging Data can then be used by the peer device to perform distance estimation. + +User interface +************** + +The sample does not require user input and will advertise using the GATT Ranging Service UUID. +The first LED on the development kit will be lit when a connection has been established. + +Building and running +******************** +.. |sample path| replace:: :file:`samples/bluetooth/channel_sounding_ras/reflector` + +.. include:: /includes/build_and_run.txt + +Testing +======= + +After programming the sample to your development kit, you can test it by connecting to another development kit with the Channel Sounding Initiator role with Ranging Requestor. + +1. |connect_terminal_specific| +#. Reset the kit. +#. Program the other kit with a Channel Sounding Initiator with Ranging Requestor sample. +#. Wait until the advertiser is detected by the Central. + In the terminal window, check for information similar to the following:: + + Connected to xx.xx.xx.xx.xx.xx (random) (err 0x00) + Pairing completed: xx.xx.xx.xx.xx.xx (random), bonded: 1 + CS capability exchange completed. + CS config creation complete. ID: 0 + CS security enabled. + CS procedures enabled. + +Dependencies +************ + +This sample uses the following |NCS| libraries: + +* :ref:`dk_buttons_and_leds_readme` +* :file:`include/bluetooth/services/ras.h` + +This sample uses the following Zephyr libraries: + +* :file:`include/sys/printk.h` +* :file:`include/zephyr/types.h` +* :ref:`zephyr:kernel_api`: + + * :file:`include/kernel.h` + +* :ref:`zephyr:bluetooth_api`: + +* :file:`include/bluetooth/bluetooth.h` +* :file:`include/bluetooth/conn.h` +* :file:`include/bluetooth/uuid.h` +* :file:`include/bluetooth/cs.h` diff --git a/samples/bluetooth/channel_sounding_ras/reflector/prj.conf b/samples/bluetooth/channel_sounding_ras/reflector/prj.conf new file mode 100644 index 00000000000..e3f2de11f21 --- /dev/null +++ b/samples/bluetooth/channel_sounding_ras/reflector/prj.conf @@ -0,0 +1,25 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_NCS_SAMPLES_DEFAULTS=y +CONFIG_DK_LIBRARY=y + +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_SMP=y +CONFIG_BT_DEVICE_NAME="Nordic CS Reflector" +CONFIG_BT_MAX_CONN=1 + +# The Ranging Profile recommends a MTU of at least 247 octets. +CONFIG_BT_L2CAP_TX_MTU=498 +CONFIG_BT_BUF_ACL_TX_SIZE=502 +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 +CONFIG_BT_CTLR_PHY_2M=y + +CONFIG_BT_CHANNEL_SOUNDING=y +CONFIG_BT_RAS=y +CONFIG_BT_RAS_RRSP=y diff --git a/samples/bluetooth/channel_sounding_ras/reflector/sample.yaml b/samples/bluetooth/channel_sounding_ras/reflector/sample.yaml new file mode 100644 index 00000000000..faa1ee419b2 --- /dev/null +++ b/samples/bluetooth/channel_sounding_ras/reflector/sample.yaml @@ -0,0 +1,12 @@ +sample: + description: Bluetooth Low Energy Channel Sounding Reflector with Ranging Service Responder + name: Bluetooth LE Channel Sounding Reflector with RRSP +tests: + sample.bluetooth.channel_sounding_ras.reflector: + sysbuild: true + build_only: true + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + tags: bluetooth ci_build sysbuild diff --git a/samples/bluetooth/channel_sounding_ras/reflector/src/main.c b/samples/bluetooth/channel_sounding_ras/reflector/src/main.c new file mode 100644 index 00000000000..dc18e9cbc5a --- /dev/null +++ b/samples/bluetooth/channel_sounding_ras/reflector/src/main.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** @file + * @brief Channel Sounding Reflector with Ranging Responder sample + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CON_STATUS_LED DK_LED1 + +static K_SEM_DEFINE(sem_connected, 0, 1); + +static struct bt_conn *connection; + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_RANGING_SERVICE_VAL)), + BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1), +}; + +static void pairing_complete(struct bt_conn *conn, bool bonded) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing completed: %s, bonded: %d\n", addr, bonded); +} + +static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing failed conn: %s, reason %d %s\n", addr, reason, + bt_security_err_to_str(reason)); +} + +static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {.pairing_complete = pairing_complete, + .pairing_failed = pairing_failed}; + +static void connected_cb(struct bt_conn *conn, uint8_t err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + printk("Connected to %s (err 0x%02X)\n", addr, err); + + if (err) { + bt_conn_unref(conn); + connection = NULL; + } + + connection = bt_conn_ref(conn); + + k_sem_give(&sem_connected); + + dk_set_led_on(CON_STATUS_LED); +} + +static void disconnected_cb(struct bt_conn *conn, uint8_t reason) +{ + printk("Disconnected (reason 0x%02X)\n", reason); + + bt_conn_unref(conn); + connection = NULL; + + dk_set_led_off(CON_STATUS_LED); +} + +static void remote_capabilities_cb(struct bt_conn *conn, struct bt_conn_le_cs_capabilities *params) +{ + ARG_UNUSED(conn); + ARG_UNUSED(params); + printk("CS capability exchange completed.\n"); +} + +static void config_created_cb(struct bt_conn *conn, struct bt_conn_le_cs_config *config) +{ + ARG_UNUSED(conn); + printk("CS config creation complete. ID: %d\n", config->id); +} + +static void security_enabled_cb(struct bt_conn *conn) +{ + ARG_UNUSED(conn); + printk("CS security enabled.\n"); +} + +static void procedure_enabled_cb(struct bt_conn *conn, + struct bt_conn_le_cs_procedure_enable_complete *params) +{ + ARG_UNUSED(conn); + if (params->state == 1) { + printk("CS procedures enabled.\n"); + } else { + printk("CS procedures disabled.\n"); + } +} + +BT_CONN_CB_DEFINE(conn_cb) = { + .connected = connected_cb, + .disconnected = disconnected_cb, + .le_cs_remote_capabilities_available = remote_capabilities_cb, + .le_cs_config_created = config_created_cb, + .le_cs_security_enabled = security_enabled_cb, + .le_cs_procedure_enabled = procedure_enabled_cb, +}; + +int main(void) +{ + int err; + + printk("Starting Channel Sounding Reflector Sample\n"); + + dk_leds_init(); + + err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks); + if (err) { + printk("Failed to register authorization info callbacks.\n"); + return 0; + } + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + + err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return 0; + } + + while (true) { + k_sem_take(&sem_connected, K_FOREVER); + + const struct bt_le_cs_set_default_settings_param default_settings = { + .enable_initiator_role = false, + .enable_reflector_role = true, + .cs_sync_antenna_selection = BT_LE_CS_ANTENNA_SELECTION_OPT_REPETITIVE, + .max_tx_power = BT_HCI_OP_LE_CS_MAX_MAX_TX_POWER, + }; + + err = bt_le_cs_set_default_settings(connection, &default_settings); + if (err) { + printk("Failed to configure default CS settings (err %d)\n", err); + } + } + + return 0; +}