Skip to content

The objective of this project is to provide an example of the implementation of the Broadcast Assistant role from the Bluetooth® LE Audio specification using the STM32 Bluetooth® LE Audio library on the STM32WBA MCUs.

License

Notifications You must be signed in to change notification settings

stm32-hotspot/STM32WBA-BLE-Audio-Broadcast-Assistant

Repository files navigation

STM32WBA Bluetooth® LE Audio - Broadcast Assistant

The objective of this project is to provide an example of the implementation of the Broadcast Assistant role from the Bluetooth® LE Audio specification using the STM32 Bluetooth® LE Audio library on the STM32WBA MCUs.

For more details about the Broadcast Assistant role, refer to the Basic Audio Profile specification.

Table of Contents

Setup

This example runs on STM32WBA55G-DK1 and STM32WBA65I-DK1 boards.

You need the following materials to execute the demo:

  • An STM32WBA55G-DK1 or an STM32WBA65I-DK1 board running the BLE_Audio_Broadcast_Assistant project (available in this repo) to act as Broadcast Assistant
  • An STM32WBA55G-DK1 or an STM32WBA65I-DK1 board running the BLE_Audio_PBP_Source or BLE_Audio_TMAP_Central STM32Cube example (available in the STM32CubeWBA firmware package) to act as a Broadcast Source
  • An STM32WBA55G-DK1 or an STM32WBA65I-DK1 board running the BLE_Audio_TMAP_Peripheral STM32Cube example (available in the STM32CubeWBA firmware package) to act as a Broadcast Sink and Scan Delegator
  • An audio source with a 3.5mm jack output (laptop or smartphone with a jack output). If the music source is powered using a power supply, use a Ground Loop isolator device or run the laptop on a battery to avoid Ground Loop issues.
  • An audio renderer with a 3.5mm jack input (headphones or speakers)

To build the project, you need one of the following IDE:

  • IAR Embedded Workbench for ARM (EWARM) 9.20.1
  • STM32CubeIDE 1.18.1

The following details how to set up the demo:

BLE_Audio_Broadcast_Assistant setup

Operate the Demo

The following steps are required to operate the demo:

  1. Flash the three boards with their firmware, as depicted in the setup section.

  1. Start advertising with Scan Delegator on the BLE_Audio_TMAP_Peripheral board by selecting the "Start Scan Deleg." menu option.
Step 2

  1. If using the BLE_Audio_TMAP_Central application as Broadcast Source instead of BLE_Audio_PBP_Source, start the Broadcast Source by using the "Start Broadcast" menu option, followed by the selection of the sample rate.
Step 3

  1. Start scanning on the Broadcast Assistant by selecting the "Scan Sc Deleg" menu option, and select the advertising Scan Delegator in the list.
Step 4

  1. After linking up, start scanning for Broadcast Source on the Broadcast Assistant by selecting the "Audio Rec Start" menu option, and select the Broadcast Source in the list.
Step 5

The BLE_Audio_TMAP_Peripheral should then be synchronized with the Broadcast Source and render audio through the 3.5mm jack output.

To stop the synchronization, use the "Audio Rec Stop" menu option on the Broadcast Assistant.

Troubleshooting

First of all, a common source of issues can be the misalignment of the bonding state between the Broadcast Assistant and the Scan Delegator, causing incomplete or failed linkup procedure. To completely reset the state of both devices, use the "Clear Sec. DB" option in the startup menu, then press the reset button on each board.

The TMAP_Peripheral doesn't sync to the Broadcast Source after the Audio Reception Start procedure
One cause of this issue is a high metadata length in the BASE structure of the Broadcast Source, which causes the Add Broadcast Source write operation size to exceed the size of the BASS characteristic. In the BLE_Audio_TMAP_Peripheral application, the default metadata size used to evaluate the size of the characteristic in the 1.6.1 Cube Firmware release is 20 and is defined in tmap_app_conf.h as MAX_BASS_METADATA_SIZE. Update this value if necessary.

Broadcast Assistant Role

