# ESP-BLE-AUDIO

[中文]

ESP-BLE-AUDIO sits directly on top of ESP-BLE-ISO and implements the Bluetooth LE Audio profiles and services. It is the layer an ESP-IDF Bluetooth LE Audio application actually programs against, through the `esp_ble_audio_*` API.

## Component Layout

The component has two distinct kinds of code, and the distinction matters for this document:

- Open source, covered here. The public API headers (`api/include/esp_ble_audio_*_api.h`), the GATT service adapters under `host/adapter/bluedroid/profiles` and `host/adapter/nimble/profiles`, the Object Transfer Service under `host/services/ots`, and the initialization orchestration in `host/common/init.c`.

- Prebuilt, out of scope. The ESP-IDF Bluetooth LE Audio profile and controller logic -- the unicast and broadcast clients, the volume, microphone, media and call control, set coordination, and the top-level profiles -- ships as a prebuilt per-target library (`lib/lib/<target>/libble_audio.a`, linked with `add_prebuilt_library`). This document does not describe its internals; it describes the public contract around it and, where it matters, the task a registered callback runs on.

## Layering

            %%{init: {'flowchart': {'nodeSpacing': 40, 'rankSpacing': 40, 'subGraphTitleMargin': {'top': 6, 'bottom': 14}}}}%%
    flowchart TB
        APP["Application"]
        API["ESP-BLE-AUDIO public API<br/>(esp_ble_audio_*)"]
        subgraph AUDIO["ESP-BLE-AUDIO"]
          direction LR
          LIB["Prebuilt profile and<br/>controller library"]
          SVC["GATT service adapters<br/>(per host)"]
          OTS["Object Transfer Service"]
          LIB ~~~ SVC
          LIB ~~~ OTS
        end
        ISO["ESP-BLE-ISO transport<br/>(GATT, GAP, ISO, L2CAP, ISO task)"]
        APP --> API --> AUDIO --> ISO

## Services and Controllers

Bluetooth LE Audio functionality divides into two kinds of building block, and the component reflects the split:

- GATT services are the attribute tables a peer reads, writes and subscribes to. They are implemented in the open per-host adapters (`host/adapter/*/profiles`), one source file per service, on top of the ESP-BLE-ISO GATT-server layer. Grouped by their profile, as in the Bluetooth LE Audio specification, the set is PACS, ASCS and BASS (BAP), MCS (MCP), TBS (CCP), CSIS (CSIP), MICS (MICP), VCS (VCP), CAS (CAP), TMAS (TMAP) and HAS (HAP).

- Profile clients and controllers are the state machines that drive those services and orchestrate streams: the Basic Audio and Common Audio profiles, volume, microphone, media and call control, coordinated-set identification, and the top-level Telephony-and-Media, Gaming, and Public-Broadcast profiles. This logic lives in the prebuilt library; the application reaches it through the matching `esp_ble_audio_*_api.h` header.

The GATT services are detailed in GATT Services, and the clients and controllers in Clients and Controllers.

## Where Profile Callbacks Run

A registered ESP-IDF Bluetooth LE Audio callback runs on whichever task delivered the underlying transport event, so the rules from Callback Execution Context carry over directly:

- A callback driven by a notification -- for example an ASCS or volume-state change arriving as a GATT notification -- runs on the ISO task on both hosts.

- A callback driven by a GATT client read or write completion -- for example reading a peer's PAC records -- runs on the ISO task on Bluedroid but on the NimBLE host task on NimBLE.

- A callback driven by an inbound attribute write to a local service -- for example a client configuring an ASE -- runs on the ISO task on Bluedroid but on the NimBLE host task on NimBLE.

The consequence is the one that runs through the whole stack: on the ISO task a callback shares the 4 KB stack and must not block the event loop, while on the NimBLE host task it executes inside the host's attribute path. In every case the global ISO lock is held, so profile state stays consistent regardless of which task the callback runs on.

## GATT Services

