Thursday 15 March 2018

Build a Raspberry Pi Musicbox using Nodejs, Docker and Mopidy

The goal of this blog post is to set up a Raspberry Pi in a nice box with a button, that when pressed plays a song from a Spotify list on a bluetooth speaker. The songs name and a link to it will be posted on Twitter. The code will be written in Nodejs and deployed using Docker. The Docker container needs to be restarted on reboot, as does the Mopidy server that handles the music.

Items needed:
  • A Raspberry Pi, model 3 with wifi and bluetooth
  • A micro SD card, 16GB
  • A micro USB cable and adapter
  • A bluetooth speaker
  • An arcade button
  • A LED sequin to insert into the button
  • Three jumper wires with a male end
  • Optional extra wire to extend the jumper wires
  • Shrink tube to cover solder joints
  • A nice box to keep the Pi in

Set up a new Raspberry Pi

  • Download the Raspbian Stretch image from https://downloads.raspberrypi.org/raspbian_latest
  • Install the image on your SD-card. I use Etcher to do that. Insert your SD card, or your SD card adapter into your computer. Open Etcher, select the downloaded zip-file and click Flash.
  • Insert the SD card into the Raspberry Pi and connect it to power, a screen, a mouse and a keyboard. Raspbian will boot up and display the desktop.
While on the desktop, it's time to do some initial configuration.
  • Open the Raspberry menu in the top left corner, go to Preferences >> Raspberry Pi Configuration
  • On the System-tab, change the password for the user. The default user 'pi' has root access and even if you only intend to use it on your home network it's a good practice to change this from the default password 'raspberry'.
  • On the System-tab, optionally change your hostname from the default 'raspberrypi'. For this one, my tenth on the network, I'm going with raspi10. :)
  • On the System-tab, check the box "Wait for network". This will make the Pi wait for the network to be available before starting up any of your services.
  • On the System-tab, click Set resolution and change the value to the one you want to use when logging in from a remote computer. I use 1920x1080 which looks good on my Macbook.
  • On the Interfaces-tab, enable VNC for remote desktop connection and SSH for remote ssh connection.
After this configuration, the Pi will need to reboot. When this is done, connect the Pi to your home network by clicking the network icon in the top right corner and selecting the network.

Pair the Pi to your bluetooth speaker

In the top right corner, click the bluetooth symbol. Select 'Add device'. Make sure your speaker is turned on and find it in the list of devices. After the pairing is done, right click the speaker symbol in the top right corner. Select your speaker.

This is the easy setup that will make sure that your Pi will connect to the bluetooth speaker on startup, if the bluetooth speaker is turned on and in range. There are some issues with bluetooth speaker connections on the Pi. My solution for now is to just turn the speaker on and reboot the Pi. :)

When this is done, you can disconnect the Pi from the external screen, mouse and keyboard and just plug it in somewhere. It is now accessible from your computer and that's how we'll do the rest of the setup.

Connect using SSH

You can connect to your Pi using SSH in your command prompt or terminal. I use ITerm on Mac and Cmder on Windows. Use the hostname of your Pi, in my case raspi10.

ssh pi@raspi10

This will prompt you for your password. Enter it and you will be logged on to the Pi, with the user directory /home/pi as your current working directory.

Connect using VNC

Another way to connect is by remote desktop. This way you will see the graphical Pi desktop on your own computer. This is slower, but quite nice if you're not used to working in the command prompt. For remote desktop, Real VNC is used. The software VNC Connect is included with Raspbian. There is a VNC server installed, which allows you to connect remotely to your Pi, and also a VNC Viewer, which allows you to connect to other desktops from your Pi. To connect to your Pi, you also need a VNC Viewer installed on your computer.

To enable and use VNC, logon to your Pi using your command prompt or terminal. Now run the Linux package manager apt-get to first update the list of available packages, then install any new versions found. Always run apt-get update before installing to make sure you get the latest version of packages.

sudo apt-get update
sudo apt-get install realvnc-vnc-server realvnc-vnc-viewer

