Raspberry Pi Zero W - Wave Player



Over the years, I've burnt a number of devices out for the very simple task of playing a recording of ocean waves in the bedroom, as a source of white noise.

The motors of the first two CD players died, each after about a year of continual playback. The batteries in various old smartphones died after being plugged in for years on end (including the battery in my old Galaxy S3 expanding enough to completely pop the back cover off). A Nook HD, an original Kindle Fire, and a Galaxy Tab 4, all died for this.

And now, my last CHIP is on life support. Every week or so, the continual ocean waves are transformed into hours of something that sounds like a fog horn. Or it just freezes up and has to be unplugged for a few minutes before it will boot back up normally.

Solution? Find a new device to work to death, in the coal mines of playing white noise! The Raspberry Pi Zero W fits the bill perfectly, with its built in WiFi and Bluetooth. This is the only device that should ever be connected to the Bluetooth speakers I have, so I'm not worried about dealing with disconnecting the Pi from the speakers gracefully so something else can use them, either.

I'm writing this because I'll probably forget just about everything I did in a few days to get this working, and in the off chance it might help some other person in a random Google search.

So, assuming you already have the sound file you want to play (and assuming you know WTF things like ssh, vim and nano are):

Step 1: Go download the latest Raspbian Lite image file

You can get it from: https://www.raspberrypi.org/downloads/raspbian/.

Step 2: Burn the image file to a micro SD card.

I used Etcher for this, but there are many other ways to do it. Win32DiskImager, dd, SDFormatter. Google them.

Step 3: Set up for headless mode.

You need to do a few things before you stick this card in the Pi and try to boot it up. After you've written the image file to the micro SD card, unmount it, remove it, then put it right back into your PC's card reader.

NOTE: The next sub-steps all involve putting the files into the "boot" partition of the card, not the "rootfs" partition. Raspbian will move these files to where they need to be, so don't be alarmed when they go missing after you first boot up with the card!

Step 3.1: Enable SSH!

You found the "boot" partition on the micro SD card? Good. Make an empty file there, and name it "ssh".

Step 3.2: Setup your wifi connection.

Make a new file named "wpa_supplicant.conf" in the "boot" partition. Here's a basic sample that you can use:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

update_config=1

country=US

network={

ssid="FBI_Van"

psk="MyPreSharedKey"

}

"ssid" is the name of your WiFi network, and "psk" is the network's password.

Step 4: Boot it up!

Go through whatever steps you have to with your OS to nicely unmount/eject the micro SD card, stick it into the Raspberry Pi Zero W and plug a micro USB cable into the "PWN IN" connector. The flickering green light means it's booting up, and it's ready to go when it's a solid green.

Step 5: Connect over SSH

You'll need to figure out what IP the Pi was given, since I'm not worrying about a static IP setup here. Personally, I logged into my gateway, found the "RASPBERRYPI" hostname on the list of connected devices, and got the IP that way.

Once you have that, set up your SSH connection with the username as "pi", and the password as "raspberry".

Once you're connected, CHANGE THE PASSWORD.

pi@raspberrypi:~ $ passwd

Type in the current password, and your new password twice. From here on out, I'm going to start lines you need to type with just "$".

And just because it's a good idea, run the following to get all recent updates for the OS:

$ sudo apt update && sudo apt upgrade

Quick note: I installed vim after this, as the default version of vi in Raspbian drives me nuts, and I'm too lazy to learn how to use it properly. If you would like to do the same:

$ sudo apt install vim

Step 6: Compile a new version of BlueZ

BlueZ is the protocol stack that runs the Bluetooth connection on the Pi Zero W. As of this writing, the version that comes in the Raspbian Lite image is old (5.43), and bugs in it means it won't work for the project.

Step 6.1: Download some required things first

Before you can compile a more recent version of BlueZ, you need to grab the packages that will let you actually compile it.

$ sudo apt install libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev

Step 6.2: Download the source

Find the link for the latest source package at http://www.bluez.org/download/. At the time of writing this, it's 5.50, so that link would be http://www.kernel.org/pub/linux/bluetooth/bluez-5.50.tar.xz

Copy that link, and back in the SSH connection, run the following commands:

To download the file:

$ wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.50.tar.xz

To unpack the file:

$ tar -xf bluez-5.50.tar.xz

Change into the directory of the unpacked files:

$ cd bluez-5.50/

Step 6.3: Compile!

Now you're going to configure, make, and install the new version of BlueZ

Configure:

$ ./configure --prefix=/usr --mandir=/usr/share/man --sysconfdir=/etc --localstatedir=/var --enable-experimental

Make:

$ make -j4

Install:

$ sudo make install

The make step can take some time. I, personally, went for a walk and played Ingress and The Walking Dead: Our World for an hour or so. Just find something to do to kill the time. When I came back, it was done.

Step 6.4: Permissions

You'll need to add the "pi" user to the "bluetooth" group, so you can actually make use of new version of BlueZ, without having to invoke sudo to do so.

$ sudo adduser pi bluetooth

Step 6.5: Fix the BlueZ config

