refactor(examples/classic_bt): Add AVRCP cover art example with LCD display

This commit is contained in:
yangfeng
2025-08-29 16:00:22 +08:00
parent 5ca0bba789
commit 3e94884160
10 changed files with 596 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(avrcp_ct_cover_art)

View File

@@ -0,0 +1,121 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
AVRCP-CT-COVER-ART EXAMPLE
======================
This is an example demonstrating the API for implementing Audio/Video Remote Control Profile to get and display cover art image.
## Required components
- [bt_app_core_utils](../common/bt_app_core_utils)
- [bredr_app_common_utils](../common/bredr_app_common_utils)
- [a2dp_sink_common_utils](../common/a2dp_utils/a2dp_sink_common_utils)
- [a2dp_sink_int_codec_utils](../common/a2dp_utils/a2dp_sink_int_codec_utils)
- [a2dp_sink_ext_codec_utils](../common/a2dp_utils/a2dp_sink_ext_codec_utils)
- [avrcp_common_utils](../common/avrcp_utils/avrcp_common_utils)
- [avrcp_metadata_utils](../common/avrcp_utils/avrcp_metadata_utils)
- [avrcp_cover_art_utils](../common/avrcp_utils/avrcp_cover_art_utils)
```
+---------------------------------------------------+---------------------+
| avrcp_cover_art_utils | |
+---------------------------------------------------+ |
| avrcp_metadata_utils | |
+---------------------------------------------------+ |
| avrcp_common_utils | |
+-------------------------+-------------------------+ bt_app_core_utils |
|a2dp_sink_int_codec_utils|a2dp_sink_ext_codec_utils| |
+-------------------------+-------------------------+ |
| a2dp_sink_common_utils | |
+---------------------------------------------------+ |
| bredr_app_common_utils | |
+---------------------------------------------------+---------------------+
```
Detailed information can be viewed through the [../common/README.md](../common/README.md).
## How to use this example
### Hardware Required
* An ESP32 development board
* A SPI-interfaced LCD
* A USB cable for power supply and programming
### Hardware Connection
The connection between ESP32 Board and the LCD is as follows:
```
ESP32 Board LCD Screen
+---------+ +---------------------------------+
| | | |
| 3V3 +--------------+ VCC +----------------------+ |
| | | | | |
| GND +--------------+ GND | | |
| | | | | |
| DATA0 +--------------+ MOSI | | |
| | | | | |
| PCLK +--------------+ SCK | | |
| | | | | |
| CS +--------------+ CS | | |
| | | | | |
| D/C +--------------+ D/C | | |
| | | | | |
| RST +--------------+ RST | | |
| | | | | |
|BK_LIGHT +--------------+ BCKL +----------------------+ |
| | | |
+---------+ +---------------------------------+
```
The GPIO number used by this example can be changed in [avrcp_cover_art_service.c](../common/avrcp_utils/avrcp_cover_art_utils/avrcp_cover_art_service.c), where:
| GPIO number | LCD pin |
| ------------------------ | ------- |
| EXAMPLE_PIN_NUM_PCLK | SCK |
| EXAMPLE_PIN_NUM_CS | CS |
| EXAMPLE_PIN_NUM_DC | DC |
| EXAMPLE_PIN_NUM_RST | RST |
| EXAMPLE_PIN_NUM_DATA0 | MOSI |
| EXAMPLE_PIN_NUM_BK_LIGHT | BCKL |
Note that the level used to turn on the LCD backlight may vary: some LCD modules need a low level to turn it on, while others require a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [avrcp_cover_art_service.c](../common/avrcp_utils/avrcp_cover_art_utils/avrcp_cover_art_service.c).
### Configure the project
```
idf.py menuconfig
```
* The AVRCP CT Cover Art feature is enabled by default. We can disable it by unselecting the menuconfig option `Component config --> Bluetooth --> Bluedroid Options --> Classic Bluetooth --> AVRCP Features --> AVRCP CT Cover Art`. This example will try to use the AVRCP CT Cover Art feature to get the cover art image, count the image size, and display it if the peer device supports it.
* **Memory Configuration**: To ensure that the A2DP sink stream and the display of AVRCP cover art on the LCD can run simultaneously, the following configurations are required and have been set in `sdkconfig.defaults`:
* `CONFIG_SPIRAM=y`: Enables external SPI RAM (PSRAM) support. This provides additional memory space needed for buffering audio data during A2DP streaming while simultaneously handling cover art image decoding and display operations. Without this, the application may run out of internal RAM when processing both audio streams and image data.
* `CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y`: Selects the single large app partition table scheme, which allocates more space for the application binary. This is necessary because the example application includes multiple Bluetooth profiles (A2DP, AVRCP), image decoding libraries, and LCD display drivers, requiring a larger application partition than the default partition table provides.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output.
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
## Example Output
The output when receiving a cover art image:
```
I (31579) RC_CT: AVRC metadata rsp: attribute id 0x80, 1000526
I (32039) RC_CA_SRV: Cover Art Client final data event, image size: 12315 bytes
I (32119) RC_CA_SRV: JPEG image decoded! Size of the decoded image is: 200px x 200px.
```
And then, the LCD screen will display the cover art image.
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@@ -0,0 +1,13 @@
if(CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE)
if(CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC)
set(EXTRA_COMPONENTS a2dp_sink_ext_codec_utils)
else()
set(EXTRA_COMPONENTS a2dp_sink_int_codec_utils)
endif()
endif()
idf_component_register(SRCS "bt_app_av.c"
"main.c"
PRIV_REQUIRES bt bt_app_core_utils bredr_app_common_utils a2dp_sink_common_utils
PRIV_REQUIRES ${EXTRA_COMPONENTS} avrcp_common_utils avrcp_metadata_utils avrcp_cover_art_utils
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,23 @@
menu "AVRCP Example Configuration"
config EXAMPLE_LOCAL_DEVICE_NAME
string "Local Device Name"
default "ESP_SPEAKER"
help
Use this option to set local device name.
config EXAMPLE_A2DP_SINK_STREAM_ENABLE
bool "Enable A2DP Sink Stream"
default y
help
This enables the A2DP sink stream. If disable this option,
audio data will not be transmitted
config EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC
bool "Use External Codec Instead of Internal"
depends on EXAMPLE_A2DP_SINK_STREAM_ENABLE
default n
select BT_A2DP_USE_EXTERNAL_CODEC
help
If enable, Bluedroid stack will not decode A2DP audio data, user need to decode it in application layer.
endmenu

View File

@@ -0,0 +1,206 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_avrc_api.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "bt_app_core_utils.h"
#include "bt_app_av.h"
#include "avrcp_utils_tags.h"
#include "avrcp_common_utils.h"
#include "avrcp_metadata_service.h"
#include "avrcp_metadata_utils.h"
#include "a2dp_utils_tags.h"
#include "a2dp_sink_common_utils.h"
#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE
#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE
#include "a2dp_sink_int_codec_utils.h"
#else
#include "a2dp_sink_ext_codec_utils.h"
#endif
#endif
#include "avrcp_cover_art_service.h"
#include "avrcp_cover_art_utils.h"
/********************************
* STATIC FUNCTION DEFINITIONS
*******************************/
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 *)(param);
switch (event) {
case ESP_AVRC_CT_PASSTHROUGH_RSP_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: {
bt_avrc_md_ct_evt_hdl(event, param);
bt_avrc_ca_ct_evt_hdl(event, param);
if (rc->conn_stat.connected) {
/* get remote supported event_ids of peer AVRCP Target */
bt_avrc_common_ct_get_peer_rn_cap();
} else {
/* clear peer notification capability record */
bt_avrc_common_ct_set_peer_rn_cap(0);
}
break;
}
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
bt_avrc_md_ct_evt_hdl(event, param);
bt_avrc_ca_ct_evt_hdl(event, param);
bt_avrc_common_ct_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter);
break;
}
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
/* 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);
bt_avrc_ca_ct_evt_hdl(event, param);
break;
}
case ESP_AVRC_CT_METADATA_RSP_EVT: {
if (rc->meta_rsp.attr_id == ESP_AVRC_MD_ATTR_COVER_ART) {
bt_avrc_ca_ct_evt_hdl(event, param);
} else {
bt_avrc_md_ct_evt_hdl(event, param);
}
break;
}
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
bt_avrc_ca_ct_evt_hdl(event, param);
break;
}
case ESP_AVRC_CT_COVER_ART_STATE_EVT:
case ESP_AVRC_CT_COVER_ART_DATA_EVT: {
bt_avrc_ca_ct_evt_hdl(event, param);
break;
}
default:
ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event);
break;
}
}
/********************************
* EXTERNAL FUNCTION DEFINITIONS
*******************************/
void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
{
switch (event) {
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_a2d_evt_def_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL);
break;
}
case ESP_A2D_CONNECTION_STATE_EVT:
case ESP_A2D_AUDIO_STATE_EVT:
case ESP_A2D_AUDIO_CFG_EVT:
case ESP_A2D_SEP_REG_STATE_EVT: {
#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE
#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE
bt_app_work_dispatch(bt_a2d_evt_int_codec_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL);
#else
bt_app_work_dispatch(bt_a2d_evt_ext_codec_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL);
#endif
#else
bt_app_work_dispatch(bt_a2d_evt_def_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL);
#endif
break;
}
default:
ESP_LOGE(BT_AV_TAG, "Invalid A2DP event: %d", event);
break;
}
}
#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE
#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE
void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
{
bt_a2d_data_hdl(data, len);
}
#else
void bt_app_a2d_audio_data_cb(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf)
{
bt_a2d_audio_data_hdl(conn_hdl, audio_buf);
}
#endif
#endif
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_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_COVER_ART_DATA_EVT: {
/* we must handle ESP_AVRC_CT_COVER_ART_DATA_EVT in avrcp controller callback, */
/* copy image data to other buff before return if need */
if (param->cover_art_data.status == ESP_BT_STATUS_SUCCESS) {
avrc_cover_art_srv_save_image_data(param->cover_art_data.p_data, param->cover_art_data.data_len);
} else {
ESP_LOGE(BT_RC_CT_TAG, "Cover Art Client get operation failed");
break;
}
bt_app_work_dispatch(bt_app_avrc_ct_evt_hdl, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
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:
case ESP_AVRC_CT_COVER_ART_STATE_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;
}
}
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:
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;
default:
ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event);
break;
}
}

