Code:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"
#include "esp_a2dp_types.h"
#include "esp_avrc_api.h"
#include "driver/i2s.h"
#include "driver/gpio.h"
static const char *TAG = "A2DP_SINK";
// ---- 硬件配置 (来自JSON, 适配ESP32, 仅用于参考) ----
// 由于芯片为CC2540, 与实际ESP32不同, 此处根据ESP32常见外设调整
// 复位: 默认复位, 未使用外部引脚
// 按钮: 使用GPIO0 (BOOT按钮) 作为控制
#define BUTTON_GPIO GPIO_NUM_0
// 32MHz 晶振: 使用ESP32内部
// 32.768KHz: 使用内部RTC
// I2S audio output (使用内置DAC, GPIO25/26)
#define I2S_BCK_IO GPIO_NUM_26
#define I2S_WS_IO GPIO_NUM_25
#define I2S_DO_IO GPIO_NUM_27 // 若使用内置DAC, 仅用DO
// ---- 音频输出配置 (I2S 或 内部DAC) ----
#define USE_INTERNAL_DAC 1 // 1: 使用ESP32内置DAC (GPIO25,26), 0: 使用外部I2S
#if USE_INTERNAL_DAC
// 内置DAC: 使用I2S在DAC模式下, 只要GPIO25/26
#define I2S_PORT I2S_NUM_0
#define I2S_MODE (I2S_MODE_DAC_BUILT_IN | I2S_MODE_TX)
#define I2S_BITS I2S_BITS_PER_SAMPLE_16BIT
#else
#define I2S_PORT I2S_NUM_0
#define I2S_MODE I2S_MODE_MASTER | I2S_MODE_TX
#define I2S_BITS I2S_BITS_PER_SAMPLE_16BIT
#endif
// ---- 函数声明 ----
static void a2dp_app_callback(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
static void a2dp_data_callback(const uint8_t *data, uint32_t len);
static void avrc_connection_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
static void i2s_init(void);
static void i2s_write_data(const uint8_t *data, uint32_t len);
// ---- 全局变量 ----
static bool bta2d_connected = false;
static QueueHandle_t audio_queue;
// ---- 主函数 ----
void app_main(void)
{
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
bt_cfg.mode = ESP_BT_MODE_CLASSIC_BT;
bt_cfg.bluetooth_mode = ESP_BT_MODE_BTDM; // 经典蓝牙模式
// 初始化蓝牙控制器
esp_err_t ret = esp_bt_controller_init(&bt_cfg);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
return;
}
// 初始化Bluedroid
ret = esp_bluedroid_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Bluedroid initialize failed: %s", esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_enable();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Bluedroid enable failed: %s", esp_err_to_name(ret));
return;
}
// 设置设备名称
esp_bt_dev_set_device_name("ESP32_A2DP_SINK");
// 初始化 A2DP Sink
esp_a2d_register_data_callback(a2dp_data_callback);
esp_a2d_register_callback(a2dp_app_callback);
esp_a2d_sink_register_data_callback(a2dp_data_callback); // 已注册, 但明确指定
esp_a2d_sink_init();
// 初始化AVRC
esp_avrc_ct_register_callback(avrc_connection_callback);
esp_avrc_ct_init();
// 创建音频数据队列
audio_queue = xQueueCreate(10, sizeof(audio_data_t)); // 未定义, 改用直接写入
// 初始化I2S
i2s_init();
// 初始化按钮GPIO (用于控制)
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << BUTTON_GPIO),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 开启蓝牙发现 (可被连接)
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
ESP_LOGI(TAG, "A2DP Sink ready. Connect from phone.");
// 主循环
while (1) {
// 检测按钮 (用于重置或切换)
if (gpio_get_level(BUTTON_GPIO) == 0) {
vTaskDelay(pdMS_TO_TICKS(200)); // 去抖动
if (gpio_get_level(BUTTON_GPIO) == 0) {
ESP_LOGI(TAG, "Button pressed. Disconnect if connected.");
if (bta2d_connected) {
esp_a2d_sink_disconnect(esp_bt_dev_get_address()); // 简化
}
}
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// ---- A2DP 应用回调 ----
static void a2dp_app_callback(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
{
switch (event) {
case ESP_A2D_CONNECTION_STATE_EVT:
ESP_LOGI(TAG, "A2DP connection state: %d", param->conn_state.state);
if (param->conn_state.state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
bta2d_connected = true;
ESP_LOGI(TAG, "A2DP connected.");
} else if (param->conn_state.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
bta2d_connected = false;
ESP_LOGI(TAG, "A2DP disconnected.");
}
break;
case ESP_A2D_AUDIO_STATE_EVT:
ESP_LOGI(TAG, "A2DP audio state: %d", param->audio_state.state);
break;
case ESP_A2D_AUDIO_CFG_EVT:
ESP_LOGI(TAG, "A2DP audio config: codec %d", param->audio_cfg.codec_type);
break;
default:
ESP_LOGW(TAG, "A2DP event: %d", event);
break;
}
}
// ---- A2DP 音频数据回调 ----
static void a2dp_data_callback(const uint8_t *data, uint32_t len)
{
// 将音频数据通过I2S输出
i2s_write_data(data, len);
}
// ---- AVRC 回调 (简化) ----
static void avrc_connection_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
{
switch (event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
ESP_LOGI(TAG, "AVRC connection state: %d", param->conn_state.connected);
break;
default:
ESP_LOGW(TAG, "AVRC event: %d", event);
break;
}
}
// ---- I2S 初始化 ----
static void i2s_init(void)
{
i2s_config_t i2s_config = {
.mode = I2S_MODE, // 根据宏定义
.sample_rate = 44100,
.bits_per_sample = I2S_BITS,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 64,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0
};
if (USE_INTERNAL_DAC) {
// 配置I2S为DAC模式
i2s_config.mode = (i2s_mode_t)(I2S_MODE_DAC_BUILT_IN);
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
// 启用DAC输出 (GPIO25和GPIO26)
i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN); // 仅左声道或双声道
// 注: 内置DAC仅支持单声道, 但此处简化使用左声道
} else {
// 外部I2S引脚
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCK_IO,
.ws_io_num = I2S_WS_IO,
.data_out_num = I2S_DO_IO,
.data_in_num = I2S_PIN_NO_CHANGE
};
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
i2s_set_pin(I2S_PORT, &pin_config);
}
ESP_LOGI(TAG, "I2S initialized.");
}
// ---- I2S 数据写入 ----
static void i2s_write_data(const uint8_t *data, uint32_t len)
{
size_t bytes_written = 0;
esp_err_t ret = i2s_write(I2S_PORT, data, len, &bytes_written, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2S write error: %s", esp_err_to_name(ret));
}
// 可添加错误处理: 若写入不完整, 丢弃剩余数据
if (bytes_written < len) {
ESP_LOGW(TAG, "I2S underflow, wrote %d out of %d bytes", bytes_written, len);
}
}
```