The Spotify Box acts as a middleman between the official Spotify app and a new or existing home audio system – allowing you to connect your smart phone to your audio setup and stream music throughout your house. The basic premise of the device revolves around Spotify’s feature known as Spotify Connect which allows you to use your Spotify app as a remote to control different devices that are on the same Wi-Fi network or associated with your account. The Spotify Box has stereo RCA jacks so you can simply plug it into a preamp, mixer, or amplifier and start playing your tunes. Connecting to a Wi-Fi network is simple enough. Just download the Spotify Box app and give the pushbutton a press to start searching for networks. Then you can choose your Wi-Fi network through my app to get connected. You can also connect over ethernet.
The origin of the Spotify Box stems from an older project of mine where I created a Bluetooth speaker with a RGB parallel touchscreen interface to display the song, volume, connectivity, etc. In the process of developing this speaker, I stumbled across Spotifyd, an open-source Spotify client that runs as a UNIX daemon. This ultimately sparked the idea to create the Spotify Box. I’ll walk you through the features and process I went through while designing and developing the Spotify Box.
Hardware / Electronics Design
At the heart of this device lies an Allwinner V3S, a rather old SoC that comes with a 1.2GHz ARM Cortex-A7 processor, 64MB of integrated DDR2 SDRAM, A high-quality 92 dB audio codec that provides a stereo DAC for playback, an integrated 10/100M Ethernet PHY, two SD/MMC controllers, and then some other peripherals like USB, SPI, UART, and I2C. The combination of a little bit of internal DRAM and the ability to provide stereo audio makes the perfect combination for a low-cost music streaming client.
Here’s a high level block diagram to illustrate the basic functionality of the Spotify Box.
Spotify Box Block Diagram
The Spotify Box boots from a MicroSD card interfaced with one of the SDC controllers, which makes it super easy to swap out different images. I could also opt for an SPI-NAND flash chip to boot, but I have a surplus of microSD cards laying around, and it’s nice to be able to quickly swap an SD card when testing different features.
In order for the Spotify Box to run Spotifyd, it needs internet access. This can either be done wirelessly or wired; the wireless connection uses the RTL8723DS Wi-Fi / Bluetooth Module. The neat part of this chip is that it has an integrated SDIO interface for controlling the Wi-Fi and a high speed UART interface that supports Dual-Mode Bluetooth (Bluetooth Classic and BLE). For a wired connection, a RJ45 jack with integrated magnetics is interfaced with the internal ethernet PHY.
The RCA Jack is connected to the stereo audio output from the V3S’s DAC.
The RBG LED illuminates different colors to show the status of the Spotify Box, e.g. internet connection, song changes, and pairing mode. This LED has an SPI interface, but it is not actually utilizing the V3S’s SPI… due to quickly rushing through drawing the schematic, I connected the LED to the wrong pins. Luckily I was able to create a little driver to use GPIOs to bit-bang the brightness and colors.
The last component shown in the block diagram is the push button. When pressed, the Spotify Box enters a pairing mode, in which a user can connect to their Wi-Fi network through the Spotify Box app.
The Allwinner V3S has many perks (integrated DRAM, a stereo audio codec, and an ethernet PHY), but is it cost effective? Well it really depends on how many you want to build and whether or not you are buying components in the United States. In my case, as a broke college student, I bought most components on Digi-Key (LDOs, Oscillators, Passives, etc.) The V3S and Wi-Fi modules are really only available overseas, so if you’re impatient it can get quite expensive if you need to ship something fast. Luckily, ordering the physical PCB is not too taxing on the wallet, as I’m only using a four layer stack up.
I’ve included a graph of the price you would have to spend on a couple different microprocessors to get the same features as the Allwinner V3S. These prices seem to drift around quite a bit, so take this with a grain of salt. All of these prices were taken from Digi-Key, except for the price for the Allwinner V3S, as it is not available there. This is where using the V3S can become problematic. You have to order it from overseas, which takes more time and can cost more money if you need it quickly. I pulled the price point for the Allwinner V3S from Aliexpress.
The Allwinner V3S has an internal 92dB SNR audio codec which is definitely not best SNR that I’ve seen in an audio codec, but it does the job for most people. I wasn’t able to find much more information about this internal codec, so I searched Digi-Key for external audio codecs with similar signal to noise ratios. The cheapest stereo DAC that I could find on Digi-Key was $1.75.
The internal PHY is really tough to beat, an ethernet PHY is usually around $2.00 (on Digi-Key). Eliminating the need for an external ethernet PHY reduces the amount of routing, passives, and hopefully mistakes when building a board up. Of course even with an ethernet PHY you will still need your magnetics. I chose to go with the Hanrun HR911103A, an RJ45 connector with integrated magnetics!
Finding an external DRAM chip with at least 64MB of RAM will also add a couple bucks to the bill of materials. Most microprocessors do not come with any internal DRAM. This really separates the V3S from other parts.
I’ve been pretty partial to the V3S as far as its integrated features, but this chip has one flaw that has almost ruined my life. The 128-pin QFP is tough to solder; In fact this is absolutely the most difficult package that I have dealt with ever. The 0.4mm pitch pins are tighter than the pitch of any other package I’ve used — so much so that the solder paste seems to stick in the stencil instead of onto the board. Even if you end up with a good paste job, the pins seem to love shorting to each other even if your alignment is spot on.
Perhaps I need to switch up my solder paste or let it warm up adequately before applying the paste to the board. I am still maybe a novice to soldering boards up, but I’ve never encountered any problems on BGAs, QFNs, or any other package. Switching to a BGA package (maybe the Allwinner S3?) would make building these boards up much easier.
I’m using a four layer stack up, with signals on the top and bottom layers, and ground and power planes on the inner layers. I’ll go over some of the component placement on this board. All of the regulators are placed together and away from important signals; this is mostly unnecessary considering two of these regulators are linear regulators, which aren’t noisy. However, for the 1V2 logic level, I’m using a buck converter, which can be quite noisy.
The Wi-Fi / Bluetooth module located in the lower left, is using a 2.4 GHz meandered inverted-F antenna. It’s important to note that the ground pour and inner power planes are pulled back under the antenna to enable it to properly resonate.
There were many learning opportunities that I encountered throughout working on this project. I don’t know about you, but as a senior undergraduate student majoring in EE, I did not have a whole lot of experience with network provisioning, BLE, app development, etc. (which is kind of sad, but I didn’t write the curriculum). It is safe to say that I ventured out of my domain of KVL and KCL, and tried something new.
Board Support Package
Getting the built up board to boot was relatively straight forward, but has many moving parts. We need a Linux kernel, bootloader, root filesystem, and a cross-compilation toolchain. For anyone looking to start an embedded Linux project, I would definitely recommend using Buildroot or Yocto to automate the processes of building a Linux system. Buildroot supports a variety of microprocessors and has many default configurations for development boards like the Lichee-Pi zero, which just happens to use the V3S as it’s processor. This provides a great starting for bringing up a board.
With a lot of the groundwork laid out, I needed to search for a solution to figure out how to get my Bluetooth / Wi-Fi chip to work, which was not in the Linux Kernel. This required cross-compiling some firmware and muxing some gpios to act as dummy regulators to supply power to the chip. The addressable LED on my board needed a driver written for it. This RGB led has an IC controller that uses an SPI protocol (Clock and Data). The driver uses GPIOs to essentially bit-bang the brightness, and colors.
Any Unix-based operating system has some sort of init system. Init is the first process started by the kernel while booting. Historically the init system would begin starting different tasks serially, waiting for each task to finish loading before going on to the next task. This is one of the reasons it takes forever for some Linux systems to boot up.
The Spotify Box uses Systemd for its init system. Systemd provides a system and service manager that starts the rest of the system. It keeps track of processes, starts daemons, manages networks, logs events, and unlike most init systems it parallelizes elements of its startup sequence, making faster boot times (sometimes).
One challenge that I faced was wrapping my head around how I would be able to accomplish connecting a device to a Wi-Fi network without using a serial console. Luckily, with my loads of experience making smart phone apps (not really), I was able to make an app that utilizes a smart phone’s Bluetooth Low Energy functionality to scan, connect, and transfer network information to and from the Spotify Box.
In the process of developing this app I had to gain an understanding of the BLE protocol, and about Bluez, the Linux Bluetooth stack. I’ve included a block diagram to illustrate how my network provisioning works. There a few terms to become familiar with before diving into the network provisioning.
- Generic Attribute Profile (GATT): Defines the format of services and their characteristics. It also defines the procedures used to interface with characteristics.
- Services: A service is a grouping of one or more characteristics. Every service has a unique numeric ID (UUID), which have 16-bit or 128-bit values.
- Characteristics: A characteristic is part of every service and represents the lowest piece of information that a server can expose to a client.
Spotify Box Network Provisioning Block Diagram
The Spotify Box uses two services: the first acting as a “Scanning” service, so essentially it will populate characteristics with all of the networks that the Spotify Box finds when scanning. This service is both readable and writable so that a user can “choose” the network. The second service is only writable and contains two characteristics, one being the SSID and the other being the password. The SSID and Password are then passed to NetworkManager which configures and connects to the network chosen via the app. I ended up finding a pretty simple Python API that makes it painless to configure, scan, activate, and get network details. I use this to get the network status and to list the actual networks that then get transferred over BLE to my app.
I have little experience with making apps for Android or iOS, but I found Xamarin Forms to be intuitive to build apps. Xamarin.Forms is an open-source framework for building apps on Android, Windows and iOS. Xamarin Forums will be deprecated in November 2021, but will be replaced with .NET MAUI. It is likely I will experiment with this in the future.
The pivotal benefit of using Xamarin.Forums is that you can push your code to multiple platforms instead of building each platform UI separately. Consequently, this allows you to develop your app for iOS or Android seamlessly in a very short time. Also for a simple app like mine, with only a couple tabs and lists, you don’t have to worry about loading any crazy animations. I’m using a Bluetooth low energy plugin to access Bluetooth functionality https://github.com/xabre/xamarin-bluetooth-le. This plugin makes scanning for BLE devices super simple, allowing you to scan for certain periods of time and then connect to that device if the GUID is known. Once connected, you can read or write to the devices services, characteristics, and descriptors.
Getting the Spotify Box to separately run Spotifyd, NetworkManager, manage the different services, and run some scripts to check the push-button, internet connection, and update the LED was somewhat trivial. Combining this all together is where things got a little sticky. This device only has 64 MB of RAM, with everything running there is only a couple MB left of free memory which makes it prone to page allocation failures. The solution to freeing up enough memory was rather ugly and involved a few compromises.
The two culprits that eat up memory (excluding Spotifyd) are NetworkManager and my “mainprogram” service. Disabling NetworkManager alone frees up quite a bit of memory, usually around 30MB. The mainprogram service actually a oneshot service that is simply going through a while loop, checking for an internet connection and a push button press. Disabling the mainprogram service saves another 10MB. This gets the amount of allocated memory to hover anywhere from 50MB to 60MB. I threw together a little table to illustrate how much RAM some of these services allocate. Note: Commited_AS isn’t the best metric to use for calculating how much memory a system is using, as it shows an estimate of how much RAM is needed to complete the workload.
Spotify Box Memory Usage
I began the initial design of an enclosure for the Spotify Box after very late into the projects design. I made sure to include mounting holes that would fit an M3 screw to fasten the board to the enclosure, but truthfully I could’ve included a few more to really anchor the board down. I added accurate STEP models of the major components in Altium so that I could have a good estimate of how the enclosure could look.
I made several iterations of the enclosure design, one being a 3D printed model, which ended up looking pretty sleek after a lots of sanding and painting.
My initial first stab at making an enclosure used a design that consisted of three components: the top, bottom, and a face plate. The face plate was designed to be fitted onto the front of the electronics and then slide into the top piece. The downside to this was that it just didn’t look quite right.
After going back to the drawing board I decided to make the enclosure out of a block of wood. I picked up some scraps of walnut and afrormosia at a local hardwood store to get a feel for how these woods would fare if thrown into a mill. The afrormosia has an interesting look, but it didn’t cut nearly as clean as the walnut, although this may have been a result of the saw I was using to cut them down to size.
I decided that walnut would be the best choice for an enclosure, but before I could do any cutting, I had to generate toolpaths for the Carvey Desktop CNC machine in UNL’s Innovation Studio. I generated toolpaths for the Carvey to follow by using Fusion 360. This required me to make a setup that specified the speed, bit size, spindle size, etc. and then I had to figure out what path the machine should take to remove all of the wood. I was limited to using an ⅛” straight bit, so the carving took several hours.
Once the CNC machine was done, I had to make a decision to either throw the stock back in to carve the face of the enclosure or to freehand the rest. I chose to freehand the face, ultimately because I didn’t trust the Carvey to cut it precisely since having the enclosure on it’s side meant much less clamping space to secure the wood to the table. I brought the shell of the enclosure home and began drilling pilot holes that would allow me to slowly shave off material using files and razor blades. The final step of the Spotify Box enclosure was to pick a product to finish the wood with. I used a Tung Oil because it gave the walnut much more depth and a warm matte appearance.
The process of creating the Spotify Box taught me a lot. I spent hundreds of hours learning about Linux, debugging, writing software, and designing the form and function of this device. I am both relived and reluctant about leaving this project. There will likely be a real second revision — utilizing a different microprocessor and external DRAM —sometime in the future, but for the next few months I’ll probably take a break from this project.