Initial commit for nrf5340_audio

This commit is contained in:
2025-06-30 09:28:01 +02:00
commit 62dbb94724
174 changed files with 27001 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
#
# Copyright (c) 2023 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
target_sources(app PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/main.c)

150
unicast_server/README.rst Normal file
View File

@@ -0,0 +1,150 @@
.. _nrf53_audio_unicast_server_app:
nRF5340 Audio: Unicast server
#############################
.. contents::
:local:
:depth: 2
The nRF5340 Audio unicast server application implements the :ref:`CIS headset mode <nrf53_audio_app_overview>`.
In this mode, one Connected Isochronous Group (CIG) can be used with two Connected Isochronous Streams (CIS).
Receiving unidirectional or transceiving bidirectional audio happens using CIG and CIS.
In addition, Coordinated Set Identification Service (CSIS) is implemented on the server side.
The following limitations apply to this application:
* One CIG, one of the two CIS streams (selectable).
* Audio output: I2S/Analog headset output.
* Audio input: PDM microphone over I2S.
* Configuration: 16 bit, several bit rates ranging from 32 kbps to 124 kbps.
.. _nrf53_audio_unicast_server_app_requirements:
Requirements
************
The application shares the :ref:`requirements common to all nRF5340 Audio application <nrf53_audio_app_requirements>`.
.. _nrf53_audio_unicast_server_app_ui:
User interface
**************
Most of the user interface mappings are common across all nRF5340 Audio applications.
See the :ref:`nrf53_audio_app_ui` page for detailed overview.
This application uses specific mapping for the following user interface elements:
* Long-pressed on the unicast server device during startup:
* **VOL-** - Changes the headset to the left channel one.
* **VOL+** - Changes the headset to the right channel one.
* **BTN5** - Clears the previously stored bonding information.
* Pressed on the unicast server device during playback:
* **PLAY/PAUSE** - Starts or pauses the playback of the stream.
* **VOL-** - Turns the playback volume down.
* **VOL+** - Turns the playback volume up.
* **BTN5** - Mutes the playback volume (and unmutes).
* **LED1** - Blinking blue - Kits have started streaming audio.
* **LED2** - Solid green - Sync achieved (both drift and presentation compensation are in the ``LOCKED`` state).
* **RGB**:
* Solid blue - The device is programmed as the left headset.
* Solid magenta - The device is programmed as the right headset.
.. _nrf53_audio_unicast_server_app_configuration:
Configuration
*************
By default, if you have not made any changes to :file:`.conf` files at :file:`applications/nrf5340_audio/`, the nRF5340 build script tries to build the CIS applications in the CIS unidirectional mode.
To switch to the bidirectional mode, see :ref:`nrf53_audio_app_configuration_select_bidirectional`.
For other configuration options, see :ref:`nrf53_audio_app_configuration` and :ref:`nrf53_audio_app_fota`.
For information about how to configure applications in the |NCS|, see :ref:`configure_application`.
.. _nrf53_audio_unicast_server_app_building:
Building and running
********************
This application can be found under :file:`applications/nrf5340_audio/unicast_server` in the nRF Connect SDK folder structure, but it uses :file:`.conf` files at :file:`applications/nrf5340_audio/`.
The nRF5340 Audio DK comes preprogrammed with basic firmware that indicates if the kit is functional.
See :ref:`nrf53_audio_app_dk_testing_out_of_the_box` for more information.
To build the application, see :ref:`nrf53_audio_app_building`.
.. _nrf53_audio_unicast_server_app_testing:
Testing
*******
After building and programming the application, you can test the default CIS headset mode using one :ref:`unicast client application <nrf53_audio_unicast_client_app>` and one or two unicast server devices (this application).
The recommended approach is to use two other nRF5340 Audio DKs programmed with the :ref:`unicast client application <nrf53_audio_unicast_client_app>` for the CIS gateway and the unicast server application (this application) for the CIS headset, respectively, but you can also use an external device that supports the role of unicast server.
.. note::
|nrf5340_audio_external_devices_note|
The following testing scenario assumes you are using USB as the audio source on the gateway.
This is the default setting.
Complete the following steps to test the unidirectional CIS mode for one gateway and two headset devices:
1. Make sure that the development kits are still plugged into the USB ports and are turned on.
.. note::
|usb_known_issues|
**LED3** starts blinking green on every device to indicate the ongoing CPU activity on the application core.
#. Wait for the **LED1** on the gateway to start blinking blue.
This happens shortly after programming the development kit and indicates that the gateway device is connected to at least one headset and ready to send data.
#. Search the list of audio devices listed in the sound settings of your operating system for *nRF5340 USB Audio* (gateway) and select it as the output device.
#. Connect headphones to the **HEADPHONE** audio jack on both headset devices.
#. Start audio playback on your PC from any source.
#. Wait for **LED1** to blink blue on the headset.
When they do, the audio stream has started on the headset.
.. note::
The audio outputs only to the left channel of the audio jack, even if the given headset is configured as the right headset.
This is because of the mono hardware codec chip used on the development kits.
If you want to play stereo sound using one development kit, you must connect an external hardware codec chip that supports stereo.
#. Wait for **LED2** to light up solid green on the headsets to indicate that the audio synchronization is achieved.
#. Press the **VOL+** button on one of the headsets.
The playback volume increases for the headset.
#. If you use more than one headset, hold down the **VOL+** button and press the **RESET** button on a headset.
After startup, this headset will be configured as the right channel headset.
#. If you use more than one headset, hold down the **VOL-** button and press the **RESET** button on a headset.
After startup, this headset will be configured as the left channel headset.
You can also just press the **RESET** button to restore the original programmed settings.
For other testing options, refer to :ref:`nrf53_audio_unicast_server_app_ui`.
After the kits have paired for the first time, they are now bonded.
This means the Long-Term Key (LTK) is stored on each side, and that the kits will only connect to each other unless the bonding information is cleared.
To clear the bonding information, press and hold **BTN 5** during boot or reprogram all the development kits.
When you finish testing, power off the nRF5340 Audio development kits by switching the power switch from On to Off.
.. _nrf53_audio_unicast_server_app_testing_steps_cis_walkie_talkie:
Testing the walkie-talkie demo
==============================
Testing the walkie-talkie demo is identical to the default testing procedure, except for the following differences:
* You must enable the Kconfig option mentioned in :ref:`nrf53_audio_app_configuration_enable_walkie_talkie` before building the application.
* Instead of controlling the playback, you can speak through the PDM microphones.
The line is open all the time, no need to press any buttons to talk, but the volume control works as in the default testing procedure.
Dependencies
************
For the list of dependencies, check the application's source files.