The Broadcast Assistant role is described in the Basic Audio Profile (BAP) specification of the Bluetooth® SIG. This role has been specified to allow devices without display capabilities like headphones, earbuds, or speakers, to delegate the selection of Broadcast Sources synchronization to another device with display capabilities like a smartphone. The audio device will use the Scan Delegator role to allow that.

The typical use case would be the following: a user enters an area where a Broadcast Source is available, for example, a train station broadcasting the latest train status updates or a silent TV broadcasting its audio. The user will be able to see the available Broadcast on their smartphone and choose to synchronize to it. The smartphone will transfer the Broadcast Source information to the user's audio device (headphones or earbuds, for example) so that the user can hear the Broadcast Source's content.

Detailed Process

The following is the process detailing how a Broadcast Assistant connects and controls a remote Scan Delegator. The devices shown in the diagrams are given as examples.

  1. First of all, the Scan Delegator, collocated with a Broadcast Sink role, starts advertising. The Broadcast Assistant is able to scan it and know the device is a Scan Delegator thanks to the Broadcast Audio Scan Service (BASS) UUID in the extended advertising data.
Broadcast Assistant process step 1


  1. The Broadcast Assistant initiates a connection and discovers the Broadcast Audio Scan Service (BASS) and the Published Audio Capability Service (PACS). The PACS will inform it of the audio capabilities of the remote device (supported sample rate, channels, audio contexts, etc.).
Broadcast Assistant process step 2


  1. The Broadcast Assistant starts scanning for Broadcast Sources and informs the Scan Delegator that it has done so using the BASS.
Broadcast Assistant process step 3


  1. The Broadcast Assistant synchronizes to the Periodic Advertising train of the Broadcast Source. If the configuration is compatible with the Scan Delegator's capabilities, details are transferred to the Scan Delegator with instructions (synchronize to Periodic Advertising or Broadcast Isochronous Stream). The Scan Delegator can accept or refuse the source.
Broadcast Assistant process step 4


  1. Optionally, the Broadcast Assistant can transfer its periodic advertising synchronization by using the Periodic Advertising Sync Transfer procedure. This feature is not mandatory to support but prevents the Scan Delegator from performing a scan, which is a power-demanding operation. Use of this procedure is recommended on devices powered by batteries with limited capacity, like headphones or earbuds.
Broadcast Assistant process step 5


  1. The Scan Delegator constantly informs the Broadcast Assistant about its synchronization status (if synchronized to the Periodic Advertising, the Broadcast Isochronous Stream, or both).
Broadcast Assistant process step 6


  1. If the Broadcast Source is encrypted and the Broadcast Assistant possesses the Broadcast Code necessary to decrypt it, it may use the BASS to send the Broadcast Code to the Scan Delegator.
Broadcast Assistant process step 7

STM32Cube Firmware APIs

This section details how to implement a Broadcast Assistant application using STM32CubeWBA Firmware.

Broadcast Assistant API List

The following APIs are available in the STM32Cube firmware for STM32WBA when a Bluetooth® device is initialized with the Broadcast Assistant role:

Functions Description Header File
CAP_BroadcastAssistant_StartAdvReportParsing() Start parsing Extended Advertising report to search for Scan Delegators soliciting assistance and Broadcast Sources on behalf of a connected Scan Delegator cap.h
CAP_BroadcastAssistant_StopAdvReportParsing() Stop parsing Extended Advertising report to search for Scan Delegators soliciting assistance and Broadcast Sources on behalf of a connected Scan Delegator cap.h
CAP_BroadcastAssistant_StartPASync() Synchronize with a Periodic Advertising train to discover BASE info about a broadcast source cap.h
CAP_BroadcastAssistant_StopPASync() Stop the synchronization to a periodic advertising train cap.h
CAP_Broadcast_AudioReceptionStart() Perform the Audio Reception Start Procedure to request remote acceptor(s) to synchronize to a Broadcast Source cap.h
CAP_Broadcast_AudioReceptionStop() Perform the Audio Reception Stop Procedure to request remote acceptor(s) to desynchronize from a Broadcast Source cap.h