View File

@@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#ifndef __BT_APP_AV_H__
#define __BT_APP_AV_H__
#include <stdint.h>
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
/**
* @brief callback function for A2DP sink
*
* @param [in] event event id
* @param [in] param callback parameter
*/
void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
/**
* @brief callback function for A2DP sink audio data stream
*
* @param [out] data data stream writteen by application task
* @param [in] len length of data stream in byte
*/
void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len);
/**
* @brief callback function for A2DP sink undecoded audio data
*
* @param [in] conn_hdl connection handle
* @param [in] audio_buf pointer to audio buff
*/
void bt_app_a2d_audio_data_cb(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
/**
* @brief callback function for AVRCP controller
*
* @param [in] event event id
* @param [in] param callback parameter
*/
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
/**
* @brief callback function for AVRCP target
*
* @param [in] event event id
* @param [in] param callback parameter
*/
void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param);
#endif /* __BT_APP_AV_H__*/

View File

@@ -0,0 +1,17 @@
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
a2dp_sink_ext_codec_utils:
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/a2dp_utils/a2dp_sink_ext_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_cover_art_utils:
path: ${IDF_PATH}/examples/bluetooth/bluedroid/classic_bt/common/avrcp_utils/avrcp_cover_art_utils

View File

@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.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"
#include "bt_app_av.h"
#include "bt_app_core_utils.h"
#include "bredr_app_common_utils.h"
#include "a2dp_utils_tags.h"
/* device name */
static const char local_device_name[] = CONFIG_EXAMPLE_LOCAL_DEVICE_NAME;
/* event for stack up */
enum {
BT_APP_EVT_STACK_UP = 0,
};
/********************************
* STATIC FUNCTION DECLARATIONS
*******************************/
/* Device callback function */
static void bt_app_dev_cb(esp_bt_dev_cb_event_t event, esp_bt_dev_cb_param_t *param);
/* GAP callback function */
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
/* handler for bluetooth stack enabled events */
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param);
/*******************************
* STATIC FUNCTION DEFINITIONS
******************************/
static void bt_app_dev_cb(esp_bt_dev_cb_event_t event, esp_bt_dev_cb_param_t *param)
{
bredr_app_dev_evt_def_hdl(event, param);
}
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
bredr_app_gap_evt_def_hdl(event, param);
}
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_AV_TAG, "%s event: %d", __func__, event);
switch (event) {
/* when do the stack up, this event comes */
case BT_APP_EVT_STACK_UP: {
esp_bt_gap_set_device_name(local_device_name);
esp_bt_dev_register_callback(bt_app_dev_cb);
esp_bt_gap_register_callback(bt_app_gap_cb);
esp_avrc_ct_register_callback(bt_app_rc_ct_cb);
assert(esp_avrc_ct_init() == ESP_OK);
esp_avrc_tg_register_callback(bt_app_rc_tg_cb);
assert(esp_avrc_tg_init() == ESP_OK);
esp_a2d_register_callback(&bt_app_a2d_cb);
assert(esp_a2d_sink_init() == ESP_OK);
#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE
#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
#else
esp_a2d_mcc_t mcc = {0};
mcc.type = ESP_A2D_MCT_SBC;
mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K |
ESP_A2D_SBC_CIE_SF_32K |
ESP_A2D_SBC_CIE_SF_44K |
ESP_A2D_SBC_CIE_SF_48K;
mcc.cie.sbc_info.ch_mode = ESP_A2D_SBC_CIE_CH_MODE_MONO |
ESP_A2D_SBC_CIE_CH_MODE_DUAL_CHANNEL |
ESP_A2D_SBC_CIE_CH_MODE_STEREO |
ESP_A2D_SBC_CIE_CH_MODE_JOINT_STEREO;
mcc.cie.sbc_info.block_len = ESP_A2D_SBC_CIE_BLOCK_LEN_4 |
ESP_A2D_SBC_CIE_BLOCK_LEN_8 |
ESP_A2D_SBC_CIE_BLOCK_LEN_12 |
ESP_A2D_SBC_CIE_BLOCK_LEN_16;
mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8;
mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SRN | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS;
mcc.cie.sbc_info.max_bitpool = 250;
mcc.cie.sbc_info.min_bitpool = 2;
/* register stream end point, only support SBC currently */
esp_a2d_sink_register_stream_endpoint(0, &mcc);
esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb);
#endif
#endif
/* Get local device name */
esp_bt_gap_get_device_name();
/* set discoverable and connectable mode, wait to be connected */
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
break;
}
/* others */
default:
ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event);
break;
}
}
/*******************************
* MAIN ENTRY POINT
******************************/
void app_main(void)
{
ESP_ERROR_CHECK(bredr_app_common_init());
bt_app_task_start_up();
/* bluetooth device name, connection mode and profile set up */
bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL);
}

View File

@@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@idf_parametrize('target', ['esp32'], indirect=['target'])
def test_bt_avrcp_cover_art(dut: Dut) -> None:
dut.expect(r'AVRCP (CT|TG) STATE: Init Complete', timeout=30)
dut.expect(r'AVRCP (CT|TG) STATE: Init Complete', timeout=30)

View File

@@ -0,0 +1,11 @@
# 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_BR_EDR_ONLY=y
CONFIG_BT_BLUEDROID_ENABLED=y
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=y
CONFIG_BT_AVRCP_CT_COVER_ART_ENABLED=y
CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=n
CONFIG_SPIRAM=y
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y