The ESP32 provides a Bluetooth A2DP API that receives sound data e.g. from your Mobile Phone and makes it available via a callback method. The output is a PCM data stream decoded from SBC format. The documentation can be found here. 

I2S is an electrical serial bus interface standard used for connecting digital audio devices together. It is used to communicate PCM audio data between integrated circuits in an electronic device.

So we can just feed the input from Bluetooth to the I2S output: An example for this A2DP Data Sink from Expressive can be found on Github.

Unfortunately this example did not make me happy so I decided to convert it into a simple C++ class that is very easy to use from an Arduino Software IDE.

A Simple I2S Example

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

#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {
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:
– bck_io_num => GPIO 26,
– ws_io_num => GPIO 25,
– data_out_num => GPIO 22

These need to be connected to an external external DAC. You can define your own pins easily by calling the set_pin_config method.

Output to the Internal DAC

You can also send the output directly to the internal DAC of the ESP32 by providing the corresponding i2s_config:

#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {
static const i2s_config_t i2s_config = {
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
.sample_rate = 44100, // corrected by info from bluetooth
.bits_per_sample = (i2s_bits_per_sample_t) 16, /* the DAC module will only take the 8bits from MSB */
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 8,
.dma_buf_len = 64,
.use_apll = false
};

a2dp_sink.set_i2s_config(i2s_config);
a2dp_sink.start("MyMusic");

}

void loop() {
}

The output goes now to the DAC pins G26/G27.

Installation

The library with the examples can be found on Github

Conclusion

This is the perfect functionality to revive my old Technics SU-Z2 Amplifier: I can use it now by streaming my music from my Android Phone or IPAD.

Initially I wanted to use the ESP32 also to stream some Internet Radios via WIFI using http – but in the end it turned out to be much simpler to use one of the many Music Apps from a Mobile Phone  with Bluetooth as output!

The only task left now is to build a casing…

Next Steps

To implement some more scenarios please have a look at the examples of my audio tools library or my related Blogs


36 Comments

Raj Kisan S · 19. November 2023 at 10:38

I want to create a bluetooth receiver sink source which will also plays connected, disconnected sounds while connecting and disconnecting
I have tried “XT_DAC_Audio.h” But cannot play the music in the same sink after starting the sink
Help me …

AndersW · 8. January 2023 at 11:41

Sounds like a great project to do!

I was wondering if this would be possible to be used as a music streamer for a car head unit, which has USB input and MP3 file playback possibility?
I’m a noob with this, but then it shouldn’t need the DAC, and can just convert it into a “MP3 signal” directly to the the input of the head unit? Or am I grabbing at straws here perhaps?

Han · 17. October 2022 at 17:56

Thanks a lot for your excellent work. I am trying the basic a2dp receiver example. However, I came across multiple errors such as below:

