esp32-a2dp Library

repository·main·Indexed 25 days ago

https://github.com/pschatzmann/esp32-a2dp

A Bluetooth A2DP library for Arduino and ESP-IDF that enables the ESP32 to function as either an audio receiver (Sink) or an audio sender (Source). It decodes SBC format into a PCM data stream (44.1kHz, 16-bit, two-channel) and supports AVRCP metadata and notifications. The library integrates with the arduino-audio-tools library for output API management and supports adding AAC or other codecs via decoders.

Tokens
1.8K
Snippets
7
Records
9
Agent score
33%

What's inside esp32-a2dp

  1. Install the ESP32-A2DP library

    main

    For Arduino users, you can install the library by downloading it as a ZIP file and using the 'Include Library -> ZIP Library' feature in the Arduino IDE. Alternatively, you can clone the repository directly into your Arduino libraries folder using Git.

    Note: If you intend to use the provided examples, you must also install the arduino-audio-tools library.

    cd  ~/Documents/Arduino/libraries
    git clone https://github.com/pschatzmann/ESP32-A2DP.git
    git clone https://github.com/pschatzmann/arduino-audio-tools.git
  2. Implement an A2DP Sink (Music Receiver)

    main

    Use the BluetoothA2DPSink class to build a Bluetooth speaker or receiver. The library decodes SBC format into a PCM data stream (44.1kHz, 16-bit, two-channel).

    It is highly recommended to use the AudioTools library for a version-independent output API. You can also output to any class inheriting from Arduino Print or use the internal ESP32 I2S API.

    #include "AudioTools.h"
    #include "BluetoothA2DPSink.h"
    
    I2SStream i2s;
    BluetoothA2DPSink a2dp_sink(i2s);
    
    void setup() {
        Serial.begin(115200);
        a2dp_sink.start("MyMusic");
    }
    
    void loop() {
    }
  3. Implement an A2DP Source (Music Sender)

    main

    Use BluetoothA2DPSource to stream audio from the ESP32 to a Bluetooth speaker. You must provide a callback function that supplies the PCM data (44.1kHz, 16-bit, two-channel).

    • set_data_callback(callback): Provides data in bytes.
    • set_data_callback_in_frames(callback): Provides data in frames.
    • start(name): Connects to a specific Bluetooth device name. You can also pass a std::vector<const char*> of names to attempt connection to multiple devices in order.
    #include "BluetoothA2DPSource.h"
    
    BluetoothA2DPSource a2dp_source;
    
    int32_t get_sound_data(uint8_t *data, int32_t byteCount) {
        // Generate or fetch PCM data here
        return byteCount; // Return effective length in bytes
    }
    
    void setup() {
      a2dp_source.set_data_callback(get_sound_data);
      a2dp_source.start("MyMusic");  
    }
    
    void loop() {}
  4. Configure I2S Pins for A2DP Sink

    main

    When using I2SStream from the AudioTools library, you can define custom pins before calling start().

    Default Pins (New API):

    • bck_io_num = 14
    • ws_io_num = 15
    • data_out_num = 22

    Note: These default pins differ from the legacy API.

    #include "AudioTools.h"
    #include "BluetoothA2DPSink.h"
    
    I2SStream i2s;
    BluetoothA2DPSink a2dp_sink(i2s);
    
    void setup() {
        Serial.begin(115200);
        auto cfg = i2s.defaultConfig();
        cfg.pin_bck = 14;
        cfg.pin_ws = 15;
        cfg.pin_data = 22;
        i2s.begin(cfg);
    
        a2dp_sink.start("MyMusic");
    }
    
    void loop() {
    }
  5. Output Audio to the ESP32 Internal DAC

    main

    To bypass external I2S DACs and use the ESP32's built-in DAC, use AnalogAudioStream from the AudioTools library. The output will be sent to GPIO25 (Channel 1) and GPIO26 (Channel 2).

    #include "AudioTools.h"
    #include "BluetoothA2DPSink.h"
    
    AnalogAudioStream out;
    BluetoothA2DPSink a2dp_sink(out);
    
    void setup() {
        Serial.begin(115200);
        a2dp_sink.start("MyMusic");
    }
    
    void loop() {
    }
  6. Access Sink Data Stream via Callbacks

    main

    You can intercept the PCM audio stream using callbacks. The data is typically 44.1kHz, two-channel, 16-bit.

    • Use set_on_data_received(callback) to be notified when a packet arrives.
    • Use set_stream_reader(callback) to access the raw byte buffer. You can pass false as a second argument to set_stream_reader to disable I2S output while reading data.
    // To be notified of arrival:
    a2dp_sink.set_on_data_received(data_received_callback);
    
    void data_received_callback() {
      Serial.println("Data packet received");
    }
    
    // To access the actual PCM data:
    a2dp_sink.set_stream_reader(read_data_stream);
    
    void read_data_stream(const uint8_t *data, uint32_t length) {
      int16_t *samples = (int16_t*) data;
      uint32_t sample_count = length/2;
      // Process samples...
    }
  7. Handle AVRC Metadata and Notifications

    main

    The library supports retrieving metadata (title, artist, etc.) and playback notifications via AVRCP.

    • Metadata: Register a callback with set_avrc_metadata_callback. Use set_avrc_metadata_attribute_mask to filter which attributes are received (e.g., ESP_AVRC_MD_ATTR_TITLE).
    • Notifications: Use set_avrc_rn_playstatus_callback, set_avrc_rn_play_pos_callback, or set_avrc_rn_track_change_callback to monitor playback status, position, and track changes.
    void avrc_metadata_callback(uint8_t data1, const uint8_t *data2) {
      Serial.printf("AVRC metadata rsp: attribute id 0x%x, %s\n", data1, data2);
    }
    
    a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
    // Optional: filter attributes
    a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_PLAYING_TIME);
    
    a2dp_sink.start("BT");