Skip to content

A2DP Sink

pschatzmann edited this page Oct 3, 2026 · 1 revision

This can be used e.g. to build your own Bluetooth Speaker.

A Simple I2S Example using Default Pins

Here is the simplest example which just uses the proper default settings:

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

void setup() {
    Serial.begin(115200);
    a2dp_sink.start("MyMusic");
}

void loop() {
}

This creates a new Bluetooth device with the name “MyMusic” and the output will be sent to the following default I2S pins which need to be connected to an external DAC:

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

Please note that these default pins have changed compared to the legacy API!

Defining Pins

You can define your own pins easily before the start.

#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() {
}

Output Using the ESP32 I2S API

You can also use the Arduino ESP32 I2S API: You do not need to install any additional library for this.

#include "ESP_I2S.h"
#include "BluetoothA2DPSink.h"

const uint8_t I2S_SCK = 5;       /* Audio data bit clock */
const uint8_t I2S_WS = 25;       /* Audio data left and right clock */
const uint8_t I2S_SDOUT = 26;    /* ESP32 audio data output (to speakers) */
I2SClass i2s;

BluetoothA2DPSink a2dp_sink(i2s);

void setup() {
    Serial.begin(115200);

    i2s.setPins(I2S_SCK, I2S_WS, I2S_SDOUT);
    if (!i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
      Serial.println("Failed to initialize I2S!");
      while (1); // do nothing
    }

    a2dp_sink.start("MyMusic");
}
void loop() {}

Please note, that this API also depends on the installed version: The example above is for ESP32 >= 3.0.0!

Output to the Internal DAC

You can also send the output directly to the internal DAC of the ESP32 by using the AnalogAudioStream from the AudioTools:

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink(out);

void setup() {
    Serial.begin(115200);
    a2dp_sink.start("MyMusic");
}

void loop() {
}

The output goes now to the DAC pins GPIO25 (Channel 1) and GPIO26 (Channel 2).

Accessing the Sink Data Stream with Callbacks

You can be notified when a packet is received. The API is using PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data.

// In the setup function:
a2dp_sink.set_on_data_received(data_received_callback);


// Then somewhere in your sketch:
void data_received_callback() {
  Serial.println("Data packet received");
}

Or you can access the packet:

// In the setup function:
a2dp_sink.set_stream_reader(read_data_stream);

// Then somewhere in your sketch:
void read_data_stream(const uint8_t *data, uint32_t length)
{
  int16_t *samples = (int16_t*) data;
  uint32_t sample_count = length/2;
  // Do something with the data packet
}

In the a2dp_sink.set_stream_reader() method you can provide an optional parameter that defines if you want the output to I2S to be active or deactive - So you can use this method to e.g. to switch off I2S just by calling a2dp_sink.set_stream_reader(read_data_stream, false)

Metadata and Notifications

You can register callbacks to receive the AVRC metadata (e.g. title and artist) and notifications (e.g. playback status and position) of the connected source. See Metadata & Notifications.

AVRC Commands

You can 'control' the connected source (e.g. your phone) with play(), pause(), stop(), next(), previous(), fast_forward() and rewind(), and synchronize the volume. See AVRC: Controlling your Phone.

Further Information

Clone this wiki locally