Bluetooth LE Audio exposes its capabilities and state through a fixed set of GATT services. These are the server side of the profiles -- the attribute tables a peer reads, writes and subscribes to -- and, as established in Services and Controllers, they are the open part of ESP-BLE-AUDIO: one source file per service under `host/adapter/bluedroid/profiles` and `host/adapter/nimble/profiles`. The state behind each service, and the command handling for its control points, lives in the prebuilt library; this section covers the services and their adapters, not that logic.

### Service Adapter Pattern

Every service is realized the same way. The prebuilt library owns the canonical, host-agnostic definition of the service's attribute table (a `bt_gatt_service`); the open adapter registers that table with the active host's GATT server and then reconciles the handles the host assigns back onto the library's definition, so the library can address its own attributes when it notifies or indicates. The two hosts differ in how the registration is done:

- The NimBLE adapter declares the service a second time in NimBLE's native form (a `ble_gatt_svc_def`), registers it with `ble_gatts_add_svcs`, checks that its characteristics match the library's definition, and maps the assigned handles back with a `*_attr_handle_set` step. Registration is synchronous and single-phase.

- The Bluedroid adapter hands the library's `bt_gatt_service` to a shared `bt_le_bluedroid_svc_init` helper that drives BTA GATTS. Because BTA registers a service in two asynchronous phases, every Bluedroid service adapter exposes a matching `*_init` and `*_start` pair and coordinates completion with the server semaphore from the GATT-server layer.

Once a service is registered, peer access to its attributes follows the per-host attribute-access path of Callback Execution Context: completed synchronously on the NimBLE host task, or posted to the ISO task on Bluedroid.

When a service is added (deferred registration)

Mechanism. Services are not all added to the GATT table during `esp_ble_audio_common_init`. The build defines `BLE_AUDIO_SVC_DEFERRED_ADD` by default, which holds a service back until the application registers the matching role through its `esp_ble_audio_<profile>_register` call -- the prebuilt library turns that call into the service's `bt_le_<service>_init` registration -- rather than adding it during init.

Reason. A firmware image usually has more Bluetooth LE Audio capabilities compiled in than any one application uses: enabling a service's Kconfig role option builds its code in, but the application may never take that role. If every compiled-in service were added at init, a connecting peer would discover and could access attributes for a service that has no backing state behind it yet -- the application has not registered it, so the prebuilt library cannot answer for it meaningfully.

Impact on the application. Deferring the add keeps an unused-but-compiled-in capability out of the GATT table until the application opts into it, so a peer only ever discovers services that are fully backed.

### Service Reference

Bluetooth LE Audio GATT services
Service | Full name | Key characteristics | Adapter | Profile
--- | --- | --- | --- | ---
PACS | Published Audio Capabilities Service | Sink and Source PAC records, audio locations, available and supported audio contexts. | `pacs.c` | BAP
ASCS | Audio Stream Control Service | Sink and Source Audio Stream Endpoints (ASEs) and the ASE control point. | `ascs.c` | BAP
BASS | Broadcast Audio Scan Service | Broadcast Receive State and the broadcast audio scan control point. | `bass.c` | BAP
MCS | Media Control Service | Media player name and track information, the media control point and related state. | `mcs.c` | MCP
TBS | Telephone Bearer Service | Bearer information, call states and the call control point. | `tbs.c` | CCP
CSIS | Coordinated Set Identification Service | Set Identity Resolving Key (SIRK), set size, set member lock and rank. | `csis.c` | CSIP
MICS | Microphone Control Service | Microphone mute; includes AICS. | `mics.c` | MICP
VCS | Volume Control Service | Volume state, the volume control point and volume flags; includes VOCS and AICS. | `vcs.c` | VCP
CAS | Common Audio Service | Identifies a Common Audio device; may include CSIS. | `cas.c` | CAP
TMAS | Telephony and Media Audio Service | The device's TMAP role. | `tmas.c` | TMAP
HAS | Hearing Access Service | Hearing-aid features, the preset control point and the active preset index. | `has.c` | HAP

Each service is configured by the application through its profile's `esp_ble_audio_*_api.h` header (for example `esp_ble_audio_vcp_api.h` for VCS, `esp_ble_audio_pacs_api.h` for PACS); those APIs are part of the client-and-controller reference that follows.