c:\Projects\Arduino\libraries\ESP32-A2DP-main\src\BluetoothA2DPSink.cpp:210:31: error: ‘I2S_MODE_DAC_BUILT_IN’ was not declared in this scope
if (i2s_config.mode & I2S_MODE_DAC_BUILT_IN) {
^~~~~~~~~~~~~~~~~~~~~

The sketch compiles fine when using ESP32 Dev Module, but these errors appeared when I’m using the ESP32 S3 Dev Module board. Could you please point me to the right direction ? Am I missing some files?

Thank you.

Han

    pschatzmann · 19. October 2022 at 20:41

    A2DP is part of Bluetooth Classic. So the explanation is simple: The S3 only supports BLE!

Syed Zeeshan · 1. May 2022 at 10:24

What functions should I call to pause the music in code. I want to pause the music in code if a condition is satisfied then continue after sometime. I don’t want to control it manually through mobile phone.

So what functions should I call for pausing and continuing?

    pschatzmann · 1. May 2022 at 10:30

    I suggest to read the Readme of the Project. Just search for AVRC Commands…

      Syed Zeeshan · 2. May 2022 at 1:40

      Thank you so much!
      I noticed that you made some recent changes to your GitHub code. I searched for these functions before in old code. I couldn’t find it. I can see these functions now in new code. Did you make changes after reading my comment?
      If yes, then thank you so much for being helpful

LEDSchlucker · 27. November 2021 at 19:28

Hello, I’ve tried the Example with the internal DAC, unfortunately the output sounds really strange, you don’t even recognize the music, you only hear the rhythm and volume of the music. Any idea why?

I also tried it with a signal generator app on my phone to have only one tone at a time, and then it sounded similar strange, except for some frequencies, where it sounded ok except for some clipping sound. The frequencies it was nearly ok were: 86Hz 172Hz , around 310 Hz etc.

I tried it with a ESP32-WROOM-32 and a ESP32-WROOM-32D.

    pschatzmann · 27. November 2021 at 19:43

    I am not sure. I was never happy with the audio quality using the internal DAC and I am just using this for testing by connecting some earphones.
    Maybe it helps if you turn down the volume (e.g with set_volume(10);)
    What output device are you using ?

Lolis · 10. August 2021 at 19:42

Hey, thanks a lot for sharing your work! This has been working flawlessly for months!!
Love it!

Jesse · 14. May 2021 at 16:42

Hey!!
Thanks for the library and your work. Everything works and the play, next functions works flawlessly.

Just a quick question:

In the espressif documentation of the avrc there is a function called esp_avrc_ct_send_set_absolute_volume_cmd. I want to use this function cause the passthrough commands for volume up and down are not working apparently with Iphone.

I tried making a function just like the play function, instead with the absolute volume function instead of the execute_avrc_command. But the compiler gives the error that the function is not defined in the scope.

I mostly program in C, so I’m not familiar with the C/Arduino hybrid programming. Am I missing something?

Thank you!

    pschatzmann · 14. May 2021 at 16:50

    As far as I know, the problem is that Arduino is still using an old Espressif release where this function is not available.
    We need to wait until they decide to include a more recent version.
    By the way, others have also tried and have failed to implement volume control…

Nick · 18. April 2021 at 2:13

Hey!
Thank you very much for your work!
Unfortunately I can’t get it running. I tried the example codes on two DOIT ESPRESSIF DEVKIT V1s and they constantly reebot and I get this data back:

Guru Meditation Error: Core 1 panic’ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400dcebc: 0c003c06 79150424 a505ad91
Core 1 register dump:
PC : 0x400dcec1 PS : 0x00060530 A0 : 0x800d9d5e A1 : 0x3ffc8450
A2 : 0x3ffc5b6c A3 : 0x3ffc64e8 A4 : 0x00000002 A5 : 0xffffffe8
A6 : 0x40084080 A7 : 0x3ffc64e8 A8 : 0x800dceb9 A9 : 0x3ffc8420
A10 : 0x400041a4 A11 : 0x3ffae0c4 A12 : 0x3ffc8427 A13 : 0x3ffc8427
A14 : 0x3ffc5b6c A15 : 0x00000000 SAR : 0x00000000 EXCCAUSE: 0x00000000
EXCVADDR: 0x00000000 LBEG : 0x400d8384 LEND : 0x400d83bc LCOUNT : 0x00000000

Backtrace: 0x400dcec1:0x3ffc8450 0x400d9d5b:0x3ffc84a0 0x400d9fcc:0x3ffc8500 0x400daea6:0x3ffc8520 0x400db294:0x3ffc8540 0x400d69af:0x3ffc8600 0x400d6c42:0x3ffc8630 0x400f58e6:0x3ffc8660 0x400d21ba:0x3ffc8680 0x400d1af7:0x3ffc86c0 0x400d1ab3:0x3ffc86e0 0x400d19ee:0x3ffc8700 0x400d294f:0x3ffc8730 0x4008e1f1:0x3ffc8750

Rebooting…

I erased the flash completely with esptool and tried again, no change.
Do you know what’s going on?

    Nick · 18. April 2021 at 2:48

    Nevermind!
    Just adding a big capacitor wasn’t enough, i had to connect an extra power supply while the esp was connected via USB. It had nothing to do with the library, sorry!
    Now that it works: Great, thank you!

      pschatzmann · 18. April 2021 at 12:42

      That’s strange – I have used this sketch with quite a few cheap Chinese ESP32 boards and I never had any issues while using the USB power supply …

Yang · 26. February 2021 at 5:40

Hello
Thank you for posting a good example.

I’m going to make a speaker using BLE. I don’t know much about esp32 and ble yet, but is this example using the ble function of esp32? Or do you use classic Bluetooth?

Thank you.

olivenhein · 28. January 2021 at 23:05

Thanks for sharing! I use it with a UDA1334A I2S Stereo DAC for AUX output and it works super smooth!

Evgeniy Marchenko · 17. January 2021 at 21:23

Is it real to set up a PIN code to the Bluetooth connection?

Steve · 6. January 2021 at 8:22

This is great! I had to add a 1k pull down resistor on the WSel pin, any idea why it would need it? Don’t have a scope unfortunately so can’t see what’s happening. Using LOLIN32 Pro and Sparkfun UDA1334A

    pschatzmann · 7. January 2021 at 9:47

    I am sorry, I don’t know either!

Michael Telatynski · 22. October 2020 at 17:44

How was the audio quality out of the internal DACs. Any complaints for music?

kjs · 9. October 2020 at 10:01

This is amazing, thank you so much! I’m looking at using this in a project I am working on, would you happen to know how I can add getting song data (artist, title, etc.) and sending instructions (volume up/down, play, etc.) to this?

Gertjan · 2. October 2020 at 13:30

Is it also possible to get the meta data of the audio stream? Like artist, title, etc.? That would be awesome.

    pschatzmann · 1. May 2022 at 10:34

    It is documented in the Readme of the project. Just search for Support for Metadata

Cemoi · 15. September 2020 at 17:24

A thousand thank you for this very good job.
Merci mille fois pour ce merveilleux travail.
I love.

i2c · 11. August 2020 at 18:00

Great documentation and excellent work! I’ve been looking for this exact esp-idf code to use as an Arduino library in my projects. I would have to use both Arduino and the entire ESP-IDF if I wanted to add this bluetooth receiving functionality, but nope! You’ve done it, thanks Phil!

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *