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 use an analog microphone. The quality of the sound however is much better if you use a digital microphone that provides the data via I2S.

Digital I2S Microphones

The Sketch

Here is the Arduino Sketch that you can use with an I2S sound source:

#include "Arduino.h"
#include "AudioTools.h"

using namespace audio_tools;  

/**
 * @brief We use a ADS1015 I2S microphone as input and send the data to A2DP
 * Unfortunatly the data type from the microphone (int32_t)  does not match with the required data type by A2DP (int16_t),
 * so we need to convert.
 */ 

BluetoothA2DPSource a2dp_source;
I2S<int32_t> i2s;
ChannelConverter<int32_t> converter(&convertFrom32To16);
FilterFillLeftAndRight<int32_t> bothChannels;
const size_t max_buffer_len = 1024;
int32_t buffer[max_buffer_len][2];

// callback used by A2DP to provide the sound data
int32_t get_sound_data(Channels* data, int32_t len) {
   size_t req_len = min(max_buffer_len,(size_t) len);

   // the microphone provides data in int32_t -> we read it into the buffer of int32_t data
   size_t result_len = i2s.read(buffer, req_len);

   // we have data only in 1 channel but we want to fill both
   bothChannels.process(buffer, result_len);

   // convert buffer to int16 for A2DP
   converter.convert(buffer, data, result_len);
   return result_len;
}

// Arduino Setup
void setup(void) {
  Serial.begin(115200);

  // start i2s input with default configuration
  Serial.println("starting I2S...");
  i2s.begin(i2s.defaultConfig(RX_MODE));

  // start the bluetooth
  Serial.println("starting A2DP...");
  a2dp_source.start("MyMusic", get_sound_data);  
}

// Arduino loop - repeated processing 
void loop() {
}

We implement a A2DP source: We stream the sound input which we read in from the I2S interface to a A2DP sink. We can use any device which provides the sound data via I2S. In order to test the functionality we use the INMP441 microphone.

We use the I2S class which just wraps the native ESP32 I2S calls. The INMP441 provides the data for one channel only. The FilterFillLeftAndRight class makes sure that we have sound on both channels and finally A2DP requires int16_t data – so we need to convert it using the ChannelConverter class;

The Device

INMP441

The INMP441 is a high-performance, low power, digital-output, omnidirectional MEMS microphone with a bottom port. The complete INMP441 solution consists of a MEMS sensor, signal conditioning, an analog-to-digital converter, anti-aliasing filters, power management, and an industry-standard 24-bit I²S interface. The I²S interface allows the INMP441 to connect directly to digital processors, such as DSPs and microcontrollers, without the need for an audio codec in the system.

Pins

INMP441 ESP32
VDD 3.3
GND GND
SD IN (GPIO32)
L/R GND
WS WS (GPIO15)
SCK BCK (GPIO14)

SCK: Serial data clock for I²S interface
WS: Select serial data words for the I²S interface
L/R: Left / right channel selection
When set to low, the microphone emits signals on the left channel of the I²S frame.
When the high level is set, the microphone will send signals on the right channel.
ExSD: Serial data output of the I²S interface
VCC: input power 1.8V to 3.3V
GND: Power ground

Source Code

Both the project and the example can be found on Github.


2 Comments

Mike · 5. May 2021 at 8:48

Hey Phil, this is excellent. I will test this asap and come back to you! Thanks a lot for your great work.

    pschatzmann · 5. May 2021 at 9:19

    I propose that you wait a couple of days. I am currently working to make things even simpler…

Leave a Reply

Your email address will not be published.