When this is done, download and install VNC Viewer on the computer from which you want to connect to the Pi: https://www.realvnc.com/en/connect/download/viewer/. Open it and enter the hostname of your Pi to connect to it. You will be prompted for username and password. If the window where the desktop is shown is too small, go into the Raspberry Pi Configuration again and set the resolution on the System-tab.

Install Docker

Docker is a great way to package and run your code in a container, and since it works perfectly on a Pi, let's install it and use it for our application. Connect to your Pi using SSH and run the following command to install Docker:

curl -sSL https://test.docker.com | sh

This will actually install the test/edge-version of Docker (18.03), which is the latest code. The reason I use this instead of the stable version (17.12) is that I want Docker to restart the container after I've unplugged the Pi and plug it in again. This feature does not work in the latest stable version.

Now, run the following command to make sure your user can access the Docker client:

sudo usermod pi -aG docker

You need to logout and login again for the user changes to take effect. Logging out is done by writing exit in the terminal.

Test your Docker installation by pulling down the Hello World-image for Raspberry Pi

docker run --rm armhf/hello-world

This will pull down the image, start a container and print the text 'Hello from Docker on armhf!' in the terminal. The --rm flag removes the container automatically after it's been run.

To remove the image you just pulled down, write

docker images

This will display a list of images on your Pi. Find the Image Id-column and use that value to write

docker rmi your_image_id

This will remove the image from the Pi. Unless you have tons of images, it's enough to use the first two characters of the image or container id when referencing them in the Docker client.

Install Mopidy

Mopidy is a music server that can run on the Pi and play music from many different sources. The default is music from local disc, but there are many extensions for playing music from Spotify, SoundCloud and others. I'm gonna use Mopidy to play music from one of my Spotify playlists. To use Spotify like this, you need a Spotify Premium account.

In the terminal, first add Mopidy to the apt package sources:

wget -q -O - https://apt.mopidy.com/mopidy.gpg | sudo apt-key add -
sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/jessie.list

Then install Mopidy and the Spotify extension using apt-get:

sudo apt-get update
sudo apt-get install mopidy
sudo apt-get install mopidy-spotify

Now we need to configure Mopidy, and make sure it runs on startup. We start by editing the config-file for Mopidy. Here you will need your Spotify user, password, client key and client secret. To get these, you need to go to developer.spotify.com, login and create a new app. After registering it, you will get the tokens needed.

sudo nano /home/pi/.config/mopidy

In the config file, uncomment and fill in the following values in the Spotify section:

[spotify]
enabled = true
username = your_user_name
password = your_password
client_id = your_client_id
client_secret = your_client_secret

Also uncomment and change the hostname in the http section to allow connections on all ip addresses.

[http]
hostname = 0.0.0.0

Lastly, uncomment and change the audio output. [audio]
output = alsasink

Exit and save your changes.

To have Mopidy run at startup, we edit the rc.local file.

sudo nano /etc/rc.local

In the file, before the line exit 0, add the path and executable command for starting Mopidy. We need to run it as the user pi, since rc.local otherwise runs as root and will miss the config and user rights we need.

sudo su pi -c /usr/bin/mopidy

Exit and save your changes. Now reboot the Pi. We're done with the software installation of it, now we need to wire it up to the button and write the Nodejs code for playing music and sending tweets! :)

Fix the wires

This project needs three wires with a male connector in one end. This end should connect to the Pi's pins. The other ends will be soldered onto the button and LED sequins connectors. I want my wires quite long so I can open the box without being afraid I'll disconnect things. So I'll extend the jumper wires by soldering them to an extra piece of wire.

Cut off one of the ends of the male jumper wires. Peel off 5 mm. If you're not happy with the length of the wire, cut an extra piece of wire that you peel off in both ends. Solder the wires together and cover the solder joint with shrink tube. I just use my hair dryer to heat it up so it shrinks and covers the joint tightly.
Cut an extra piece of wire, around 6 centimetres, to connect the LED sequin's ground connector to the button's ground connector. That way they can share the ground pin on the Pi and we get one wire less to handle. Peel off 5 mm in both ends.

