From a84d2b2e1a977d97e04de3e1c765d9d40eb3d9ec Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 18 Mar 2022 14:18:42 +0100 Subject: [PATCH] samples: Bluetooth: Add broadcast audio source sample Add a sample using the Basic Audio Profile (BAP) broadcast source role, that advertises mock audio data over ISO. Signed-off-by: Emil Gydesen --- .../broadcast_audio_source/CMakeLists.txt | 11 ++ .../broadcast_audio_source/README.rst | 27 +++ .../boards/nrf52840dk_nrf52840.conf | 3 + .../boards/nrf5340dk_nrf5340_cpuapp.conf | 3 + .../bluetooth/broadcast_audio_source/prj.conf | 9 + .../broadcast_audio_source/sample.yaml | 8 + .../broadcast_audio_source/src/main.c | 164 ++++++++++++++++++ 7 files changed, 225 insertions(+) create mode 100644 samples/bluetooth/broadcast_audio_source/CMakeLists.txt create mode 100644 samples/bluetooth/broadcast_audio_source/README.rst create mode 100644 samples/bluetooth/broadcast_audio_source/boards/nrf52840dk_nrf52840.conf create mode 100644 samples/bluetooth/broadcast_audio_source/boards/nrf5340dk_nrf5340_cpuapp.conf create mode 100644 samples/bluetooth/broadcast_audio_source/prj.conf create mode 100644 samples/bluetooth/broadcast_audio_source/sample.yaml create mode 100644 samples/bluetooth/broadcast_audio_source/src/main.c diff --git a/samples/bluetooth/broadcast_audio_source/CMakeLists.txt b/samples/bluetooth/broadcast_audio_source/CMakeLists.txt new file mode 100644 index 00000000000..2b20af75d56 --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/CMakeLists.txt @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(unicast_audio_server) + +target_sources(app PRIVATE + src/main.c +) + +zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth) diff --git a/samples/bluetooth/broadcast_audio_source/README.rst b/samples/bluetooth/broadcast_audio_source/README.rst new file mode 100644 index 00000000000..00a90d0de04 --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/README.rst @@ -0,0 +1,27 @@ +.. _bluetooth_broadcast_audio_source: + +Bluetooth: Broadcast Audio Source +################################# + +Overview +******** + +Application demonstrating the LE Audio broadcast audio source functionality. +Will start advertising extended advertising with audio flags, periodic advertising with the +broadcast audio source endpoint (BASE) and finally the BIGinfo together with +(mock) Audio (ISO) data. + +The broadcast source will reset every 30 seconds to show the full API. + +Requirements +************ + +* BlueZ running on the host, or +* A board with Bluetooth Low Energy 5.2 support + +Building and Running +******************** +This sample can be found under +:zephyr_file:`samples/bluetooth/broadcast_audio_source` in the Zephyr tree. + +See :ref:`bluetooth samples section ` for details. diff --git a/samples/bluetooth/broadcast_audio_source/boards/nrf52840dk_nrf52840.conf b/samples/bluetooth/broadcast_audio_source/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 00000000000..5e666547a73 --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,3 @@ +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y +CONFIG_BT_CTLR_ADV_ISO=y diff --git a/samples/bluetooth/broadcast_audio_source/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/bluetooth/broadcast_audio_source/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 00000000000..5e666547a73 --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,3 @@ +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y +CONFIG_BT_CTLR_ADV_ISO=y diff --git a/samples/bluetooth/broadcast_audio_source/prj.conf b/samples/bluetooth/broadcast_audio_source/prj.conf new file mode 100644 index 00000000000..7c15b715a6c --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/prj.conf @@ -0,0 +1,9 @@ +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_AUDIO=y +CONFIG_BT_AUDIO_BROADCAST_SOURCE=y + +CONFIG_BT_AUDIO_BROADCAST_SRC_STREAM_COUNT=1 +CONFIG_BT_ISO_TX_BUF_COUNT=2 + +CONFIG_BT_DEVICE_NAME="Broadcast Audio Source" diff --git a/samples/bluetooth/broadcast_audio_source/sample.yaml b/samples/bluetooth/broadcast_audio_source/sample.yaml new file mode 100644 index 00000000000..6839c44ec8c --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Bluetooth Low Energy Broadcast Audio Source sample + name: Bluetooth Low Energy Broadcast Audio Source sample +tests: + sample.bluetooth.broadcast_audio_source: + harness: bluetooth + platform_allow: qemu_cortex_m3 qemu_x86 nrf52_bsim + tags: bluetooth diff --git a/samples/bluetooth/broadcast_audio_source/src/main.c b/samples/bluetooth/broadcast_audio_source/src/main.c new file mode 100644 index 00000000000..e154e1e1e14 --- /dev/null +++ b/samples/bluetooth/broadcast_audio_source/src/main.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that + * the controller is never idle + */ +#define BROADCAST_ENQUEUE_COUNT 2U +#define TOTAL_BUF_NEEDED (BROADCAST_ENQUEUE_COUNT * CONFIG_BT_AUDIO_BROADCAST_SRC_STREAM_COUNT) + +BUILD_ASSERT(CONFIG_BT_ISO_TX_BUF_COUNT >= TOTAL_BUF_NEEDED, + "CONFIG_BT_ISO_TX_BUF_COUNT should be at least " + "BROADCAST_ENQUEUE_COUNT * CONFIG_BT_AUDIO_BROADCAST_SRC_STREAM_COUNT"); + +static struct bt_audio_lc3_preset preset_16_2_1 = BT_AUDIO_LC3_BROADCAST_PRESET_16_2_1; +static struct bt_audio_stream streams[CONFIG_BT_AUDIO_BROADCAST_SRC_STREAM_COUNT]; +static struct bt_audio_broadcast_source *broadcast_source; + +NET_BUF_POOL_FIXED_DEFINE(tx_pool, + TOTAL_BUF_NEEDED, + BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), 8, NULL); +static uint8_t mock_data[CONFIG_BT_ISO_TX_MTU]; +static bool stopping; + +static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams)); +static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams)); + +#define BROADCAST_SOURCE_LIFETIME 30U /* seconds */ + +static void stream_started_cb(struct bt_audio_stream *stream) +{ + k_sem_give(&sem_started); +} + +static void stream_stopped_cb(struct bt_audio_stream *stream) +{ + k_sem_give(&sem_stopped); +} + +static void stream_sent_cb(struct bt_audio_stream *stream) +{ + static uint32_t sent_cnt; + struct net_buf *buf; + int ret; + + if (stopping) { + return; + } + + buf = net_buf_alloc(&tx_pool, K_FOREVER); + if (buf == NULL) { + printk("Could not allocate buffer when sending on %p\n", + stream); + return; + } + + net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE); + net_buf_add_mem(buf, mock_data, preset_16_2_1.qos.sdu); + ret = bt_audio_stream_send(stream, buf); + if (ret < 0) { + /* This will end broadcasting on this stream. */ + printk("Unable to broadcast data on %p: %d\n", stream, ret); + net_buf_unref(buf); + return; + } + + sent_cnt++; + if ((sent_cnt % 1000U) == 0U) { + printk("Sent %u total ISO packets\n", sent_cnt); + } +} + +struct bt_audio_stream_ops stream_ops = { + .started = stream_started_cb, + .stopped = stream_stopped_cb, + .sent = stream_sent_cb +}; + +void main(void) +{ + int err; + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + printk("Bluetooth initialized\n"); + + for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { + streams[i].ops = &stream_ops; + } + + for (size_t i = 0U; i < ARRAY_SIZE(mock_data); i++) { + /* Initialize mock data */ + mock_data[i] = i; + } + + while (true) { + printk("Creating broadcast source\n"); + err = bt_audio_broadcast_source_create(streams, + ARRAY_SIZE(streams), + &preset_16_2_1.codec, + &preset_16_2_1.qos, + &broadcast_source); + if (err != 0) { + printk("Unable to create broadcast source: %d\n", err); + return; + } + + printk("Starting broadcast source\n"); + stopping = false; + err = bt_audio_broadcast_source_start(broadcast_source); + if (err != 0) { + printk("Unable to start broadcast source: %d\n", err); + return; + } + + /* Wait for all to be started */ + for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { + k_sem_take(&sem_started, K_FOREVER); + } + printk("Broadcast source started\n"); + + /* Initialize sending */ + for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { + for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) { + stream_sent_cb(&streams[i]); + } + } + + printk("Waiting %u seconds before stopped\n", + BROADCAST_SOURCE_LIFETIME); + k_sleep(K_SECONDS(BROADCAST_SOURCE_LIFETIME)); + + printk("Stopping broadcast source\n"); + stopping = true; + err = bt_audio_broadcast_source_stop(broadcast_source); + if (err != 0) { + printk("Unable to stop broadcast source: %d\n", err); + return; + } + + /* Wait for all to be stopped */ + for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { + k_sem_take(&sem_stopped, K_FOREVER); + } + printk("Broadcast source stopped\n"); + + printk("Deleting broadcast source\n"); + err = bt_audio_broadcast_source_delete(broadcast_source); + if (err != 0) { + printk("Unable to delete broadcast source: %d\n", err); + return; + } + printk("Broadcast source deleted\n"); + broadcast_source = NULL; + } +}