The process of installing the new version of BlueZ overwrote the config file for the old version, and you need to fix it up a bit.

Backup the existing config file first:

$ sudo cp /etc/dbus-1/system.d/bluetooth.conf /etc/dbus-1/system.d/bluetooth.conf.back

Copy and paste the following into the file /etc/dbus-1/system.d/bluetooth.conf.

I used vim for this.



<!-- This configuration file specifies the required security policies for Bluetooth core daemon to work. -->

<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"

"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">

<busconfig>

<!-- ../system.conf have denied everything, so we just punch some holes -->

<policy user="root">

<allow own="org.bluez"/>

<allow send_destination="org.bluez"/>

<allow send_interface="org.bluez.Agent1"/>

<allow send_interface="org.bluez.MediaEndpoint1"/>

<allow send_interface="org.bluez.MediaPlayer1"/>

<allow send_interface="org.bluez.Profile1"/>

<allow send_interface="org.bluez.AlertAgent1"/>

<allow send_interface="org.bluez.ThermometerWatcher1"/>

<allow send_interface="org.bluez.HeartRateWatcher1"/>

<allow send_interface="org.bluez.CyclingSpeedWatcher1"/>

<allow send_interface="org.bluez.GattCharacteristic1"/>

<allow send_interface="org.bluez.GattDescriptor1"/>

<allow send_interface="org.bluez.LEAdvertisement1"/>

<allow send_interface="org.freedesktop.DBus.ObjectManager"/>

<allow send_interface="org.freedesktop.DBus.Properties"/>

</policy>

<!-- allow users of bluetooth group to communicate -->

<policy group="bluetooth">

<allow send_destination="org.bluez"/>

</policy>

<policy at_console="true">

<allow send_destination="org.bluez"/>

</policy>

</busconfig>

Step 6.6: Reboot and verify

Now you're going to gracefully reboot the pi, and double check the version of BlueZ.

Reboot:

$ sudo reboot now

Wait for the green light to stop flickering, and reconnect via SSH, just like you did in step 5.

Check the version:

$ bluetoothctl -v

If you used version 5.50, you'll see this:

bluetoothctl: 5.50

And Step 6 is finally done.

Step 7: Configure your Bluetooth Speaker

We're going to do a few things here, through the application "bluetoothctl". It's a command line interface application, and it can throw a lot of information at you if there are a lot of Bluetooth devices around you.

Make sure your Bluetooth speakers are on and in pairing mode before going further!

Step 7.1: Start the app

$ bluetoothctl

Simple enough. This will drop you into the applications own command line interface, however, and your prompt will now show as [bluetooth]#. When I start a line with # here, type out everything after that as the command.

Step 7.2: Scan for your device

# scan on

This will start showing you a bunch of lines that start with "[CHG] Device", followed by a string of numbers like "45:51:B9:98:3D:BF". This is the speakers MAC address. We need to find that string of numbers for your device. If you're lucky, the name of your device will show up at the end of the line that's for your device.

Once you've found that string of numbers, copy it out somewhere else you can easily get at it again, because we'll be needing it later. You will also want to turn scanning off.

# scan off

Note: I had a hard time finding my speaker in this list. I had a flood of devices show up, and the list was constantly updating. My second way of figuring out my speakers MAC address was to look at information for the speakers connection in my Android's Bluetooth settings.

Step 7.3: Pair, Connect and Trust

Now that you have the MAC address, we're going to pair, connect and trust the device. Once you've done these steps, you won't have to go through the pair and trust parts again, and we're going to automate the connect part.

Pair:

# pair <mac address here>

This will actually get the Pi talking with the speaker.

Connect:

# connect <mac address here>

This will confirm the connection between the Pi and the speaker, which will allow you later play audio through it. Most Bluetooth speakers that I know of will stop flashing their pairing light, and make a beep noise when this step is successful.

Trust:

# trust <mac address here>

This will make the Bluetooth speaker be remembered by the Pi, so you don't have to re-pair the device after a reboot.

We're hopefully done with the bluetoothctl app for now, so you can exit the program.

# exit

Step 7.4: Verify the Bluetooth service is running

At this point, the service may not be running, so double check that.

$ sudo systemctl status bluetooth.service

A few lines down, if the line starting with "Active:" is followed by anything other than "active (running) since ...", type the following:

$ sudo systemctl start bluetooth.service

This will start the Bluetooth service up on the Pi.

Step 8: Play something!

Finally close to the end here. First we're going to try playing a wav file, to make sure everything works, then go into how to automate things to automatically reconnect and start playing after the Pi has been rebooted (like after a power outage).

Step 8.1: One last piece of software to install

We need to install "bluealsa" here, which is going to let us pipe sound out to the Bluetooth device.

$ sudo apt install bluealsa

Step 8.2: Transfer your sound file to the Pi

There are a number of ways to do this. You can turn off the Pi, remove the SD card, stick it back in your computer, and drop the file somewhere in the "rootfs". You can download it from somewhere on the internet with "wget". You can SFTP the file over from another machine on your network. Just remember where you put it. I, personally, put it in /home/pi/, since that is the default location you are put into when you connect to the Pi over SSH.

