Learn how to create a wireless microphone using ESP32 and Bluetooth Hands-Free Profile (HFP). Although the audio quality isn't perfect, the project pushes the ESP32's capabilities to their limits, resulting in an effective wireless speakerphone.
[0:00] Let’s make a Bluetooth microphone I said to myself, how hard can it be?
[0:03] We’ve already made a bluetooth speaker so making a Bluetooth microphone should be easy.
[0:08] So, What are we doing in this video?
[0:09] First off we’ll discuss why this seemingly trivial thing is not trivial
[0:14] spoiler alert, you can’t simply create an A2DP Bluetooth source and expect it to work as a microphone.
[0:20] We’ll work around this limitation by using the Bluetooth Hands-Free Profile
[0:23] another spoiler alert - this is very cool, but honestly it’s not great.
[0:28] Finally we’ll actually make a working wireless microphone
[0:31] And then, at the end of the video there a nice little bonus feature that I think you’ll like.
[0:35] We’ll get started after this quick word about the channel sponsor PCBWay.
[0:39] PCBWay have been supporting the channel for a while, they offer PCB Production, CNC and 3D Printing,
[0:46] PCB Assembly and much much more.
[0:48] I’ve made quite a few boards with them and they are great to work with. You can find a link to their details in the description.
[0:53] So, what’s the issue with the A2DP Bluetooth profile?
[0:57] We used this to create our Bluetooth speaker in a previous video.
[1:00] And it was trivially easy and required just a few lines of code with some helper libraries.
[1:05] What’s more you can also output audio using the A2DP Bluetooth profile,
[1:09] this also just requires a few lines of code.
[1:11] The problem is, my PC and most probably your PC is not a Bluetooth speaker - so it’s not
[1:17] going to pick up our A2DP source as a potential input it can use.
[1:21] It’s simply not the way the standards have been defined and implemented.
[1:24] This leads me to my nice trouble shooting diagram for future projects.
[1:28] Am I using bluetooth?
[1:29] If the answer is no - it’s going to be great
[1:31] If the answer is yes - you’re in for a world of consfusion
[1:34] Now, I can hear you all right now saying, what’s wrong with this guy - you can definitely have
[1:38] Bluetooth microphones - I’ve got a bluetooth headset and it works with my phone and my laptop.
[1:44] Enter the Bluetooth Hands-Free Profile or HFP. This is used by hands-free kits to
[1:49] communicate with mobile phones and now that we spend all day on Zoom calls, computers.
[1:53] The Hands-Free Profile defines two roles,
[1:56] an Audio Gateway known as AG and a Hands-Free unit known as HF. We’re going to turn our ESP32
[2:03] into a hands free unit so that it can connect to our computer which will quite happily act
[2:07] as an audio gateway. This will then let us send audio data from the microphone to the computer.
[2:12] There are several issues with this.
[2:15] The first is a fundamental issue with the codecs used.
[2:18] These are limited to 8KHz or 16KHz sampling rates.
[2:23] As we know from our previous videos on audio input on the ESP32 8KHz is just about good
[2:29] enough for speach, but it’s not great. Unfortunately in my experiments with the
[2:33] ESP32 and my computer I’ve not managed to get them to agree to the 16KHz sampling rate. So I’m
[2:40] stuck with 8KHz. My phone will however connect at 16KHz so will get slightly better quality.
[2:46] The second issue is just getting this to work. I tried running the examples in the ESP-IDF
[2:51] and hit a number of problems. The main one being that it was unable to stay connected
[2:56] for more than a few seconds. The code is also pretty hard to understand.
[2:59] I have however managed to find an alternative bluetooth stack that seems to be stable
[3:04] and has sample code that I was able to understand.
[3:06] The sample code was missing the parts required to get the microphone working,
[3:10] but I’ve managed to hack together enough code to get it to work.
[3:13] This is very much alpha code and support for this
[3:15] kind of thing seems to be pretty rough - hopefully it will improve over time.
[3:20] I’ve run the code up on my ESP32 and we are now able to connect the ESP32
[3:24] with my computer and it shows up as an audio input and output device.
[3:28] It’s pretty cool - the volume controls work
[3:30] and send messages to the ESP32 and we can see it sending data in the logs.
[3:35] And, as the computer recognises it as an audio input we can record directly from it.
[3:40] I’ve recorded a few seconds of me speaking and some music. Let’s have a listen.
[4:03] It’s definitely understandable - but it’s not great. We can see
[4:07] if we prot a spectrum of the audio it’s cut off at 4KHz so it sounds pretty muffled.
[4:12] This seems to be a limitation of my computer as it does the same
[4:15] thing with my expensive noise cancelling headpohones which also cut off at 4KHz.
[4:20] So my computer won’t do the 16kHz sampling rate.
[4:23] So, for me this is not really usable as a wireless mike.
[4:26] Having said that we can do some fun things. I’ve got it paired with my phone
[4:31] and we can make phone calls and play DTMF tones.
[4:34] We can also call this test number and get it to record the audio and play it back to us. So we can see what the quality is like.
[5:31] We’ve managed to make a hands free speakerphone - I’m not sure
[5:34] I’d actually want to use it for anything serious.
[5:35] And it’s not really what I set out to do.
[5:38] I really want a wireless microphone.
[5:41] Let’s have a look at alternatives. If we look at proper wireless microphones that connect to PCs,
[5:46] we see they normally transmit the audio data over UHF and use a USB
[5:50] dongle to act as USB microphone to get the data into the computer.
[5:54] We’re not going to do that, but we do have an ESP32 which has WiFi.
[5:58] Why not use WiFi to send our audio.
[6:00] We did something similar to this with the Walky-Talky project where
[6:03] we used UDP packets to stream our audio data.
[6:06] Let’s try it out using TCP sockets. I’ve set up some very simple code that runs a TCP server on
[6:12] port 9090. It then listens for new connections and streams audio data out to anyone who is listening.
[6:18] I’ve also got a very simple Python script that will connect to our sever
[6:22] and playback the audio data as it is coming through.
[6:25] I’ve also set up a virtual audio device using some free software called Blackhole. This acts as a
[6:30] virtual audio device that will route audio from an output device to an input device. This means that
[6:36] we can play audio data to the Blackhole output device and then other software can record the audio
[6:42] from the Blackhole input device. There is a similar piece of software for windows called VB-Cable.
[6:47] Let’s give it a quick go.
[6:48] I’ve got the software up and running on my ESP32 waiting for connections.
[6:52] And I’ve started up the python script to receive audio and play it to the Blackhole input device.
[6:58] I can now just record the audio in Audacity from the Blackhole device.
[7:30] It works pretty well!
[7:31] Let’s have a listen to what we recorded.
[7:56] Quality is significantly better and if we look at the spectrum
[7:59] we now get the full audio bandwidth of around 20KHz.
[8:02] We’ve had to jump through a few hoops, but we have a working wireless microphone.
[8:07] I promised you a little bonus - so here it is.
[8:09] I thought it might be good to do this without needing a python program,
[8:13] so I’ve embedded a simple HTML audio player that let’s you use your browser to play the audio.
[8:18] This can be served directly from the ESP32 - just don’t forget to follow
[8:22] the instructions to upload the SPIFFS file system.
[8:24] This will receive audio directly from the ESP32 over a websocket
[8:28] and play it back over the default output device.
[8:31] There is a slight limitation with selecting audio devices when you are not serving files over HTTPS or localhost
[8:37] There’s a couple of things you can do to work around this. I use a handy plugin for VSCode
[8:42] called Live Server. This lets you serve HTML files from localhost which gets around this restriction.
[8:47] And you can now pick the audio output device you want to use. This great if you don’t want to use the standard output device and get massive feedback.
[8:55] Or, you can add an exception to chrome for the URL we are getting from the ESP32 that
[8:58] will make it treat it as a secure site even though it’s not coming over HTTPS.
[9:03] Just go to chrome://flags and enter an exception for “Insecure origins treated as secure”.
[9:10] With that done we can now route the audio to the
[9:12] virtual audio device and record the audio that is coming from the ESP32.
[9:16] That works pretty well and we can do a similar thing to before where we record the audio using Audacity.
[9:21] We’ve also got a nice little audio visualiser.
[9:24] All in all, quite a nice successful project. Thanks for watching and I’ll see you next time!