14
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【ESP Arduinoライブラリのご紹介】ESP32-A2DP

Last updated at Posted at 2021-10-02

はじめに

今まで使ってきたESPマイコン用Arduinoライブラリについてご紹介したいと思います。
これらの記事がどなたかの何らかに貢献できれば幸いです。

ESP32-A2DP Library

今回はESP32-A2DPライブラリをご紹介します。

Thanks to @pschatzmann, author of the ESP32-A2DP library.

概要

スマートフォンからESP32へ音声データをBluetooth(A2DP)でストリーミングして、ESP32から出力したI2S信号をDAC(Digital Analog Converter)へ入力できます。つまり、このライブラリを使ってBluetoothスピーカーを構成できます。
A2DP(Advanced Audio Distribution Profile)とは無線通信を利用して音声データをストリーミングするBluetoothのプロファイルです。iOSのAirPlayはこのプロファイルに準拠しています。

はじめ、ESP-IDF開発環境のGitHubリポジトリにC言語で書かれたa2dp_sink1ドライバが公開されていました。その後、@pschatzmannさんがESP-IDF A2DP-SINK demoを使いやすいArduinoクラスライブラリに変換?してくれました。Thanks👍

(かんたんな構成図)
[iPhone] ---(Bluetooth [Radio Frequency 2.4GHz])--->[ESP32]---(I2S)--->[DAC]---(RCA Cable)--->[AMP]---(Cu Cable)--->[Speaker]

お試し環境

つぎの環境でこのライブラリの動作確認をおこないました。

KP-ESP32Cと中華DAC(I2Sインターフェイス付き)
ESP-WROOM-32 変換基板 KP-ESP32C中華DAC

動作の様子

iPhoneからApple Musicでダウンロードした曲をAirPlayで再生しています。
ESP32基板と中華DACをタカチのケースに収めました。

電子回路的な配線の説明はここでは省略します。詳細はESP-IDFのGitHubリポジトリなどを参照してください。

サンプルコード

こちらに私がモディファイしたサンプルがあります。よろしければご参照ください。

Application.h

#include <Arduino.h>
#include <BluetoothA2DPSink.h>
#include <esp32-hal-log.h>

class Application {
   public:
    Application() {}

    ~Application() {}

    static void avrc_metadata_callback(uint8_t id, const uint8_t* text) {
        log_i("==> AVRC metadata rsp");
        String str((char*)text);

        switch (id) {
            case 0x01:
                log_i("==> 曲名:%s", str.c_str());
                break;
            case 0x02:
                log_i("==> アーティスト:%s", str.c_str());
                break;
            case 0x04:
                log_i("==> アルバム名:%s", str.c_str());
                break;
            case 0x20:
                log_i("==> ジャンル:%s", str.c_str());
                break;
            default:
                log_i("==> Unknown:%s", str.c_str());
                break;
        }
    }

    static void on_data_receive_callback(void) {
        static int count;
        if (++count % 100 == 0) {
            digitalWrite(_BLUE_LED_PORT, HIGH);
        } else {
            digitalWrite(_BLUE_LED_PORT, LOW);
        }
    }
#ifdef TEST
    BluetoothA2DPSink* getA2DPSink(void) {
        return &_a2dp_sink;
    }
#endif
    void setup(void) {
        pinMode(_BLUE_LED_PORT, OUTPUT);
        pinMode(_GREEN_LED_PORT, OUTPUT);

        i2s_pin_config_t pin_config = {
            .bck_io_num   = 26,
            .ws_io_num    = 22,
            .data_out_num = 25,
            .data_in_num  = I2S_PIN_NO_CHANGE  //Use in i2s_pin_config_t for pins which should not be changed
        };

        //Settings for ES9038Q2M VR1.07 DAC Board(eBay item number:263908779821)
        i2s_config_t i2s_config = {
            .mode                 = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
            .sample_rate          = 44100,                       // corrected by info from bluetooth
            .bits_per_sample      = (i2s_bits_per_sample_t)16,   // set_bits_per_sample()
            .channel_format       = I2S_CHANNEL_FMT_RIGHT_LEFT,  // 2-channels
            .communication_format = I2S_COMM_FORMAT_I2S,         // I2S communication format I2S
            .intr_alloc_flags     = ESP_INTR_FLAG_LEVEL1,        // default interrupt priority
            .dma_buf_count        = 8,                           // default
            .dma_buf_len          = 64,                          // default
            .use_apll             = false,                       // I2S using APLL as main I2S clock, enable it to get accurate clock
            .tx_desc_auto_clear   = true                         // I2S auto clear tx descriptor if there is underflow condition
        };

        _a2dp_sink.set_pin_config(pin_config);
        _a2dp_sink.set_i2s_config(i2s_config);
        _a2dp_sink.set_bits_per_sample(I2S_BITS_PER_SAMPLE_32BIT);  //for I2S : PCM 44.1K-384K 32BIT
        _a2dp_sink.set_on_data_received(on_data_receive_callback);
        _a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);