570
unicast_server/main.c Normal file
View File

@@ -0,0 +1,570 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include "streamctrl.h"
#include <zephyr/zbus/zbus.h>
#include "unicast_server.h"
#include "zbus_common.h"
#include "nrf5340_audio_dk.h"
#include "led.h"
#include "button_assignments.h"
#include "macros_common.h"
#include "audio_system.h"
#include "button_handler.h"
#include "bt_le_audio_tx.h"
#include "bt_mgmt.h"
#include "bt_rendering_and_capture.h"
#include "audio_datapath.h"
#include "bt_content_ctrl.h"
#include "le_audio.h"
#include "le_audio_rx.h"
#include "fw_info_app.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, CONFIG_MAIN_LOG_LEVEL);
ZBUS_SUBSCRIBER_DEFINE(button_evt_sub, CONFIG_BUTTON_MSG_SUB_QUEUE_SIZE);
ZBUS_MSG_SUBSCRIBER_DEFINE(le_audio_evt_sub);
ZBUS_CHAN_DECLARE(button_chan);
ZBUS_CHAN_DECLARE(le_audio_chan);
ZBUS_CHAN_DECLARE(bt_mgmt_chan);
ZBUS_CHAN_DECLARE(volume_chan);
ZBUS_OBS_DECLARE(volume_evt_sub);
static struct k_thread button_msg_sub_thread_data;
static struct k_thread le_audio_msg_sub_thread_data;
static k_tid_t button_msg_sub_thread_id;
static k_tid_t le_audio_msg_sub_thread_id;
K_THREAD_STACK_DEFINE(button_msg_sub_thread_stack, CONFIG_BUTTON_MSG_SUB_STACK_SIZE);
K_THREAD_STACK_DEFINE(le_audio_msg_sub_thread_stack, CONFIG_LE_AUDIO_MSG_SUB_STACK_SIZE);
static enum stream_state strm_state = STATE_PAUSED;
/* Function for handling all stream state changes */
static void stream_state_set(enum stream_state stream_state_new)
{
strm_state = stream_state_new;
}
/**
* @brief Handle button activity.
*/
static void button_msg_sub_thread(void)
{
int ret;
const struct zbus_channel *chan;
while (1) {
ret = zbus_sub_wait(&button_evt_sub, &chan, K_FOREVER);
ERR_CHK(ret);
struct button_msg msg;
ret = zbus_chan_read(chan, &msg, ZBUS_READ_TIMEOUT_MS);
ERR_CHK(ret);
LOG_DBG("Got btn evt from queue - id = %d, action = %d", msg.button_pin,
msg.button_action);
if (msg.button_action != BUTTON_PRESS) {
LOG_WRN("Unhandled button action");
return;
}
switch (msg.button_pin) {
case BUTTON_PLAY_PAUSE:
if (IS_ENABLED(CONFIG_WALKIE_TALKIE_DEMO)) {
LOG_WRN("Play/pause not supported in walkie-talkie mode");
break;
}
if (bt_content_ctlr_media_state_playing()) {
ret = bt_content_ctrl_stop(NULL);
if (ret) {
LOG_WRN("Could not stop: %d", ret);
}
} else if (!bt_content_ctlr_media_state_playing()) {
ret = bt_content_ctrl_start(NULL);
if (ret) {
LOG_WRN("Could not start: %d", ret);
}
} else {
LOG_WRN("In invalid state: %d", strm_state);
}
break;
case BUTTON_VOLUME_UP:
ret = bt_r_and_c_volume_up();
if (ret) {
LOG_WRN("Failed to increase volume: %d", ret);
}
break;
case BUTTON_VOLUME_DOWN:
ret = bt_r_and_c_volume_down();
if (ret) {
LOG_WRN("Failed to decrease volume: %d", ret);
}
break;
case BUTTON_4:
if (IS_ENABLED(CONFIG_AUDIO_TEST_TONE)) {
if (IS_ENABLED(CONFIG_WALKIE_TALKIE_DEMO)) {
LOG_DBG("Test tone is disabled in walkie-talkie mode");
break;
}
if (strm_state != STATE_STREAMING) {
LOG_WRN("Not in streaming state");
break;
}
ret = audio_system_encode_test_tone_step();
if (ret) {
LOG_WRN("Failed to play test tone, ret: %d", ret);
}
break;
}
break;
case BUTTON_5:
if (IS_ENABLED(CONFIG_AUDIO_MUTE)) {
ret = bt_r_and_c_volume_mute(false);
if (ret) {
LOG_WRN("Failed to mute, ret: %d", ret);
}
break;
}
break;
default:
LOG_WRN("Unexpected/unhandled button id: %d", msg.button_pin);
}
STACK_USAGE_PRINT("button_msg_thread", &button_msg_sub_thread_data);
}
}
/**
* @brief Handle Bluetooth LE audio events.
*/
static void le_audio_msg_sub_thread(void)
{
int ret;
uint32_t pres_delay_us;
uint32_t bitrate_bps;
uint32_t sampling_rate_hz;
const struct zbus_channel *chan;
while (1) {
struct le_audio_msg msg;
ret = zbus_sub_wait_msg(&le_audio_evt_sub, &chan, &msg, K_FOREVER);
ERR_CHK(ret);
LOG_DBG("Received event = %d, current state = %d", msg.event, strm_state);
switch (msg.event) {
case LE_AUDIO_EVT_STREAMING:
LOG_DBG("LE audio evt streaming");
if (msg.dir == BT_AUDIO_DIR_SOURCE) {
audio_system_encoder_start();
}
if (strm_state == STATE_STREAMING) {
LOG_DBG("Got streaming event in streaming state");
break;
}
audio_system_start();
stream_state_set(STATE_STREAMING);
ret = led_blink(LED_APP_1_BLUE);
ERR_CHK(ret);
break;
case LE_AUDIO_EVT_NOT_STREAMING:
LOG_DBG("LE audio evt not streaming");
if (strm_state == STATE_PAUSED) {
LOG_DBG("Got not_streaming event in paused state");
break;
}
if (msg.dir == BT_AUDIO_DIR_SOURCE) {
audio_system_encoder_stop();
}
stream_state_set(STATE_PAUSED);
audio_system_stop();
ret = led_on(LED_APP_1_BLUE);
ERR_CHK(ret);
break;
case LE_AUDIO_EVT_CONFIG_RECEIVED:
LOG_DBG("LE audio config received");
ret = unicast_server_config_get(msg.conn, msg.dir, &bitrate_bps,
&sampling_rate_hz, NULL);
if (ret) {
LOG_WRN("Failed to get config: %d", ret);
break;
}
LOG_DBG("\tSampling rate: %d Hz", sampling_rate_hz);
LOG_DBG("\tBitrate (compressed): %d bps", bitrate_bps);
if (msg.dir == BT_AUDIO_DIR_SINK) {
ret = audio_system_config_set(VALUE_NOT_SET, VALUE_NOT_SET,
sampling_rate_hz);
ERR_CHK(ret);
} else if (msg.dir == BT_AUDIO_DIR_SOURCE) {
ret = audio_system_config_set(sampling_rate_hz, bitrate_bps,
VALUE_NOT_SET);
ERR_CHK(ret);
}
break;
case LE_AUDIO_EVT_PRES_DELAY_SET:
LOG_DBG("Set presentation delay");
ret = unicast_server_config_get(msg.conn, BT_AUDIO_DIR_SINK, NULL, NULL,
&pres_delay_us);
if (ret) {
LOG_ERR("Failed to get config: %d", ret);
break;
}
ret = audio_datapath_pres_delay_us_set(pres_delay_us);
if (ret) {
LOG_ERR("Failed to set presentation delay to %d", pres_delay_us);
break;
}
LOG_INF("Presentation delay %d us is set by initiator", pres_delay_us);
break;
case LE_AUDIO_EVT_NO_VALID_CFG:
LOG_WRN("No valid configurations found, will disconnect");
ret = bt_mgmt_conn_disconnect(msg.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (ret) {
LOG_ERR("Failed to disconnect: %d", ret);
}
break;
case LE_AUDIO_EVT_STREAM_SENT:
/* Nothing to do. */
break;
default:
LOG_WRN("Unexpected/unhandled le_audio event: %d", msg.event);
break;
}
STACK_USAGE_PRINT("le_audio_msg_thread", &le_audio_msg_sub_thread_data);
}
}
/**
* @brief Create zbus subscriber threads.
*
* @return 0 for success, error otherwise.
*/
static int zbus_subscribers_create(void)
{
int ret;
button_msg_sub_thread_id = k_thread_create(
&button_msg_sub_thread_data, button_msg_sub_thread_stack,
CONFIG_BUTTON_MSG_SUB_STACK_SIZE, (k_thread_entry_t)button_msg_sub_thread, NULL,
NULL, NULL, K_PRIO_PREEMPT(CONFIG_BUTTON_MSG_SUB_THREAD_PRIO), 0, K_NO_WAIT);
ret = k_thread_name_set(button_msg_sub_thread_id, "BUTTON_MSG_SUB");
if (ret) {
LOG_ERR("Failed to create button_msg thread");
return ret;
}
le_audio_msg_sub_thread_id = k_thread_create(
&le_audio_msg_sub_thread_data, le_audio_msg_sub_thread_stack,
CONFIG_LE_AUDIO_MSG_SUB_STACK_SIZE, (k_thread_entry_t)le_audio_msg_sub_thread, NULL,
NULL, NULL, K_PRIO_PREEMPT(CONFIG_LE_AUDIO_MSG_SUB_THREAD_PRIO), 0, K_NO_WAIT);
ret = k_thread_name_set(le_audio_msg_sub_thread_id, "LE_AUDIO_MSG_SUB");
if (ret) {
LOG_ERR("Failed to create le_audio_msg thread");
return ret;
}
return 0;
}
/**
* @brief Zbus listener to receive events from bt_mgmt.
*
* @param[in] chan Zbus channel.
*
* @note Will in most cases be called from BT_RX context,
* so there should not be too much processing done here.
*/
static void bt_mgmt_evt_handler(const struct zbus_channel *chan)
{
int ret;
const struct bt_mgmt_msg *msg;
uint8_t num_conn = 0;
msg = zbus_chan_const_msg(chan);
bt_mgmt_num_conn_get(&num_conn);
switch (msg->event) {
case BT_MGMT_CONNECTED:
/* NOTE: The string below is used by the Nordic CI system */
LOG_INF("Connection event. Num connections: %u", num_conn);
break;
case BT_MGMT_DISCONNECTED:
/* NOTE: The string below is used by the Nordic CI system */
LOG_INF("Disconnection event. Num connections: %u", num_conn);
ret = bt_content_ctrl_conn_disconnected(msg->conn);
if (ret) {
LOG_ERR("Failed to handle disconnection in content control: %d", ret);
}
break;
case BT_MGMT_SECURITY_CHANGED:
LOG_INF("Security changed");
ret = bt_content_ctrl_discover(msg->conn);
if (ret == -EALREADY) {
LOG_DBG("Discovery in progress or already done");
} else if (ret) {
LOG_ERR("Failed to start discovery of content control: %d", ret);
}
break;
default:
LOG_WRN("Unexpected/unhandled bt_mgmt event: %d", msg->event);
break;
}
}
ZBUS_LISTENER_DEFINE(bt_mgmt_evt_listen, bt_mgmt_evt_handler);
/**
* @brief Link zbus producers and observers.
*
* @return 0 for success, error otherwise.
*/
static int zbus_link_producers_observers(void)
{
int ret;
if (!IS_ENABLED(CONFIG_ZBUS)) {
return -ENOTSUP;
}
ret = zbus_chan_add_obs(&button_chan, &button_evt_sub, ZBUS_ADD_OBS_TIMEOUT_MS);
if (ret) {
LOG_ERR("Failed to add button sub");
return ret;
}
ret = zbus_chan_add_obs(&le_audio_chan, &le_audio_evt_sub, ZBUS_ADD_OBS_TIMEOUT_MS);
if (ret) {
LOG_ERR("Failed to add le_audio sub");
return ret;
}
ret = zbus_chan_add_obs(&bt_mgmt_chan, &bt_mgmt_evt_listen, ZBUS_ADD_OBS_TIMEOUT_MS);
if (ret) {
LOG_ERR("Failed to add bt_mgmt sub");
return ret;
}
ret = zbus_chan_add_obs(&volume_chan, &volume_evt_sub, ZBUS_ADD_OBS_TIMEOUT_MS);
if (ret) {
LOG_ERR("Failed to add volume sub");
return ret;
}
return 0;
}
/*
* @brief The following configures the data for the extended advertising. This includes the
* Audio Stream Control Service requirements [BAP 3.7.2.1.1] in the AUX_ADV_IND Extended
* Announcements.
*
* @param ext_adv_buf Pointer to the bt_data used for extended advertising.
* @param ext_adv_buf_size Size of @p ext_adv_buf.
* @param ext_adv_count Pointer to the number of elements added to @p adv_buf.
*
* @return 0 for success, error otherwise.
*/
static int ext_adv_populate(struct bt_data *ext_adv_buf, size_t ext_adv_buf_size,
size_t *ext_adv_count)
{
int ret;
size_t ext_adv_buf_cnt = 0;
NET_BUF_SIMPLE_DEFINE_STATIC(uuid_buf, CONFIG_EXT_ADV_UUID_BUF_MAX);
ext_adv_buf[ext_adv_buf_cnt].type = BT_DATA_UUID16_SOME;
ext_adv_buf[ext_adv_buf_cnt].data = uuid_buf.data;
ext_adv_buf_cnt++;
ret = bt_r_and_c_uuid_populate(&uuid_buf);
if (ret) {
LOG_ERR("Failed to add adv data from renderer: %d", ret);
return ret;
}
ret = bt_content_ctrl_uuid_populate(&uuid_buf);
if (ret) {
LOG_ERR("Failed to add adv data from content ctrl: %d", ret);
return ret;
}
ret = bt_mgmt_manufacturer_uuid_populate(&uuid_buf, CONFIG_BT_DEVICE_MANUFACTURER_ID);
if (ret) {
LOG_ERR("Failed to add adv data with manufacturer ID: %d", ret);
return ret;
}
ret = unicast_server_adv_populate(&ext_adv_buf[ext_adv_buf_cnt],
ext_adv_buf_size - ext_adv_buf_cnt);
if (ret < 0) {
LOG_ERR("Failed to add adv data from unicast server: %d", ret);
return ret;
}
ext_adv_buf_cnt += ret;
/* Add the number of UUIDs */
ext_adv_buf[0].data_len = uuid_buf.len;
LOG_DBG("Size of adv data: %d, num_elements: %d", sizeof(struct bt_data) * ext_adv_buf_cnt,
ext_adv_buf_cnt);
*ext_adv_count = ext_adv_buf_cnt;
return 0;
}
uint8_t stream_state_get(void)
{
return strm_state;
}
void streamctrl_send(void const *const data, size_t size, uint8_t num_ch)
{
int ret;
static int prev_ret;
struct le_audio_encoded_audio enc_audio = {.data = data, .size = size, .num_ch = num_ch};
if (strm_state == STATE_STREAMING) {
ret = unicast_server_send(enc_audio);
if (ret != 0 && ret != prev_ret) {
if (ret == -ECANCELED) {
LOG_WRN("Sending operation cancelled");
} else {
LOG_WRN("Problem with sending LE audio data, ret: %d", ret);
}
}
prev_ret = ret;
}
}
int main(void)
{
int ret;
enum bt_audio_location location;
enum audio_channel channel;
static struct bt_data ext_adv_buf[CONFIG_EXT_ADV_BUF_MAX];
LOG_DBG("Main started");
size_t ext_adv_buf_cnt = 0;
ret = nrf5340_audio_dk_init();
ERR_CHK(ret);
ret = fw_info_app_print();
ERR_CHK(ret);
ret = bt_mgmt_init();
ERR_CHK(ret);
ret = audio_system_init();
ERR_CHK(ret);
ret = zbus_subscribers_create();
ERR_CHK_MSG(ret, "Failed to create zbus subscriber threads");
ret = zbus_link_producers_observers();
ERR_CHK_MSG(ret, "Failed to link zbus producers and observers");
ret = le_audio_rx_init();
ERR_CHK_MSG(ret, "Failed to initialize rx path");
channel_assignment_get(&channel);
if (channel == AUDIO_CH_L) {
location = BT_AUDIO_LOCATION_FRONT_LEFT;
} else {
location = BT_AUDIO_LOCATION_FRONT_RIGHT;
}
ret = unicast_server_enable(le_audio_rx_data_handler, location);
ERR_CHK_MSG(ret, "Failed to enable LE Audio");
ret = bt_r_and_c_init();
ERR_CHK(ret);
ret = bt_content_ctrl_init();
ERR_CHK(ret);
ret = ext_adv_populate(ext_adv_buf, ARRAY_SIZE(ext_adv_buf), &ext_adv_buf_cnt);
ERR_CHK(ret);
ret = bt_mgmt_adv_start(0, ext_adv_buf, ext_adv_buf_cnt, NULL, 0, true);
ERR_CHK(ret);
return 0;
}

View File

@@ -0,0 +1,49 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_AUDIO_DEV=1
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
CONFIG_BT_GATT_AUTO_RESUBSCRIBE=n
CONFIG_BT_GATT_AUTO_SEC_REQ=n
CONFIG_BT_GATT_AUTO_UPDATE_MTU=y
CONFIG_BT_GATT_CACHING=n
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_MAX_CONN=4
CONFIG_BT_MAX_PAIRED=4
CONFIG_BT_EXT_ADV=y
CONFIG_BT_AUDIO=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_ISO_PERIPHERAL=y
CONFIG_BT_BAP_UNICAST_SERVER=y
CONFIG_BT_CAP_ACCEPTOR=y
CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER=y
CONFIG_BT_PAC_SNK=y
CONFIG_BT_PAC_SNK_NOTIFIABLE=y
CONFIG_BT_PAC_SRC=y
CONFIG_BT_PAC_SRC_NOTIFIABLE=y
CONFIG_BT_CSIP_SET_MEMBER=y
CONFIG_BT_DEVICE_APPEARANCE=2369
CONFIG_BT_ISO_MAX_CHAN=2
CONFIG_BT_ASCS=y
CONFIG_BT_ASCS_MAX_ASE_SNK_COUNT=1
CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT=1
CONFIG_BT_VCP_VOL_REND=y
CONFIG_BT_MCC=y
CONFIG_BT_MCC_READ_MEDIA_STATE=y
CONFIG_BT_MCC_SET_MEDIA_CONTROL_POINT=y
CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS=n
CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE=25
CONFIG_LC3_ENC_CHAN_MAX=1
CONFIG_LC3_DEC_CHAN_MAX=1
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=2048