mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(ble_audio): Support ISO & LE Audio functionalities (Preview)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "ble_audio_example_init.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES bt)
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_gatt.h"
|
||||
#include "host/util/util.h"
|
||||
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
|
||||
#define TAG "INIT"
|
||||
|
||||
static SemaphoreHandle_t example_audio_sem;
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
static void example_audio_gatt_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
||||
{
|
||||
char buf[BLE_UUID_STR_LEN];
|
||||
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_REGISTER_OP_SVC:
|
||||
ESP_LOGI(TAG, "Register svc %s, handle %u",
|
||||
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
|
||||
ctxt->svc.handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_CHR:
|
||||
ESP_LOGI(TAG, "Register chr %s, handles %u/%u",
|
||||
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
|
||||
ctxt->chr.def_handle, ctxt->chr.val_handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_DSC:
|
||||
ESP_LOGI(TAG, "Register dsc %s, handle %u",
|
||||
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), ctxt->dsc.handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void example_audio_on_reset(int reason)
|
||||
{
|
||||
ESP_LOGI(TAG, "Resetting state; reason=%d", reason);
|
||||
}
|
||||
|
||||
static void example_audio_on_sync(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
|
||||
xSemaphoreGive(example_audio_sem);
|
||||
}
|
||||
|
||||
static void example_audio_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
esp_err_t bluetooth_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
example_audio_sem = xSemaphoreCreateBinary();
|
||||
if (example_audio_sem == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create audio semaphore");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to init nimble %d ", ret);
|
||||
vSemaphoreDelete(example_audio_sem);
|
||||
example_audio_sem = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration */
|
||||
ble_hs_cfg.gatts_register_cb = example_audio_gatt_register_cb;
|
||||
ble_hs_cfg.reset_cb = example_audio_on_reset;
|
||||
ble_hs_cfg.sync_cb = example_audio_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
#if 0
|
||||
ble_hs_cfg.sm_our_key_dist = 1;
|
||||
ble_hs_cfg.sm_their_key_dist = 1;
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
ble_hs_cfg.sm_mitm = 0;
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO;
|
||||
#endif
|
||||
|
||||
/* XXX Need to have template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(example_audio_host_task);
|
||||
|
||||
xSemaphoreTake(example_audio_sem, portMAX_DELAY);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef BLE_AUDIO_EXAMPLE_INIT_H_
|
||||
#define BLE_AUDIO_EXAMPLE_INIT_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
esp_err_t bluetooth_init(void);
|
||||
|
||||
#endif /* BLE_AUDIO_EXAMPLE_INIT_H_ */
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "ble_audio_example_utils.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES bt esp_timer freertos)
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Nordic Semiconductor ASA
|
||||
* SPDX-FileCopyrightText: 2024 Demant A/S
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "ble_audio_example_utils.h"
|
||||
|
||||
#include "esp_ble_audio_common_api.h"
|
||||
|
||||
#define TAG "UTILS"
|
||||
|
||||
#define LOG_GREEN "\033[0;" "32" "m"
|
||||
#define LOG_RESET "\033[0m"
|
||||
|
||||
int example_audio_gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
if (event->type == BLE_GAP_EVENT_EXT_DISC ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_SYNC ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_REPORT ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_SYNC_LOST ||
|
||||
event->type == BLE_GAP_EVENT_CONNECT ||
|
||||
event->type == BLE_GAP_EVENT_DISCONNECT ||
|
||||
event->type == BLE_GAP_EVENT_ENC_CHANGE) {
|
||||
esp_ble_audio_gap_app_post_event(event->type, event);
|
||||
} else if (event->type == BLE_GAP_EVENT_MTU ||
|
||||
event->type == BLE_GAP_EVENT_NOTIFY_RX ||
|
||||
event->type == BLE_GAP_EVENT_NOTIFY_TX ||
|
||||
event->type == BLE_GAP_EVENT_SUBSCRIBE) {
|
||||
esp_ble_audio_gatt_app_post_event(event->type, event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_hex(const uint8_t *ptr, size_t len)
|
||||
{
|
||||
while (len--) {
|
||||
printf(LOG_GREEN "%02x" LOG_RESET, *ptr++);
|
||||
}
|
||||
}
|
||||
|
||||
static bool print_cb(uint8_t type, const uint8_t *data,
|
||||
uint8_t data_len, void *user_data)
|
||||
{
|
||||
const char *str = (const char *)user_data;
|
||||
|
||||
printf(LOG_GREEN "I (%lu) %s: %s, type 0x%02x len %u data " LOG_RESET,
|
||||
esp_log_timestamp(), TAG, str, type, data_len);
|
||||
print_hex(data, data_len);
|
||||
printf("\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void example_print_codec_cfg(const esp_ble_audio_codec_cfg_t *codec_cfg)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
||||
ESP_LOGI(TAG, "codec_cfg, id 0x%02x cid 0x%04x vid 0x%04x count %u",
|
||||
codec_cfg->id, codec_cfg->cid,
|
||||
codec_cfg->vid, codec_cfg->data_len);
|
||||
|
||||
if (codec_cfg->id == ESP_BLE_ISO_CODING_FORMAT_LC3) {
|
||||
esp_ble_audio_codec_cfg_frame_dur_t frame_dur;
|
||||
esp_ble_audio_location_t chan_allocation;
|
||||
esp_ble_audio_codec_cfg_freq_t freq;
|
||||
uint16_t octets_per_frame;
|
||||
uint32_t frame_dur_us;
|
||||
uint8_t frame_blocks;
|
||||
uint32_t freq_hz;
|
||||
|
||||
/* LC3 uses the generic LTV format - other codecs might do as well */
|
||||
|
||||
err = esp_ble_audio_data_parse(codec_cfg->data, codec_cfg->data_len, print_cb, "data");
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to parse codec_cfg data");
|
||||
return;
|
||||
}
|
||||
|
||||
err = esp_ble_audio_codec_cfg_get_freq(codec_cfg, &freq);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get frequency");
|
||||
return;
|
||||
}
|
||||
|
||||
err = esp_ble_audio_codec_cfg_freq_to_freq_hz(freq, &freq_hz);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get frequency hz");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Frequency: %lu Hz", freq_hz);
|
||||
|
||||
err = esp_ble_audio_codec_cfg_get_frame_dur(codec_cfg, &frame_dur);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get frame duration");
|
||||
return;
|
||||
}
|
||||
|
||||
err = esp_ble_audio_codec_cfg_frame_dur_to_frame_dur_us(frame_dur, &frame_dur_us);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get frame duration us");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Frame Duration: %lu us", frame_dur_us);
|
||||
|
||||
err = esp_ble_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation, false);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get channel allocation");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Channel allocation: 0x%08lx", chan_allocation);
|
||||
|
||||
err = esp_ble_audio_codec_cfg_get_octets_per_frame(codec_cfg, &octets_per_frame);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get octets per frame");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Octets per frame: %u", octets_per_frame);
|
||||
|
||||
err = esp_ble_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, &frame_blocks, true);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to get frame blocks per sdu");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Frames per SDU: %u", frame_blocks);
|
||||
} else {
|
||||
print_hex(codec_cfg->data, codec_cfg->data_len);
|
||||
}
|
||||
|
||||
err = esp_ble_audio_data_parse(codec_cfg->meta, codec_cfg->meta_len, print_cb, "meta");
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to parse codec_cfg meta");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void example_print_codec_cap(const esp_ble_audio_codec_cap_t *codec_cap)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
||||
ESP_LOGI(TAG, "codec_cap, id 0x%02x cid 0x%04x vid 0x%04x count %u",
|
||||
codec_cap->id, codec_cap->cid,
|
||||
codec_cap->vid, codec_cap->data_len);
|
||||
|
||||
if (codec_cap->id == ESP_BLE_ISO_CODING_FORMAT_LC3) {
|
||||
err = esp_ble_audio_data_parse(codec_cap->data, codec_cap->data_len, print_cb, "data");
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to parse codec_cap data");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
printf(LOG_GREEN "I (%lu) %s: data: " LOG_RESET, esp_log_timestamp(), TAG);
|
||||
print_hex(codec_cap->data, codec_cap->data_len);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
err = esp_ble_audio_data_parse(codec_cap->meta, codec_cap->meta_len, print_cb, "meta");
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to parse codec_cap meta");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void example_print_qos(const esp_ble_audio_bap_qos_cfg_t *qos)
|
||||
{
|
||||
ESP_LOGI(TAG, "QoS: interval %u framing 0x%02x phy 0x%02x", qos->interval, qos->framing, qos->phy);
|
||||
ESP_LOGI(TAG, " sdu %u rtn %u latency %u pd %u", qos->sdu, qos->rtn, qos->latency, qos->pd);
|
||||
}
|
||||
|
||||
void example_print_qos_pref(const esp_ble_audio_bap_qos_cfg_pref_t *pref)
|
||||
{
|
||||
ESP_LOGI(TAG, "QoS pref: unframed %s, phy %u, rtn %u, latency %u",
|
||||
pref->unframed_supported ? "supported" : "not supported",
|
||||
pref->phy, pref->rtn, pref->latency);
|
||||
ESP_LOGI(TAG, " pd_min %u, pd_max %u",
|
||||
pref->pd_min, pref->pd_max);
|
||||
ESP_LOGI(TAG, " pref_pd_min %u, pref_pd_max %u",
|
||||
pref->pref_pd_min, pref->pref_pd_max);
|
||||
}
|
||||
|
||||
bool example_is_substring(const char *substr, const char *str)
|
||||
{
|
||||
const size_t sub_str_len = strlen(substr);
|
||||
const size_t str_len = strlen(str);
|
||||
|
||||
if (sub_str_len > str_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t pos = 0; pos < str_len; pos++) {
|
||||
if (pos + sub_str_len > str_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncasecmp(substr, &str[pos], sub_str_len) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void example_audio_tx_work_handler(struct k_work *work)
|
||||
{
|
||||
example_audio_tx_scheduler_t *scheduler = work->user_data;
|
||||
size_t count;
|
||||
|
||||
assert(scheduler);
|
||||
assert(scheduler->cb);
|
||||
|
||||
count = 1 + scheduler->drift;
|
||||
scheduler->drift = 0;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
scheduler->cb(scheduler->arg);
|
||||
}
|
||||
}
|
||||
|
||||
int example_audio_tx_scheduler_init(example_audio_tx_scheduler_t *scheduler,
|
||||
example_audio_tx_send_cb_t cb,
|
||||
void *arg)
|
||||
{
|
||||
assert(scheduler);
|
||||
assert(cb);
|
||||
|
||||
memset(scheduler, 0, sizeof(*scheduler));
|
||||
scheduler->cb = cb;
|
||||
scheduler->arg = arg;
|
||||
|
||||
k_work_init_delayable(&scheduler->timer, example_audio_tx_work_handler);
|
||||
scheduler->timer.work.user_data = scheduler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void example_audio_tx_scheduler_reset(example_audio_tx_scheduler_t *scheduler)
|
||||
{
|
||||
assert(scheduler);
|
||||
|
||||
scheduler->drift = 0;
|
||||
scheduler->count = 0;
|
||||
}
|
||||
|
||||
int example_audio_tx_scheduler_start(example_audio_tx_scheduler_t *scheduler,
|
||||
uint64_t period_us)
|
||||
{
|
||||
assert(scheduler);
|
||||
assert(period_us > 0);
|
||||
|
||||
return k_work_schedule_periodic(&scheduler->timer, (uint32_t)(period_us / 1000));
|
||||
}
|
||||
|
||||
int example_audio_tx_scheduler_stop(example_audio_tx_scheduler_t *scheduler)
|
||||
{
|
||||
assert(scheduler);
|
||||
|
||||
return k_work_cancel_delayable(&scheduler->timer);
|
||||
}
|
||||
|
||||
void example_audio_tx_scheduler_on_sent(example_audio_tx_scheduler_t *scheduler,
|
||||
const esp_ble_iso_tx_cb_info_t *info,
|
||||
const char *tag,
|
||||
const char *obj_name,
|
||||
const void *obj)
|
||||
{
|
||||
size_t drift_count = 0;
|
||||
|
||||
assert(scheduler);
|
||||
assert(info);
|
||||
|
||||
for (size_t i = 0; i < info->pkt_cnt; i++) {
|
||||
if (info->pkt[i].drift) {
|
||||
drift_count++;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler->drift += drift_count;
|
||||
scheduler->count++;
|
||||
|
||||
if (scheduler->count % 1000 == 0) {
|
||||
ESP_LOGI(tag, "Transmitted %u ISO data packets (%s %p)",
|
||||
scheduler->count, (obj_name ? obj_name : ""), obj);
|
||||
}
|
||||
}
|
||||
|
||||
void example_audio_rx_metrics_reset(example_audio_rx_metrics_t *metrics)
|
||||
{
|
||||
assert(metrics);
|
||||
|
||||
metrics->recv_count = 0;
|
||||
metrics->valid_count = 0;
|
||||
metrics->error_count = 0;
|
||||
metrics->lost_count = 0;
|
||||
metrics->null_sdu_count = 0;
|
||||
metrics->last_sdu_len = 0;
|
||||
}
|
||||
|
||||
void example_audio_rx_metrics_on_recv(const esp_ble_iso_recv_info_t *info,
|
||||
example_audio_rx_metrics_t *metrics,
|
||||
const char *tag,
|
||||
const char *obj_name,
|
||||
const void *obj)
|
||||
{
|
||||
assert(info);
|
||||
assert(metrics);
|
||||
|
||||
if (info->flags & ESP_BLE_ISO_FLAGS_ERROR) {
|
||||
metrics->error_count++;
|
||||
}
|
||||
|
||||
if (info->flags & ESP_BLE_ISO_FLAGS_LOST) {
|
||||
metrics->lost_count++;
|
||||
}
|
||||
|
||||
if (info->flags & ESP_BLE_ISO_FLAGS_VALID) {
|
||||
metrics->valid_count++;
|
||||
if (metrics->last_sdu_len == 0) {
|
||||
metrics->null_sdu_count++;
|
||||
}
|
||||
}
|
||||
|
||||
metrics->recv_count++;
|
||||
|
||||
if (metrics->recv_count % 1000 == 0) {
|
||||
ESP_LOGI(tag, "Received %u ISO data packets (%s %p)",
|
||||
metrics->recv_count, (obj_name ? obj_name : ""), obj);
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Nordic Semiconductor ASA
|
||||
* SPDX-FileCopyrightText: 2024 Demant A/S
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef BLE_AUDIO_EXAMPLE_UTILS_H_
|
||||
#define BLE_AUDIO_EXAMPLE_UTILS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "zephyr/kernel.h"
|
||||
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
|
||||
#include "esp_ble_audio_bap_api.h"
|
||||
|
||||
#define EXAMPLE_AD_TYPE_FLAGS BT_DATA_FLAGS
|
||||
#define EXAMPLE_AD_TYPE_UUID16_SOME BT_DATA_UUID16_SOME
|
||||
#define EXAMPLE_AD_TYPE_UUID16_ALL BT_DATA_UUID16_ALL
|
||||
#define EXAMPLE_AD_TYPE_NAME_SHORTENED BT_DATA_NAME_SHORTENED
|
||||
#define EXAMPLE_AD_TYPE_NAME_COMPLETE BT_DATA_NAME_COMPLETE
|
||||
#define EXAMPLE_AD_TYPE_SERVICE_DATA16 BT_DATA_SVC_DATA16
|
||||
#define EXAMPLE_AD_TYPE_GAP_APPEARANCE BT_DATA_GAP_APPEARANCE
|
||||
#define EXAMPLE_AD_TYPE_CSIS_RSI BT_DATA_CSIS_RSI
|
||||
#define EXAMPLE_AD_TYPE_BROADCAST_NAME BT_DATA_BROADCAST_NAME
|
||||
|
||||
#define EXAMPLE_AD_FLAGS_LIMITED BT_LE_AD_LIMITED
|
||||
#define EXAMPLE_AD_FLAGS_GENERAL BT_LE_AD_GENERAL
|
||||
#define EXAMPLE_AD_FLAGS_NO_BREDR BT_LE_AD_NO_BREDR
|
||||
|
||||
#define EXAMPLE_ADV_PROP_CONNECTABLE BT_GAP_ADV_PROP_CONNECTABLE
|
||||
#define EXAMPLE_ADV_PROP_SCANNABLE BT_GAP_ADV_PROP_SCANNABLE
|
||||
#define EXAMPLE_ADV_PROP_DIRECTED BT_GAP_ADV_PROP_DIRECTED
|
||||
#define EXAMPLE_ADV_PROP_SCAN_RESPONSE BT_GAP_ADV_PROP_SCAN_RESPONSE
|
||||
#define EXAMPLE_ADV_PROP_EXT_ADV BT_GAP_ADV_PROP_EXT_ADV
|
||||
|
||||
#define EXAMPLE_BYTES_LIST_LE16 BT_BYTES_LIST_LE16
|
||||
#define EXAMPLE_BYTES_LIST_LE24 BT_BYTES_LIST_LE24
|
||||
#define EXAMPLE_BYTES_LIST_LE32 BT_BYTES_LIST_LE32
|
||||
#define EXAMPLE_BYTES_LIST_LE40 BT_BYTES_LIST_LE40
|
||||
#define EXAMPLE_BYTES_LIST_LE48 BT_BYTES_LIST_LE48
|
||||
#define EXAMPLE_BYTES_LIST_LE64 BT_BYTES_LIST_LE64
|
||||
|
||||
int example_audio_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||
|
||||
void example_print_codec_cfg(const esp_ble_audio_codec_cfg_t *codec_cfg);
|
||||
|
||||
void example_print_codec_cap(const esp_ble_audio_codec_cap_t *codec_cap);
|
||||
|
||||
void example_print_qos(const esp_ble_audio_bap_qos_cfg_t *qos);
|
||||
|
||||
void example_print_qos_pref(const esp_ble_audio_bap_qos_cfg_pref_t *pref);
|
||||
|
||||
bool example_is_substring(const char *substr, const char *str);
|
||||
|
||||
/**
|
||||
* @brief TX scheduler for periodic audio data transmission.
|
||||
*
|
||||
* Uses k_work_delayable to schedule send callbacks in the ISO task context,
|
||||
* ensuring thread safety without mutexes since both the timer handler and
|
||||
* BLE audio callbacks execute in the same task.
|
||||
*/
|
||||
typedef void (*example_audio_tx_send_cb_t)(void *ctx);
|
||||
|
||||
typedef struct {
|
||||
struct k_work_delayable timer;
|
||||
size_t drift;
|
||||
uint32_t count;
|
||||
/* Called periodically in ISO task context to transmit data */
|
||||
example_audio_tx_send_cb_t cb;
|
||||
void *arg;
|
||||
} example_audio_tx_scheduler_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t recv_count;
|
||||
uint32_t valid_count;
|
||||
uint32_t error_count;
|
||||
uint32_t lost_count;
|
||||
uint32_t null_sdu_count;
|
||||
uint16_t last_sdu_len;
|
||||
} example_audio_rx_metrics_t;
|
||||
|
||||
int example_audio_tx_scheduler_init(example_audio_tx_scheduler_t *scheduler,
|
||||
example_audio_tx_send_cb_t cb,
|
||||
void *arg);
|
||||
|
||||
void example_audio_tx_scheduler_reset(example_audio_tx_scheduler_t *scheduler);
|
||||
|
||||
int example_audio_tx_scheduler_start(example_audio_tx_scheduler_t *scheduler,
|
||||
uint64_t period_us);
|
||||
|
||||
int example_audio_tx_scheduler_stop(example_audio_tx_scheduler_t *scheduler);
|
||||
|
||||
void example_audio_tx_scheduler_on_sent(example_audio_tx_scheduler_t *scheduler,
|
||||
const esp_ble_iso_tx_cb_info_t *info,
|
||||
const char *tag,
|
||||
const char *obj_name,
|
||||
const void *obj);
|
||||
|
||||
void example_audio_rx_metrics_reset(example_audio_rx_metrics_t *metrics);
|
||||
|
||||
void example_audio_rx_metrics_on_recv(const esp_ble_iso_recv_info_t *info,
|
||||
example_audio_rx_metrics_t *metrics,
|
||||
const char *tag,
|
||||
const char *obj_name,
|
||||
const void *obj);
|
||||
|
||||
#endif /* BLE_AUDIO_EXAMPLE_UTILS_H_ */
|
||||
Reference in New Issue
Block a user