These commands are executed through the Common Audio Profile (CAP) acting as CAP Commander. During or after the execution of these functions, events will be generated in CAP_Notification(). The following table lists these events:

Functions Description Header File
CAP_BA_LINKUP_EVT Event notified during CAP linkup procedure if CAP Commander supports BAP Broadcast Assistant role and discovers that the remote device supports Scan Delegator role. cap.h
CAP_BA_REM_BROADCAST_RECEIVE_STATE_INFO_EVT This event is notified by Commander with Broadcast Assistant role to report Broadcast Receive State value. This event is notified during CAP linkup process or when remote device updates it through GATT Notifications cap.h
CAP_BA_AUDIO_RECEPTION_STARTED_EVT This event is notified by Commander with Broadcast Assistant role to report that the Audio Reception Start Procedure is complete cap.h
CAP_BA_AUDIO_RECEPTION_STOPPED_EVT This event is notified by Commander with Broadcast Assistant role to report that the Audio Reception Stop Procedure is complete cap.h
CAP_BA_REM_REMOVED_SOURCE_EVT This event is notified by Commander with Broadcast Assistant role to report that a remote Scan Delegator has removed a source cap.h
CAP_BA_SOLICITING_SCAN_DELEGATOR_REPORT_EVT This event is notified by Commander with Broadcast Assistant role when a Scan Delegator soliciting request has been scanned cap.h
CAP_BA_BROADCAST_SOURCE_ADV_REPORT_EVT This event is notified by Commander with Broadcast Assistant role when a Broadcast Source has been scanned cap.h
CAP_BA_PA_SYNC_ESTABLISHED_EVT This event is notified by Commander with Broadcast Assistant role when synchronization to a periodic advertising train has been established cap.h
CAP_BA_PA_SYNC_LOST_EVT This event is notified by Commander with Broadcast Assistant role when synchronization to a periodic advertising train has been lost cap.h
CAP_BA_BASE_REPORT_EVT This event is notified by Commander with Broadcast Assistant role when a BASE report is received through a periodic advertising train cap.h
CAP_BA_BIGINFO_REPORT_EVT This event is notified by Commander with Broadcast Assistant role when a BIGInfo report is received through a periodic advertising train cap.h

Code Example

This section details code sections needed to initialize and use the Broadcast Assistant role using STM32CubeWBA Firmware. For more details about the BLE Audio stack configuration, refer to the STM32WBA Architecture and Integration wiki page.

Initialization

First of all, initialize the BLE Audio Stack library with a maximum number of links and an initialization buffer.

/* Initialize the Audio IP*/
BleAudioInit_t pBleAudioInit;
pBleAudioInit.NumOfLinks = CFG_BLE_NUM_LINK;
pBleAudioInit.bleStartRamAddress = (uint8_t*)audio_init_buffer;
pBleAudioInit.total_buffer_size = BLE_AUDIO_DYN_ALLOC_SIZE;

BLE_AUDIO_STACK_Init(&pBleAudioInit);

Then, perform the CAP initialization with required roles

/* Configure CAP with Commander role */
CAP_Config_t APP_CAP_Config = {0};
APP_CAP_Config.Role = CAP_ROLE_COMMANDER;
APP_CAP_Config.MaxNumLinks = CFG_BLE_NUM_LINK;
APP_CAP_Config.pStartRamAddr = (uint8_t *)&aCAPMemBuffer;
APP_CAP_Config.RamSize = CAP_DYN_ALLOC_SIZE;

/* Configure BAP with Broadcast Assistant role */
BAP_Config_t APP_BAP_Config = {0};
APP_BAP_Config.Role = BAP_ROLE_BROADCAST_ASSISTANT;
APP_BAP_Config.MaxNumBleLinks = CFG_BLE_NUM_LINK;
APP_BAP_Config.MaxNumBALinks = CFG_BLE_NUM_LINK;

