mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
refactor: Refactor the example of the coexistence of A2DP sink and GATT service
This commit is contained in:
@@ -1,4 +1,15 @@
|
||||
set(MY_COMPONENT_REQUIRES
|
||||
bt_app_core_utils
|
||||
bredr_app_common_utils
|
||||
a2dp_sink_common_utils
|
||||
a2dp_sink_int_codec_utils
|
||||
avrcp_common_utils
|
||||
avrcp_metadata_utils
|
||||
avrcp_abs_vol_utils
|
||||
)
|
||||
|
||||
idf_component_register(SRCS "bt_app_av.c"
|
||||
"bt_app_core.c"
|
||||
"main.c"
|
||||
PRIV_REQUIRES bt nvs_flash
|
||||
PRIV_REQUIRES ${MY_COMPONENT_REQUIRES}
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
menu "A2DP Example Configuration"
|
||||
config EXAMPLE_A2DP_SINK_SSP_ENABLED
|
||||
bool "Secure Simple Pairing"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default y
|
||||
help
|
||||
This enables the Secure Simple Pairing. If disable this option,
|
||||
Bluedroid will only support Legacy Pairing
|
||||
|
||||
choice EXAMPLE_A2DP_SINK_OUTPUT
|
||||
prompt "A2DP Sink Output"
|
||||
default EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
|
||||
help
|
||||
Select to use Internal DAC or external I2S driver
|
||||
|
||||
config EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
bool "Internal DAC"
|
||||
help
|
||||
Select this to use Internal DAC sink output,
|
||||
note that DAC_DMA_AUTO_16BIT_ALIGN should be turned off
|
||||
because the audio data are already 16-bit width
|
||||
|
||||
config EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
|
||||
bool "External I2S Codec"
|
||||
help
|
||||
Select this to use External I2S sink output
|
||||
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_I2S_LRCK_PIN
|
||||
int "I2S LRCK (WS) GPIO"
|
||||
default 22
|
||||
depends on EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
|
||||
help
|
||||
GPIO number to use for I2S LRCK(WS) Driver.
|
||||
|
||||
config EXAMPLE_I2S_BCK_PIN
|
||||
int "I2S BCK GPIO"
|
||||
default 26
|
||||
depends on EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
|
||||
help
|
||||
GPIO number to use for I2S BCK Driver.
|
||||
|
||||
config EXAMPLE_I2S_DATA_PIN
|
||||
int "I2S DATA GPIO"
|
||||
default 25
|
||||
depends on EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
|
||||
help
|
||||
GPIO number to use for I2S Data Driver.
|
||||
|
||||
endmenu
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -9,503 +9,75 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "bt_app_core.h"
|
||||
#include "bt_app_av.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_a2dp_api.h"
|
||||
#include "esp_avrc_api.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
#include "driver/dac_continuous.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
|
||||
#include "sys/lock.h"
|
||||
|
||||
/* AVRCP used transaction labels */
|
||||
#define APP_RC_CT_TL_GET_CAPS (0)
|
||||
#define APP_RC_CT_TL_GET_META_DATA (1)
|
||||
#define APP_RC_CT_TL_RN_TRACK_CHANGE (2)
|
||||
#define APP_RC_CT_TL_RN_PLAYBACK_CHANGE (3)
|
||||
#define APP_RC_CT_TL_RN_PLAY_POS_CHANGE (4)
|
||||
|
||||
/* Application layer causes delay value */
|
||||
#define APP_DELAY_VALUE 50 // 5ms
|
||||
|
||||
/*******************************
|
||||
* STATIC FUNCTION DECLARATIONS
|
||||
******************************/
|
||||
|
||||
/* allocate new meta buffer */
|
||||
static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param);
|
||||
/* handler for new track is loaded */
|
||||
static void bt_av_new_track(void);
|
||||
/* handler for track status change */
|
||||
static void bt_av_playback_changed(void);
|
||||
/* handler for track playing position change */
|
||||
static void bt_av_play_pos_changed(void);
|
||||
/* notification event handler */
|
||||
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter);
|
||||
/* installation for i2s */
|
||||
static void bt_i2s_driver_install(void);
|
||||
/* uninstallation for i2s */
|
||||
static void bt_i2s_driver_uninstall(void);
|
||||
/* set volume by remote controller */
|
||||
static void volume_set_by_controller(uint8_t volume);
|
||||
/* set volume by local host */
|
||||
static void volume_set_by_local_host(uint8_t volume);
|
||||
/* simulation volume change */
|
||||
static void volume_change_simulation(void *arg);
|
||||
/* a2dp event handler */
|
||||
static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
|
||||
/* avrc controller event handler */
|
||||
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
|
||||
/* avrc target event handler */
|
||||
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
|
||||
|
||||
/*******************************
|
||||
* STATIC VARIABLE DEFINITIONS
|
||||
******************************/
|
||||
|
||||
static uint32_t s_pkt_cnt = 0; /* count for audio packet */
|
||||
static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
|
||||
/* audio stream datapath state */
|
||||
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
|
||||
/* connection state in string */
|
||||
static const char *s_a2d_audio_state_str[] = {"Suspended", "Started"};
|
||||
/* audio stream datapath state in string */
|
||||
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
|
||||
/* AVRC target notification capability bit mask */
|
||||
static _lock_t s_volume_lock;
|
||||
static TaskHandle_t s_vcs_task_hdl = NULL; /* handle for volume change simulation task */
|
||||
static uint8_t s_volume = 0; /* local volume value */
|
||||
static bool s_volume_notify; /* notify volume change or not */
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
i2s_chan_handle_t tx_chan = NULL;
|
||||
#else
|
||||
dac_continuous_handle_t tx_chan;
|
||||
#endif
|
||||
#include "bt_app_core_utils.h"
|
||||
#include "avrcp_utils_tags.h"
|
||||
#include "avrcp_common_utils.h"
|
||||
#include "avrcp_metadata_utils.h"
|
||||
#include "avrcp_abs_vol_utils.h"
|
||||
#include "a2dp_utils_tags.h"
|
||||
#include "a2dp_sink_common_utils.h"
|
||||
#include "a2dp_sink_int_codec_utils.h"
|
||||
#include "bt_app_av.h"
|
||||
|
||||
/********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
*******************************/
|
||||
|
||||
static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param)
|
||||
{
|
||||
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param);
|
||||
uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1);
|
||||
|
||||
memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length);
|
||||
attr_text[rc->meta_rsp.attr_length] = 0;
|
||||
rc->meta_rsp.attr_text = attr_text;
|
||||
}
|
||||
|
||||
static void bt_av_new_track(void)
|
||||
{
|
||||
/* request metadata */
|
||||
uint8_t attr_mask = ESP_AVRC_MD_ATTR_TITLE |
|
||||
ESP_AVRC_MD_ATTR_ARTIST |
|
||||
ESP_AVRC_MD_ATTR_ALBUM |
|
||||
ESP_AVRC_MD_ATTR_GENRE;
|
||||
esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask);
|
||||
|
||||
/* register notification if peer support the event_id */
|
||||
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
|
||||
ESP_AVRC_RN_TRACK_CHANGE)) {
|
||||
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_TRACK_CHANGE,
|
||||
ESP_AVRC_RN_TRACK_CHANGE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_av_playback_changed(void)
|
||||
{
|
||||
/* register notification if peer support the event_id */
|
||||
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
|
||||
ESP_AVRC_RN_PLAY_STATUS_CHANGE)) {
|
||||
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAYBACK_CHANGE,
|
||||
ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_av_play_pos_changed(void)
|
||||
{
|
||||
/* register notification if peer support the event_id */
|
||||
if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap,
|
||||
ESP_AVRC_RN_PLAY_POS_CHANGED)) {
|
||||
esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAY_POS_CHANGE,
|
||||
ESP_AVRC_RN_PLAY_POS_CHANGED, 10);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
|
||||
{
|
||||
switch (event_id) {
|
||||
/* when new track is loaded, this event comes */
|
||||
case ESP_AVRC_RN_TRACK_CHANGE:
|
||||
bt_av_new_track();
|
||||
break;
|
||||
/* when track status changed, this event comes */
|
||||
case ESP_AVRC_RN_PLAY_STATUS_CHANGE:
|
||||
ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback);
|
||||
bt_av_playback_changed();
|
||||
break;
|
||||
/* when track playing position changed, this event comes */
|
||||
case ESP_AVRC_RN_PLAY_POS_CHANGED:
|
||||
ESP_LOGI(BT_AV_TAG, "Play position changed: %"PRIu32"-ms", event_parameter->play_pos);
|
||||
bt_av_play_pos_changed();
|
||||
break;
|
||||
/* others */
|
||||
default:
|
||||
ESP_LOGI(BT_AV_TAG, "unhandled event: %d", event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_i2s_driver_install(void)
|
||||
{
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
dac_continuous_config_t cont_cfg = {
|
||||
.chan_mask = DAC_CHANNEL_MASK_ALL,
|
||||
.desc_num = 8,
|
||||
.buf_size = 2048,
|
||||
.freq_hz = 44100,
|
||||
.offset = 127,
|
||||
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
|
||||
.chan_mode = DAC_CHANNEL_MODE_ALTER,
|
||||
};
|
||||
/* Allocate continuous channels */
|
||||
ESP_ERROR_CHECK(dac_continuous_new_channels(&cont_cfg, &tx_chan));
|
||||
/* Enable the continuous channels */
|
||||
ESP_ERROR_CHECK(dac_continuous_enable(tx_chan));
|
||||
#else
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
|
||||
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = CONFIG_EXAMPLE_I2S_BCK_PIN,
|
||||
.ws = CONFIG_EXAMPLE_I2S_LRCK_PIN,
|
||||
.dout = CONFIG_EXAMPLE_I2S_DATA_PIN,
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* enable I2S */
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
||||
#endif
|
||||
}
|
||||
|
||||
void bt_i2s_driver_uninstall(void)
|
||||
{
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
ESP_ERROR_CHECK(dac_continuous_disable(tx_chan));
|
||||
ESP_ERROR_CHECK(dac_continuous_del_channels(tx_chan));
|
||||
#else
|
||||
ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
|
||||
ESP_ERROR_CHECK(i2s_del_channel(tx_chan));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void volume_set_by_controller(uint8_t volume)
|
||||
{
|
||||
ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller to: %"PRIu32"%%", (uint32_t)volume * 100 / 0x7f);
|
||||
/* set the volume in protection of lock */
|
||||
_lock_acquire(&s_volume_lock);
|
||||
s_volume = volume;
|
||||
_lock_release(&s_volume_lock);
|
||||
}
|
||||
|
||||
static void volume_set_by_local_host(uint8_t volume)
|
||||
{
|
||||
ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %"PRIu32"%%", (uint32_t)volume * 100 / 0x7f);
|
||||
/* set the volume in protection of lock */
|
||||
_lock_acquire(&s_volume_lock);
|
||||
s_volume = volume;
|
||||
_lock_release(&s_volume_lock);
|
||||
|
||||
/* send notification response to remote AVRCP controller */
|
||||
if (s_volume_notify) {
|
||||
esp_avrc_rn_param_t rn_param;
|
||||
rn_param.volume = s_volume;
|
||||
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param);
|
||||
s_volume_notify = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void volume_change_simulation(void *arg)
|
||||
{
|
||||
ESP_LOGI(BT_RC_TG_TAG, "start volume change simulation");
|
||||
|
||||
for (;;) {
|
||||
/* volume up locally every 10 seconds */
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
uint8_t volume = (s_volume + 5) & 0x7f;
|
||||
volume_set_by_local_host(volume);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
|
||||
{
|
||||
ESP_LOGD(BT_AV_TAG, "%s event: %d", __func__, event);
|
||||
|
||||
esp_a2d_cb_param_t *a2d = NULL;
|
||||
|
||||
switch (event) {
|
||||
/* when connection state changed, this event comes */
|
||||
case ESP_A2D_CONNECTION_STATE_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
uint8_t *bda = a2d->conn_stat.remote_bda;
|
||||
ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
|
||||
s_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
||||
bt_i2s_driver_uninstall();
|
||||
bt_i2s_task_shut_down();
|
||||
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
|
||||
bt_i2s_task_start_up();
|
||||
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTING) {
|
||||
bt_i2s_driver_install();
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when audio stream transmission state changed, this event comes */
|
||||
case ESP_A2D_AUDIO_STATE_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]);
|
||||
s_audio_state = a2d->audio_stat.state;
|
||||
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
|
||||
s_pkt_cnt = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when audio codec is configured, this event comes */
|
||||
case ESP_A2D_AUDIO_CFG_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type: %d", a2d->audio_cfg.mcc.type);
|
||||
/* for now only SBC stream is supported */
|
||||
if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
|
||||
int sample_rate = 16000;
|
||||
int ch_count = 2;
|
||||
char oct0 = a2d->audio_cfg.mcc.cie.sbc[0];
|
||||
if (oct0 & (0x01 << 6)) {
|
||||
sample_rate = 32000;
|
||||
} else if (oct0 & (0x01 << 5)) {
|
||||
sample_rate = 44100;
|
||||
} else if (oct0 & (0x01 << 4)) {
|
||||
sample_rate = 48000;
|
||||
}
|
||||
|
||||
if (oct0 & (0x01 << 3)) {
|
||||
ch_count = 1;
|
||||
}
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
dac_continuous_disable(tx_chan);
|
||||
dac_continuous_del_channels(tx_chan);
|
||||
dac_continuous_config_t cont_cfg = {
|
||||
.chan_mask = DAC_CHANNEL_MASK_ALL,
|
||||
.desc_num = 8,
|
||||
.buf_size = 2048,
|
||||
.freq_hz = sample_rate,
|
||||
.offset = 127,
|
||||
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
|
||||
.chan_mode = (ch_count == 1) ? DAC_CHANNEL_MODE_SIMUL : DAC_CHANNEL_MODE_ALTER,
|
||||
};
|
||||
/* Allocate continuous channels */
|
||||
dac_continuous_new_channels(&cont_cfg, &tx_chan);
|
||||
/* Enable the continuous channels */
|
||||
dac_continuous_enable(tx_chan);
|
||||
#else
|
||||
i2s_channel_disable(tx_chan);
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
|
||||
i2s_std_slot_config_t slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, ch_count);
|
||||
i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg);
|
||||
i2s_channel_reconfig_std_slot(tx_chan, &slot_cfg);
|
||||
i2s_channel_enable(tx_chan);
|
||||
#endif
|
||||
ESP_LOGI(BT_AV_TAG, "Configure audio player: %x-%x-%x-%x",
|
||||
a2d->audio_cfg.mcc.cie.sbc[0],
|
||||
a2d->audio_cfg.mcc.cie.sbc[1],
|
||||
a2d->audio_cfg.mcc.cie.sbc[2],
|
||||
a2d->audio_cfg.mcc.cie.sbc[3]);
|
||||
ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate: %d", sample_rate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when a2dp init or deinit completed, this event comes */
|
||||
case ESP_A2D_PROF_STATE_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) {
|
||||
ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete");
|
||||
} else {
|
||||
ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete");
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* When protocol service capabilities configured, this event comes */
|
||||
case ESP_A2D_SNK_PSC_CFG_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
ESP_LOGI(BT_AV_TAG, "protocol service capabilities configured: 0x%x ", a2d->a2d_psc_cfg_stat.psc_mask);
|
||||
if (a2d->a2d_psc_cfg_stat.psc_mask & ESP_A2D_PSC_DELAY_RPT) {
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device support delay reporting");
|
||||
} else {
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device unsupport delay reporting");
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when set delay value completed, this event comes */
|
||||
case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
if (ESP_A2D_SET_INVALID_PARAMS == a2d->a2d_set_delay_value_stat.set_state) {
|
||||
ESP_LOGI(BT_AV_TAG, "Set delay report value: fail");
|
||||
} else {
|
||||
ESP_LOGI(BT_AV_TAG, "Set delay report value: success, delay_value: %u * 1/10 ms", a2d->a2d_set_delay_value_stat.delay_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when get delay value completed, this event comes */
|
||||
case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: {
|
||||
a2d = (esp_a2d_cb_param_t *)(p_param);
|
||||
ESP_LOGI(BT_AV_TAG, "Get delay report value: delay_value: %u * 1/10 ms", a2d->a2d_get_delay_value_stat.delay_value);
|
||||
/* Default delay value plus delay caused by application layer */
|
||||
esp_a2d_sink_set_delay_value(a2d->a2d_get_delay_value_stat.delay_value + APP_DELAY_VALUE);
|
||||
break;
|
||||
}
|
||||
/* others */
|
||||
default:
|
||||
ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
/* handler for AVRCP controller events */
|
||||
static void bt_app_avrc_ct_evt_hdl(uint16_t event, void *param)
|
||||
{
|
||||
ESP_LOGD(BT_RC_CT_TAG, "%s event: %d", __func__, event);
|
||||
|
||||
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param);
|
||||
esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param);
|
||||
|
||||
switch (event) {
|
||||
/* when connection state changed, this event comes */
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
|
||||
case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
|
||||
case ESP_AVRC_CT_PROF_STATE_EVT: {
|
||||
bt_avrc_common_ct_evt_def_hdl(event, param);
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_CONNECTION_STATE_EVT: {
|
||||
uint8_t *bda = rc->conn_stat.remote_bda;
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state event: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
|
||||
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
|
||||
bt_avrc_md_ct_evt_hdl(event, param);
|
||||
if (rc->conn_stat.connected) {
|
||||
/* get remote supported event_ids of peer AVRCP Target */
|
||||
esp_avrc_ct_send_get_rn_capabilities_cmd(APP_RC_CT_TL_GET_CAPS);
|
||||
bt_avrc_common_ct_get_peer_rn_cap();
|
||||
} else {
|
||||
/* clear peer notification capability record */
|
||||
s_avrc_peer_rn_cap.bits = 0;
|
||||
bt_avrc_common_ct_set_peer_rn_cap(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when passthrough responsed, this event comes */
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d, rsp_code %d", rc->psth_rsp.key_code,
|
||||
rc->psth_rsp.key_state, rc->psth_rsp.rsp_code);
|
||||
break;
|
||||
}
|
||||
/* when metadata responsed, this event comes */
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
|
||||
free(rc->meta_rsp.attr_text);
|
||||
break;
|
||||
}
|
||||
/* when notified, this event comes */
|
||||
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC event notification: %d", rc->change_ntf.event_id);
|
||||
bt_av_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter);
|
||||
bt_avrc_md_ct_evt_hdl(event, param);
|
||||
bt_avrc_common_ct_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter);
|
||||
break;
|
||||
}
|
||||
/* when feature of remote device indicated, this event comes */
|
||||
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %"PRIx32", TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
|
||||
break;
|
||||
}
|
||||
/* when notification capability of peer device got, this event comes */
|
||||
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count,
|
||||
rc->get_rn_caps_rsp.evt_set.bits);
|
||||
s_avrc_peer_rn_cap.bits = rc->get_rn_caps_rsp.evt_set.bits;
|
||||
bt_av_new_track();
|
||||
bt_av_playback_changed();
|
||||
bt_av_play_pos_changed();
|
||||
/* set peer notification capability record */
|
||||
bt_avrc_common_ct_set_peer_rn_cap(rc->get_rn_caps_rsp.evt_set.bits);
|
||||
bt_avrc_common_ct_rn_track_changed();
|
||||
bt_avrc_common_ct_rn_play_status_changed();
|
||||
bt_avrc_common_ct_rn_play_pos_changed();
|
||||
|
||||
bt_avrc_md_ct_evt_hdl(event, param);
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT: {
|
||||
bt_avrc_md_ct_evt_hdl(event, param);
|
||||
break;
|
||||
}
|
||||
/* others */
|
||||
default:
|
||||
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled event: %d", __func__, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
|
||||
{
|
||||
ESP_LOGD(BT_RC_TG_TAG, "%s event: %d", __func__, event);
|
||||
|
||||
esp_avrc_tg_cb_param_t *rc = (esp_avrc_tg_cb_param_t *)(p_param);
|
||||
|
||||
switch (event) {
|
||||
/* when connection state changed, this event comes */
|
||||
case ESP_AVRC_TG_CONNECTION_STATE_EVT: {
|
||||
uint8_t *bda = rc->conn_stat.remote_bda;
|
||||
ESP_LOGI(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
|
||||
rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
||||
if (rc->conn_stat.connected) {
|
||||
/* create task to simulate volume change */
|
||||
xTaskCreate(volume_change_simulation, "vcsTask", 2048, NULL, 5, &s_vcs_task_hdl);
|
||||
} else {
|
||||
vTaskDelete(s_vcs_task_hdl);
|
||||
ESP_LOGI(BT_RC_TG_TAG, "Stop volume change simulation");
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when passthrough commanded, this event comes */
|
||||
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: {
|
||||
ESP_LOGI(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state);
|
||||
break;
|
||||
}
|
||||
/* when absolute volume command from remote device set, this event comes */
|
||||
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: {
|
||||
ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100 / 0x7f);
|
||||
volume_set_by_controller(rc->set_abs_vol.volume);
|
||||
break;
|
||||
}
|
||||
/* when notification registered, this event comes */
|
||||
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: {
|
||||
ESP_LOGI(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%"PRIx32, rc->reg_ntf.event_id, rc->reg_ntf.event_parameter);
|
||||
if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) {
|
||||
s_volume_notify = true;
|
||||
esp_avrc_rn_param_t rn_param;
|
||||
rn_param.volume = s_volume;
|
||||
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_INTERIM, &rn_param);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when feature of remote device indicated, this event comes */
|
||||
case ESP_AVRC_TG_REMOTE_FEATURES_EVT: {
|
||||
ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features: %"PRIx32", CT features: %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag);
|
||||
break;
|
||||
}
|
||||
/* others */
|
||||
default:
|
||||
ESP_LOGE(BT_RC_TG_TAG, "%s unhandled event: %d", __func__, event);
|
||||
ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -519,12 +91,15 @@ void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
|
||||
switch (event) {
|
||||
case ESP_A2D_CONNECTION_STATE_EVT:
|
||||
case ESP_A2D_AUDIO_STATE_EVT:
|
||||
case ESP_A2D_AUDIO_CFG_EVT:
|
||||
case ESP_A2D_AUDIO_CFG_EVT: {
|
||||
bt_app_work_dispatch(bt_a2d_evt_int_codec_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
case ESP_A2D_PROF_STATE_EVT:
|
||||
case ESP_A2D_SNK_PSC_CFG_EVT:
|
||||
case ESP_A2D_SNK_SET_DELAY_VALUE_EVT:
|
||||
case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: {
|
||||
bt_app_work_dispatch(bt_av_hdl_a2d_evt, event, param, sizeof(esp_a2d_cb_param_t), NULL);
|
||||
bt_app_work_dispatch(bt_a2d_evt_def_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -535,28 +110,24 @@ void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
|
||||
|
||||
void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
write_ringbuf(data, len);
|
||||
|
||||
/* log the number every 100 packets */
|
||||
if (++s_pkt_cnt % 100 == 0) {
|
||||
ESP_LOGI(BT_AV_TAG, "Audio packet count: %"PRIu32, s_pkt_cnt);
|
||||
}
|
||||
bt_a2d_data_hdl(data, len);
|
||||
}
|
||||
|
||||
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT:
|
||||
bt_app_alloc_meta_buffer(param);
|
||||
/* fall through */
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT: {
|
||||
bt_app_work_dispatch(bt_app_avrc_ct_evt_hdl, event, param, sizeof(esp_avrc_ct_cb_param_t), bt_avrc_common_copy_metadata);
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
|
||||
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
|
||||
case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
|
||||
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
|
||||
bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
|
||||
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
|
||||
case ESP_AVRC_CT_PROF_STATE_EVT:
|
||||
bt_app_work_dispatch(bt_app_avrc_ct_evt_hdl, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event);
|
||||
break;
|
||||
@@ -566,14 +137,19 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param
|
||||
void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_AVRC_TG_CONNECTION_STATE_EVT:
|
||||
case ESP_AVRC_TG_REMOTE_FEATURES_EVT:
|
||||
case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT:
|
||||
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
|
||||
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT:
|
||||
case ESP_AVRC_TG_SET_PLAYER_APP_VALUE_EVT:
|
||||
bt_app_work_dispatch(bt_av_hdl_avrc_tg_evt, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL);
|
||||
case ESP_AVRC_TG_PROF_STATE_EVT: {
|
||||
bt_app_work_dispatch(bt_avrc_common_tg_evt_def_hdl, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_TG_CONNECTION_STATE_EVT:
|
||||
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
|
||||
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: {
|
||||
bt_app_work_dispatch(bt_avrc_avc_tg_evt_hdl, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event);
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -11,11 +11,6 @@
|
||||
#include "esp_a2dp_api.h"
|
||||
#include "esp_avrc_api.h"
|
||||
|
||||
/* log tags */
|
||||
#define BT_AV_TAG "BT_AV"
|
||||
#define BT_RC_TG_TAG "RC_TG"
|
||||
#define BT_RC_CT_TAG "RC_CT"
|
||||
|
||||
/**
|
||||
* @brief callback function for A2DP sink
|
||||
*
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "bt_app_core.h"
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
#include "driver/dac_continuous.h"
|
||||
#else
|
||||
#include "driver/i2s_std.h"
|
||||
#endif
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
|
||||
#define RINGBUF_HIGHEST_WATER_LEVEL (32 * 1024)
|
||||
#define RINGBUF_PREFETCH_WATER_LEVEL (20 * 1024)
|
||||
|
||||
enum {
|
||||
RINGBUFFER_MODE_PROCESSING, /* ringbuffer is buffering incoming audio data, I2S is working */
|
||||
RINGBUFFER_MODE_PREFETCHING, /* ringbuffer is buffering incoming audio data, I2S is waiting */
|
||||
RINGBUFFER_MODE_DROPPING /* ringbuffer is not buffering (dropping) incoming audio data, I2S is working */
|
||||
};
|
||||
|
||||
/*******************************
|
||||
* STATIC FUNCTION DECLARATIONS
|
||||
******************************/
|
||||
|
||||
/* handler for application task */
|
||||
static void bt_app_task_handler(void *arg);
|
||||
/* handler for I2S task */
|
||||
static void bt_i2s_task_handler(void *arg);
|
||||
/* message sender */
|
||||
static bool bt_app_send_msg(bt_app_msg_t *msg);
|
||||
/* handle dispatched messages */
|
||||
static void bt_app_work_dispatched(bt_app_msg_t *msg);
|
||||
|
||||
/*******************************
|
||||
* STATIC VARIABLE DEFINITIONS
|
||||
******************************/
|
||||
|
||||
static QueueHandle_t s_bt_app_task_queue = NULL; /* handle of work queue */
|
||||
static TaskHandle_t s_bt_app_task_handle = NULL; /* handle of application task */
|
||||
static TaskHandle_t s_bt_i2s_task_handle = NULL; /* handle of I2S task */
|
||||
static RingbufHandle_t s_ringbuf_i2s = NULL; /* handle of ringbuffer for I2S */
|
||||
static SemaphoreHandle_t s_i2s_write_semaphore = NULL;
|
||||
static uint16_t ringbuffer_mode = RINGBUFFER_MODE_PROCESSING;
|
||||
|
||||
/*********************************
|
||||
* EXTERNAL FUNCTION DECLARATIONS
|
||||
********************************/
|
||||
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
extern i2s_chan_handle_t tx_chan;
|
||||
#else
|
||||
extern dac_continuous_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
/*******************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
******************************/
|
||||
|
||||
static bool bt_app_send_msg(bt_app_msg_t *msg)
|
||||
{
|
||||
if (msg == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* send the message to work queue */
|
||||
if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bt_app_work_dispatched(bt_app_msg_t *msg)
|
||||
{
|
||||
if (msg->cb) {
|
||||
msg->cb(msg->event, msg->param);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_app_task_handler(void *arg)
|
||||
{
|
||||
bt_app_msg_t msg;
|
||||
|
||||
for (;;) {
|
||||
/* receive message from work queue and handle it */
|
||||
if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (TickType_t)portMAX_DELAY)) {
|
||||
ESP_LOGD(BT_APP_CORE_TAG, "%s, signal: 0x%x, event: 0x%x", __func__, msg.sig, msg.event);
|
||||
|
||||
switch (msg.sig) {
|
||||
case BT_APP_SIG_WORK_DISPATCH:
|
||||
bt_app_work_dispatched(&msg);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled signal: %d", __func__, msg.sig);
|
||||
break;
|
||||
} /* switch (msg.sig) */
|
||||
|
||||
if (msg.param) {
|
||||
free(msg.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_i2s_task_handler(void *arg)
|
||||
{
|
||||
uint8_t *data = NULL;
|
||||
size_t item_size = 0;
|
||||
/**
|
||||
* The total length of DMA buffer of I2S is:
|
||||
* `dma_frame_num * dma_desc_num * i2s_channel_num * i2s_data_bit_width / 8`.
|
||||
* Transmit `dma_frame_num * dma_desc_num` bytes to DMA is trade-off.
|
||||
*/
|
||||
const size_t item_size_upto = 240 * 6;
|
||||
size_t bytes_written = 0;
|
||||
|
||||
for (;;) {
|
||||
if (pdTRUE == xSemaphoreTake(s_i2s_write_semaphore, portMAX_DELAY)) {
|
||||
for (;;) {
|
||||
item_size = 0;
|
||||
/* receive data from ringbuffer and write it to I2S DMA transmit buffer */
|
||||
data = (uint8_t *)xRingbufferReceiveUpTo(s_ringbuf_i2s, &item_size, (TickType_t)pdMS_TO_TICKS(20), item_size_upto);
|
||||
if (item_size == 0) {
|
||||
ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer underflowed! mode changed: RINGBUFFER_MODE_PREFETCHING");
|
||||
ringbuffer_mode = RINGBUFFER_MODE_PREFETCHING;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
|
||||
dac_continuous_write(tx_chan, data, item_size, &bytes_written, -1);
|
||||
#else
|
||||
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
|
||||
#endif
|
||||
vRingbufferReturnItem(s_ringbuf_i2s, (void *)data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************
|
||||
* EXTERNAL FUNCTION DEFINITIONS
|
||||
*******************************/
|
||||
|
||||
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
|
||||
{
|
||||
ESP_LOGD(BT_APP_CORE_TAG, "%s event: 0x%x, param len: %d", __func__, event, param_len);
|
||||
|
||||
bt_app_msg_t msg;
|
||||
memset(&msg, 0, sizeof(bt_app_msg_t));
|
||||
|
||||
msg.sig = BT_APP_SIG_WORK_DISPATCH;
|
||||
msg.event = event;
|
||||
msg.cb = p_cback;
|
||||
|
||||
if (param_len == 0) {
|
||||
return bt_app_send_msg(&msg);
|
||||
} else if (p_params && param_len > 0) {
|
||||
if ((msg.param = malloc(param_len)) != NULL) {
|
||||
memcpy(msg.param, p_params, param_len);
|
||||
/* check if caller has provided a copy callback to do the deep copy */
|
||||
if (p_copy_cback) {
|
||||
p_copy_cback(msg.param, p_params, param_len);
|
||||
}
|
||||
return bt_app_send_msg(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void bt_app_task_start_up(void)
|
||||
{
|
||||
s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
|
||||
xTaskCreate(bt_app_task_handler, "BtAppTask", 3072, NULL, 10, &s_bt_app_task_handle);
|
||||
}
|
||||
|
||||
void bt_app_task_shut_down(void)
|
||||
{
|
||||
if (s_bt_app_task_handle) {
|
||||
vTaskDelete(s_bt_app_task_handle);
|
||||
s_bt_app_task_handle = NULL;
|
||||
}
|
||||
if (s_bt_app_task_queue) {
|
||||
vQueueDelete(s_bt_app_task_queue);
|
||||
s_bt_app_task_queue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_i2s_task_start_up(void)
|
||||
{
|
||||
ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer data empty! mode changed: RINGBUFFER_MODE_PREFETCHING");
|
||||
ringbuffer_mode = RINGBUFFER_MODE_PREFETCHING;
|
||||
if ((s_i2s_write_semaphore = xSemaphoreCreateBinary()) == NULL) {
|
||||
ESP_LOGE(BT_APP_CORE_TAG, "%s, Semaphore create failed", __func__);
|
||||
return;
|
||||
}
|
||||
if ((s_ringbuf_i2s = xRingbufferCreate(RINGBUF_HIGHEST_WATER_LEVEL, RINGBUF_TYPE_BYTEBUF)) == NULL) {
|
||||
ESP_LOGE(BT_APP_CORE_TAG, "%s, ringbuffer create failed", __func__);
|
||||
return;
|
||||
}
|
||||
xTaskCreate(bt_i2s_task_handler, "BtI2STask", 2048, NULL, configMAX_PRIORITIES - 3, &s_bt_i2s_task_handle);
|
||||
}
|
||||
|
||||
void bt_i2s_task_shut_down(void)
|
||||
{
|
||||
if (s_bt_i2s_task_handle) {
|
||||
vTaskDelete(s_bt_i2s_task_handle);
|
||||
s_bt_i2s_task_handle = NULL;
|
||||
}
|
||||
if (s_ringbuf_i2s) {
|
||||
vRingbufferDelete(s_ringbuf_i2s);
|
||||
s_ringbuf_i2s = NULL;
|
||||
}
|
||||
if (s_i2s_write_semaphore) {
|
||||
vSemaphoreDelete(s_i2s_write_semaphore);
|
||||
s_i2s_write_semaphore = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size_t write_ringbuf(const uint8_t *data, size_t size)
|
||||
{
|
||||
size_t item_size = 0;
|
||||
BaseType_t done = pdFALSE;
|
||||
|
||||
if (ringbuffer_mode == RINGBUFFER_MODE_DROPPING) {
|
||||
ESP_LOGW(BT_APP_CORE_TAG, "ringbuffer is full, drop this packet!");
|
||||
vRingbufferGetInfo(s_ringbuf_i2s, NULL, NULL, NULL, NULL, &item_size);
|
||||
if (item_size <= RINGBUF_PREFETCH_WATER_LEVEL) {
|
||||
ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer data decreased! mode changed: RINGBUFFER_MODE_PROCESSING");
|
||||
ringbuffer_mode = RINGBUFFER_MODE_PROCESSING;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
done = xRingbufferSend(s_ringbuf_i2s, (void *)data, size, (TickType_t)0);
|
||||
|
||||
if (!done) {
|
||||
ESP_LOGW(BT_APP_CORE_TAG, "ringbuffer overflowed, ready to decrease data! mode changed: RINGBUFFER_MODE_DROPPING");
|
||||
ringbuffer_mode = RINGBUFFER_MODE_DROPPING;
|
||||
}
|
||||
|
||||
if (ringbuffer_mode == RINGBUFFER_MODE_PREFETCHING) {
|
||||
vRingbufferGetInfo(s_ringbuf_i2s, NULL, NULL, NULL, NULL, &item_size);
|
||||
if (item_size >= RINGBUF_PREFETCH_WATER_LEVEL) {
|
||||
ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer data increased! mode changed: RINGBUFFER_MODE_PROCESSING");
|
||||
ringbuffer_mode = RINGBUFFER_MODE_PROCESSING;
|
||||
if (pdFALSE == xSemaphoreGive(s_i2s_write_semaphore)) {
|
||||
ESP_LOGE(BT_APP_CORE_TAG, "semphore give failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return done ? size : 0;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#ifndef __BT_APP_CORE_H__
|
||||
#define __BT_APP_CORE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* log tag */
|
||||
#define BT_APP_CORE_TAG "BT_APP_CORE"
|
||||
|
||||
/* signal for `bt_app_work_dispatch` */
|
||||
#define BT_APP_SIG_WORK_DISPATCH (0x01)
|
||||
|
||||
/**
|
||||
* @brief handler for the dispatched work
|
||||
*
|
||||
* @param [in] event event id
|
||||
* @param [in] param handler parameter
|
||||
*/
|
||||
typedef void (* bt_app_cb_t) (uint16_t event, void *param);
|
||||
|
||||
/* message to be sent */
|
||||
typedef struct {
|
||||
uint16_t sig; /*!< signal to bt_app_task */
|
||||
uint16_t event; /*!< message event id */
|
||||
bt_app_cb_t cb; /*!< context switch callback */
|
||||
void *param; /*!< parameter area needs to be last */
|
||||
} bt_app_msg_t;
|
||||
|
||||
/**
|
||||
* @brief parameter deep-copy function to be customized
|
||||
*
|
||||
* @param [out] p_dest pointer to destination data
|
||||
* @param [in] p_src pointer to source data
|
||||
* @param [in] len data length in byte
|
||||
*/
|
||||
typedef void (* bt_app_copy_cb_t) (void *p_dest, void *p_src, int len);
|
||||
|
||||
/**
|
||||
* @brief work dispatcher for the application task
|
||||
*
|
||||
* @param [in] p_cback callback function
|
||||
* @param [in] event event id
|
||||
* @param [in] p_params callback paramters
|
||||
* @param [in] param_len parameter length in byte
|
||||
* @param [in] p_copy_cback parameter deep-copy function
|
||||
*
|
||||
* @return true if work dispatch successfully, false otherwise
|
||||
*/
|
||||
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback);
|
||||
|
||||
/**
|
||||
* @brief start up the application task
|
||||
*/
|
||||
void bt_app_task_start_up(void);
|
||||
|
||||
/**
|
||||
* @brief shut down the application task
|
||||
*/
|
||||
void bt_app_task_shut_down(void);
|
||||
|
||||
/**
|
||||
* @brief start up the is task
|
||||
*/
|
||||
void bt_i2s_task_start_up(void);
|
||||
|
||||
/**
|
||||
* @brief shut down the I2S task
|
||||
*/
|
||||
void bt_i2s_task_shut_down(void);
|
||||
|
||||
/**
|
||||
* @brief write data to ringbuffer
|
||||
*
|
||||
* @param [in] data pointer to data stream
|
||||
* @param [in] size data length in byte
|
||||
*
|
||||
* @return size if writteen ringbuffer successfully, 0 others
|
||||
*/
|
||||
size_t write_ringbuf(const uint8_t *data, size_t size);
|
||||
|
||||
#endif /* __BT_APP_CORE_H__ */
|
||||
@@ -0,0 +1,15 @@
|
||||
dependencies:
|
||||
bt_app_core_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/bt_app_core_utils
|
||||
bredr_app_common_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/bredr_app_common_utils
|
||||
a2dp_sink_common_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/a2dp_utils/a2dp_sink_common_utils
|
||||
a2dp_sink_int_codec_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/a2dp_utils/a2dp_sink_int_codec_utils
|
||||
avrcp_common_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/avrcp_utils/avrcp_common_utils
|
||||
avrcp_metadata_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/avrcp_utils/avrcp_metadata_utils
|
||||
avrcp_abs_vol_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/avrcp_utils/avrcp_abs_vol_utils
|
||||
@@ -21,27 +21,30 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#include "bt_app_core.h"
|
||||
#include "bt_app_av.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_a2dp_api.h"
|
||||
#include "esp_avrc_api.h"
|
||||
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_gatt_common_api.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "bt_app_core_utils.h"
|
||||
#include "bredr_app_common_utils.h"
|
||||
#include "a2dp_sink_int_codec_utils.h"
|
||||
#include "bt_app_av.h"
|
||||
|
||||
/* log tag */
|
||||
#define BT_BLE_COEX_TAG "BT_BLE_COEX"
|
||||
/* device name */
|
||||
@@ -138,8 +141,8 @@ static void ble_init_adv_data(const char *name)
|
||||
|
||||
size_t len = strlen(name);
|
||||
// ADV data max is 31 bytes; overhead is 5 bytes (Flags: 3, Name header: 2)
|
||||
#define ADV_DATA_MAX_LEN 31
|
||||
#define ADV_DATA_OVERHEAD 5
|
||||
#define ADV_DATA_MAX_LEN 31
|
||||
#define ADV_DATA_OVERHEAD 5
|
||||
|
||||
if (len > (ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD)) {
|
||||
ESP_LOGW(BT_BLE_COEX_TAG, "ADV name too long (%d), truncating to %d", (int)len, ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD);
|
||||
@@ -160,11 +163,11 @@ static void ble_init_adv_data(const char *name)
|
||||
|
||||
//The length of adv data must be less than 31 bytes
|
||||
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, adv_data_len);
|
||||
if (raw_adv_ret){
|
||||
if (raw_adv_ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "config raw adv data failed, error code = 0x%x ", raw_adv_ret);
|
||||
}
|
||||
esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_adv_data, adv_data_len);
|
||||
if (raw_scan_ret){
|
||||
if (raw_scan_ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "config raw scan rsp data failed, error code = 0x%x", raw_scan_ret);
|
||||
}
|
||||
}
|
||||
@@ -182,33 +185,33 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
|
||||
//advertising start complete event to indicate advertising start successfully or failed
|
||||
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "Advertising start failed");
|
||||
}else {
|
||||
} else {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "Start adv successfully");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "Advertising stop failed");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "Stop adv successfully");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "update connection params status = %d, conn_int = %d, latency = %d, timeout = %d",
|
||||
param->update_conn_params.status,
|
||||
param->update_conn_params.conn_int,
|
||||
param->update_conn_params.latency,
|
||||
param->update_conn_params.timeout);
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "update connection params status = %d, conn_int = %d, latency = %d, timeout = %d",
|
||||
param->update_conn_params.status,
|
||||
param->update_conn_params.conn_int,
|
||||
param->update_conn_params.latency,
|
||||
param->update_conn_params.timeout);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){
|
||||
void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
esp_gatt_status_t status = ESP_GATT_OK;
|
||||
if (param->write.need_rsp){
|
||||
if (param->write.need_rsp) {
|
||||
if (param->write.is_prep) {
|
||||
if (param->write.offset > PREPARE_BUF_MAX_SIZE) {
|
||||
status = ESP_GATT_INVALID_OFFSET;
|
||||
@@ -217,7 +220,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
|
||||
}
|
||||
|
||||
if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) {
|
||||
prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE*sizeof(uint8_t));
|
||||
prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
|
||||
prepare_write_env->prepare_len = 0;
|
||||
if (prepare_write_env->prepare_buf == NULL) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "Gatt_server prep no mem");
|
||||
@@ -233,7 +236,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
|
||||
gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
|
||||
memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
|
||||
esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);
|
||||
if (response_err != ESP_OK){
|
||||
if (response_err != ESP_OK) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "Send response error\n");
|
||||
}
|
||||
free(gatt_rsp);
|
||||
@@ -241,7 +244,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "%s, malloc failed", __func__);
|
||||
status = ESP_GATT_NO_RESOURCES;
|
||||
}
|
||||
if (status != ESP_GATT_OK){
|
||||
if (status != ESP_GATT_OK) {
|
||||
return;
|
||||
}
|
||||
memcpy(prepare_write_env->prepare_buf + param->write.offset,
|
||||
@@ -249,7 +252,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
|
||||
param->write.len);
|
||||
prepare_write_env->prepare_len += param->write.len;
|
||||
|
||||
}else{
|
||||
} else {
|
||||
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL);
|
||||
}
|
||||
}
|
||||
@@ -268,7 +271,8 @@ void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble
|
||||
prepare_write_env->prepare_len = 0;
|
||||
}
|
||||
|
||||
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
|
||||
static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_GATTS_REG_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "REGISTER_APP_EVT, status %d, app_id %d", param->reg.status, param->reg.app_id);
|
||||
@@ -297,7 +301,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
}
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %"PRIu32", handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
|
||||
if (!param->write.is_prep){
|
||||
if (!param->write.is_prep) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
|
||||
esp_log_buffer_hex(BT_BLE_COEX_TAG, param->write.value, param->write.len);
|
||||
if (gl_profile_tab[PROFILE_A_APP_ID].descr_handle == param->write.handle && param->write.len == 2){
|
||||
@@ -306,30 +310,27 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
if (a_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "notify enable");
|
||||
uint8_t notify_data[15];
|
||||
for (int i = 0; i < sizeof(notify_data); ++i)
|
||||
{
|
||||
notify_data[i] = i%0xff;
|
||||
for (int i = 0; i < sizeof(notify_data); ++i) {
|
||||
notify_data[i] = i % 0xff;
|
||||
}
|
||||
//the size of notify_data[] need less than MTU size
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle,
|
||||
sizeof(notify_data), notify_data, false);
|
||||
sizeof(notify_data), notify_data, false);
|
||||
}
|
||||
}else if (descr_value == 0x0002){
|
||||
if (a_property & ESP_GATT_CHAR_PROP_BIT_INDICATE){
|
||||
} else if (descr_value == 0x0002) {
|
||||
if (a_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "indicate enable");
|
||||
uint8_t indicate_data[15];
|
||||
for (int i = 0; i < sizeof(indicate_data); ++i)
|
||||
{
|
||||
indicate_data[i] = i%0xff;
|
||||
for (int i = 0; i < sizeof(indicate_data); ++i) {
|
||||
indicate_data[i] = i % 0xff;
|
||||
}
|
||||
//the size of indicate_data[] need less than MTU size
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle,
|
||||
sizeof(indicate_data), indicate_data, true);
|
||||
sizeof(indicate_data), indicate_data, true);
|
||||
}
|
||||
}
|
||||
else if (descr_value == 0x0000){
|
||||
} else if (descr_value == 0x0000) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "notify/indicate disable ");
|
||||
}else{
|
||||
} else {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "unknown descr value");
|
||||
esp_log_buffer_hex(BT_BLE_COEX_TAG, param->write.value, param->write.len);
|
||||
}
|
||||
@@ -340,7 +341,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
break;
|
||||
}
|
||||
case ESP_GATTS_EXEC_WRITE_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG,"ESP_GATTS_EXEC_WRITE_EVT");
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_GATTS_EXEC_WRITE_EVT");
|
||||
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
|
||||
example_exec_write_event_env(&a_prepare_write_env, param);
|
||||
break;
|
||||
@@ -361,21 +362,21 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
a_property,
|
||||
&gatts_initial_char_val, NULL);
|
||||
if (add_char_ret){
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "add char failed, error code = 0x%x",add_char_ret);
|
||||
if (add_char_ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "add char failed, error code = 0x%x", add_char_ret);
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_ADD_INCL_SRVC_EVT:
|
||||
break;
|
||||
case ESP_GATTS_ADD_CHAR_EVT: {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d",
|
||||
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
|
||||
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
|
||||
gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle;
|
||||
gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16;
|
||||
gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
|
||||
esp_err_t add_descr_ret = esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid,
|
||||
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL);
|
||||
if (add_descr_ret){
|
||||
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL);
|
||||
if (add_descr_ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "add char descr failed, error code = 0x%x", add_descr_ret);
|
||||
}
|
||||
break;
|
||||
@@ -418,7 +419,8 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
}
|
||||
}
|
||||
|
||||
static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
|
||||
static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_GATTS_REG_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "REGISTER_APP_EVT, status %d, app_id %d", param->reg.status, param->reg.app_id);
|
||||
@@ -445,7 +447,7 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
}
|
||||
case ESP_GATTS_WRITE_EVT: {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %"PRIu32", handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
|
||||
if (!param->write.is_prep){
|
||||
if (!param->write.is_prep) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
|
||||
esp_log_buffer_hex(BT_BLE_COEX_TAG, param->write.value, param->write.len);
|
||||
if (gl_profile_tab[PROFILE_B_APP_ID].descr_handle == param->write.handle && param->write.len == 2){
|
||||
@@ -454,30 +456,27 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
if (b_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "notify enable");
|
||||
uint8_t notify_data[15];
|
||||
for (int i = 0; i < sizeof(notify_data); ++i)
|
||||
{
|
||||
notify_data[i] = i%0xff;
|
||||
for (int i = 0; i < sizeof(notify_data); ++i) {
|
||||
notify_data[i] = i % 0xff;
|
||||
}
|
||||
//the size of notify_data[] need less than MTU size
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_B_APP_ID].char_handle,
|
||||
sizeof(notify_data), notify_data, false);
|
||||
sizeof(notify_data), notify_data, false);
|
||||
}
|
||||
}else if (descr_value == 0x0002){
|
||||
if (b_property & ESP_GATT_CHAR_PROP_BIT_INDICATE){
|
||||
} else if (descr_value == 0x0002) {
|
||||
if (b_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "indicate enable");
|
||||
uint8_t indicate_data[15];
|
||||
for (int i = 0; i < sizeof(indicate_data); ++i)
|
||||
{
|
||||
indicate_data[i] = i%0xff;
|
||||
for (int i = 0; i < sizeof(indicate_data); ++i) {
|
||||
indicate_data[i] = i % 0xff;
|
||||
}
|
||||
//the size of indicate_data[] need less than MTU size
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_B_APP_ID].char_handle,
|
||||
sizeof(indicate_data), indicate_data, true);
|
||||
sizeof(indicate_data), indicate_data, true);
|
||||
}
|
||||
}
|
||||
else if (descr_value == 0x0000){
|
||||
} else if (descr_value == 0x0000) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "notify/indicate disable ");
|
||||
}else{
|
||||
} else {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "unknown value");
|
||||
}
|
||||
|
||||
@@ -487,7 +486,7 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
break;
|
||||
}
|
||||
case ESP_GATTS_EXEC_WRITE_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG,"ESP_GATTS_EXEC_WRITE_EVT");
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_GATTS_EXEC_WRITE_EVT");
|
||||
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
|
||||
example_exec_write_event_env(&b_prepare_write_env, param);
|
||||
break;
|
||||
@@ -504,12 +503,12 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
|
||||
esp_ble_gatts_start_service(gl_profile_tab[PROFILE_B_APP_ID].service_handle);
|
||||
b_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
|
||||
esp_err_t add_char_ret =esp_ble_gatts_add_char( gl_profile_tab[PROFILE_B_APP_ID].service_handle, &gl_profile_tab[PROFILE_B_APP_ID].char_uuid,
|
||||
esp_err_t add_char_ret = esp_ble_gatts_add_char(gl_profile_tab[PROFILE_B_APP_ID].service_handle, &gl_profile_tab[PROFILE_B_APP_ID].char_uuid,
|
||||
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
b_property,
|
||||
NULL, NULL);
|
||||
if (add_char_ret){
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "add char failed, error code = 0x%x",add_char_ret);
|
||||
if (add_char_ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "add char failed, error code = 0x%x", add_char_ret);
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_ADD_INCL_SRVC_EVT:
|
||||
@@ -550,7 +549,7 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
if (param->conf.status != ESP_GATT_OK){
|
||||
esp_log_buffer_hex(BT_BLE_COEX_TAG, param->conf.value, param->conf.len);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ESP_GATTS_DISCONNECT_EVT:
|
||||
case ESP_GATTS_OPEN_EVT:
|
||||
case ESP_GATTS_CANCEL_OPEN_EVT:
|
||||
@@ -570,8 +569,8 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_
|
||||
gl_profile_tab[param->reg.app_id].gatts_if = gatts_if;
|
||||
} else {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "Reg app failed, app_id %04x, status %d",
|
||||
param->reg.app_id,
|
||||
param->reg.status);
|
||||
param->reg.app_id,
|
||||
param->reg.status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -594,27 +593,27 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_
|
||||
static void ble_gatts_init(void)
|
||||
{
|
||||
esp_err_t ret = esp_ble_gatts_register_callback(gatts_event_handler);
|
||||
if (ret){
|
||||
if (ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "gatts register error, error code = 0x%x", ret);
|
||||
return;
|
||||
}
|
||||
ret = esp_ble_gap_register_callback(gap_event_handler);
|
||||
if (ret){
|
||||
if (ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "gap register error, error code = 0x%x", ret);
|
||||
return;
|
||||
}
|
||||
ret = esp_ble_gatts_app_register(PROFILE_A_APP_ID);
|
||||
if (ret){
|
||||
if (ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "gatts app register error, error code = 0x%x", ret);
|
||||
return;
|
||||
}
|
||||
ret = esp_ble_gatts_app_register(PROFILE_B_APP_ID);
|
||||
if (ret){
|
||||
if (ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "gatts app register error, error code = 0x%x", ret);
|
||||
return;
|
||||
}
|
||||
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
|
||||
if (local_mtu_ret){
|
||||
if (local_mtu_ret) {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "set local MTU failed, error code = 0x%x", local_mtu_ret);
|
||||
}
|
||||
}
|
||||
@@ -634,44 +633,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param);
|
||||
|
||||
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
/* when authentication completed, this event comes */
|
||||
case ESP_BT_GAP_AUTH_CMPL_EVT: {
|
||||
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "authentication success: %s", param->auth_cmpl.device_name);
|
||||
esp_log_buffer_hex(BT_BLE_COEX_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
|
||||
} else {
|
||||
ESP_LOGE(BT_BLE_COEX_TAG, "authentication failed, status: %d", param->auth_cmpl.stat);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if (CONFIG_EXAMPLE_A2DP_SINK_SSP_ENABLED == true)
|
||||
/* when Security Simple Pairing user confirmation requested, this event comes */
|
||||
case ESP_BT_GAP_CFM_REQ_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %06"PRIu32, param->cfm_req.num_val);
|
||||
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
|
||||
break;
|
||||
/* when Security Simple Pairing passkey notified, this event comes */
|
||||
case ESP_BT_GAP_KEY_NOTIF_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey: %06"PRIu32, param->key_notif.passkey);
|
||||
break;
|
||||
/* when Security Simple Pairing passkey requested, this event comes */
|
||||
case ESP_BT_GAP_KEY_REQ_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* when GAP mode changed, this event comes */
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode: %d", param->mode_chg.mode);
|
||||
break;
|
||||
/* others */
|
||||
default: {
|
||||
ESP_LOGI(BT_BLE_COEX_TAG, "event: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bredr_app_gap_evt_def_hdl(event, param);
|
||||
}
|
||||
|
||||
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
||||
@@ -738,7 +700,7 @@ void app_main(void)
|
||||
}
|
||||
|
||||
esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
|
||||
#if (CONFIG_EXAMPLE_A2DP_SINK_SSP_ENABLED == false)
|
||||
#if (CONFIG_EXAMPLE_SSP_ENABLED == false)
|
||||
bluedroid_cfg.ssp_en = false;
|
||||
#endif
|
||||
if ((err = esp_bluedroid_init_with_cfg(&bluedroid_cfg)) != ESP_OK) {
|
||||
@@ -751,7 +713,7 @@ void app_main(void)
|
||||
return;
|
||||
}
|
||||
|
||||
#if (CONFIG_EXAMPLE_A2DP_SINK_SSP_ENABLED == true)
|
||||
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)
|
||||
/* set default parameters for Secure Simple Pairing */
|
||||
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
|
||||
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Override some defaults so BT stack is enabled and
|
||||
# Classic BT is enabled and BT_DRAM_RELEASE is disabled
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=y
|
||||
CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y
|
||||
CONFIG_BTDM_CTRL_PINNED_TO_CORE_1=n
|
||||
|
||||
Reference in New Issue
Block a user