Control the playback and volume of an audio player via Bluetooth
Devices and components
Seeeduino Xiao BLE Meaning
Closed stereo speaker - 3 W 8 Ω
SD card module
I2S 3W class D amplifier - MAX98357A
iPhone
Software and tools
nrf Connect
Project description
BCLK -> Arduino D1 (p0.03)
LRCK -> Arduino D3 (p0.29)
DIN -> Arduino D5 (p0.05)
The above pins can be defined in your sketch by specifying the GPIO number and/or port before calling the start() function:
To use D7 (p1.12) for LRCK, users would call aaAudio.I2S_PIN_LRCK = 12; and aaAudio.I2S_PORT_LRCK = 1;
SD -> Arduino D6
The CS pin of the SD card is connected to the Arduino D2 in the sample code provided. The other pins are connected as shown
Install the Auto Analog Audio library using the Arduino Library Manager
Go to samples -> Auto Analog Audio > Platforms -> nRF52 -> nRF52_BLE
Choose your example based on the Board Support Package you are using, mbed or non-mbed.
You can also use the code attached to this project
Install the nRF Connect app on your mobile device
Search for devices: Search for “BLE Audio Player” and connect to it.
Once connected you should see “Common Audio”
Also “Audio Input Type” – Here you can enter a UTF8 string, for example: mywavfile.wav to start playback.
Also "Audio Input Control Point" - Here you can enter an unsigned value to control the volume, 1 for lowest volume, 10 for maximum volume, 11 if you like rock.
Automatic analog audio library
BLE audio controller - Mbed
1/* Arduino BLE controlled Audio Player for nRF52
2 *
3 * This is an example of me playing around with BLE control and different
4 * services/characteristics to test the AutoAnalogAudio library.
5 *
6 * Requirements:
7 * 1. nRF52 Device (Tested on nRF52840)
8* 2. SD Card with WAV files: 8-bit, 16-24kHz, Mono
9 * 3. I2S or Analog Amplifier + Speaker connected
10 * 4. Mobile device or 'other' with nRF Connect installed
11 *
12 * Connect via nRF Connect App:
13 * 1. Device should come up as BLE Audio Player
14 * 2. You should see:
15 * a: Common Audio
16* b: Audio Input Type:
17 Send a UTF-8 String to play a file: myfileDirectory/myfilename.wav
18 * c: Audio Input Control Point:
19 Send an Unsigned value between 0-10 to set the volume low-high
20 */
21
22
23#include <SPI.h>
24#include <SD.h>
25#include <ArduinoBLE.h>
26#include <AutoAnalogAudio.h>
27
28AutoAnalog aaAudio;
29
30/************** USER CONFIG ***********/
31// File to play on startup
32const char* audioFilename = "far8b16k.wav"; // 8-bit @ 24kHz audio is the max over SD card while BLE is running
33uint8_t SD_CS_PIN = 2; // Set this to your CS pin for the SD card/module
34#define USE_I2S 1 // Set this to 0 for analog (PWM) audio output instead of I2S
35
36/*********************************************************/
37/* Tested with MAX98357A I2S breakout
38/* BCLK connected to Arduino D1 (p0.03)
39/* LRCK connected to Arduino D3 (p0.29)
40/* DIN connected to Arduino D5 (p0.05)
41/* SD connected to Arduino D6 (p1.11)
42/*********************************************************/
43
44#define FILENAME_BUFFER_LENGTH 64
45char songName[FILENAME_BUFFER_LENGTH];
46float volumeControl = 0.2;
47#define AUDIO_BUFFER_SIZE 1600
48
49BLEService audioService("1853"); // BLE LED Service
50
51// BLE Audio Charactaristic
52BLECharacteristic audioDataCharacteristic("2b79", BLERead | BLEWrite, FILENAME_BUFFER_LENGTH);
53BLEByteCharacteristic audioVolumeCharactaristic("2b7b", BLERead | BLEWrite);
54
55void setup() {
56 Serial.begin(115200);
57 while (!Serial) delay(10);
58
59 aaAudio.begin(0, 1, USE_I2S); //Setup aaAudio using DAC and I2S or PWM
60
61 // BLE initialization
62 if (!BLE.begin()) {
63 Serial.println("Starting BLE failed!");
64 while (1) {};
65 }
66
67 BLE.setLocalName("BLE Audio Player");
68 BLE.setAdvertisedService(audioService);
69
70 audioService.addCharacteristic(audioDataCharacteristic);
71 audioService.addCharacteristic(audioVolumeCharactaristic);
72 BLE.addService(audioService);
73
74 BLE.advertise();
75 Serial.println("BLE Peripheral is now advertising");
76
77 Serial.print("Init SD card...");
78 if (!SD.begin(SD_CS_PIN)) {
79 Serial.println("init failed!");
80 return;
81 }
82 Serial.println("SD init ok");
83 pinMode(6, OUTPUT); //Connected to SD pin of MAX98357A
84 digitalWrite(6, HIGH);
85
86 playAudio(audioFilename);
87}
88
89void loop() {
90
91 BLEDevice central = BLE.central();
92
93 if (central) {
94
95 if (central.connected()) {
96 if (audioDataCharacteristic.written()) {
97 memset(songName, 0, sizeof(songName));
98 audioDataCharacteristic.readValue((uint8_t*)songName, FILENAME_BUFFER_LENGTH);
99 playAudio(songName);
100 Serial.println(songName);
101 }
102 if (audioVolumeCharactaristic.written()) {
103 uint8_t vol;
104 audioVolumeCharactaristic.readValue(vol);
105 volumeControl = vol / 10.0;
106 Serial.print("BLE Set Volume: ");
107 Serial.println(volumeControl);
108 }
109 }
110 }
111
112 loadBuffer();
113
114 // Control via Serial for testing
115 if (Serial.available()) {
116 char c = Serial.read();
117 if (c == '=') {
118 volumeControl += 0.1;
119 } else if (c == '-') {
120 volumeControl -= 0.1;
121 volumeControl = max(0.0, volumeControl);
122 } else if (c == 'p') {
123 playAudio("brick/brick24.wav");
124 }
125 Serial.println(volumeControl);
126 }
127}
128
129/*********************************************************/
130/* A simple function to handle playing audio files
131/*********************************************************/
132
133File myFile;
134
135void playAudio(const char* audioFile) {
136
137 if (myFile) {
138 myFile.close();
139 }
140 //Open the designated file
141 myFile = SD.open(audioFile);
142
143 myFile.seek(22);
144 uint16_t var;
145 uint32_t var2;
146 myFile.read(&var, 2); // Get channels (Stereo or Mono)
147 myFile.read(&var2, 4); // Get Sample Rate
148 aaAudio.setSampleRate(var2, var - 1);
149
150 myFile.seek(34);
151 myFile.read(&var, 2); // Get Bits Per Sample
152 aaAudio.dacBitsPerSample = var;
153
154 myFile.seek(44); //Skip past the WAV header
155}
156
157void loadBuffer() {
158
159 if (myFile.available()) {
160
161 if (aaAudio.dacBitsPerSample == 8) {
162 myFile.read(aaAudio.dacBuffer, AUDIO_BUFFER_SIZE);
163 for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE; i++) {
164 aaAudio.dacBuffer[i] *= volumeControl;
165 }
166 aaAudio.feedDAC(0, AUDIO_BUFFER_SIZE);
167 } else {
168 myFile.read(aaAudio.dacBuffer16, AUDIO_BUFFER_SIZE);
169 for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE / 2; i++) {
170 int16_t sample = aaAudio.dacBuffer16[i];
171 sample *= volumeControl;
172 aaAudio.dacBuffer16[i] = (uint16_t)sample;
173 }
174 aaAudio.feedDAC(0, AUDIO_BUFFER_SIZE / 2);
175 }
176
177 } else {
178 myFile.seek(44);
179 }
180}
BLE audio controller - nRF52
1/* Arduino BLE controlled Audio Player for nRF52
2 *
3 * This is an example of me playing around with BLE control and different
4 * services/characteristics to test the AutoAnalogAudio library.
5 *
6 * Requirements:
7 * 1. nRF52 Device (Tested on nRF52840)
8* 2. SD Card with WAV files: 8-bit, 16-24kHz, Mono
9 * 3. I2S or Analog Amplifier + Speaker connected
10 * 4. Mobile device or 'other' with nRF Connect installed
11 *
12 * Connect via nRF Connect App:
13 * 1. Device should come up as BLE Audio Player
14 * 2. You should see:
15 * a: Common Audio
16* b: Audio Input Type:
17 Send a UTF-8 String to play a file: myfileDirectory/myfilename.wav
18 * c: Audio Input Control Point:
19 Send an Unsigned value between 0-10 to set the volume low-high
20 */
21
22
23#include <SPI.h>
24#include <SD.h>
25#include <bluefruit.h>
26#include <AutoAnalogAudio.h>
27
28AutoAnalog aaAudio;
29
30/************** USER CONFIG ***********/
31// File to play on startup
32const char* audioFilename = "calibrat.wav"; // 8-bit @ 24kHz audio is the max over SD card while BLE is running
33uint8_t SD_CS_PIN = 2; // Set this to your CS pin for the SD card/module
34#define USE_I2S 1
35
36/*********************************************************/
37/* Tested with MAX98357A I2S breakout
38/* BCLK connected to Arduino D1 (p0.03)
39/* LRCK connected to Arduino D3 (p0.29)
40/* DIN connected to Arduino D5 (p0.05)
41/* SD connected to Arduino D6 (p1.11)
42/*********************************************************/
43
44char songName[64];
45float volumeControl = 0.2;
46#define AUDIO_BUFFER_SIZE 1600
47
48BLEService audioService = BLEService(0x1853);
49
50BLECharacteristic audioDataCharacteristic = BLECharacteristic(0x2b79);
51BLECharacteristic audioVolumeCharacteristic = BLECharacteristic(0x2b7b);
52
53uint8_t beaconUuid[16] = {
54 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,
55 0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0
56};
57BLEBeacon beacon(beaconUuid, 1, 2, -54);
58
59
60void setup() {
61
62 Serial.begin(115200);
63 while (!Serial) delay(10);
64
65 Serial.print("Init SD card...");
66 if (!SD.begin(SD_CS_PIN)) {
67 Serial.println("init failed!");
68 return;
69 }
70 Serial.println("SD init ok");
71
72 Bluefruit.begin();
73 Bluefruit.setName("BLE Audio Player");
74 audioService.begin();
75
76 audioDataCharacteristic.setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE);
77 audioDataCharacteristic.setPermission(SECMODE_OPEN, SECMODE_OPEN);
78 audioDataCharacteristic.setMaxLen(64);
79 audioDataCharacteristic.setWriteCallback(playBack);
80 audioDataCharacteristic.begin();
81
82 audioVolumeCharacteristic.setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE);
83 audioVolumeCharacteristic.setPermission(SECMODE_OPEN, SECMODE_OPEN);
84 audioVolumeCharacteristic.setMaxLen(1);
85 audioVolumeCharacteristic.setWriteCallback(volume);
86 audioVolumeCharacteristic.begin();
87
88 Bluefruit.Advertising.setBeacon(beacon);
89 Bluefruit.ScanResponse.addName();
90 Bluefruit.Advertising.restartOnDisconnect(true);
91 Bluefruit.Advertising.setInterval(160, 160); // in unit of 0.625 ms
92 Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
93 Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
94
95 pinMode(6, OUTPUT); //Connected to SD pin of MAX98357A
96 digitalWrite(6, HIGH);
97
98 aaAudio.begin(0, 1, USE_I2S);
99 playAudio(audioFilename);
100}
101
102void loop() {
103
104 loadBuffer();
105
106 // Control via Serial for testing
107 if (Serial.available()) {
108 char c = Serial.read();
109 if (c == '=') {
110 volumeControl += 0.1;
111 } else if (c == '-') {
112 volumeControl -= 0.1;
113 volumeControl = max(0.0, volumeControl);
114 } else if (c == 'p') {
115 playAudio("brick/brick24.wav");
116 }
117 Serial.println(volumeControl);
118 }
119
120}
121
122void playBack(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len) {
123 memset(songName, 0, sizeof(songName));
124 memcpy(songName,data,len);
125 playAudio(songName);
126 Serial.println(songName);
127}
128
129void volume(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len) {
130 volumeControl = data[0] / 10.0;
131 Serial.print("BLE Set Volume: ");
132 Serial.println(volumeControl);
133}
134
135
136/*********************************************************/
137/* A simple function to handle playing audio files
138/*********************************************************/
139
140File myFile;
141
142void playAudio(const char* audioFile) {
143
144 if (myFile) {
145 myFile.close();
146 }
147 //Open the designated file
148 myFile = SD.open(audioFile);
149
150 myFile.seek(22);
151 uint16_t var;
152 uint32_t var2;
153 myFile.read(&var, 2); // Get channels (Stereo or Mono)
154 myFile.read(&var2, 4); // Get Sample Rate
155 aaAudio.setSampleRate(var2, var - 1);
156
157 myFile.seek(34);
158 myFile.read(&var, 2); // Get Bits Per Sample
159 aaAudio.dacBitsPerSample = var;
160
161 myFile.seek(44); //Skip past the WAV header
162}
163
164void loadBuffer() {
165
166 if (myFile.available() > AUDIO_BUFFER_SIZE) {
167
168 if (aaAudio.dacBitsPerSample == 8) {
169 myFile.read(aaAudio.dacBuffer, AUDIO_BUFFER_SIZE);
170 for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE; i++) {
171 aaAudio.dacBuffer[i] *= volumeControl;
172 }
173 aaAudio.feedDAC(0, AUDIO_BUFFER_SIZE);
174 } else {
175 myFile.read(aaAudio.dacBuffer16, AUDIO_BUFFER_SIZE);
176 for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE / 2; i++) {
177 int16_t sample = aaAudio.dacBuffer16[i];
178 sample *= volumeControl;
179 aaAudio.dacBuffer16[i] = (uint16_t)sample;
180 }
181 aaAudio.feedDAC(0, AUDIO_BUFFER_SIZE / 2);
182 }
183
184 } else {
185 myFile.seek(44);
186 }
187}
Note: Content and images are from: https://projecthub.arduino.cc/, with some modifications.
If you want it removed due to copyright reasons, please leave a comment. Thank you.
I want to share this article more widely so that everyone knows about Arduino and your project.