        _a2dp_sink.start("Riraosan Player", true);
    }

    void handle(void) {
        if (_a2dp_sink.isConnected()) {
            digitalWrite(_GREEN_LED_PORT, HIGH);
        } else {
            digitalWrite(_GREEN_LED_PORT, LOW);
        }

        switch (_a2dp_sink.get_audio_state()) {
            case ESP_A2D_AUDIO_STATE_STARTED:
                break;
            case ESP_A2D_AUDIO_STATE_STOPPED:
            case ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND:
                digitalWrite(_BLUE_LED_PORT, LOW);
                break;
            default:
                break;
        }
    }

   private:
    BluetoothA2DPSink _a2dp_sink;
    constexpr static int _BLUE_LED_PORT  = 33;
    constexpr static int _GREEN_LED_PORT = 32;
};

main.cpp
#include <Application.h>

static Application app;

void setup() {
    app.setup();
}

void loop() {
    app.handle();
}
platformio.ini
[platformio]
default_envs    = esp32_a2dp_debug

[env:esp32_a2dp_release]
build_type      = release
extends         = esp32, arduino-esp32, serial, Windows

build_flags =
        -D CORE_DEBUG_LEVEL=0

[env:esp32_a2dp_debug]
build_type      = debug
extends         = esp32, arduino-esp32, serial, Windows

monitor_filters = esp32_exception_decoder
build_flags =
        -D TEST
        -D CORE_DEBUG_LEVEL=4
        -D CONFIG_ARDUHAL_LOG_COLORS

test_port               = COM7
test_speed              = 115200
test_build_project_src  = no

[esp32]
board = esp32dev

;OTA or Serial
[ota]
;upload_protocol = espota
;upload_port    = url
;upload_port    = 192.168.xx.xx
;monitor_port   = /dev/tty.usbserial-1952FF03F3
;monitor_speed  = 115200
;targets        = monitor

[serial]
upload_protocol = esptool
upload_speed    = 115200
monitor_speed   = 115200
targets         = upload, monitor

;Serial Port(Mac or Linux or Windows)
[Mac]
upload_port     = /dev/tty.usbserial-1952FF03F3
monitor_port    = /dev/tty.usbserial-1952FF03F3

[Linux]
upload_port     = /dev/ttyUSB0
monitor_port    = /dev/ttyUSB0

[Windows]
upload_port     = COM7
monitor_port    = COM7

[arduino-esp32]
platform        = espressif32
framework       = arduino
platform_packages = framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#1.0.6
build_flags =
        -std = gnu++14
        -D ARDUINO_ARCH_ESP32
        -D ESP32

build_unflags =
        ;-std = gnu++11

board_build.mcu         = esp32
board_build.f_cpu       = 240000000L
board_build.f_flash     = 80000000L
board_build.flash_mode  = qio
board_build.partitions  = default.csv

lib_deps =
        ;https://github.com/riraosan/ESP32-A2DP.git ;modified for ES9038Q2M VR1.07 DAC Board
        https://github.com/pschatzmann/ESP32-A2DP.git

気をつけること

  • ESP8266はペリフェラル(周辺機器)にBluetoothが内蔵されていないので、このライブラリは使えません。
  • このサンプルはarduino-esp32ライブラリ1.0.6で動作確認しています。最新のarduino-esp32ライブラリを使用するとI2SライブラリのI2S定数定義が変更されているので、コンパイルエラーとなります。
  • I2Sポートはi2s_pin_config_tで設定しています。
  • I2Sドライバはi2s_config_t で設定しています。お持ちのDACに応じて設定を変更してください。
  • コーデックはSBCのみです。AACと比べて音質の差は感じられませんでした。
  • ジョン・ウィリアムズ指揮のオーケストラ映像をiPhoneで視聴した際に、指揮振り(映像)と音楽のタイミング差を感じました。(厳密に測定してませんが、体感で約0.5秒未満ぐらい遅延を感じます)リップシンクができればよいのですが。どうすればリップシンクできるのか、どなたか教えて下さい。🙏
  • 一曲送る、一曲戻る、早送り、巻き戻し、一時停止、停止、再生、一曲の再生が終了して次の曲の再生が開始する時などに、「プツッ」というクリックノイズが聞こえることがあるかもしれません。その際は、ESP32-A2DPライブラリのDMAバッファを適切なタイミングでクリアしてください。

さいごに

私はESP-IDF版のa2dp_sinkサンプル(C言語版)をいろいろ調べながら、動作するところまでこぎつけました。
その時に、「このC言語ドライバをクラス化したらいいのでは?」とは思っていましたが、その思いつきを完全に忘れてしまいました。
その後2年ぐらい経って、何気なくGitHubを調べてみたら、pschatzmannさんがArduino環境向けにクラスを作成してくれていて驚きました。私は彼と同じことを考えていたけれども、私には実装できなかったと思います。
@pschatzmannさん、素敵なライブラリを作ってくれてありがとう。あなたの貢献に感謝します。👍

See Also

(了)

  1. sink:吸収源≒受信 source:供給源≒送信

14
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?