| Supported Targets | ESP32-H4 | ESP32-S31 |
|---|
TMAP Peripheral Example
(See the README.md file in the upper level examples directory for more information about examples.)
Overview
This example takes the TMAP Call Terminal (CT) and Unicast Media Receiver (UMR) roles, registered together via esp_ble_audio_tmap_register(ESP_BLE_AUDIO_TMAP_ROLE_CT | ESP_BLE_AUDIO_TMAP_ROLE_UMR). It runs connectable extended advertising at a 200 ms interval that includes the GAP Earbud appearance, the ASCS/CAS/TMAS UUIDs, an ASCS targeted unicast announcement carrying sink and source contexts (UNSPECIFIED | CONVERSATIONAL | MEDIA | GAME | INSTRUCTIONAL), a CAS targeted announcement, the TMAS UMR|CT role payload, and the device name tmap_peripheral. When the Duo earbuds option is selected, a CSIS RSI is added to the advertisement.
The example uses the ESP-BLE-AUDIO library pieces for: BAP Unicast Server with PACS (LC3 cap with 16/32/48 kHz, 7.5/10 ms duration, 30–155 octets), VCP Volume Renderer (initial volume 10, unmuted, with VOCS+AICS instances built from Kconfig counts), TBS client (CCP Call Terminal — discovers GTBS, reads URI list, originates/terminates calls), MCC controller (reads player name, track title/duration/position, playback/seeking speed, playing order, media state, opcodes, CCID, plus a mcp_send_cmd for PLAY/PAUSE), and optionally CSIP Set Member with SIRK and rank from Kconfig. Sink streams are auto-started from the enabled callback. Device name is set to TMAP Peripheral.
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
idf.py menuconfig
Under Example: TMAP Peripheral (CT & UMR):
- Earbuds type —
Single ear headsetorDuo headset. Duo selectsBT_CSIP_SET_MEMBERandBT_CAP_ACCEPTOR_SET_MEMBER, enabling CSIS and adding RSI to the advertisement. - Device rank in set — integer 1–2, only when Duo is selected; written into the CSIS register parameters.
- Earbud Location —
Left EarorRight Ear. AddsFRONT_LEFT/FRONT_RIGHTto the PACS sink/source location bitmap.
Security & Pairing
Just-Works pairing (LE Secure Connections, no MITM, no I/O capability) with bonding enabled, inherited from ../../common_components/example_init/ble_audio_example_init.c.
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, Bluetooth, and the audio common layer, then registers TMAP CT+UMR.- If
BT_CSIP_SET_MEMBERis on, the CSIP set member registers via the CAP acceptor, generates an RSI, and is plumbed intostart_info.csis_insts[0]. - VCP volume renderer, BAP unicast server (PACS, ASCS callbacks, stream ops), CCP call control (TBS client), and MCP controller (MCC) are initialized.
- The audio stack starts, device name is set, and extended advertising starts on handle 0.
- On ACL connect the connection handle is stored; on MTU change GATT service discovery starts; on discovery complete
tmap_discover_tmasruns. - TMAP discovery records whether the peer is CG and/or UMS, then
ccp_discover_tbstriggers TBS discovery. - After GTBS is discovered the TBS URI list is read;
read_uri_schemes_string_cbsaves the first URI and chains intomcp_discover_mcs. - MCS discovery walks player name → track title → duration → position → playback speed → seeking speed → playing order → orders supported → media state → opcodes → CCID.
- ASCS callbacks accept config/QoS/enable/start; the
enabledstream op auto-starts sink streams (receiver-start-ready); RX metrics are reset on start and updated per received SDU. - Helpers
initiate_call/terminate_call(gated on peer CG) andplay_media/pause_media(gated on peer UMS) issue TBS and MCC commands. - On disconnect, the connection handle is cleared and advertising restarts.
Expected Log
Initialization phase:
TMAP_PER: TMAP CT UMR initialized
TMAP_PER: CSIP set member initialized
TMAP_PER: PRSI: 0x<hex>
TMAP_PER: vcp vol renderer, vocs_cnt <n> aics_cnt <n>
TMAP_PER: VCP volume renderer initialized
TMAP_PER: BAP unicast server initialized
TMAP_PER: CCP call controller initialized
TMAP_PER: MCP controller initialized
TMAP_PER: Advertising started (handle 0)
Connection / discovery:
TMAP_PER: Connected: handle <h> role <r> peer <addr>
TMAP_PER: Security: handle <h> level <l> bonded <b>
TMAP_PER: MTU updated: handle <h> mtu <m>
TMAP_PER: Service discovery started: handle <h>
TMAP_PER: Service discovery complete: handle <h> status 0
TMAP_PER: TMAP discovery done
TMAP_PER: Discovered GTBS
TMAP_PER: Discovered remote URI <uri>
TMAP_PER: Discover MCS succeeded
TMAP_PER: Read player name succeeded, name <name>
TMAP_PER: Read track title succeeded, title <title>
TMAP_PER: Read media state succeeded, state <s>
TMAP_PER: Read content control id succeeded, ccid <c>
ASCS / unicast streaming:
TMAP_PER: [SNK] Config request:
TMAP_PER: [SNK #0] QoS request:
TMAP_PER: [SNK #0] Enable request (meta_len <n>)
TMAP_PER: [SNK #0] Stream enabled
TMAP_PER: [SNK #0] Start request
TMAP_PER: [SNK #0] Stream started
TMAP_PER: [SNK #0] Stop request
TMAP_PER: [SNK #0] Stream stopped, reason 0x<rr>
TMAP_PER: [SNK #0] Disable request
TMAP_PER: [SNK #0] Release request
VCP / call control / disconnect:
TMAP_PER: VCS volume <v>, mute <m>
TMAP_PER: VCS flags 0x<ff>
TMAP_PER: Call <i> originated
TMAP_PER: Call <i> terminated
TMAP_PER: Disconnected: handle <h> reason 0x<rr>
Note — disconnect race. On peer-initiated disconnect (e.g. supervision timeout, peer drops the link) with active streams, an additional Bluedroid error may interleave:
W BT_APPL: gattc_conn_cb: if=4 st=0 id=4 rsn=0x8
W BT_HCI: hcif disc complete: hdl 0x0, rsn 0x8 dev_find 1
TMAP_PER: [SNK #0] ISO disconnected, reason 0x08
TMAP_PER: [SNK #0] Stream disabled
E BT_APPL: Unknown connection ID: 3 fail sending notification
TMAP_PER: [SNK #0] Stream stopped, reason 0x08
Unknown connection ID is harmless. GATT notifications for the ASE state changes are queued for Bluedroid to send. If Bluedroid clears the BTA connection on the ACL disconnect before those queued sends drain, they fail with this error. The peer has already disconnected, so the missed notifications have no effect on either side.
Tag is TMAP_PER.
Peer Pairing
Run tmap_central on a second board.
- Flash this peripheral and start it; advertising begins on handle 0.
- Flash and start the central; it scans for TMAS+UMR and connects to this device.
- After pairing and MTU exchange, both sides complete TMAP discovery; this peripheral then discovers GTBS and reads the URI list.
- The MCC chain reads the central's media proxy state; the central's CAP initiator configures and starts the sink stream.
- The peripheral auto-starts the sink stream from the
enabledcallback and logs received SDU metrics. - Use
initiate_call/terminate_callto drive the central's TBS, andplay_media/pause_mediato send PLAY/PAUSE via MCC.