/* Configure PACS Client */
APP_BAP_Config.PACSCltConfig.MaxNumSnkPACRecordsPerLink = MAX_NUM_CLT_SNK_PAC_RECORDS_PER_LINK;
APP_BAP_Config.PACSCltConfig.MaxNumSrcPACRecordsPerLink = MAX_NUM_CLT_SRC_PAC_RECORDS_PER_LINK;
APP_BAP_Config.PACSCltConfig.pStartRamAddr = (uint8_t *)&aPACSCltMemBuffer;
APP_BAP_Config.PACSCltConfig.RamSize = BAP_PACS_CLT_DYN_ALLOC_SIZE;

/* Configure BASS Client */
APP_BAP_Config.BASSCltConfig.MaxNumBSRCInfo = MAX_NUM_BA_BSRC_INFO_PER_LINK;
APP_BAP_Config.BASSCltConfig.pStartRamAddr = (uint8_t *)&aBASSCltMemBuffer;
APP_BAP_Config.BASSCltConfig.RamSize = BAP_BASS_CLT_DYN_ALLOC_SIZE;

/* Non-Volatile Memory Management for BAP Services restoration */
APP_BAP_Config.NvmMgmtConfig.pStartRamAddr = (uint8_t *)&aNvmMgmtMemBuffer;
APP_BAP_Config.NvmMgmtConfig.RamSize = BAP_NVM_MGMT_DYN_ALLOC_SIZE;

/* Configure VCP with Controller Role */
VCP_Config_t APP_VCP_Config = {0};
APP_VCP_Config.Role = VCP_ROLE_CONTROLLER;
APP_VCP_Config.MaxNumBleLinks = CFG_BLE_NUM_LINK;
APP_VCP_Config.Controller.MaxNumAICInstPerConn = APP_VCP_CTLR_NUM_AIC_INSTANCES;
APP_VCP_Config.Controller.MaxNumVOCInstPerConn = APP_VCP_CTLR_NUM_VOC_INSTANCES;
APP_VCP_Config.Controller.pStartRamAddr = (uint8_t*)aCltrMemBuffer;
APP_VCP_Config.Controller.RamSize = BLE_VCP_CTLR_DYN_ALLOC_SIZE;

/* Configure CSIP with Set Coordinator Role */
CSIP_Config_t APP_CSIP_Config = {0};
APP_CSIP_Config.Role = CSIP_ROLE_SET_COORDINATOR;
APP_CSIP_Config.MaxNumBleLinks = CFG_BLE_NUM_LINK;
APP_CSIP_Config.Set_Coordinator_Config.pStartRamAddr = (uint8_t*)aSetCoordinatorMemBuffer;
APP_CSIP_Config.Set_Coordinator_Config.RamSize = BLE_CSIP_SET_COORDINATOR_DYN_ALLOC_SIZE;

/* Init CAP */
status = CAP_Init(&APP_CAP_Config,
                  &APP_BAP_Config,
                  &APP_VCP_Config,
                  0,
                  0,
                  0,
                  &APP_CSIP_Config);

Connection and Linkup

Start scanning for remote Scan Delegators:

/* Activate parsing and reports for the Broadcast Assistant role */
CAP_BroadcastAssistant_StartAdvReportParsing(0);

Scan_Param_Phy_t scan_param_phy;
scan_param_phy.Scan_Type = 0x00; /* Passive scanning */
scan_param_phy.Scan_Interval = 0x50;
scan_param_phy.Scan_Window = 0x50;

/* Start Scanning */
status = aci_gap_ext_start_scan(0x00,
                                Procedure,
                                0x00,
                                0x01,
                                0x0000,
                                0x0000,
                                0x00,
                                0x01,
                                &scan_param_phy);

When a Scan Delegator is scanned, CAP_BA_SOLICITING_SCAN_DELEGATOR_REPORT_EVT will be generated in CAP_Notification:

