Implementing a Bluetooth Speaker Using the ESP32

I have made several nixie clocks built around the ESP32. They incorporate a speaker, and I thought it would be nice if I could also stream music to them. The ESP32 has built-in Bluetooth support, so I figured I should try and use that first – essentially turn the clocks into a Bluetooth speaker.

I set about looking for examples and rapidly discovered that this was a little-used feature. Almost all the coding examples on the internet focus on Bluetooth BLE. I needed to use Bluetooth A2DP. Furthermore, I needed to implement an A2DP sink – that is something that audio data is sent to rather than from.

All of the code I could find was very low level and hard to understand – it would have been hard to incorporate it into an existing code base. It was all written in C, had zero encapsulation and seemed to be littered with code that was unnecessary for what I was trying to achieve.

To cut a long story short I wrapped it all in to some C++ classes. There are four main classes:

  • Audio
  • DataSink
  • EventSink
  • Out

Audio

This class just initializes the bluetooth protocol stack.

DataSink

This class listens for audio data and writes it to a ring buffer.

EventSink

This class listens for A2DP lifecycle events such as connected, suspended, configure etc. and publishes the events to any objects that subscribed to them.

Out

This class reads the audio data from the ring buffer and streams it out to an external DAC connected to the I2S ports.

The code is here. It can be used with or without the ESP32 Arduino framework. There is an example that uses the Arduino framework in the repo.

In the end I’m not using it with my clocks. All the code together blows the heap allocation and requires me to use a different memory layout. I might look in to sorting that out later.