| Supported Targets | ESP32-H4 | ESP32-S31 |
|---|
CAP Initiator Example
(See the README.md file in the upper level examples directory for more information about examples.)
Overview
This example implements the Common Audio Profile (CAP) Initiator role on top of the selected BLE host stack (Bluedroid by default; NimBLE via the sdkconfig.defaults.nimble overlay) with ISO and LE Audio support. It is built in one of two mutually exclusive sub-modes selected at build time: a CAP Unicast Initiator / BAP Unicast Client, or a CAP Broadcast Source. A small TX module (cap_initiator_tx.c) drives audio packets onto every started stream using the LE Audio example TX scheduler.
In unicast mode, the initiator scans for connectable extended advertising that carries CAS service data, connects, initiates pairing, exchanges MTU, performs GATT service discovery, then discovers CAS, sink ASEs and source ASEs, configures the codec with the LC3 16_2_1 unicast preset, creates an ad-hoc unicast group, and starts the streams. In broadcast mode, the initiator creates a CAP broadcast source with the LC3 16_2_1 broadcast preset, starts non-connectable extended advertising plus periodic advertising carrying the Broadcast Audio Announcement UUID, a hardcoded broadcast ID (0x123456), the local device name (CAP Broadcast Source), and the BASE, then starts the broadcast stream.
The GAP/GATT device name follows the build-time mode: CAP Initiator in unicast mode, CAP Broadcast Source in broadcast mode (matching the local-name field embedded in extended advertising, which is what the acceptor's self-scan path matches against). Stream location and context are hardcoded to MONO_AUDIO / UNSPECIFIED in the LC3 preset.
Requirements
- A board with BLE 5.2, ISO, and LE Audio support (e.g. ESP32-H4, ESP32-S31)
- Peer device running the paired example
Configuration
Open menuconfig:
idf.py menuconfig
Under Example: CAP Initiator -> CAP Initiator mode:
- Unicast (
EXAMPLE_UNICAST, default) — scan for a CAP Acceptor and set up unicast streams as BAP Unicast Client. Mutually exclusive with Broadcast. - Broadcast (
EXAMPLE_BROADCAST) — act as a CAP Broadcast Source; start extended + periodic advertising and a BIG so Broadcast Sinks can sync. Mutually exclusive with Unicast.
Security & Pairing
Just-Works pairing (LE Secure Connections, no MITM, BLE_SM_IO_CAP_NO_IO) with bonding enabled, inherited from ../../common_components/example_init/ble_audio_example_init.c. Change pairing or IO-cap there.
Build & Flash
The base sdkconfig.defaults defaults to the Bluedroid host; idf.py automatically merges the per-target overlay (sdkconfig.defaults.$IDF_TARGET). To build with NimBLE host instead, layer sdkconfig.defaults.nimble on top via -DSDKCONFIG_DEFAULTS.
Bluedroid host (default)
idf.py set-target esp32h4
idf.py -p PORT flash monitor
NimBLE host
idf.py set-target esp32h4
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.esp32h4;sdkconfig.defaults.nimble" -p PORT flash monitor
For esp32s31, replace the chip overlay accordingly.
(Exit serial monitor with Ctrl-].)
Example Flow
app_maininitializes NVS, the Bluetooth controller (bluetooth_init) and the LE Audio common layer (esp_ble_audio_common_init); GAP/GATT callbacks are registered only in unicast mode.- The selected sub-mode initializes itself:
cap_initiator_unicast_initregisters CAP and BAP unicast-client callbacks, orcap_initiator_broadcast_initregisters the broadcast stream ops. cap_initiator_tx_initinitializes the TX scheduler slot(s);esp_ble_audio_common_startstarts the audio stack and the GAP device name is set per build-time mode (CAP Initiatorfor unicast,CAP Broadcast Sourcefor broadcast).- Unicast:
cap_initiator_unicast_startbegins extended scanning. On a connectable advertising report containing CAS service data, it cancels scanning and callsble_gap_connect. - Unicast: After ACL connect the initiator starts pairing, then on
SECURITY_CHANGEexchanges MTU; on MTU change it starts GATT service discovery. Once both MTU and discovery are done, it callsesp_ble_audio_cap_initiator_unicast_discover(CAS), then BAPdiscoverfor SINK and SOURCE. - Unicast: It creates an ad-hoc unicast group with the LC3 16_2_1 QoS, then starts unicast audio. Stream callbacks log configured / qos_set / enabled / started; on start the sink (TX) stream is registered with the TX module.
- Broadcast:
cap_initiator_broadcast_startcreates the broadcast source, configures and starts extended + periodic advertising (handle 0), adds the adv to the broadcast source, then starts the broadcast audio. On stream started the source stream is registered with the TX module. - On stream stopped/disconnected the TX module is unregistered; in unicast mode an ACL disconnect deletes the unicast group and re-arms scanning.
Expected Log
TAG: CAP_INI.
Unicast mode
I (xxx) CAP_INI: CAP initiator unicast initialized
I (xxx) CAP_INI: Scanning for CAP Acceptor...
I (xxx) CAP_INI: Found CAS in peer adv data!
I (xxx) CAP_INI: Connected: handle ... role ... peer ...
I (xxx) CAP_INI: Security: handle ... level ... bonded ...
I (xxx) CAP_INI: MTU updated: handle ... mtu ...
I (xxx) CAP_INI: Service discovery started: handle ...
I (xxx) CAP_INI: Service discovery complete: handle ...
I (xxx) CAP_INI: Discovering CAS
I (xxx) CAP_INI: Found CAS
I (xxx) CAP_INI: Discovering sinks
I (xxx) CAP_INI: [SNK] Endpoint discovered
I (xxx) CAP_INI: Discover sinks complete
I (xxx) CAP_INI: Discovering source ASEs
I (xxx) CAP_INI: [SRC] Endpoint discovered
I (xxx) CAP_INI: Discover sources complete
I (xxx) CAP_INI: Created unicast group
I (xxx) CAP_INI: Starting unicast streams
I (xxx) CAP_INI: [SNK #0] Stream configured, QoS preference:
I (xxx) CAP_INI: [SRC #0] Stream configured, QoS preference:
I (xxx) CAP_INI: [SNK #0] QoS set
I (xxx) CAP_INI: [SRC #0] QoS set
I (xxx) CAP_INI: [SNK #0] Stream enabled
I (xxx) CAP_INI: [SRC #0] Stream enabled
I (xxx) CAP_INI: Unicast start completed
I (xxx) CAP_INI: [SNK #0] Stream started
I (xxx) CAP_INI: [SNK #0] Started (SDU ... interval ... us)
I (xxx) CAP_INI: [SRC #0] Stream started
On disconnect:
I (xxx) CAP_INI: Disconnected: handle ... reason 0x...
I (xxx) CAP_INI: Deleted unicast group
I (xxx) CAP_INI: Scanning for CAP Acceptor...
Broadcast mode
I (xxx) CAP_INI: CAP initiator broadcast initialized
I (xxx) CAP_INI: Creating broadcast source
I (xxx) CAP_INI: Advertising started (handle 0)
I (xxx) CAP_INI: [SRC #0] Stream started
I (xxx) CAP_INI: [SRC #0] Started (SDU ... interval ... us)
Peer Pairing
Run the acceptor on a second board. The initiator and acceptor must be configured for the same sub-mode — both EXAMPLE_UNICAST, or both EXAMPLE_BROADCAST — otherwise they will not pair.
Unicast
- Flash the acceptor with
EXAMPLE_UNICAST; it advertises with CAS + ASCS service data. - Flash this initiator with
EXAMPLE_UNICAST; it scans, finds CAS in the peer's adv data and connects. - Pairing, MTU exchange and GATT service discovery complete.
- CAS, sink and source discovery complete; the initiator creates the unicast group with the LC3 16_2_1 preset.
- Streams are configured, QoS-set, enabled and started; bidirectional audio begins.
- On ACL disconnect the initiator deletes the unicast group and resumes scanning.
Broadcast
- Flash this initiator with
EXAMPLE_BROADCAST; it advertises asCAP Broadcast Source(broadcast ID0x123456) and starts the BIG. - Flash the acceptor with
EXAMPLE_BROADCAST. WithEXAMPLE_SCAN_SELFset the acceptor scans for the broadcast source by name and broadcast ID; without it, a separate Broadcast Assistant must drive PA sync via BASS. - The acceptor PA-syncs, receives the BASE/BIGInfo and syncs the BIG.
- The acceptor's
[SNK #0] Stream startedand the initiator's TX flow are now active.