Raspberry Pi, Tutorials

Audio Streaming with Raspberry Pi Zero W and Adafruit’s I2S Mic

The Raspberry Pi came to revolutionize the world of makers alike. Unlike Arduino, the Raspberry Pi runs a whole Linux distribution on it (Raspbian) and allows you to work with software in a much higher level. You can use pretty much any programming language to create whatever you want. You can even use its graphic interface, and still connect with low-level components through its I/O pins.

This tutorial turns a tiny Raspberry Pi Zero W (with built-in wireless) into an audio streamer, using Adafruit’s I2S microphone (also very small). We’ll also show two different methods to create a live audio stream using RTP.

1. What you’ll need

2. Setting up the Microphone

i2s microphone

The tiny I2S microphone from Adafruit is just perfect for projects using the Raspberry Pi Zero. It doesn’t require an analog input pin, since its output is purely digital. Notice that the actual microphone port is located on its back, so you have to make sure it’s facing out / up when you use it on your project.

It comes without the headers as you can see in the photo, so you’ll need to first solder them into place. If you’re new to this, don’t panic! Check out the page “Basic Soldering” on my Noob’s crash course on electronics.

Once you’re done, it should like something similar to this:

3. Breadboard Wiring for Rasp Pi Zero and I2S Microphone

One thing to notice when working with a Raspberry Pi Zero is that you normally won’t connect the board directly to the breadboard as it happens, for instance, with an Arduino micro. To connect it to the breadboard you can either use female-male jumper wires, or you can use a GPIO breakout board / cable like the one I’m using here. This is very useful for prototyping, so I totally recommend you get one of these if you plan on working various projects with a Raspberry Pi Zero. But it’s OK to start with jumper wires! To have an idea, this is how the whole shebang looks like:

The wiring for a Raspberry Pi Zero is demonstrated below (same as official documentation but using a Rasp Zero):

 

3. Enable / Configure I2S Microphone on Raspbian

Now that you’re all set, turn the board ON and let’s deal with the configuration. The official documentation covers it in detail, so there’s no sense in replicating it in here. It basically consists in editing a couple files and recompiling the kernel to enable support. It’s not difficult, but recompiling can take up to 1 hour in a Raspberry Pi Zero, so be patient! 😉

Start with Raspberry Pi i2s Configuration and follow the step by step guide until you’re able to test the microphone and actually record something with arecord.

A Note on Volume

The official documentation has a section about adjusting volume (soft volume control) with alsamixer. You should also follow this section, but be aware that the .asoundrc file proposed might need a few adjustments on your part. That’s what happened around here. Even though the test recording was working, when using the soft volume definition our audio was all chopped and weird, nothing recognizable. As it turns out, it was the max_DB param. It only worked when we lowered the value from 30 to 10.

So this is how my .asoundrc looks like at the moment:

pcm.dmic_hw {
  type hw
  card sndrpisimplecar
  channels 2
  format S32_LE
}
pcm.dmic_sv {
  type softvol
  slave.pcm dmic_hw
  control {
    name "Boost Capture Volume"
    card sndrpisimplecar
  }
  min_dB -2.0
  max_dB 10.0
}

4. Set up streaming

Now, to the fun part! And, surprisingly, the part that took us more time and effort to figure out. It should be something simple, but in reality it was quite challenging because some solutions simply wouldn’t work with the Rasp Zero and there was not enough documentation to look for answers. Luckily, we found out two methods that can work well, and I’m gonna share both here so you can test them and see what works better for your specific needs.

4.1 Streaming with VLC

I particularly didn’t know that you could do this, but you can use VLC to stream right away the input of a microphone. There’s a minimal delay (less than 1sec I believe) but that’s fine for most applications (works just fine for baby audio monitor, as I intend to use).

This is the command that works here. You’ll need to change “192.168.0.11” to the IP address of your own Raspberry Pi Zero. This command should be executed on the streamer, the Raspberry Pi Zero.

$ cvlc alsa://dmic_sv --sout '#transcode{acodec=ulaw,samerate=8000,ab=128}:rtp{sdp=rtsp://192.168.0.11:8000/front.dsp}'

Don’t forget to replace “192.168.0.11″ with your Raspberry Pi IP. Notice that we are binding the stream to the private IP address of the Raspberry. It’s also possible to use “0.0.0.0” instead, this will allow connections from any interface.

Now, from another computer in the same network (that will be the listener), open VLC using the RTP address defined. You can do this via command line with:

vlc rtsp://192.168.0.11:8000/front.dsp

