开发工具:

  • Arduino IDE

MAX98357A 引脚 ESP32 引脚 说明
VCC 3.3V 模块电源(⚠️不要接 5V,否则可能烧芯片)
GND GND 公共地线
DIN GPIO25 I2S 数据线(音频数据输出)
BCLK GPIO27 I2S 位时钟(Bit Clock)
LRCK / WS GPIO26 I2S 采样帧同步(左右声道选择)
GAIN (可选) 接地或悬空 增益控制:GND=9dB,悬空=15dB,接VCC=6dB
SD (或 SD_MODE) (可选) 可接 ESP32 GPIO 控制 使能/关闭功放:高电平=启用,低电平=静音

源代码

#include <Arduino.h>
#include <BluetoothA2DPSink.h>
#include <driver/i2s.h>

#define I2S_DATA_PIN 25
#define I2S_CLOCK_PIN 27
#define I2S_WS_PIN 26

BluetoothA2DPSink a2dp_sink;

bool is_connected = false;  // 标志位,表示蓝牙是否连接

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 A2DP Receiver with MAX98357A Playback");

  // I2S 配置
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = 44100,  // 可尝试 48000 或 32000
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_I2S_MSB,
    .intr_alloc_flags = 0,
    .dma_buf_count = 32,
    .dma_buf_len = 32,
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_CLOCK_PIN,
    .ws_io_num = I2S_WS_PIN,
    .data_out_num = I2S_DATA_PIN,
    .data_in_num = I2S_PIN_NO_CHANGE
  };

  // 初始化 I2S
  esp_err_t err = i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
  if (err != ESP_OK) {
    Serial.printf("I2S driver install failed: %d\n", err);
    while (true);
  }
  err = i2s_set_pin(I2S_NUM_0, &pin_config);
  if (err != ESP_OK) {
    Serial.printf("I2S set pin failed: %d\n", err);
    while (true);
  }

  // 设置 I2S 时钟
  err = i2s_set_clk(I2S_NUM_0, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
  if (err != ESP_OK) {
    Serial.printf("I2S set clock failed: %d\n", err);
  }

  // 配置蓝牙 A2DP 直接输出到 I2S,降低音量以减少噪声
  a2dp_sink.set_stream_reader([](const uint8_t *data, uint32_t len) {
    static uint32_t last_time = 0;
    uint32_t now = millis();
    Serial.printf("Received audio data: %d bytes, time since last: %d ms\n", len, now - last_time);
    last_time = now;

    // 软件降低音量(模拟 GAIN 控制)
    int16_t *samples = (int16_t *)data;
    for (uint32_t i = 0; i < len / 2; i++) {
      samples[i] = samples[i] * 0.5;  // 降低 50% 音量
    }

    if (is_connected) {
      size_t bytes_written;
      esp_err_t err = i2s_write(I2S_NUM_0, data, len, &bytes_written, portMAX_DELAY);
      if (err != ESP_OK) {
        Serial.printf("I2S write error: %d\n", err);
      } else if (bytes_written != len) {
        Serial.printf("I2S wrote %d bytes, expected %d\n", bytes_written, len);
      }
    }
  }, false);

  // 蓝牙连接状态回调
  a2dp_sink.set_on_connection_state_changed([](esp_a2d_connection_state_t state, void*) {
    Serial.printf("Connection state: %d\n", state);
    if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
      Serial.println("Bluetooth connected!");
      is_connected = true;
    } else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
      Serial.println("Bluetooth disconnected!");
      is_connected = false;

      // 清空I2S缓冲区
      i2s_zero_dma_buffer(I2S_NUM_0);
    }
  });

  // 启动蓝牙
  a2dp_sink.start("ESP32 Audio Sink");
  Serial.println("Connecting to Bluetooth...");
}

void loop() {
  if (!is_connected) {
    Serial.println("Bluetooth not connected, playing test tone...");
    int16_t samples[64];
    for (int i = 0; i < 64; i++) {
      samples[i] = sin(2 * PI * 440 * i / 44100) * 1000; // 降低测试音音量
    }
    size_t bytes_written;
    esp_err_t err = i2s_write(I2S_NUM_0, samples, sizeof(samples), &bytes_written, portMAX_DELAY);
    if (err != ESP_OK) {
      Serial.printf("I2S tone write error: %d\n", err);
    } else {
      Serial.printf("Wrote %d bytes for tone\n", bytes_written);
    }
  }
  delay(200);
}

成品效果

用 esp32 蓝牙播放器

https://www.bilibili.com/video/BV1oU1jBXEcu/

更多推荐