void CAP_Notification(CAP_Notification_Evt_t *pNotification)
{
  switch(pNotification->EvtOpcode)
  {
    case CAP_BA_SOLICITING_SCAN_DELEGATOR_REPORT_EVT:
    {
      BAP_Soliciting_Scan_Delegator_Data_t *p_data = (BAP_Soliciting_Scan_Delegator_Data_t *) pNotification->pInfo;
      break;
    }
  }
}

Initiate a connection to an advertising device:

Init_Param_Phy_t init_param_phy;

init_param_phy.Scan_Interval       = 0x0400;
init_param_phy.Scan_Window         = 0x0400;
init_param_phy.Conn_Interval_Min   = 0x18;
init_param_phy.Conn_Interval_Max   = 0x18;
init_param_phy.Conn_Latency        = 0x0000;
init_param_phy.Supervision_Timeout = 0x03E8;
init_param_phy.Min_CE_Length       = 0x0000;
init_param_phy.Max_CE_Length       = 0x03E8;

status = aci_gap_ext_create_connection( 0x00,
                                        GAP_DIRECT_CONNECTION_ESTABLISHMENT_PROC,
                                        0x00,
                                        AddressType,
                                        pAddress,
                                        0xFFU,
                                        0xFFU,
                                        HCI_INIT_FILTER_NO,
                                        HCI_INIT_PHYS_SCAN_CONN_LE_1M,
                                        &init_param_phy);

When the connection is established, initiate CAP Linkup:

CAP_Linkup(ConnHandle,
           BAP_BA_LINK | VCP_LINK | CSIP_LINK,
           0x01u /* 0x01 for a complete linkup, 0x00 to restore data saved in database */
           );

CAP_LINKUP_COMPLETE_EVT and CAP_BA_LINKUP_EVT will be generated in CAP_Notification:

case CAP_LINKUP_COMPLETE_EVT:
{
    break;
}

case CAP_BA_LINKUP_EVT:
{
    BAP_Broadcast_Assistant_Info_t *p_info = (BAP_Broadcast_Assistant_Info_t *)pNotification->pInfo;
    LOG_INFO_APP("Number of Broadcast Receive State Characteristics: %d\n",
                p_info->NumberBroadcastReceiveStateChar);
    break;
}

Scan for a Broadcast Source

Find a Broadcast Source to send to the remote Scan Delegator:

/* Activate parsing and reports for the Broadcast Assistant role */
CAP_BroadcastAssistant_StartAdvReportParsing(ConnHandle); /* Set Scan Delegator connection handle to inform it that scan has started */

Scan_Param_Phy_t scan_param_phy;
scan_param_phy.Scan_Type = 0x00; /* Passive scanning */
scan_param_phy.Scan_Interval = 0x50;
scan_param_phy.Scan_Window = 0x50;

/* Start Scanning */
status = aci_gap_ext_start_scan(0x00,
                                Procedure,
                                0x00,
                                0x01,
                                0x0000,
                                0x0000,
                                0x00,
                                0x01,
                                &scan_param_phy);

CAP_BA_BROADCAST_SOURCE_ADV_REPORT_EVT will be generated in CAP_Notification. Start Periodic Advertising (PA) Synchronization:

case CAP_BA_BROADCAST_SOURCE_ADV_REPORT_EVT:
{
    BAP_Broadcast_Source_Adv_Report_Data_t *data = (BAP_Broadcast_Source_Adv_Report_Data_t*) pNotification->pInfo;

    /* Start PA Sync */
    status = CAP_BroadcastAssistant_StartPASync(data->AdvSID,
                                                data->pAdvAddress,
                                                data->AdvAddressType,
                                                PA_EVENT_SKIP,
                                                PA_SYNC_TIMEOUT);
    break;
}

Upon Periodic Advertising synchronization, CAP_BA_PA_SYNC_ESTABLISHED_EVT will be generated, followed by multiple CAP_BA_BASE_REPORT_EVT with the BASE data of the periodic advertising train:

case CAP_BA_PA_SYNC_ESTABLISHED_EVT:
{
    BAP_PA_Sync_Established_Data_t *data = (BAP_PA_Sync_Established_Data_t*) pNotification->pInfo;
    break;
}