### Included Services and Control Points

Several services are not standalone but are included by another service:

- CAS includes CSIS when the device is a member of a coordinated set, so a peer discovers set membership through the common audio device.

- VCS includes zero or more VOCS (Volume Offset Control Service) and AICS (Audio Input Control Service) instances -- one VOCS per independently offset output, one AICS per audio input.

- MICS includes zero or more AICS instances, one per audio input.

AICS and VOCS therefore have public API headers (`esp_ble_audio_aics_api.h` and `esp_ble_audio_vocs_api.h`) but no standalone adapter file; the including service registers them as part of its own attribute table.

Several services -- ASCS, BASS, MCS, TBS, CSIS, VCS and HAS -- expose a control point: a writable characteristic that carries a command and is answered by notifying the affected state characteristic. The command handling lives in the prebuilt library; the adapter only carries the write inward and the resulting notification outward, on the task described in Callback Execution Context.

## Clients and Controllers

The profiles and their client and controller roles are the part of ESP-BLE-AUDIO that ships in the prebuilt library. This section describes them at the level this document can: their roles, the public API through which an application drives them, and the task their callbacks run on. The internal state machines are out of scope.

A client or controller role acts on a remote device over a connection -- for example a volume controller changing a renderer's volume -- while a renderer, device, member or server role responds locally and is backed by one of the GATT services in the previous section. A single application can hold several roles at once.

### Profile Relationships

Bluetooth LE Audio profiles build on one another. The Basic Audio Profile (BAP) is the foundation: it establishes and carries the audio streams, both unicast (over Connected Isochronous Streams) and broadcast (over Broadcast Isochronous Streams). The control profiles -- volume, microphone, coordinated-set, media and call control -- manage features and are each backed by a GATT service. The Common Audio Profile (CAP) coordinates the others so an operation applies consistently across every member of a set. The top-level profiles -- Telephony and Media Audio (TMAP), Gaming Audio (GMAP), Public Broadcast (PBP) and Hearing Access (HAP) -- are defined combinations of the layers below.

            flowchart TB
        TOP["Top-level profiles — TMAP, GMAP, PBP, HAP"]
        CAP["CAP — coordinated control across a set"]
        CTRL["Control profiles — VCP, MICP, CSIP, MCP, CCP"]
        BAP["BAP — unicast (CIS) and broadcast (BIS) streams"]
        SVC["Bluetooth LE Audio GATT services"]
        ISO["ESP-BLE-ISO transport"]
        TOP --> CAP
        CAP --> BAP
        CAP --> CTRL
        TOP --> CTRL
        CTRL --> SVC
        BAP --> ISO

### Profile Reference

Bluetooth LE Audio profiles
Profile | Full name | Roles | API header
--- | --- | --- | ---
BAP | Basic Audio Profile | Unicast client and server, broadcast source and sink, broadcast assistant, scan delegator. | `bap_api.h`
CAP | Common Audio Profile | Initiator, commander and handover across a coordinated set. | `cap_api.h`
VCP | Volume Control Profile | Volume controller (client) and volume renderer (server). | `vcp_api.h`
MICP | Microphone Control Profile | Microphone controller (client) and microphone device (server). | `micp_api.h`
CSIP | Coordinated Set Identification Profile | Set coordinator (client) and set member (server). | `csip_api.h`
MCP | Media Control Profile | Media Control Client (client) and the media proxy (server). | `mcc_api.h`, `media_proxy_api.h`
CCP | Call Control Profile | Call Control Client (the server side is the Telephone Bearer Service). | `ccp_api.h`
TMAP | Telephony and Media Audio Profile | Role configuration combining CAP, BAP and the control profiles. | `tmap_api.h`
GMAP | Gaming Audio Profile | Role configuration for low-latency gaming audio. | `gmap_api.h`
PBP | Public Broadcast Profile | Public broadcast announcement helpers over BAP broadcast. | `pbp_api.h`

### API Shape and Roles

