やっつけ仕事です…

回路図(PDF)
A2DPライブラリ

 気軽にレコードが聞けるかも?と思い一番最初に買ったESP32-DevKitC-32Dの消化も兼ねて作ってみました。先人の苦労の上に全乗りしています。
 A/Dコンバーターは、秋月のPCM1808 Dip化キットを使いました。

 Bluetooth A2DPプロファイルのSourceを使っています。AVRCP Targetも色々と試してみたのですが上手く動かすことが出来ませんでした。:-p(br>
//ESP32 board file V2.0.6
#include "BluetoothA2DPSource.h"  //V1.7.2 2022/11/29commit
#include <driver/i2s.h>
#include "hal/brownout_hal.h"

#define kSamplingFreq 44100
#define kBlockSize 128
#define kBlockCount 4

BluetoothA2DPSource a2dp_source;
int32_t rxbuf[kBlockSize * 2];  // int32 * 2ch * kBlockSize
esp_err_t err = ESP_OK;

hw_timer_t* timer = NULL;
volatile uint8_t wink;

void ARDUINO_ISR_ATTR onTimer() {
  // 4mS interval
  wink++;
}

// The supported audio codec in ESP32 A2DP is SBC. SBC audio stream is encoded
// from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data
int32_t get_data_channels(Frame* frame, int32_t channel_len) {
  size_t readsize = 0;
  //Input
  esp_err_t res = i2s_read(I2S_NUM_0, &rxbuf[0], channel_len * 2 * sizeof(int32_t), &readsize, portMAX_DELAY);
  if (res == ESP_OK && readsize == kBlockSize * 2 * sizeof(int32_t)) {
    int j = 0;
    for (int i = 0; i < kBlockSize; i++) {
      frame->channel1 = (int16_t)(rxbuf[j] >> 16);
      frame->channel2 = (int16_t)(rxbuf[j + 1] >> 16);
      j += 2;
      frame++;
    }
  } else {
    err = res;
  }
  return channel_len;
}

// Return true to connect, false will continue scanning
bool isValid(const char* ssid, esp_bd_addr_t address, int rssi) {
  bool connect = false;

  Serial.print("available SSID: ");
  Serial.println(ssid);

  if (strcmp("MTC+", ssid) == 0) {
    Serial.println("Find...MTC+(NAVI)");
    connect = true;
  }
  if (strcmp("JBL T450BT", ssid) == 0) {
    Serial.println("Find...JBL HEADPHONE");
    connect = true;
  }
  if (strcmp("JBL Flip 4", ssid) == 0) {
    Serial.println("Find...JBL SPEAKER");
    connect = true;
  }
  return connect;
}

void setup() {
  esp_err_t err;

  Serial.begin(115200);
  delay(1);
  //WATCHDOG
  err = esp_task_wdt_init(1, true);
  Serial.printf("esp_task_wdt_init() %s\n", esp_err_to_name(err));
  err = esp_task_wdt_add(NULL);
  Serial.printf("esp_task_wdt_add() %s\n", esp_err_to_name(err));

  //BROWN OUT
  brownout_hal_config_t cfg;
  Serial.printf("before= 0x%lx\n", READ_PERI_REG(RTC_CNTL_BROWN_OUT_REG));
  cfg.reset_enabled = true;  //リセットだけで
  cfg.flash_power_down = false;
  cfg.rf_power_down = false;
  cfg.enabled = true;
  cfg.threshold = 0;  //2.43V Flash = 40MHz
  brownout_hal_config(&cfg);
  // WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0x47ff0000);  //直接書換は、NG??
  Serial.printf("after= 0x%lx\n", READ_PERI_REG(RTC_CNTL_BROWN_OUT_REG));

  pinMode(12, OUTPUT);  //connect LED

#if 0
  if (nvs_flash_erase() == ESP_OK) {
    Serial.println("NVS erase...");
    nvs_flash_init();
  }
#endif
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = kSamplingFreq,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = 0,
    .dma_buf_count = kBlockCount,
    .dma_buf_len = kBlockSize * 2 * sizeof(int32_t),
    .use_apll = true,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0,
    .mclk_multiple = I2S_MCLK_MULTIPLE_256,
    .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
  };
  err = i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
  Serial.printf("i2s_driver_install() %s\n", esp_err_to_name(err));

  i2s_pin_config_t pin_config = {
    .mck_io_num = 0,
    .bck_io_num = 15,
    .ws_io_num = 2,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = 13
  };
  err = i2s_set_pin(I2S_NUM_0, &pin_config);
  Serial.printf("i2s_set_pin() %s\n", esp_err_to_name(err));

  a2dp_source.set_auto_reconnect(false);
  a2dp_source.set_ssp_enabled(true);  // for car navi.
  a2dp_source.set_local_name("ESP-PLAYER");
  a2dp_source.set_ssid_callback(isValid);
  a2dp_source.start(get_data_channels);

  a2dp_source.set_volume(180);

  timer = timerBegin(0, 80, true);  //divider 1uS count
  timerAttachInterrupt(timer, &onTimer, false);
  timerAlarmWrite(timer, 4000, true);  //4000uS interval
  timerAlarmEnable(timer);
}

void loop() {
  esp_task_wdt_reset();
  if (a2dp_source.is_connected()) {
    digitalWrite(12, HIGH);
  } else {
    if (wink & 0x20) {
      digitalWrite(12, HIGH);
    } else {
      digitalWrite(12, LOW);
    }
  }
  delay(20);
}