Make the button pretty

If you already have a button with a LED in it, you don't have to do this. My button is without a LED, but that can easily be solved using a LED sequin. These are normally used in wearables, but they work fine in all types of projects. They're really small and easy to connect. Start with soldering one of the long wires to the positive connector on the sequin. This will be connected to a GPIO pin on the Pi. The GPIO pin will be programmatically set to high or low. Then solder the short wire to the sequins negative connector. This will be connected to ground on the Pi, via the buttons ground connector, and that will close the circuit. When this is in place, the LED will be turned on when the GPIO pin is high and turned off when the GPIO pin is low.

Always test your connections before moving on. Just plug in the Pi and hold the positive wire against a 3V pin and the negative wire against a ground pin. The LED should light up.

Open the button using a small screwdriver. Glue the sequin to the inner activator, pull out the wires through the opening in the sides and push the button closed again.
If you have a button with a build in LED, the same rule applies. Solder the long wire to the LED's positive connector and the short to the negative.

Connect the button

Now it's time to solder the button's connectors. Solder the short wire from the LED to one of the connectors. To the same connector, also solder a long wire that should connect to the Pi. This is now the ground wire for both LED and button. Solder another long wire to the other connector. This is the GPIO pin wire. When done, attach the ground wire to one of the ground pins, the LED wire to pin GPIO17 and the button wire to pin GPIO2. A complete pinout of the Raspberry Pi 3 can be found here.

The code

The code for running the application can be found at http://www.github.com/asalilje/musicbox. It contains a Dockerfile that just packages the code without running npm install. This must be done on the host, since the epoll library used for pin connection needs to be installed on the correct host.

There is a Docker image for the project: asalilje/portablemusic. If you want to build the image yourself from the code you write:

docker build -t yourtag .

Deploy to Pi using Docker

SSH in to your Pi. The code needs some environment variables for your Twitter account. If you don't have any, go to apps.twitter.com and create a new app. Since these variables won't change we put them in a file. In the directory /home/pi create a new file named env.list:

sudo nano /home/pi/env.list

In that file, insert your own tokens:

TWITTER_CONSUMER_KEY=your_own_consumer_key TWITTER_CONSUMER_SECRET=your_own_consumer_secret TWITTER_ACCESS_TOKEN_KEY=your_own_access_token_key TWITTER_ACCESS_TOKEN_SECRET=your_own_access_token_secret

When this is done, you can run the app using the following command:

docker run -w=/app --network=host --name=your_container_name_here -t --privileged --restart=unless-stopped -e SPOTIFY_PLAYLIST_URL="your_playlist_here" --env-file=/home/pi/env.list your_image_here /bin/bash -c "npm install; node app.js"

Let's go through this command in detail.
  • -w=/app is the working directory inside the container.
  • --network=host makes the container run on the same network as the host, the Pi. This means we can call localhost on the Pi from within the container
  • --name is the name that will show up in the list of running containers
  • -t will let us terminate the Docker run command and get control of the command prompt again using CTRL+C
  • --privileged means the container will run in privileged mode and be able to access the pins
  • --restart=unless-stopped makes sure the container restarts after reboot
  • -e is an environment variable that the node app inside can access using process.env.VARIABLE_NAME
  • --env-file is the path to a file containing multiple enviroment variables
  • your_image_here is the path to the Docker image
  • /bin/bash -c is a list of commands that will be run in the container, in the given work directory. Here we run npm install and then start the app.
In my case, using the Docker image I built and a nice 80's Spotify playlist, the command looks like this:

docker run -w=/app --network=host --name=portablemusic -t --privileged --restart=unless-stopped -e SPOTIFY_PLAYLIST_URL="spotify:user:asaki:playlist:12V6KDKzfi3m0qRZZTXeCb" --env-file=/home/pi/env.list asalilje/portablemusic /bin/bash -c "npm install; node app.js"

When it comes to playlists, Mopidy will only be able to load playlists from your own user account.

Now, place the box and speaker somewhere and enjoy your music!