Each profile exposes a small, uniform API in its `esp_ble_audio_<profile>_api.h` header: the application registers a callback structure for the role it is taking and calls functions to start operations -- discover a peer, configure a stream, set a volume, place a call. Results and state changes then arrive asynchronously through that callback, on the task described in Callback Execution Context -- the ISO task in most cases, the NimBLE host task for GATT read and write completions and for inbound writes to a local service.

Because registration and dispatch are uniform across profiles, adding a role to an application always has the same shape: enable the role's Kconfig option, register its callback structure, and drive it through its functions. The role options also determine which GATT services are compiled in, as the initialization flow below shows.

## Object Transfer (Draft)

Warning

Object Transfer is an early draft and is not yet officially supported. It builds on the draft L2CAP channel (L2CAP), so it runs only on NimBLE and not on Bluedroid, and its API and behavior are provisional and may change. Do not rely on it in production.

The Object Transfer Service (OTS) moves bulk objects -- larger than an ordinary GATT read can carry -- over the credit-based L2CAP channel from L2CAP. In Bluetooth LE Audio it is used by Media Control: a media player exposes objects such as the current track segmentation and group structure, and a client transfers them with OTS, reached through the media API (`esp_ble_audio_mcs_get_ots` and the Media Control Client).

Unlike the profiles, OTS is open source, under `host/services/ots`. Its parts are:

- a server (`ots.c`) and a client (`ots_client.c`);

- the Object Action Control Point (`ots_oacp.c`), which carries the read, write, create and similar object operations;

- the Object List Control Point (`ots_olcp.c`), which navigates the list of objects;

- an object manager (`ots_obj_manager.c`) and a directory listing object (`ots_dir_list.c`);

- the L2CAP transport (`ots_l2cap.c`) that carries object data over the credit-based channel.

## Initialization Flow

An ESP-IDF Bluetooth LE Audio application brings the stack up with two calls, mirroring the transport: `esp_ble_audio_common_init` followed by `esp_ble_audio_common_start`.

`esp_ble_audio_common_init` runs `bt_le_audio_init` in `init.c`, which first establishes the boundary with the prebuilt library -- it checks that the shared-structure ABI matches and pushes the active Kconfig values into the library -- and then calls the host-specific `bt_le_{host}_audio_init`. The host initializer brings up the standard GAP and GATT services and then registers each enabled Bluetooth LE Audio service through its adapter, in a fixed order: PACS, ASCS, BASS, TMAS, GTBS, HAS, CSIS and CAS, then the media, volume and microphone services. Each service is compiled in only when its Kconfig role option is set, so a build contains exactly the services its roles need. That fixed order is the order the services register in; when most of them actually enter the GATT table is a separate question -- by default the add is deferred to the application's per-role `esp_ble_audio_<profile>_register` call rather than happening here at init (see deferred registration).

`esp_ble_audio_common_start` runs `bt_le_audio_start` and dispatches to the host-specific start, which first registers the coordinated-set services (CSIS and CAS) from `start_info` and then commits the GATT server. On Bluedroid the commit is the second phase that actually starts the BTA services registered during init -- the `*_init` and `*_start` pairing from The Service Adapter Pattern; on NimBLE it calls `ble_gatts_start` and then reconciles the assigned attribute handles back into the library.

            %%{init: {'sequence': {'noteAlign': 'left'}}}%%
    sequenceDiagram
        participant APP as Application
        participant CMN as ESP-BLE-AUDIO (init.c)
        participant LIB as Prebuilt library
        participant AD as Host adapter
        APP->>CMN: esp_ble_audio_common_init (gap_cb, gatt_cb)
        CMN->>LIB: check ABI, push configuration
        CMN->>AD: bt_le_{host}_audio_init
        Note over AD: bring up GAP/GATT, then register<br/>enabled services in fixed order
        APP->>CMN: esp_ble_audio_common_start
        CMN->>AD: bt_le_{host}_audio_start
        Note over AD: register CSIS/CAS from start_info, then commit the GATT server<br/>Bluedroid: phase-2 start of the registered BTA services<br/>NimBLE: ble_gatts_start, then reconcile attr handles

### ATT MTU

