Repository navigation
Communication A2DP
A2DP is the Bluetooth profile for high quality audio: it lets your microcontroller play music from a mobile phone (A2DP sink - it acts as a Bluetooth speaker), or send audio to Bluetooth speakers and headphones (A2DP source). It is based on my ESP32-A2DP library - install it. See Communication for the general considerations.
A2DP needs Bluetooth Classic, which only the original ESP32 provides: the ESP32-S2, -S3, -C3, -C6, -H2 and -P4 support Bluetooth LE only (or no Bluetooth at all) and can't do A2DP.
-
The callbacks of ESP32-A2DP (recommended): the A2DP library calls your function with the received data, or asks your function for the data to send. This is the most efficient way - all examples starting with
basic-use it. -
A2DPStream (
AudioTools/Communication/A2DPStream.h): wraps ESP32-A2DP as anAudioStream, so you can use it like any other source or sink withStreamCopy. This is convenient, but less efficient, because it needs a big buffer between the callbacks and the stream. Only one instance is supported.
The audio format of A2DP is fixed: 16 bit stereo at 44100 Hz (a sink may receive 48000 Hz, depending on the phone).
From basic-a2dp-i2s.ino: the received data is written to I2S in the callback:
#include "AudioTools.h"
#include "BluetoothA2DPSink.h" // https://github.com/pschatzmann/ESP32-A2DP
BluetoothA2DPSink a2dp_sink;
I2SStream i2s;
void read_data_stream(const uint8_t *data, uint32_t length) {
i2s.write(data, length);
}
void setup() {
a2dp_sink.set_stream_reader(read_data_stream, false);
a2dp_sink.start("a2dp-i2s"); // the name that the phone shows
auto cfg = i2s.defaultConfig();
cfg.pin_data = 23;
cfg.sample_rate = a2dp_sink.sample_rate();
cfg.channels = 2;
cfg.bits_per_sample = 16;
i2s.begin(cfg);
}
void loop() { delay(100); }ESP32-A2DP itself also provides an I2SStream based sink (BluetoothA2DPSink a2dp_sink(i2s)) - see its documentation.
From streams-generator-a2dp.ino, using A2DPStream as output:
#include "AudioTools.h"
#include "AudioTools/Communication/A2DPStream.h"
AudioInfo info(44100, 2, 16);
SineGenerator<int16_t> sineWave(32000);
GeneratedSoundStream<int16_t> in(sineWave);
A2DPStream out;
StreamCopy copier(out, in);
void setup(void) {
sineWave.setFrequency(N_B4);
auto cfg = in.defaultConfig();
cfg.copyFrom(info);
in.addNotifyAudioChange(out);
in.begin(cfg);
auto cfgA2DP = out.defaultConfig(TX_MODE);
cfgA2DP.name = "LEXON MINO L"; // the name of your speaker
out.begin(cfgA2DP); // waits until the speaker is connected
out.setVolume(0.3);
}
void loop() { copier.copy(); }The A2DP source pulls the data at its own rate, so a fast source (generator, file) is automatically throttled.
| Field | Description |
|---|---|
mode |
TX_MODE (source: sends to a speaker) or RX_MODE (sink: receives from a phone) |
name |
in TX_MODE the name of the speaker to connect to, in RX_MODE the name shown to the phone |
auto_reconnect |
reconnect automatically if the connection is lost |
wait_for_connection |
begin() waits until a device is connected (default true) |
buffer_size |
buffer between the A2DP callbacks and the stream |
startup_logic, startup_nodata, silence_on_nodata
|
what to do before the buffer is filled / when there is no data |
tx_write_timeout_ms |
write timeout in TX_MODE (-1: blocking) |
The a2dp examples show the receiving side with I2S, AudioKit, an equalizer, FFT and LED displays, a mixer and SPDIF, and the sending side from an ADC, a microphone, a synthesizer, MP3 files and the SD card.
- Memory: Bluetooth Classic needs a lot of RAM - WiFi and A2DP at the same time are possible, but tight.
-
Sample rate: in RX_MODE check
a2dp_sink.sample_rate()(or use the AudioInfo notifications ofA2DPStream), since some phones send 48000 Hz. - See the ESP32-A2DP Wiki for the details of the A2DP library.