Step 8.3: Play a wav file

We're going to use the application "aplay" to play your wav file. You can do it with an MP3, but I found for the sake of simplicity, a wav file was easier. We will also need that MAC address you saved from step 7.2 again.

$ aplay -D bluealsa:<MAC address here> ocean_waves.wav

To break this down:

aplay - the name of the application

-D - route audio out to a different device

bluealsa:<mac address> - the device we want to send audio to

ocean_waves.wav - the name of the file we want to play.

If everything has worked so far, you should start hearing the audio coming out of your Bluetooth speaker! You chose a 20 minute long sound file though, didn't you? ctrl-x on your keyboard to kill aplay and stop the file.

Step 9: Automation

My whole reason for doing this project is to have the wav file play over the speakers for me at all times. Power outages are not uncommon where I live, so having it all come back on afterwards without intervention from me is a positive. I've set up a cron job to start at reboot, as well as another script to nicely release the Bluetooth connection when the Pi is properly shut down.

Step 9.1: Script to connect to Bluetooth and play the audio

First we're going to make our script file that will connect to the Bluetooth speaker and play the audio file through aplay. You can do this through vi, vim, emacs, nano. I'm a vim user, so I did:

$ vim waves.sh

This will open a blank file. You can copy and paste my script below.

#!/bin/bash

sleep 10

echo -e "connect 45:51:B9:98:3D:BF" | bluetoothctl

sleep 5

while [ 1 ]; do

aplay -D bluealsa:DEV=45:51:B9:98:3D:BF ocean_waves.wav

done

Save the file and quit out of your editor.

Notes:

I found I was having problems with the Bluetooth connection not being finished at times before the audio started playing, so I added a few sleep lines in. The first sleep of 10 seconds at the start is to let the Pi finish booting, and the following sleep of 5 seconds after the connection was to give the Bluetooth speaker enough time to "wake up" and accept the connection.

45:51:B9:98:3D:BF is my own speakers MAC address, so don't try to use it, unless you plan on jumping on my home wifi.

Lastly, I'm putting aplay into an endless loop with "while [ 1 ]; do". As soon as the wav file finishes playing, aplay will be started back up again immediately.

Step 9.2: Make the script executable

You're not going to get far if you skip this step! Type the following out so you can actually execute the script and have it do things.

$ chmod +x waves.sh

Step 9.3: Test it

Before you get it automated, lets make sure it's going to work as you want it to. Go ahead and reboot the pi again with

$ sudo reboot now

Wait for the Pi to come back up, and your Bluetooth speaker should be awaiting a fresh connection.

SSH back into the Pi, and try to run the script!

$ ./waves.sh

If all has gone well, you'll be hearing the audio coming out of your speaker. Almost done!

Step 9.4: Cron

And now to get things going automatically after a reboot, we'll use "cron", the time based scheduler.

First we'll need to open "cron" for editing.

$ crontab -e

This open your "crontab" file for editing. If this is your first time editing cron, you'l need to select the editor application to use. Just press the number for your editor of choice.

Add this line to the end of the file:

@reboot /home/pi/waves.sh > /home/pi/waves_log 2>&1

Save the file and close out of the editor. If you don't know, > /home/pi/waves_log 2>&1 will write out error information if the script has a problem and can't start.

Reboot one more time, after a minute or two, you should have audio coming out of your speaker again, but this time without any intervention from you.

Now that you have it working, you will probably want to go back into

$ crontab -e

and remove > /home/pi/waves_log 2>&1 from the end of the line. If you don't, you will end up with a potentially massive log file (I generated a 30 meg file just while writing this out, with the Bluetooth speaker off).

Step 10: One last thing

While I was working on this project, and setting the Pi through many reboots, I noticed that it does not always gracefully disconnect from the speaker. I don't know if that's a bug with BlueZ, the Pi itself, or the VTIN R2 speaker I tested this setup on.

Whatever the case, if that's a problem for you, you can make a file in /etc/init.d/

$ sudo vim /etc/init.d/btshutdown.sh

and put this into it:

#!/bin/bash

echo -e "disconnect <MAC address here>" | bluetoothctl

Save and close out of your editor, then make the file executable:

$ sudo chmod +x /etc/init.d/btshutdown.sh

Lastly, run this command to make the script fire off when you shut the Pi down:

$ sudo update-rc.d btshutdown.sh stop 01 6

Thanks!

I forgot how much fun and enjoyment I get out making these little one-off special projects with SBC's, but I do remember how much I forget within days of finishing them. This was mostly to help reinforce the knowledge in my brain-meats, so I don't have to remember as much when this new Zero W burns out and I have to replace it :)

I cribbed a lot of information for setting up the Bluetooth connection from Michel Desierres. He goes into much greater detail than I needed for this project, I've paired it down to the essentials of just getting it working for this project, but you can read it all at https://www.sigmdel.ca/michel/ha/rpi/bluetooth_02_en.html