Bluetooth LE Audio depends on an ATT MTU large enough to carry its control PDUs.

Spec floor. The Basic Audio Profile (BAP) requires a minimum ATT MTU of only 64 octets (the public `ESP_BLE_AUDIO_ATT_MTU_MIN`).

Why 128. 64 octets is not enough to operate four ASEs at once, so ESP-IDF raises the default to 128 octets (`BLE_AUDIO_ATT_MTU_MIN`) to leave headroom for the ASCS and PACS control PDUs that Bluetooth LE Audio relies on. Both hosts set the device's preferred ATT MTU to this value during initialization (`bt_le_{host}_audio_init`), through `BTA_GATT_SetLocalMTU` on Bluedroid and `ble_att_set_preferred_mtu` on NimBLE.

Negotiation rule. This is the value the device offers as a client and accepts as a server, so the MTU that ends up in force is the smaller of the two peers' preferred values.

The exchange itself is the GATT procedure described in MTU Exchange: the client sends the request, the server responds, and one negotiation governs the whole connection. In the common Bluetooth LE Audio topology -- a phone as central and an ESP device as peripheral -- each device runs both a GATT client and a GATT server, since the GATT roles are independent of the GAP role. Yet a single MTU exchange covers the whole connection: the phone's client initiates it, the ESP's server responds, and the one negotiated value then governs all ATT traffic between them -- no matter which side is acting as client or server at a given moment:

            sequenceDiagram
        box transparent Phone (GAP central)
        participant PS as GATT server
        participant PC as GATT client
        end
        box transparent ESP (GAP peripheral)
        participant ES as GATT server
        participant EC as GATT client
        end
        PC->>ES: ATT Exchange MTU Request
        ES-->>PC: ATT Exchange MTU Response
        Note over ES: server responds, never initiates
        Note over PS,EC: the one negotiated MTU (min of both sides)<br/>governs all ATT traffic, both directions
        PC->>ES: service discovery
        PC->>ES: subscribe (CCCD), then PACS / ASCS reads, writes
        EC->>PS: service discovery (peripheral as client, reuses the same MTU)

The other two roles -- the phone's GATT server and the ESP's GATT client -- are present on the same connection: the ESP's client can run its own service discovery against the phone's server, but neither role runs a second MTU exchange; both reuse the value the phone's client already negotiated. Because the ESP peripheral only responds and never initiates the MTU exchange, that one negotiation is paced by the central, not by the peripheral's host stack.

## End-to-End Flows

The four flows below tie the layers together for the two unicast roles and the two broadcast roles. They are deliberately high-level -- each arrow may stand for several exchanges -- and show which component owns each step.

### Unicast Initiator

A unicast initiator connects to an acceptor, configures its audio stream endpoints, establishes a Connected Isochronous Stream and starts sending audio:

            sequenceDiagram
        participant APP as Application (initiator)
        participant AUD as ESP-BLE-AUDIO
        participant ISO as ESP-BLE-ISO
        participant CTRL as Controller
        APP->>ISO: scan and connect (GAP)
        APP->>ISO: start GATT discovery
        AUD->>ISO: discover PACS and ASCS, subscribe to ASEs
        AUD->>ISO: configure ASEs (write the ASE control point)
        AUD->>ISO: create CIG and connect CIS
        ISO->>CTRL: LE Set CIG Parameters, Create CIS
        CTRL->>ISO: CIS Established event
        AUD->>ISO: set up the ISO data path (input)
        APP->>CTRL: send ISO SDUs
        CTRL->>ISO: ISO transmit complete
        Note over APP,CTRL: subsequent SDUs repeat the send / transmit-complete cycle

The stream setup -- discovering PACS and ASCS, configuring the ASEs, creating the CIG, connecting the CIS and setting up the data path -- is driven by the ESP-BLE-AUDIO profile library on top of ESP-BLE-ISO's GATT and ISO layers, not by the application directly. The application drives only the high-level flow through the `esp_ble_audio_*` API -- connect, start discovery, start the stream and send audio -- and ESP-BLE-AUDIO carries out the underlying ESP-BLE-ISO operations on its behalf.

