\documentclass{report}
\author{\href{mailto:me@xengineering.eu}{me@xengineering.eu}}
\title{soundbox documentation}
\date{\input{../date.txt}- version \input{../version.txt}}
\usepackage{graphicx}
\graphicspath{ {./diagrams/} }
\usepackage{array}
\usepackage{hyperref}
\usepackage{parskip}
\usepackage{upquote}
\begin{document}
\maketitle
\tableofcontents
\listoffigures
\listoftables
\chapter{Introduction}
\label{chap:introduction}
\texttt{soundbox} is a small Raspberry Pi based device with a micro USB power
input and two cinch connectors as audio output. It can be used to connect
classic audio systems to a WLAN network.
Music or other audio content can be streamed to the \texttt{soundbox} device.
Using multiple devices allows to build up a multi-room audio setup.
This project is Open
Hardware\footnote{\href{https://en.wikipedia.org/wiki/Open-source\_hardware}{https://en.wikipedia.org/wiki/Open-source\_hardware}}
and licensed under the CERN Open Hardware License S (see \autoref{license}).
\section{Current state}
The following features are supported by the given \texttt{soundbox} version.
\begin{itemize}
\item audio streaming with original sound quality
\item multi room audio without audible delay
\item 3D printed case to protect internals
\item safe shutdown by power cut
\end{itemize}
Nevertheless the usage of \texttt{soundbox} is restricted by the limitations
below.
\begin{itemize}
\item only the command line tool \texttt{ffmpeg} is currently supported to
stream content to \texttt{soundbox} devices
\item no user friendly way to set the WLAN name (SSID) and password
\item no user friendly update mechanism
\end{itemize}
Improvements regarding these limitations are planned but future progress cannot
be guaranteed.
\section{Getting soundbox}
Further information might be available via the \texttt{soundbox} project
page\footnote{\href{https://xengineering.eu/git/soundbox}{https://xengineering.eu/git/soundbox}}.
The source code can be browsed on the cgit
instance\footnote{\href{https://cgit.xengineering.eu/soundbox}{https://cgit.xengineering.eu/soundbox}}
hosting the \texttt{soundbox} code.
The complete source code can be retrieved with the version control software
Git\footnote{\href{https://git-scm.com/}{https://git-scm.com/}}.
\begin{verbatim}
git clone https://cgit.xengineering.eu/soundbox
\end{verbatim}
The included \texttt{README.md} file describes how to generate all required
files from the source code. This is currently the only way to get them. This
PDF file is one of the resulting files.
\chapter{User guide}
In addition to \autoref{chap:introduction} `\nameref{chap:introduction}` the
following sections document everything required to use \texttt{soundbox}
devices.
\section{Getting the IP address}\label{get-ip}
Each soundbox device has a label with the MAC address of the device in a format
like \texttt{xx:xx:xx:xx:xx:xx}.
It can be converted to an IPv6 link-local address starting with
\texttt{fe80::}. This online
tool\footnote{\href{https://ben.akrin.com/mac-address-to-ipv6-link-local-address-online-converter/}{https://ben.akrin.com/mac-address-to-ipv6-link-local-address-online-converter/}}
is a convenient way to do that.
\section{Playing audio} \label{playing-audio}
Audio can be streamed to \texttt{soundbox} devices with the \texttt{ffmpeg}
command. Audio files or internet stream addresses can be passed as input. The
IPv6 address retrieved in \autoref{get-ip} should be used as IP and the
interface has to be the one showing up in \texttt{ip link show} which is
connected to the same network as the \texttt{soundbox} device.
\begin{verbatim}
ffmpeg -re -i -acodec flac -f ogg tcp://[%]:5316
\end{verbatim}
The part starting with \texttt{-acodec} can be repeated multiple times to
stream to more than one soundbox devices for a multi-room audio setup.
More details about FFmpeg streaming can be read from the FFmpeg streaming
guide\footnote{\href{https://trac.ffmpeg.org/wiki/StreamingGuide}{https://trac.ffmpeg.org/wiki/StreamingGuide}}.
\chapter{Production guide}
The given chapter contains documentation about how to produce a
\texttt{soundbox} device.
\section{Bill of materials}
The parts below are required for one \texttt{soundbox} device.
\begin{itemize}
\item Raspberry Pi Zero 2 W
\item HiFiBerry DAC+ Zero
\item micro SD card (at least 200 MB capacity)
\item 3D printed parts from \texttt{production.stl}
\item 8 x M3x3 clinch nuts
\item 4 x M3x22 ISO 4762 bolts
\item 4 x M3x10 ISO 4762 bolts
\item (if not already on the Raspberry Pi) 1 x 20x2 or 2 x 20x1 pin headers
\end{itemize}
The following accessories are optional.
\begin{itemize}
\item micro USB power supply
\item audio adapter cable(s) from Cinch to other interfaces
\item adapter mini HDMI to other interfaces
\end{itemize}
\section{Alpine Linux installation on a Raspberry Pi}
Alpine Linux for the Raspberry Pi can be downloaded from the Alpine
Linux\footnote{\href{https://alpinelinux.org/}{https://alpinelinux.org/}}
download page. This section describes the installation procedure for the
variant targeting the AArch64 architecture provided as a compressed tar
archive. It can be downloaded and verified against a checksum with the
\texttt{wget} and \texttt{sha256sum} utilities.
\begin{verbatim}
wget https://dl-cdn.alpinelinux.org/alpine/v3.19/releases\
/aarch64/alpine-rpi-3.19.0-aarch64.tar.gz
wget https://dl-cdn.alpinelinux.org/alpine/v3.19/releases\
/aarch64/alpine-rpi-3.19.0-aarch64.tar.gz.sha256
sha256sum -c alpine-rpi-3.19.0-aarch64.tar.gz.sha256
\end{verbatim}
The image has to be flashed to a SD card which will be inserted into the
Raspberry Pi. This SD card has to be put into a Linux PC first. In Linux every
SD card is represented as a block device like \texttt{sda} or \texttt{sdb} with
a file path like \texttt{/dev/sda}. Possibly existing partitions on the SD card
are represented with that path and a number as suffix like in
\texttt{/dev/sda1}. The program \texttt{lsblk} gives an overview of the
currently connected block devices. The correct device name for the SD card can
be identified by its size.
\begin{verbatim}
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 1 59,5G 0 disk
`-sda1 8:1 1 59,5G 0 part
\end{verbatim}
The SD card has to be formatted with a single bootable FAT32 partition. This
can be achieved with the command line program \texttt{parted}. First a
\texttt{msdos} partition table has to be created followed by a partition taking
the full size of the SD card. The boot flag of the partition is enabled and a
FAT32 file system is created with \texttt{mkfs.vfat}. All of these operations
require \texttt{root} privileges.
\textbf{Warning:} These operations might cause data loss if the wrong device is
specified. It has to be asserted that not the wrong block device (e.g. the PC
disk) is chosen.
\begin{verbatim}
parted /dev/ mklabel msdos
parted /dev/ mkpart primary fat32 0% 100%
parted /dev/ toggle 1 boot
mkfs.vfat /dev/1
\end{verbatim}
After the file system is created the downloaded Alpine Linux tar archive has to
be extracted to it. A temporary mount point can be created with
\texttt{mktemp}. After mounting with \texttt{mount} the archive can be
extracted with \texttt{tar}. Finally the file system is unmounted and it is
waited for all buffered write operations to the SD card with \texttt{sync}.
Also these command require \texttt{root} privileges.
\begin{verbatim}
mountpoint=$(mktemp -d)
mount /dev/1 "$mountpoint"
tar -C "$mountpoint" -xf alpine-rpi-3.19.0-aarch64.tar.gz
umount /dev/1
sync
\end{verbatim}
Afterwards the SD card can be ejected. The installation is completed while the
system configuration is not covered.
\section{Configure Alpine Linux for soundbox}
An Alpine Linux installation can be configured as a soundbox host with the
steps explained in the given section.
For this configuration the SD card with an Alpine Linux installation has to be
inserted into the Raspberry Pi. Furthermore a keyboard, monitor and USB power
supply has to be connected. A mouse is not required. As soon as the power
supply is attached text from the boot process should be shown on the monitor.
\subsection{Basic Alpine Linux setup}
After logging in with the username \texttt{root} and no password basic system
configuration on Alpine Linux can be done interactively with
\texttt{setup-alpine}. The Alpine Linux installation
guide\footnote{\href{https://wiki.alpinelinux.org/wiki/Installation}{https://wiki.alpinelinux.org/wiki/Installation}}
contains further details. A diskless installation should be chosen for
soundbox. This makes sure that the operating system is running from random
access memory (RAM) and thus the system can be powered off by just removing the
power supply.
\begin{verbatim}
setup-alpine
\end{verbatim}
These changes are only temporary until they are made persistent as described in
section~\ref{lbu-persistence}.
\subsection{Enabling IPv6}
While Alpine Linux usually uses IPv4 only \texttt{soundbox} requires IPv6 for
streaming. It can be enabled by adding a line to the related configuration
file.
\begin{verbatim}
echo 'iface wlan0 inet6 auto' >> /etc/network/interfaces
\end{verbatim}
The change will take effect after the final reboot.
\subsection{Retrieve MAC address}\label{retrieve-mac}
To be able to connect to the device in a reliable and simple way using IPv6 the
MAC address of the \texttt{wlan0} interface has to be retrieved and noted down.
\begin{verbatim}
ip link show wlan0 # MAC address right after 'link/ether'
\end{verbatim}
This MAC address can be converted to the corresponding IPv6 link-local address
used for streaming audio to \texttt{soundbox}.
\subsection{Package installation}
soundbox requires packages from the Alpine Linux community package repository.
Since this is not enabled by default it has to be done manually. This can be
done by editing the \texttt{/etc/apk/repositories} file with an editor like
\texttt{vi}. The hash sign \texttt{\#} in front of the URL to the community
repository has to be removed. Instead also \texttt{sed} can remove this sign on
a fresh installation quite easily since the community repository is the only
one with such a sign in front of it.
\begin{verbatim}
sed -i 's/^#//g' /etc/apk/repositories
\end{verbatim}
With an enabled community repository all required packages for soundbox can be
installed. The list with required packages can be written to the
\texttt{/etc/apk/world} file with \texttt{echo}. Running \texttt{apk add} after
it will install all of these packages including their dependencies and removes
everything else. This makes sure that always exactly the same set of packages
is used for a soundbox device.
\begin{verbatim}
echo 'alpine-base
alsa-utils
alsaconf
busybox-mdev-openrc
chrony
ffplay
iw
openssh
openssl
wpa_supplicant' > /etc/apk/world
apk add
\end{verbatim}
\subsection{Audio configuration}
The only user on a soundbox device is \texttt{root}. Even that super user has
to be added to the \texttt{audio} group to be able to play sound via ALSA. This
can be achieved with \texttt{addgroup}.
\begin{verbatim}
addgroup root audio
\end{verbatim}
soundbox uses the HiFiBerry DAC+ Zero shield to output high-quality audio. This
shield needs to be enabled as an audio output by specifying a Devicetree
overlay in the Raspberry Pi configuration file with \texttt{echo}. Since this
file is stored on the SD card only which is mounted as read-only it has to be
remounted with the read-write option first.
\begin{verbatim}
mount -o remount,rw /media/mmcblk0p1
echo 'dtoverlay=hifiberry-dac' >> /media/mmcblk0p1/config.txt
\end{verbatim}
Only the HiFiBerry DAC+ Zero is officially supported for soundbox. Nevertheless
\autoref{hardware-variants-table} shows the configuration line for other
hardware.
\begin{table}[h]
\begin{center}
\begin{tabular}{ | l | l | l | }
\hline
Audio hardware & Config line & Support \\
\hline
\hline
HiFiBerry DAC+ Zero & \texttt{dtoverlay=hifiberry-dac} & official \\
\hline
HiFiBerry Amp2 & \texttt{dtoverlay=hifiberry-dacplus} & unofficial \\
\hline
Raspberry Pi 4 audio & \texttt{dtparam=audio=on} & unofficial \\
\hline
\end{tabular}
\end{center}
\caption{Audio settings for soundbox variants}
\label{hardware-variants-table}
\end{table}
It has to be noted that the unofficially supported hardware does not fit into
the \texttt{soundbox} case.
\subsection{soundbox script}
soundbox uses the command \texttt{ffplay} for playback. A script has to be
added to call the command over and over again to allow new connections. The
script can be added with \texttt{echo} and is made executable with
\texttt{chmod}. \texttt{lbu add} will make sure that the script will be made
persistent by calling \texttt{lbu commit} later (see
section~\ref{lbu-persistence}).
\begin{verbatim}
echo '#!/bin/ash
set -e
while true
do
ffplay -autoexit tcp://[%wlan0]:5316?listen
done' > /usr/local/bin/soundbox
chmod 755 /usr/local/bin/soundbox
lbu add /usr/local/bin/soundbox
\end{verbatim}
\texttt{} has to be replaced by the IPv6 link-local address of the
\texttt{wlan0} interface. It can be retrieved by calling \texttt{ip addr show}
and starts with \texttt{fe80::}.
This makes the command \texttt{soundbox} available on the system.
\subsection{Service configuration}
To receive audio streams via the network it is required to run the
\texttt{soundbox} command continuously. This is possible by creating a service
file for the OpenRC init system with \texttt{echo}, making that executable with
\texttt{chmod} and enabling the service with \texttt{rc-update}. Also here
\texttt{lbu add} makes sure that the service file will be made persistent later
(see section~\ref{lbu-persistence} for details).
\begin{verbatim}
echo '#!/sbin/openrc-run
name="soundbox"
command="/usr/local/bin/soundbox"
command_args=""
pidfile="/run/soundbox.pid"
command_background=true' > /etc/init.d/soundbox
chmod 755 /etc/init.d/soundbox
rc-update add soundbox default
lbu add /etc/init.d/soundbox
\end{verbatim}
This will make sure the soundbox script is run on every boot.
\subsection{Persistence of changes and reboot}\label{lbu-persistence}
Since a diskless Alpine Linux installation is preferred for soundbox the
changes have to be made persistent with \texttt{lbu}. Rebooting after it makes
sure the SD card is again mounted in read-only mode and all Kernel-affecting
changes can be applied.
\begin{verbatim}
lbu commit -d
reboot
\end{verbatim}
After this reboot the soundbox device should be able to play audio like
described in section~\ref{playing-audio}.
\section{Device label}
A device label has to be added to the \texttt{soundbox} device after assembly
to let the user reference and access the device by the MAC address.
\begin{verbatim}
soundbox
MAC:
\end{verbatim}
The current version \texttt{\input{../version.txt}} and the MAC address noted down in
\autoref{retrieve-mac} should be used.
\chapter{Developer documentation}
\section{Versioning}
This device is versioned with Semantic
Versioning\footnote{\href{https://semver.org}{https://semver.org}}. The
resulting version numbers have the format \texttt{..} like
e.g. \texttt{2.0.3}. While Semantic Versioning is usually targeted at software
only it is here used for the whole device. This includes mechanical, electronic
and software aspects as shown in figure \ref{diagram:device-semver}.
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{device-semver.pdf}
\caption{API for a device versioned with Semantic Versioning}
\label{diagram:device-semver}
\end{figure}
This versioning makes sure that users do not have to care about the device
internals at all. Devices can be seen as atomic from a user's perspective. This
level of granularity is choosen because users are not expected to disassemble
devices or to care about the software internals.
\appendix
\chapter{Changelog}\label{changelog}
All notable changes to \texttt{soundbox} will be documented here.
\texttt{soundbox} adheres to Semantic
Versioning\footnote{\href{https://semver.org/spec/v2.0.0.html}{https://semver.org/spec/v2.0.0.html}}.
\section{0.2.0 - 2024-09-29}
\href{https://cgit.xengineering.eu/soundbox}{https://cgit.xengineering.eu/soundbox}
\paragraph{Added}
\begin{itemize}
\item device label containing project name, version and MAC address
\item unofficial support for HiFiBerry Amp2
\end{itemize}
\paragraph{Changed}
\begin{itemize}
\item switch to IPv6 link-local addresses for streaming
\end{itemize}
\paragraph{Removed}
\begin{itemize}
\item network discovery based on the LLDP protocol
\end{itemize}
\section{0.1.0 - 2024-02-02}
\href{https://cgit.xengineering.eu/soundbox/tag/?h=0.1.0}{https://cgit.xengineering.eu/soundbox/tag/?h=0.1.0}
\paragraph{Added}
\begin{itemize}
\item soundbox device with 3D-printed case
\item lossless WLAN-based multi-room audio streaming
\item network discovery based on the LLDP protocol
\item device resists power cut without any risk
\end{itemize}
\chapter{License}\label{license}
\texttt{\input{../LICENSE.txt}}
\end{document}