You should be able to listen to the stream. You will notice a little delay, and maybe… a lot of noise. That’s what happened here. Unfortunately, as you’ll notice, the quality of the streaming using VLC this way is not the best, and usually doesn’t get even close to what we get using arecord to a local file instead of streaming to the network. Which leads us to the next option!

4.2 Unicast with arecord and avconv

From what we’ve been investigating, and this stackoverflow answer, I suspect the problem with audio choppiness while streaming via RTP on VLC is related to the multicast nature of the transmission. We managed to get a much better audio quality using arecord and avconv in a unicast streaming. The problem with that approach is that the device will be hard-coded with the client that will be listening – which is good for security but might be a bit “bad for business”. Anyways, this is the line you need:

/usr/bin/arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono | avconv -i - -acodec libmp3lame -b 32k -f rtp rtp://192.168.0.22:8000/4

While “192.168.0.22” should be replaced with the IP of the device that will be listening to the transmission. It’s sort of a walkie-talkie style, you get?

In the “listener” device, you should use aplay or ffplay and run:

$ ffplay rtp://192.168.0.22:8000

Yeah, this is right – you run aplay or ffplay using your own IP address as parameter, the same you used to run avconv on the “server”.  That sort of attached the stream to your IP, and more than that I can’t explain because I’m still trying to understand myself 😀 but it works!

So, I recommend you try both methods (VLC and unicast with arecord and avconv) to see which one works better for your case.

If you have other solutions that work matter-of-factly, please share in the comments!

5. Starting at boot

Once you have things working fine, you might want to add your command to a service that gets started at boot time, this way you only need to turn the Raspberry ON and the audio stream will start as soon as the system is fully initiated. There are different ways to accomplish this; my approach was to create a little Python script where I can easily extend the code to implement more stuff afterwards. Then, through systemctl, I set this script as a service to be executed at boot time.

This is my Python script – very, very basic, but you can later on extend it to do more stuff, like lightning an LED when the audio stream starts. Create a new file at /opt/audiostream.py with the following contents:

#!/usr/bin/python

import os
import time

CLIENT_IP = "192.168.0.22"
CLIENT_PORT = 8000

STREAM_VLC_CMD = "/usr/bin/vlc -I 'dummy' -vvv alsa://dmic_sv --sout '#transcode{acodec=ulaw,samplerate=8000,ab=128}:rtp{sdp=rtsp://0.0.0.0:8000/front.dsp}' --file-logging --logfile=/home/pi/audiostreaming.log"

STREAM_AVCONV_CMD = "/usr/bin/arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono | avconv -i - -acodec libmp3lame -b 32k -f rtp rtp://{}:{}/4".format(CLIENT_IP, CLIENT_PORT)  

os.system(STREAM_AVCONV_CMD)

This is a simple Python script that runs the command for starting the audio streaming using either one of the methods described before. Notice that I’m now using the avconv unicast method here. Just change the variable to reflect what you’ll be using.

If you use this method (avconv), you need to set up the CLIENT_IP variable to your listening device IP address.

Now turn it into an executable script with the following command:

$ sudo chmod +x /opt/audiostream.py

Now, to create a service that will run at boot time, create a new file at /lib/systemd/system/audiostream.service with the following contents:

[Unit]
Description=Audio Streaming Service
After=multi-user.target

[Service]
Type=idle
ExecStart=/opt/audiostream.py
User=pi

[Install]
WantedBy=multi-user.target

Now, to enable and start the service, run:

$ sudo systemctl daemon-reload
$ sudo systemctl enable audiostream
$ sudo systemctl start audiostream

Now, when you restart your Raspberry Pi Zero, in a few moments the audio stream should start automatically.

Troubleshooting

Some protips for debugging and troubleshooting:

  • Make sure the microphone is able to record to a local file using arecord before you can stream (download the file via sftp and listen to it)
  • Check the log files
  • Check dmesg
  • Is the service running at boot time? If not, check the status with systectl status audiostream. If it’s running but doesn’t work, stop / restart the service and test.

Great! Things are working. What now?

You can add an LED as a silent reminder that the device is currently streaming audio. You could also add a button to start and stop it… and a camera, to have a full baby monitor or security camera solution. And, of course, how about creating a nice little Raspberry Pi Bonnet to turn this into a proper prototype? These are all subjects for other posts 😉 keep an eye on my Twitter to see what else you can do using this tutorial as a base. See you next time!

Liked it? Take a second to support erikaheidi on Patreon!

1 thought on “Audio Streaming with Raspberry Pi Zero W and Adafruit’s I2S Mic”

Leave a Reply

Your email address will not be published. Required fields are marked *