### Unicast Acceptor

A unicast acceptor advertises, accepts a connection from an initiator and lets the initiator configure its audio stream endpoints before receiving audio:

            sequenceDiagram
        participant APP as Application (acceptor)
        participant AUD as ESP-BLE-AUDIO
        participant ISO as ESP-BLE-ISO
        participant CTRL as Controller
        APP->>ISO: advertise (GAP, audio announcement)
        CTRL->>ISO: ACL connection established (peripheral)
        CTRL->>ISO: remote initiator writes the ASE control point
        ISO->>AUD: ASCS server applies config / QoS / enable
        AUD->>APP: config / QoS / enable callbacks
        APP->>AUD: response (accept / reject + QoS preferences)
        AUD->>ISO: notify ASE state and control-point result
        ISO->>CTRL: send to the remote initiator
        CTRL->>ISO: CIS Established event
        AUD->>ISO: set up the ISO data path (output)
        CTRL->>ISO: ISO SDUs
        ISO->>APP: recv callback on the ISO task
        Note over APP,CTRL: subsequent ISO SDUs repeat the receive / recv-callback cycle

Unlike the initiator, the acceptor is passive. The remote initiator drives the ASCS state machine by writing the ASE control point, and the acceptor's ESP-BLE-AUDIO ASCS server surfaces each step -- config, QoS, enable -- to the application as callbacks. Each callback returns the application's response -- accept or reject, plus QoS preferences at codec configuration -- which the ASCS server notifies back to the initiator as the updated ASE state and control-point result. The Connected Isochronous Stream is established by the initiator (the acceptor is the peripheral); ESP-BLE-AUDIO sets up the ISO data path and delivers the received SDUs to the application through the recv callback on the ISO task. As on the initiator, the application never calls the ESP-BLE-ISO ISO API directly.

### Broadcast Source

A broadcast source configures its audio, starts a periodic advertising train carrying the BASE and transmits audio into a Broadcast Isochronous Group without any connection:

            sequenceDiagram
        participant APP as Application (broadcast source)
        participant AUD as ESP-BLE-AUDIO
        participant ISO as ESP-BLE-ISO
        participant CTRL as Controller
        APP->>AUD: create broadcast source (codec, QoS, BASE)
        APP->>ISO: start extended and periodic advertising (GAP, carries the BASE)
        APP->>AUD: start the broadcast source
        AUD->>ISO: create BIG
        ISO->>CTRL: LE Create BIG
        CTRL->>ISO: BIG Complete event
        AUD->>ISO: set up the ISO data path (input)
        AUD->>APP: stream started callback
        APP->>CTRL: send ISO SDUs
        CTRL->>ISO: ISO transmit complete
        Note over APP,CTRL: subsequent SDUs repeat the send / transmit-complete cycle

The application drives the high-level flow through the `esp_ble_audio_*` API -- create the broadcast source, start advertising, start the source and send audio -- while ESP-BLE-AUDIO builds the BASE, creates the BIG on top of ESP-BLE-ISO's ISO subsystem and sets up the data path. As with the unicast initiator, the underlying ESP-BLE-ISO operations are carried out by ESP-BLE-AUDIO, not the application. A broadcast source has no connection and no GATT: it only advertises and transmits.

### Broadcast Sink

A broadcast sink synchronizes to a broadcaster's periodic train, joins its Broadcast Isochronous Group and receives audio:

            sequenceDiagram
        participant APP as Application (broadcast sink)
        participant AUD as ESP-BLE-AUDIO
        participant ISO as ESP-BLE-ISO
        participant CTRL as Controller
        APP->>ISO: scan and synchronize to periodic advertising
        CTRL->>ISO: BIGInfo report
        AUD->>ISO: BIG sync (with broadcast code if encrypted)
        ISO->>CTRL: LE BIG Create Sync
        CTRL->>ISO: BIG Sync Established event
        AUD->>ISO: set up the ISO data path (output)
        CTRL->>ISO: ISO SDUs
        ISO->>APP: recv callback on the ISO task
        Note over APP,CTRL: subsequent ISO SDUs repeat the receive / recv-callback cycle

