fix(ble_audio): Miscellaneous fixes for ISO & LE Audio examples

This commit is contained in:
Liu Linyan
2026-05-06 09:31:07 +08:00
parent 92288a2d84
commit 325b08e023
108 changed files with 3051 additions and 1269 deletions
@@ -5,60 +5,122 @@
(See the README.md file in the upper level `examples` directory for more information about examples.)
This example implements the **Telephone and Media Audio Profile (TMAP) Central** roles: **Call Gateway (CG)** and **Unicast Media Sender (UMS)**. The central scans for devices that advertise the TMAS (Telephone and Media Audio Service) with the **UMR (Unicast Media Receiver)** role, connects to the first such device, performs pairing, MTU exchange, and GATT service discovery, then runs TMAP discovery and VCP discovery. After TMAP discovery completes, it sets up unicast audio streams (CAP initiator): discovers ASEs, configures codec (e.g. LC3 48_2_1), sets QoS, enables and connects streams, then starts them and sends audio to the peer’s sink. It also acts as **VCP Volume Controller** (volume/mute on the peer), **MCP server** (Media Control Profile / media proxy), and **CCP server** (Call Control Profile with TBS – Telephone Bearer Service) for call originate/terminate. Run it with a device that advertises TMAP UMR (e.g. a TMAP peripheral or CAP acceptor with unicast).
## Overview
The implementation uses the NimBLE host stack with ISO and LE Audio support, ESP-BLE-AUDIO (TMAP CG+UMS, CAP initiator unicast client, VCP volume controller, TBS, CSIP set coordinator, MCP/MCS/MCC, media proxy). Optional Kconfig: device name.
This example takes the **TMAP Call Gateway (CG)** and **Unicast Media Sender (UMS)** roles, registered together via `esp_ble_audio_tmap_register(ESP_BLE_AUDIO_TMAP_ROLE_CG | ESP_BLE_AUDIO_TMAP_ROLE_UMS)`. It scans for connectable extended advertising that carries TMAS service data with the **UMR** role bit set, connects to the first match, pairs, exchanges MTU, and then drives TMAP and VCP discovery before bringing up unicast audio.
The host stack is NimBLE. The example uses the ESP-BLE-AUDIO library pieces for: CAP initiator with the BAP Unicast Client (LC3 preset 48_2_1, sink direction, FRONT_LEFT, MEDIA context), VCP Volume Controller, MCP server backed by the media proxy player, and CCP server registering a single GTBS bearer (`Generic TBS`, UCI `un000`, `tel,wechat` URI schemes, 5G technology). A periodic TX scheduler in the ISO task feeds dummy ISO SDUs filled with the sequence number. Device name is set to `TMAP Central`.
## Requirements
* A board with Bluetooth LE 5.2, ISO, and LE Audio support (e.g. ESP32-H4)
* A device advertising TMAP UMR (Unicast Media Receiver), e.g. a CAP Acceptor or TMAP peripheral with unicast
* A board with BLE 5.2, ISO, and LE Audio support (e.g. ESP32-H4, ESP32-S31)
* Peer device running the paired example
## How to Use Example
## Configuration
Before project configuration and build, set the correct chip target:
```bash
idf.py menuconfig
```
No build-time options — runtime defaults are baked into source.
### 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
```bash
idf.py set-target esp32h4
```
### Build and Flash
Run the following to build, flash and monitor:
```bash
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF.
(Exit serial monitor with `Ctrl-]`.)
## Example Flow
1. **Initialization**: NVS, Bluetooth stack, and LE Audio common layer (`esp_ble_audio_common_init`) with GAP and GATT callbacks. Register TMAP roles CG and UMS (`esp_ble_audio_tmap_register(ESP_BLE_AUDIO_TMAP_ROLE_CG | ESP_BLE_AUDIO_TMAP_ROLE_UMS)`). Initialize CAP initiator (unicast client, stream callbacks, TX timer). Initialize VCP volume controller. Initialize MCP server (media proxy). Initialize CCP server (TBS bearer, originate/terminate callbacks). Start the audio stack and set the device name.
2. **Scanning**: Start extended scanning. On scan result with connectable advertising, parse for TMAS with UMR role; when found, stop scan and connect to the peer.
3. **Connection**: On ACL connect, store connection handle and initiate pairing. On MTU change, start GATT service discovery. On discovery complete (and when MTU has been exchanged), start TMAP discovery and VCP discovery.
4. **TMAP discovery complete**: Call `cap_initiator_setup()` to create the unicast group, discover ASEs, configure codec, set QoS, enable and connect streams, then start streams.
5. **Unicast streaming**: When a sink-direction stream (TX from central to peer) is started, a periodic TX scheduler based on `k_work_delayable` runs in the ISO task context and sends ISO SDUs at the stream QoS interval; the sent callback logs packet counts. Source streams (RX) can be used to receive audio from the peer. Note that the scheduler timer resolution is in milliseconds, which may not match the exact SDU interval for all configurations.
6. **VCP**: After VCP discovery, the example can send mute/volume commands; state and flags callbacks log volume and mute.
1. `app_main` initializes NVS, Bluetooth, the audio common layer, and registers TMAP CG+UMS.
2. Sub-modules initialize in order: CAP initiator (with TX scheduler), VCP volume controller, MCP server (media proxy), CCP server (GTBS bearer).
3. The audio stack starts, the device name is set, and extended scanning begins.
4. Each connectable scan result is parsed for TMAS service data; if the peer's TMAP role contains UMR, scan is cancelled and a connection is created.
5. On ACL connect, security is initiated; on security change, an MTU exchange is requested.
6. After the MTU exchange and GATT discovery both complete, TMAP and VCP discovery start.
7. TMAP discovery completion triggers `cap_initiator_setup` → CAS discover → sink and source ASE discovery → unicast group create → unicast audio start.
8. When a sink stream reaches "started", the TX scheduler is started at the QoS interval and `unicast_audio_tx` sends ISO SDUs filled with the sequence number.
9. After VCP discovery, the volume state is read to sync the change counter, then a mute command is issued from the state callback.
10. On disconnect, the unicast group is deleted and scanning restarts.
## Example Output
## Expected Log
Initialization phase:
```
I (xxx) TMAP_CEN: Found TMAS in peer adv data!
I (xxx) TMAP_CEN: connection established, status 0
I (xxx) TMAP_CEN: gatt mtu change, conn_handle 1, mtu ...
I (xxx) TMAP_CEN: gattc disc cmpl, status 0, conn_handle 1
I (xxx) TMAP_CEN: TMAP discovery done
I (xxx) TMAP_CEN: Configured stream 0x...
I (xxx) TMAP_CEN: Unicast stream 0x... started
I (xxx) TMAP_CEN: Transmitted 1000 ISO data packets (stream 0x...)
...
TMAP_CEN: CAP initiator initialized
TMAP_CEN: VCP volume controller initialized
TMAP_CEN: MCP server initialized
TMAP_CEN: Registered gtbs bearer <n>
TMAP_CEN: Scanning for peripheral...
```
If the connection is lost:
Scan and connect:
```
I (xxx) TMAP_CEN: connection disconnected, reason 0x...
TMAP_CEN: Found TMAS in peer adv data!
TMAP_CEN: Connected: handle <h> role <r> peer <addr>
TMAP_CEN: Security: handle <h> level <l> bonded <b>
TMAP_CEN: MTU updated: handle <h> mtu <m>
TMAP_CEN: Service discovery started: handle <h>
TMAP_CEN: Service discovery complete: handle <h> status 0
```
TMAP / VCP / CAP setup:
```
TMAP_CEN: TMAP discovery done
TMAP_CEN: CAP initiator setup
TMAP_CEN: Found CAS
TMAP_CEN: [SNK #0] Endpoint discovered
TMAP_CEN: Sink discover complete
TMAP_CEN: Source discover complete
TMAP_CEN: Created unicast group
TMAP_CEN: Started unicast audio
TMAP_CEN: VCP volume controller discovering
TMAP_CEN: VCP discovery done
TMAP_CEN: VCP state cb done, volume <v> mute <m>
```
Unicast streaming phase:
```
TMAP_CEN: [SNK #0] Stream configured, QoS preference:
TMAP_CEN: [SNK #0] QoS set
TMAP_CEN: [SNK #0] Stream enabled
TMAP_CEN: [SNK #0] Stream connected
TMAP_CEN: [SNK #0] Stream started
TMAP_CEN: Streaming, interval <i>, length <l>
TMAP_CEN: Unicast start completed
```
Call control / disconnect:
```
TMAP_CEN: CCP: Placing call to remote with id <i> to <caller>
TMAP_CEN: CCP: Call terminated for id <i> with reason <r>
TMAP_CEN: [SNK #0] Stream stopped, reason 0x<rr>
TMAP_CEN: [SNK #0] ISO disconnected, reason 0x<rr>
TMAP_CEN: Disconnected: handle <h> reason 0x<rr>
TMAP_CEN: Deleted unicast group
```
Tag is `TMAP_CEN`.
## Peer Pairing
Run [tmap_peripheral](../peripheral/) on a second board.
1. Flash the peripheral and let it start extended advertising.
2. Flash and start this central; it scans for TMAS+UMR.
3. The central connects, pairs, and exchanges MTU with the peripheral.
4. TMAP discovery completes on both sides; the central runs CAP unicast setup against the peripheral's ASCS.
5. The central starts the sink stream and feeds ISO SDUs to the peripheral at the QoS interval.
6. The central can drive the peripheral's volume via VCP and accept TBS originate/terminate from the peripheral.
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "esp_timer.h"
@@ -33,20 +34,59 @@ ESP_BLE_AUDIO_BAP_LC3_UNICAST_PRESET_48_2_1_DEFINE(unicast_preset_48_2_1,
ESP_BLE_AUDIO_LOCATION_FRONT_LEFT,
ESP_BLE_AUDIO_CONTEXT_TYPE_MEDIA);
static const char *dir_str(esp_ble_audio_dir_t dir)
{
return dir == ESP_BLE_AUDIO_DIR_SINK ? "SNK" : "SRC";
}
static bool stream_is_sink(const esp_ble_audio_bap_stream_t *stream)
{
for (size_t i = 0; i < ARRAY_SIZE(unicast_sink_streams); i++) {
if (&unicast_sink_streams[i].bap_stream == stream) {
return true;
}
}
return false;
}
static const char *stream_dir_str(const esp_ble_audio_bap_stream_t *stream)
{
return stream_is_sink(stream) ? "SNK" : "SRC";
}
static int stream_index(const esp_ble_audio_bap_stream_t *stream)
{
for (size_t i = 0; i < ARRAY_SIZE(unicast_sink_streams); i++) {
if (&unicast_sink_streams[i].bap_stream == stream) {
return (int)i;
}
}
for (size_t i = 0; i < ARRAY_SIZE(unicast_source_streams); i++) {
if (&unicast_source_streams[i].bap_stream == stream) {
return (int)i;
}
}
return -1;
}
static void unicast_stream_configured_cb(esp_ble_audio_bap_stream_t *stream,
const esp_ble_audio_bap_qos_cfg_pref_t *pref)
{
ESP_LOGI(TAG, "Unicast stream %p configured", stream);
ESP_LOGI(TAG, "[%s #%d] Stream configured, QoS preference:",
stream_dir_str(stream), stream_index(stream));
example_print_qos_pref(TAG, pref);
}
static void unicast_stream_qos_set_cb(esp_ble_audio_bap_stream_t *stream)
{
ESP_LOGI(TAG, "Unicast stream %p QoS set", stream);
ESP_LOGI(TAG, "[%s #%d] QoS set",
stream_dir_str(stream), stream_index(stream));
}
static void unicast_stream_enabled_cb(esp_ble_audio_bap_stream_t *stream)
{
ESP_LOGI(TAG, "Unicast stream %p enabled", stream);
ESP_LOGI(TAG, "[%s #%d] Stream enabled",
stream_dir_str(stream), stream_index(stream));
}
static void unicast_stream_started_cb(esp_ble_audio_bap_stream_t *stream)
@@ -54,7 +94,8 @@ static void unicast_stream_started_cb(esp_ble_audio_bap_stream_t *stream)
esp_ble_audio_bap_ep_info_t ep_info = {0};
esp_err_t err;
ESP_LOGI(TAG, "Unicast stream %p started", stream);
ESP_LOGI(TAG, "[%s #%d] Stream started",
stream_dir_str(stream), stream_index(stream));
err = esp_ble_audio_bap_ep_get_info(stream->ep, &ep_info);
if (err) {
@@ -99,12 +140,14 @@ static void unicast_stream_started_cb(esp_ble_audio_bap_stream_t *stream)
static void unicast_stream_metadata_updated_cb(esp_ble_audio_bap_stream_t *stream)
{
ESP_LOGI(TAG, "Unicast stream %p metadata updated", stream);
ESP_LOGI(TAG, "[%s #%d] Metadata updated",
stream_dir_str(stream), stream_index(stream));
}
static void unicast_stream_disabled_cb(esp_ble_audio_bap_stream_t *stream)
{
ESP_LOGI(TAG, "Unicast stream %p disabled", stream);
ESP_LOGI(TAG, "[%s #%d] Stream disabled",
stream_dir_str(stream), stream_index(stream));
}
static void unicast_stream_stopped_cb(esp_ble_audio_bap_stream_t *stream, uint8_t reason)
@@ -112,7 +155,8 @@ static void unicast_stream_stopped_cb(esp_ble_audio_bap_stream_t *stream, uint8_
esp_ble_audio_bap_ep_info_t ep_info = {0};
esp_err_t err;
ESP_LOGI(TAG, "Unicast stream %p stopped, reason 0x%02x", stream, reason);
ESP_LOGI(TAG, "[%s #%d] Stream stopped, reason 0x%02x",
stream_dir_str(stream), stream_index(stream), reason);
err = esp_ble_audio_bap_ep_get_info(stream->ep, &ep_info);
if (err) {
@@ -135,17 +179,23 @@ static void unicast_stream_stopped_cb(esp_ble_audio_bap_stream_t *stream, uint8_
static void unicast_stream_released_cb(esp_ble_audio_bap_stream_t *stream)
{
ESP_LOGI(TAG, "Unicast stream %p released", stream);
ESP_LOGI(TAG, "[%s #%d] Stream released",
stream_dir_str(stream), stream_index(stream));
}
static void unicast_stream_sent_cb(esp_ble_audio_bap_stream_t *stream, void *user_data)
{
example_audio_tx_scheduler_on_sent(&tx_scheduler, user_data, TAG, "stream", stream);
char name[24];
snprintf(name, sizeof(name), "%s #%d",
stream_dir_str(stream), stream_index(stream));
example_audio_tx_scheduler_on_sent(&tx_scheduler, user_data, TAG, name);
}
static void unicast_stream_connected_cb(esp_ble_audio_bap_stream_t *stream)
{
ESP_LOGI(TAG, "Unicast stream %p connected", stream);
ESP_LOGI(TAG, "[%s #%d] Stream connected",
stream_dir_str(stream), stream_index(stream));
}
static void unicast_stream_disconnected_cb(esp_ble_audio_bap_stream_t *stream, uint8_t reason)
@@ -153,7 +203,8 @@ static void unicast_stream_disconnected_cb(esp_ble_audio_bap_stream_t *stream, u
esp_ble_audio_bap_ep_info_t ep_info = {0};
esp_err_t err;
ESP_LOGI(TAG, "Unicast stream %p disconnected, reason 0x%02x", stream, reason);
ESP_LOGI(TAG, "[%s #%d] ISO disconnected, reason 0x%02x",
stream_dir_str(stream), stream_index(stream), reason);
err = esp_ble_audio_bap_ep_get_info(stream->ep, &ep_info);
if (err) {
@@ -208,7 +259,7 @@ static void unicast_discovery_complete_cb(esp_ble_conn_t *conn, int err,
return;
}
ESP_LOGI(TAG, "Found CAS with CSIS %p", csis_inst);
ESP_LOGI(TAG, "Found CAS with CSIS");
} else {
ESP_LOGI(TAG, "Found CAS");
}
@@ -289,6 +340,17 @@ int unicast_group_delete(void)
{
int err;
/* The endpoint pointers become stale once the peer disconnects and are
* rediscovered on every reconnect. Clear them here so the next discovery
* does not find filled slots and warn "Unhandled endpoint".
*/
for (size_t i = 0; i < ARRAY_SIZE(unicast_sink_eps); i++) {
unicast_sink_eps[i] = NULL;
}
for (size_t i = 0; i < ARRAY_SIZE(unicast_source_eps); i++) {
unicast_source_eps[i] = NULL;
}
if (unicast_group == NULL) {
return 0;
}
@@ -355,56 +417,64 @@ static void config_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Config, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Config response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void qos_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Qos, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] QoS response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void enable_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Enable, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Enable response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void start_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Start, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Start response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void stop_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Stop, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Stop response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void disable_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Disable, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Disable response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void metadata_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Metadata, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Metadata response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void release_cb(esp_ble_audio_bap_stream_t *stream,
esp_ble_audio_bap_ascs_rsp_code_t rsp_code,
esp_ble_audio_bap_ascs_reason_t reason)
{
ESP_LOGI(TAG, "Release, stream %p rsp_code %u reason %u", stream, rsp_code, reason);
ESP_LOGI(TAG, "[%s #%d] Release response, rsp_code %u reason %u",
stream_dir_str(stream), stream_index(stream), rsp_code, reason);
}
static void discover_cb(esp_ble_conn_t *conn, int err, esp_ble_audio_dir_t dir)
@@ -446,7 +516,7 @@ static void discover_cb(esp_ble_conn_t *conn, int err, esp_ble_audio_dir_t dir)
static void pac_record_cb(esp_ble_conn_t *conn, esp_ble_audio_dir_t dir,
const esp_ble_audio_codec_cap_t *codec_cap)
{
example_print_codec_cap(codec_cap);
example_print_codec_cap(TAG, codec_cap);
}
static void endpoint_cb(esp_ble_conn_t *conn,
@@ -456,7 +526,7 @@ static void endpoint_cb(esp_ble_conn_t *conn,
if (dir == ESP_BLE_AUDIO_DIR_SOURCE) {
for (size_t i = 0; i < ARRAY_SIZE(unicast_source_eps); i++) {
if (unicast_source_eps[i] == NULL) {
ESP_LOGI(TAG, "Source #%zu: ep %p", i, ep);
ESP_LOGI(TAG, "[SRC #%zu] Endpoint discovered", i);
unicast_source_eps[i] = ep;
return;
}
@@ -464,14 +534,14 @@ static void endpoint_cb(esp_ble_conn_t *conn,
} else if (dir == ESP_BLE_AUDIO_DIR_SINK) {
for (size_t i = 0; i < ARRAY_SIZE(unicast_sink_eps); i++) {
if (unicast_sink_eps[i] == NULL) {
ESP_LOGI(TAG, "Sink #%zu: ep %p", i, ep);
ESP_LOGI(TAG, "[SNK #%zu] Endpoint discovered", i);
unicast_sink_eps[i] = ep;
return;
}
}
}
ESP_LOGW(TAG, "Unhandled endpoint %p, dir %u", ep, dir);
ESP_LOGW(TAG, "[%s] Unhandled endpoint", dir_str(dir));
}
static esp_ble_audio_bap_unicast_client_cb_t unicast_client_cbs = {
@@ -91,7 +91,7 @@ static void ext_scan_start(void)
return;
}
ESP_LOGI(TAG, "Extended scan started");
ESP_LOGI(TAG, "Scanning for peripheral...");
}
static int conn_create(ble_addr_t *dst)
@@ -214,30 +214,28 @@ static void acl_connect(esp_ble_audio_gap_app_event_t *event)
int err;
if (event->acl_connect.status) {
ESP_LOGE(TAG, "connection failed, status %d", event->acl_connect.status);
ESP_LOGE(TAG, "Connection failed, status %d", event->acl_connect.status);
return;
}
ESP_LOGI(TAG, "Conn established:");
ESP_LOGI(TAG, "conn_handle 0x%04x status 0x%02x role %u peer %02x:%02x:%02x:%02x:%02x:%02x",
event->acl_connect.conn_handle, event->acl_connect.status,
event->acl_connect.role, event->acl_connect.dst.val[5],
event->acl_connect.dst.val[4], event->acl_connect.dst.val[3],
event->acl_connect.dst.val[2], event->acl_connect.dst.val[1],
event->acl_connect.dst.val[0]);
ESP_LOGI(TAG, "Connected: handle %u role %u peer %02x:%02x:%02x:%02x:%02x:%02x",
event->acl_connect.conn_handle, event->acl_connect.role,
event->acl_connect.dst.val[5], event->acl_connect.dst.val[4],
event->acl_connect.dst.val[3], event->acl_connect.dst.val[2],
event->acl_connect.dst.val[1], event->acl_connect.dst.val[0]);
default_conn_handle = event->acl_connect.conn_handle;
err = pairing_start(event->acl_connect.conn_handle);
if (err) {
ESP_LOGE(TAG, "Failed to initiate security, err %d", err);
return;
}
default_conn_handle = event->acl_connect.conn_handle;
}
static void acl_disconnect(esp_ble_audio_gap_app_event_t *event)
{
ESP_LOGI(TAG, "Conn terminated: conn_handle 0x%04x reason 0x%02x",
ESP_LOGI(TAG, "Disconnected: handle %u reason 0x%02x",
event->acl_disconnect.conn_handle, event->acl_disconnect.reason);
default_conn_handle = CONN_HANDLE_INIT;
@@ -255,19 +253,14 @@ static void security_change(esp_ble_iso_gap_app_event_t *event)
int err;
if (event->security_change.status) {
ESP_LOGE(TAG, "security change failed, status %d", event->security_change.status);
ESP_LOGE(TAG, "Security change failed, status %d", event->security_change.status);
return;
}
ESP_LOGI(TAG, "Security change:");
ESP_LOGI(TAG, "conn_handle 0x%04x status 0x%02x role %u sec_level %u bonded %u "
"peer %02x:%02x:%02x:%02x:%02x:%02x",
event->security_change.conn_handle, event->security_change.status,
event->security_change.role, event->security_change.sec_level,
event->security_change.bonded, event->security_change.dst.val[5],
event->security_change.dst.val[4], event->security_change.dst.val[3],
event->security_change.dst.val[2], event->security_change.dst.val[1],
event->security_change.dst.val[0]);
ESP_LOGI(TAG, "Security: handle %u level %u bonded %u",
event->security_change.conn_handle,
event->security_change.sec_level,
event->security_change.bonded);
err = exchange_mtu(event->security_change.conn_handle);
if (err) {
@@ -300,22 +293,23 @@ static void gatt_mtu_change(esp_ble_audio_gatt_app_event_t *event)
{
esp_err_t err;
ESP_LOGI(TAG, "gatt mtu change, conn_handle %u, mtu %u",
ESP_LOGI(TAG, "MTU updated: handle %u mtu %u",
event->gatt_mtu_change.conn_handle, event->gatt_mtu_change.mtu);
if (event->gatt_mtu_change.mtu < ESP_BLE_AUDIO_ATT_MTU_MIN) {
ESP_LOGW(TAG, "Invalid new mtu %u, shall be at least %u",
ESP_LOGW(TAG, "MTU %u below minimum %u",
event->gatt_mtu_change.mtu, ESP_BLE_AUDIO_ATT_MTU_MIN);
return;
}
err = esp_ble_audio_gattc_disc_start(event->gatt_mtu_change.conn_handle);
if (err) {
ESP_LOGE(TAG, "Failed to start svc disc, err %d", err);
ESP_LOGE(TAG, "Failed to start service discovery, err %d", err);
return;
}
ESP_LOGI(TAG, "Start discovering gatt services");
ESP_LOGI(TAG, "Service discovery started: handle %u",
event->gatt_mtu_change.conn_handle);
/* Note:
* MTU exchanged event may arrived after discover completed event.
@@ -337,11 +331,11 @@ static void gattc_disc_cmpl(esp_ble_audio_gatt_app_event_t *event)
{
esp_err_t err;
ESP_LOGI(TAG, "gattc disc cmpl, status %u, conn_handle %u",
event->gattc_disc_cmpl.status, event->gattc_disc_cmpl.conn_handle);
ESP_LOGI(TAG, "Service discovery complete: handle %u status %u",
event->gattc_disc_cmpl.conn_handle,
event->gattc_disc_cmpl.status);
if (event->gattc_disc_cmpl.status) {
ESP_LOGE(TAG, "gattc disc failed, status %u", event->gattc_disc_cmpl.status);
return;
}
@@ -13,6 +13,12 @@
#include "tmap_central.h"
static esp_ble_audio_vcp_vol_ctlr_t *vcp_vol_ctlr;
/* VCS control-point writes carry a change counter that must match the server's.
* After discovery the local counter is 0 while the server may have incremented it
* during a previous session (e.g. before the central rebooted). Read the state
* first to sync the counter, then issue the demo mute from the state callback.
*/
static bool initial_state_read_pending;
static void vcs_discover_cb(esp_ble_audio_vcp_vol_ctlr_t *vol_ctlr, int err,
uint8_t vocs_count, uint8_t aics_count)
@@ -22,11 +28,13 @@ static void vcs_discover_cb(esp_ble_audio_vcp_vol_ctlr_t *vol_ctlr, int err,
return;
}
ESP_LOGI(TAG, "VCP discovery done, %p %p", vol_ctlr, vcp_vol_ctlr);
ESP_LOGI(TAG, "VCP discovery done");
err = esp_ble_audio_vcp_vol_ctlr_mute(vol_ctlr);
initial_state_read_pending = true;
err = esp_ble_audio_vcp_vol_ctlr_read_state(vol_ctlr);
if (err) {
ESP_LOGE(TAG, "Failed to send mute command, err %d", err);
ESP_LOGE(TAG, "Failed to read VCS state, err %d", err);
initial_state_read_pending = false;
}
}
@@ -44,8 +52,19 @@ static void vcs_state_cb(esp_ble_audio_vcp_vol_ctlr_t *vol_ctlr,
{
if (err) {
ESP_LOGE(TAG, "VCP state cb failed, err %d", err);
} else {
ESP_LOGI(TAG, "VCP state cb done, volume %u mute %u", volume, mute);
initial_state_read_pending = false;
return;
}
ESP_LOGI(TAG, "VCP state cb done, volume %u mute %u", volume, mute);
if (initial_state_read_pending) {
initial_state_read_pending = false;
err = esp_ble_audio_vcp_vol_ctlr_mute(vol_ctlr);
if (err) {
ESP_LOGE(TAG, "Failed to send mute command, err %d", err);
}
}
}
@@ -9,6 +9,7 @@ CONFIG_BT_ENABLED=y
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_EXT_ADV=y
CONFIG_BT_NIMBLE_NVS_PERSIST=y
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
CONFIG_BT_NIMBLE_MAX_CCCDS=40
CONFIG_BT_NIMBLE_ISO=y