case CAP_BA_BASE_REPORT_EVT:
{
    BAP_BASE_Report_Data_t *base_data;
    /* Retrieve BASE Data with CAP_Broadcast_ParseBASEGroup, CAP_Broadcast_ParseBASESubgroup, and CAP_Broadcast_ParseBASEBIS */
    /* [...] */
    break;
}

Broadcast Audio Reception Start Procedure

When the remote Scan Delegator is connected and linked up, the Broadcast Audio Reception Start procedure can be performed:

/* Build Audio Reception Start parameters */
CAP_Broadcast_AudioReceptionStart_Params_t ars_param;
BAP_BA_Broadcast_Source_Subgroup_t Subgroup[2];

ars_param.ConnHandle = ConnHandle; /* Set Scan Delegator's Connection Handle */
/* CAP_BA_ADD_SOURCE_OPERATION for a new source, CAP_BA_MODIFY_SOURCE_OPERATION for an existing source */
ars_param.Operation = CAP_BA_ADD_SOURCE_OPERATION; 
ars_param.SourceID = 0; /* Identifier of an existing source, use only with CAP_BA_MODIFY_SOURCE_OPERATION */
ars_param.AdvAddressType = AdvAddressType; /* Set Broadcast Source's Advertising Address Type */
ars_param.aAdvAddress[0] = AdvAddress[0]; /* Set Broadcast Source's Advertising Address */
ars_param.aAdvAddress[1] = AdvAddress[1];
ars_param.aAdvAddress[2] = AdvAddress[2];
ars_param.aAdvAddress[3] = AdvAddress[3];
ars_param.aAdvAddress[4] = AdvAddress[4];
ars_param.aAdvAddress[5] = AdvAddress[5];
ars_param.AdvSid = AdvSID;
ars_param.aBroadcastId[0] = BroadcastID[0]; /* Set Broadcast Source's Broadcast ID */
ars_param.aBroadcastId[1] = BroadcastID[1];
ars_param.aBroadcastId[2] = BroadcastID[2];
ars_param.PaSync = 0x01; /* 0x00: Do not sync PA, 0x01: Sync with PAST, 0x02: Sync without PAST */
ars_param.SyncHandle = SyncHandle; /* Set Broadcast Source's Sync Handle */
ars_param.PaInterval = PAInterval; /* Set Broadcast Source's PA Interval */
ars_param.Encryption = 0x00; /* 0x00: No encryption, 0x01: BIS encrypted */
ars_param.pBroadcastCode = &BroadcastCode[0]; /* Set Broadcast Code if BIS is encrypted */
ars_param.NumSubgroups = NumSubgroups; /* Number of subgroups of the BASE */
ars_param.pSubgroup = &Subgroup[0]; /* Pointer to the subgroup structure */

Subgroup[0].NumBISes = NumBIS; /* Number of BIS of the Broadcast Source */
Subgroup[0].BisSync = 0b1; /* Bitfield of BIS to sync: 0b1 for first BIS, 0b11 for 2 BIS */
Subgroup[0].MetadataLength = MetadataLength; /* Length of the metadata inside the BASE */
Subgroup[0].pMetadata = pMetadata; /* Pointer to BASE metadata */

/* Initiate procedure */
CAP_Broadcast_AudioReceptionStart(CAP_SET_TYPE_AD_HOC,
                                  1,
                                  &ars_param);

Finally, when the remote Scan Delegator is synced with the Broadcast Source, CAP_BA_AUDIO_RECEPTION_STARTED_EVT will be generated:

case CAP_BA_AUDIO_RECEPTION_STARTED_EVT:
{
    break;
}

Additional Resources

For general resources about the Bluetooth® Low Energy specification, refer to the following wiki pages:

For more resources on Bluetooth® Low Energy Audio application development on STM32WBA, refer to the following wiki pages:

About

The objective of this project is to provide an example of the implementation of the Broadcast Assistant role from the Bluetooth® LE Audio specification using the STM32 Bluetooth® LE Audio library on the STM32WBA MCUs.

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published