Turning a Raspberry Pi 2 into a shared Spotify Jukebox
Introduction
In the past role we had music playing in the office through some wall-mounted power speakers. We wanted our staff to be able to control what is playing, skip tracks, change volume, etc.
Initially we had Spotify running on a Mac Mini, using a home-made Rails website and some Apple Script commands to control skip, pause, play and volume change but naturally that was not very flexible, or reliable and created a maintenance burden when any new features needed adding.
While looking into creating a better solution using libspotify came across the Pi Musicbox project which promoted the use of Mopidy.
Mopidy is essentially a music server which supports plugins to add functionality, for example music services like Spotify & Soundcloud but also for control through web interfaces.
We started running Mopidy on a Linux Server with the Music Box web client (of the Pi Musicbox project).
This has served us very well but is not the best use of a Dell server, so to minimise waste (and downtime should we need to tinker with the server) we are moving the Mopidy setup to a Raspberry Pi 2. This post outlines the steps to make that happen...
What You'll Need
- Raspberry Pi 2
- Micro SD Card (ideally 8 GB or larger)
- Another Computer (to download/copy the OS image and ssh)
- A way to read/write the micro SD card on the computer
- Powered Speakers?
I have heard that headphones are not a good way to test volume change etc - If all goes well you shouldn't need a keyboard or mouse for the Pi!
Guide
This guide is based on running Linux on the computer used to prepare the SD card however there are many guides around the web on how to prepare the SD using Mac OS or Windows. Mac is fairly similar, most notably the devices are identified a little differently and you use diskutil
to find and unmount your device before using dd
.
Step 1 - Install Raspbian
First download Raspbian Jessie Lite from https://www.raspberrypi.org/downloads/raspbian/ and extract the img file within.
2015-11-21-raspbian-jessie-lite.zip
paul@box [09:35:00] [~/Downloads]
-> % unzip 2015-11-21-raspbian-jessie-lite.zip
Archive: 2015-11-21-raspbian-jessie-lite.zip
inflating: 2015-11-21-raspbian-jessie-lite.img
Then 'copy' the image to the SD card. This is done at the block level rather than the file level using the dd
command. This step (usually requiring sudo) is destructive so you need to be sure which device is your SD card - using fdisk one can check.
fdisk -l
will list all disks, partitions, sizes. Again sudo is likely required.
fdisk will list entries that look similar to:
Disk /dev/sda: 894.3 GiB, 960197124096 bytes, 1875385008 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 62FB3A67-F01B-4A6A-903A-A5573C2A7E96
Device Start End Sectors Size Type
/dev/sda1 2048 1050623 1048576 512M EFI System
/dev/sda2 1050624 1808625663 1807575040 861.9G Linux filesystem
/dev/sda3 1808625664 1875384319 66758656 31.9G Linux swap
So we can either look for a disk the same size as the SD card, in my case it's /dev/sde
(which already has two partitions in this case):
Disk /dev/sde: 59.5 GiB, 63864569856 bytes, 124735488 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xea0e7380
Device Boot Start End Sectors Size Id Type
/dev/sde1 8192 131071 122880 60M c W95 FAT32 (LBA)
/dev/sde2 131072 124735487 124604416 59.4G 83 Linux
Or we can check the device list before, and after inserting the device.
Before:
paul@box [09:41:17] [~/Downloads]
-> % sudo fdisk -l | grep 'Disk /dev/'
Disk /dev/sda: 894.3 GiB, 960197124096 bytes, 1875385008 sectors
Disk /dev/sdb: 232.9 GiB, 250059350016 bytes, 488397168 sectors
Disk /dev/sdc: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Disk /dev/sdd: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
After:
paul@box [09:41:21] [~/Downloads]
-> % sudo fdisk -l | grep 'Disk /dev/'
Disk /dev/sda: 894.3 GiB, 960197124096 bytes, 1875385008 sectors
Disk /dev/sdb: 232.9 GiB, 250059350016 bytes, 488397168 sectors
Disk /dev/sdc: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Disk /dev/sdd: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Disk /dev/sde: 59.5 GiB, 63864569856 bytes, 124735488 sectors
It's quite likely it will come after the last used letter, for example if you last device is /dev/sdc
then it's likely to be /dev/sdd
.
Now that we know the device, copying the data is simple -
paul@box [09:51:19] [~/Downloads]
-> % sudo dd if=2015-11-21-raspbian-jessie-lite.img of=/dev/sde bs=1M
1391+0 records in
1391+0 records out
1458569216 bytes (1.5 GB) copied, 93.7754 s, 15.6 MB/s
Now the card should boot Raspbian in the Raspberry Pi 2.
Step 2 - SSH Access
When powering up the Pi with a monitor connected it should boot into Raspbian and tell you it's IP. The Lite version does not boot into a graphical (X) environment so you will be presented with a login prompt.
[TODO: Image Here]
Once you have the IP you should be able to manage it externally:
paul@box [04:54:04] [~]
-> % ssh pi@192.168.1.57
The authenticity of host '192.168.1.57 (192.168.1.57)' can't be established.
ECDSA key fingerprint is SHA256:2pI1YWSU6pjrr0Q15kriKfuFjgIL4GwnWmDtLY1eyCA.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.57' (ECDSA) to the list of known hosts.
pi@192.168.1.57's password:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pi@raspberrypi:~ $
The default username is pi
and password is raspberry
.
Step 3 - Configuration & Security
Set the hostname:
sudo vi /etc/hostname
I set it to musicbox
.
It will pick up after a reboot:
pi@musicbox:~ $ hostname
musicbox
When running commands like sudo you may see:
sudo: unable to resolve host musicbox
If your hostname does not match a valid DNS entry you'll need to update the /etc/hosts
file. Note the last entry probably read raspberrypi
which I have changed to musicbox
.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.1.1 musicbox
Add your management user:
pi@raspberrypi:~ $ sudo adduser paul
Adding user `paul' ...
Adding new group `paul' (1001) ...
Adding new user `paul' (1001) with group `paul' ...
Creating home directory `/home/paul' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for paul
Enter the new value, or press ENTER for the default
Full Name []: Paul
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
Allow the new user sudo access:
sudo usermod -aG sudo paul
Login as the new user and remove the pi
user:
paul@musicbox:~ $ sudo userdel pi
Step 4 - Install Mopidy
The steps here are based on the Official Install Guide for Debian.
paul@musicbox:~ $ wget -q -O - https://apt.mopidy.com/mopidy.gpg | sudo apt-key add -
OK
paul@musicbox:~ $ sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/jessie.list
paul@musicbox:~ $ sudo apt-get update
[ long output removed ]
Fetched 9,299 kB in 20s (455 kB/s)
Reading package lists... Done
paul@musicbox:~ $ sudo apt-get install mopidy
[ long output removed ]
0 upgraded, 145 newly installed, 0 to remove and 21 not upgraded.
Need to get 39.3 MB of archives.
After this operation, 130 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
[ long output removed ]
paul@musicbox:~ $
The install should only take a few minutes - though that depends in part on the speed of your internet connection.
Once complete, install the Spotify plug-in:
paul@musicbox:~ $ sudo apt-get install mopidy-spotify
[ long output removed ]
0 upgraded, 7 newly installed, 0 to remove and 21 not upgraded.
Need to get 1,141 kB of archives.
After this operation, 3,551 kB of additional disk space will be used.
Do you want to continue? [Y/n]
[ long output removed ]
paul@musicbox:~ $
And the last piece to install is the musicbox plug-in. The project's GitHub Page suggests using pip to install - which is generally easier and what I'd recommend:
paul@musicbox:~ $ sudo apt-get install python-pip
[ long output removed ]
paul@musicbox:~ $ sudo pip install Mopidy-MusicBox-Webclient
[ long output removed ]
paul@musicbox:~ $
Step 5 - Configuring Mopidy
If you run mopidy without any configuration (in the foreground) you'll likely get output that looks similar to this:
paul@musicbox:~ $ mopidy
INFO Starting Mopidy 1.1.1
INFO Loading config from builtin defaults
INFO Loading config from command line options
INFO Creating dir /home/paul/.cache/mopidy
INFO Creating dir /home/paul/.config/mopidy
INFO Creating dir /home/paul/.local/share/mopidy
INFO Loading config from builtin defaults
INFO Loading config from command line options
INFO Creating file /home/paul/.config/mopidy/mopidy.conf
INFO Initialized /home/paul/.config/mopidy/mopidy.conf with default config
INFO Enabled extensions: mpd, http, stream, m3u, softwaremixer, file, musicbox_webclient
INFO Disabled extensions: spotify, local
WARNING Found local configuration errors, the extension has been automatically disabled:
WARNING local/media_dir must be set.
WARNING Found spotify configuration errors, the extension has been automatically disabled:
WARNING spotify/username must be set.
WARNING spotify/password must be set.
WARNING Please fix the extension configuration errors or disable the extensions to silence these messages.
INFO Starting Mopidy mixer: SoftwareMixer
INFO Starting Mopidy audio
INFO Starting Mopidy backends: StreamBackend, M3UBackend, FileBackend
INFO Creating dir /home/paul/.local/share/mopidy/m3u
INFO Loaded 0 M3U playlists from /home/paul/.local/share/mopidy/m3u
INFO Audio output set to "autoaudiosink"
INFO Starting Mopidy core
INFO Starting Mopidy frontends: MpdFrontend, HttpFrontend
INFO MPD server running at [::ffff:127.0.0.1]:6600
INFO HTTP server running at [::ffff:127.0.0.1]:6680
The Warnings tell us that Spotify is not configured (and disabled) as is local media (which I'm not bothered about for now).
The configuration for mopidy is stored within /etc/mopidy/mopidy.conf
.
Add the following configuration:
[spotify]
username = xxxxx
password = xxxxx
bitrate = 320
volume_normalization = true
The GitHub Page for the Spotify Plugin lists the configuration options available - as we pay for Premium we might as well have the highest bit rate!
Then we can test by running mopidy in the foreground again, to tell it to use the default config file we use the --config argument, and to avoid changing permissions on the config file it is run using sudo as by default only the mopidy user has access:
paul@musicbox:~ $ sudo mopidy --config /etc/mopidy/mopidy.conf
INFO Starting Mopidy 1.1.1
INFO Loading config from builtin defaults
INFO Loading config from /etc/mopidy/mopidy.conf
INFO Loading config from command line options
INFO Enabled extensions: spotify, mpd, http, stream, m3u, softwaremixer, file, musicbox_webclient, local
INFO Disabled extensions: none
INFO Starting Mopidy mixer: SoftwareMixer
INFO Starting Mopidy audio
INFO Starting Mopidy backends: SpotifyBackend, StreamBackend, M3UBackend, FileBackend, LocalBackend
INFO Creating dir /var/cache/mopidy/spotify
INFO Creating dir /var/lib/mopidy/spotify
INFO Loaded 0 M3U playlists from /var/lib/mopidy/playlists
INFO Audio output set to "autoaudiosink"
INFO No local library metadata cache found at /var/lib/mopidy/local/library.json.gz. Please run `mopidy local scan` to index your local music library. If you do not have a local music collection, you can disable the local backend to hide this message.
INFO Loaded 0 local tracks using json
INFO Starting Mopidy core
INFO Starting Mopidy frontends: MpdFrontend, HttpFrontend
INFO MPD server running at [::ffff:127.0.0.1]:6600
INFO HTTP server running at [::ffff:127.0.0.1]:6680
INFO Logged in to Spotify in online mode
Now Spotify has logged in.
Note: If you specify bad credentials you will likely get an error like:
ERROR Spotify login error: <ErrorType.BAD_USERNAME_OR_PASSWORD: 6>
While Mopidy started you may have noticed two log entries of interest:
INFO MPD server running at [::ffff:127.0.0.1]:6600
INFO HTTP server running at [::ffff:127.0.0.1]:6680
The MPD server is Mopidy's own API (for which there are various clients).
The HTTP server is where the musicbox web plugin is mounted. As the log line suggests it is bound only to the loopback interface, which can be verified using lsof
.
paul@musicbox:~ $ sudo lsof -i | grep 6680
mopidy 8993 root 20u IPv4 17770 0t0 TCP localhost:6680 (LISTEN)
This means that right now no clients on the same network as the Pi can access mopidy.
The HTTP Configuration of the Mopidy docs explains the config that can be specified - I changed to the following:
[http]
enabled = true
hostname = 0.0.0.0
port = 6680
zeroconf = Mopidy HTTP server on $hostname
The Port is unchanged (but I'd rather have it explicity in the config), and it now binds to all interfaces. On restarting Mopidy we should see:
INFO MPD server running at [::ffff:127.0.0.1]:6600
INFO HTTP server running at [::ffff:0.0.0.0]:6680
And if I browse to http://192.168.1.57:6680/
from another computer on the nextwork I am redirected to http://192.168.1.57:6680/mopidy/
which presents a page which in turn lets us navigate to MusicBox.
Now we can see if it works.
It doesn't. Upon trying to play a track there is no sound, and the following message shows in the log:
WARNING Element doesn't implement handling of this stream. Please file a bug.
It seems that by default the Pi sends audio out over HDMI, the following command forces it to use the Headphone Jack:
sudo amixer cset numid=3 1
The final argument in the above command is the output:
0=auto
1=analog
2=HDMI
TODO: sudo apt-get install mopidy-spotify mopidy-alsamixer
Additionally, OK AT FULL NOISY LOWER, KNOWN ISSUE - USB
[audio]
mixer = alsamixer
output = alsasink
[alsamixer]
card = 0
control = PCM
[softwaremixer]
enabled = false
Final for USB:
[spotify]
username = xxx
password = xxx
bitrate = 320
volume_normalization = true
[http]
enabled = true
hostname = 0.0.0.0
port = 6680
zeroconf = Mopidy HTTP server on $hostname
[audio]
mixer = alsamixer
output = alsasink device=plughw:1,0
[alsamixer]
card = 1
control = PCM
[softwaremixer]
enabled = false
Step 6 - Automation
Pre clean up (made as root)
sudo rm /var/log/mopidy/mopidy.log
START ON BOOT
sudo dpkg-reconfigure mopidy
Start on boot
- Reboot to test
Once booted you can tail the logs with tail -f /var/log/mopidy/mopidy.log
to check everything is working as expected.
We found at times the service got a bit stuck so we have a cron job to restart it at 7 PM which conveniently also stops the music should anyone forget.
The job is in root's crontab - sudo nano crontab -e
0 19 * * * /etc/init.d/mopidy restart
Note: The Raspian Jessie Lite image seems to have NTP installed as default so the time syncs regularly.