In my Arduino ESP32-A2DP library I am providing some very simple examples that show how to transform the ESP32 into a A2DP source and transmit sound to a Bluetooth Sink (e.g. some Bluetooth Speakers).
I had quite a few questions on how to do this with files, microphones and I2S as input. So I started a small “glue” project which provides some additional audio tools. I plan to provide plenty of additional examples and some more advances scenarios.
In my last blog, I showed how to read audio data from I2S using a digital microphone. In this blog I provide the RAW audio data directly from a file on a SD card! In some future posts, I will demonstrate how to process different file formats.
Streaming from Files on a SD card
The Sketch
Here is the Arduino Sketch that you can use with an I2S audio source:
#include "BluetoothA2DPSource.h"
#include <SPI.h>
#include <SD.h>
BluetoothA2DPSource a2dp_source;
File sound_file;
const char* file_name = "/audio.raw";
const int sd_ss_pin = 5;
const int frame_size_bytes = sizeof(int16_t) * 2;
// callback used by A2DP to provide the sound data
int32_t get_sound_data(Channels* data, int32_t len) {
// the data in the file must be in int16 with 2 channels
size_t result_len_bytes = sound_file.read((uint8_t*)data, len * frame_size_bytes );
// result is in number of frames
int32_t result_len = result_len_bytes / frame_size_bytes;
ESP_LOGD("get_sound_data", "%d -> %d",len );
return result_len;
}
// Arduino Setup
void setup(void) {
Serial.begin(115200);
// Setup SD and open file
SD.begin(sd_ss_pin);
sound_file = SD.open(file_name, FILE_READ);
// start the bluetooth
Serial.println("starting A2DP...");
a2dp_source.start("MyMusic", get_sound_data);
}
// Arduino loop - repeated processing
void loop() {
}
The get_sound_data() callback is quite straight forward: We are directly reading the raw audio file from the SD card. The audio file must be available using 16 bit integers with 2 channels. In the callback we just need to translate between number of frames and bytes!
Audacity might help you to create the right file format: export with the file name audio.raw as RAW signed 16 bit PCM and copy it to the SD card. In my example I was using the file audio.raw.
The Device
The SD module is connected with the help of the SPI bus
Pins
SD | ESP32 |
---|---|
VCC | 5V |
GND | GND |
CS | CS GP5 |
SCK | SCK GP18 |
MOSI | MOSI GP23 |
MISO | MISO GP19 |
12 Comments
Sebastian · 8. March 2023 at 23:43
Hey there.
I´m having some issues trying to connect a ESP32 board with some earbuds, have you try doing that?
pschatzmann · 9. March 2023 at 7:46
For me this is working very reliably. If you have problems please follow the recommendations that are given in the Wiki of the AudioTools project: https://github.com/pschatzmann/arduino-audio-tools/wiki/It's-not-working.
Bill · 16. December 2022 at 4:09
hello:
Cannot compile. I have the following message can you help? Thanks:
BluetoothSD:13:24: error: ‘Channels’ was not declared in this scope
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~~~~~
D:\temp\BluetoothSD\BluetoothSD.ino:13:24: note: suggested alternative: ‘ChannelInfo’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~~~~~
ChannelInfo
BluetoothSD:13:34: error: ‘data’ was not declared in this scope
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~
D:\temp\BluetoothSD\BluetoothSD.ino:13:34: note: suggested alternative: ‘atan’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~
atan
BluetoothSD:13:48: error: expected primary-expression before ‘len’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~
BluetoothSD:13:51: error: expression list treated as compound expression in initializer [-fpermissive]
int32_t get_sound_data(Channels* data, int32_t len) {
^
BluetoothSD:13:24: error: redefinition of ‘int32_t get_sound_data’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~~~~~
D:\temp\BluetoothSD\BluetoothSD.ino:13:9: note: ‘int32_t get_sound_data’ previously defined here
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~~~~~~~~~~~
BluetoothSD:13:24: error: ‘Channels’ was not declared in this scope
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~~~~~
D:\temp\BluetoothSD\BluetoothSD.ino:13:24: note: suggested alternative: ‘ChannelInfo’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~~~~~
ChannelInfo
BluetoothSD:13:34: error: ‘data’ was not declared in this scope
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~
D:\temp\BluetoothSD\BluetoothSD.ino:13:34: note: suggested alternative: ‘atan’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~~
atan
BluetoothSD:13:48: error: expected primary-expression before ‘len’
int32_t get_sound_data(Channels* data, int32_t len) {
^~~
D:\temp\BluetoothSD\BluetoothSD.ino: In function ‘void setup()’:
BluetoothSD:32:32: error: invalid conversion from ‘int32_t’ {aka ‘int’} to ‘music_data_channels_cb_t’ {aka ‘int ()(Frame, int)’} [-fpermissive]
a2dp_source.start(“MyMusic”, get_sound_data);
^~~~~~~~~~~~~~
In file included from D:\temp\BluetoothSD\BluetoothSD.ino:2:
C:\Users\chris\Documents\Arduino\libraries\libraries\ESP32-A2DP-main\src/BluetoothA2DPSource.h:95:67: note: initializing argument 2 of ‘virtual void BluetoothA2DPSource::start(const char*, music_data_channels_cb_t)’
virtual void start(const char* name, music_data_channels_cb_t callback = NULL);
^
Plusieurs bibliothèque trouvées pour “SD.h”
Utilisé : C:\Users\chris\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.5\libraries\SD
Non utilisé : C:\Program Files (x86)\Arduino\libraries\SD
exit status 1
‘Channels’ was not declared in this scope
pschatzmann · 16. December 2022 at 4:17
I don’t understand why you don’t just use the example that is provided in Arduino or the indicated in the link to the updated code on Github!
As stated in my last answer the Blog represents only what was valid at the time of posting!
Anonymous · 15. December 2022 at 8:17
Cannot compile using ESP32-A2DP V1.7.1
Error msg: ‘Channels’ was not declared in this scope
Do you have a suggestion?
Thanks
Bill
billglass38@gmail.com
pschatzmann · 15. December 2022 at 9:40
Yes, this is a pretty old blog which documents how things were at that point of time!
Just use the updated examples that you can find in Github or in the Arduino examples…
Bill · 16. December 2022 at 5:17
I don’t use Github but I seem to have your latest ESP32-A2DP library. In my Arduino examples I don’t have streaming from SD card. Only from PROGMEM (piano) or sine generator. They both work fine.
pschatzmann · 16. December 2022 at 8:58
Just click on the link in the blog under the Github title and you will see the updated example.
It’s as simple a that…
ps. I committed some correction to the A2DP Library that will accept even this obsolete syntax…
Bill · 16. December 2022 at 12:22
Thank you very much! I will try it.
Bill Glass · 29. December 2022 at 10:38
yes it works!! thank you
Anonymous · 26. November 2021 at 16:41
Hi there, how do you play another file after playing the first? Running a2dp_source.start again gives an error
pschatzmann · 26. November 2021 at 16:49
I suggest that you have a look at the AudioPlayer: https://github.com/pschatzmann/arduino-audio-tools/wiki/The-Audio-Player-Class