The application drives the high level -- periodic-advertising synchronization (through GAP) and the broadcast-sink start -- while the ESP-BLE-AUDIO broadcast-sink profile performs the BIG sync and the data path. Both rest on ESP-BLE-ISO: its scanning layer for PA sync and the BIGInfo report, its ISO subsystem for the BIG sync and data path. If the group is encrypted and the broadcast code is wrong, the controller still synchronizes but the stream fails its message-integrity check shortly afterward, so an application should treat an early post-sync failure as a likely bad code.

## Public API

The application drives the ESP-IDF Bluetooth LE Audio implementation through the `esp_ble_audio_*` API. It has two surfaces: one common header, `api/include/esp_ble_audio_common_api.h`, for bring-up and the cross-cutting entry points, and one `<profile>_api.h` header per profile and service for the role-specific operations -- the bulk of the API, cataloged in Clients and Controllers.

### Shape and Conventions

The common API sits on the ESP-BLE-ISO transport and the prebuilt profile library, and follows the same conventions as the transport's public API:

- Two-call bring-up. `esp_ble_audio_common_init` takes an `esp_ble_audio_init_info_t` -- a GAP callback and a GATT callback, the single application sinks of Application Event Interface. `esp_ble_audio_common_start` takes an `esp_ble_audio_start_info_t` carrying the coordinated-set (CSIS) service instances to start. What the two calls do internally is the subject of Initialization Flow: a handshake with the prebuilt library -- the shared-structure ABI (Application Binary Interface) check and the Kconfig configuration push -- followed by the per-service registration.

- Opaque, transport-aliased types. The event types an application handles -- `esp_ble_audio_gap_app_event_t` and `esp_ble_audio_gatt_app_event_t` -- are typedefs of the transport's `bt_le_gap_app_event` / `bt_le_gatt_app_event`, and the `ESP_BLE_AUDIO_GAP_EVENT_*` / `ESP_BLE_AUDIO_GATT_EVENT_*` codes alias the transport's, so the audio and ISO layers present one event model.

- Error codes. Every function returns `esp_err_t`.

- Per-role registration. A profile role follows the uniform shape from Clients and Controllers: enable the role's Kconfig option, register its callback structure through the role's `<profile>_api.h`, and drive it through that header's functions. Registering a role is also what adds its GATT service to the table (see deferred registration).

### Functional Groups

ESP-BLE-AUDIO public API by purpose
Group | Representative functions | Purpose
--- | --- | ---
Initialization | `esp_ble_audio_common_init` | Register the GAP and GATT callbacks and bring up the prebuilt library, GAP and GATT.
Start | `esp_ble_audio_common_start` | Register the coordinated-set services (CSIS, CAS) from `start_info` and start the GATT server (the second BTA phase on Bluedroid).
GATT discovery | `esp_ble_audio_gattc_disc_start` | Start GATT discovery of a peer's services on a connection.
Event forwarding | `esp_ble_audio_gap_app_post_event`, `esp_ble_audio_gatt_app_post_event` | Forward host GAP and GATT events into the engine (NimBLE only -- see below).
LTV helpers | `esp_ble_audio_data_parse`, `esp_ble_audio_data_get_val` | Parse the length-type-value metadata Bluetooth LE Audio uses throughout.
Per-profile roles | the `<profile>_api.h` register and operation functions | Take and drive a profile role -- the bulk of the API (see Clients and Controllers).

### Per-Host Integration Differences

As with ESP-BLE-ISO, the only host-divergent entry points are the event-forwarding functions, and they exist for the same reason -- letting the engine see the GAP and GATT events for a connection the application drives:

- `esp_ble_audio_gap_app_post_event` and `esp_ble_audio_gatt_app_post_event` are needed only on NimBLE: the host delivers GAP and GATT events to the callbacks the application registered, and the application forwards them inward with these. On Bluedroid the adapter installs its own BTA/BTM callbacks and captures the events directly, so no forwarding is required -- `esp_ble_audio_gatt_app_post_event` is even hidden from Bluedroid builds, so calling it there is a compile-time error.

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.