Raspberry Music Box
Ich habe hier bereits einmal ein Musikplayer mit einem Raspberry Pi gebaut. Die alte Version bestand noch aus einem Raspi der ersten Generation und hatte ein einfaches monochromes Display.
Der hier vorgestellte Player greift nun auf die breite Auswahl an verfügbarer Peripherie zurück, die mittlerweile für den Raspberry verfügbar ist:
- Raspberry Zero
- Hifiberry MiniAmp
- SPI-Display mit ili9341 kompatiblem Controller
- Taster, LEDs, etc.
Herausgekommen ist ein kleines Wochenendprojekt, das auch ohne allzu große Elektronikkentnisse bewältigt werden kann.
Gehäuse
Schaltung
Eine eigene Platine ist nicht nötig. Taster und Display habe ich direkt, entsprechend der Tabelle, über Litze mit dem Raspberry verbunden.
Zusätzlich habe ich einen Taster an die Reset-Leitung des Raspberry angeschlossen, um den Pi nach dem Shutdown wieder “wecken” zu können.
Funktion | GPIO | PIN |
---|---|---|
Status-LED | 12 | 32 |
Taste Enter | 17 | 11 |
Taste Stop | 22 | 15 |
Taste Down | 27 | 13 |
Taste Up | 5 | 29 |
Taste Ausschalten | 2 | 3 |
Taste Lautstärke + | 3 | 5 |
Taste Lautstärke - | 4 | 7 |
Display MOSI | 10 | 19 |
Display SCK | 11 | 23 |
Display CE | 8 | 24 |
Display DC | 24 | 18 |
Display Reset | 23 | 16 |
Display Led | 25 | 22 |
Software
Die folgende Anleitung bezieht sich auf die Raspbian Version vom 14.02.2020, die es hier herunterzuladen gibt: 2020-02-13-raspbian-buster-lite.zip
Raspbian installieren
Unter Linux kann man das oben verlinkte Image einfach per dd auf eine SD-Karte kopieren. Eine ausführliche Anleitung, auch für andere Betriebssysteme, gibt es hier: Anleitung
WLAN und SSH
Um den SSH-Server auf dem Pi zu aktivieren, wird eine eine Datei ‘ssh’ im Verzeichnis /boot angelegt.
touch [mountpoint]/boot/ssh
Anschließend die WLAN Konfiguration in der Datei etc/wpa_supplicant/wpa_supplicant.conf hinterlegen.
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ ssid="ssid" psk="passwd" }
Dann kann der Raspberry Pi mit der SD-Karte auch schon gestartet werden…
Device Tree Overlay (Display, LEDs und Taster)
Für die angeschlossene Hardware müssen wir die entsprechenden Treiber laden und diese Konfigurieren. Dazu wird ein Device Tree overlay auf dem Raspberry hinterlegt.
Folgenden Code legen wir dazu in einer Datei mydevice.dts auf dem Raspberry Pi ab.
/dts-v1/; /plugin/; / { compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; fragment@0 { target = <&leds>; __overlay__ { led1: led1 { label = "led1"; linux,default-trigger = "mmc0"; gpios = <&gpio 12 0>; }; }; }; fragment@1 { target = <&gpio>; __overlay__ { rpi_display_pins: rpi_display_pins { brcm,pins = <23 24 25>; brcm,function = <1 1 1>; brcm,pull = <0 0 0>; }; }; }; fragment@2 { target = <&spi0>; __overlay__ { #address-cells = <1>; #size-cells = <0>; rpidisplay: rpi-display@0{ compatible = "ilitek,ili9341"; reg = <0>; pinctrl-names = "default"; pinctrl-0 = <&rpi_display_pins>; spi-max-frequency = <32000000>; rotate = <270>; bgr; fps = <30>; buswidth = <8>; reset-gpios = <&gpio 23 0>; dc-gpios = <&gpio 24 0>; led-gpios = <&gpio 25 1>; debug = <0>; }; }; }; fragment@3 { target-path = "/"; __overlay__ { keypad: device_keys { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; autorepeat; button1 { label = "Off"; linux,code = <44>; gpios = <&gpio 2 1>; }; button2 { label = "VolUp"; linux,code = <55>; gpios = <&gpio 3 1>; }; button3 { label = "VolDown"; linux,code = <53>; gpios = <&gpio 4 1>; }; button4 { label = "Enter"; linux,code = <28>; gpios = <&gpio 17 1>; }; button5 { label = "Down"; linux,code = <108>; gpios = <&gpio 27 1>; }; button6 { label = "Quit"; linux,code = <16>; gpios = <&gpio 22 1>; }; button7 { label = "Up"; linux,code = <103>; gpios = <&gpio 5 1>; }; }; }; }; };
- Im fragment@0 fügen wir eine weitere LED hinzu, die am GPIO12 angeschlossen ist. Der Trigger "mmc" bedeutet, dass diese LED bei Zugriffen auf die SD-Karte aufleuchtet. Ich habe die LED im device tree als activ-low eingetragen und activ-high angeschlossen, so dass diese bei eingeschaltetem Gerät leuchtet (und bei Zugriffen auf die SD-Karte kurz ausgeht).
- Das Display ist an der SPI0-Schnittstelle angeschlossen. Der zugehörige Treiber wird im fragment@2 geladen und konfiguriert. fragment@1 konfiguriert die zusätzlichen Pins für das Display.
- fragment@3 konfiguriert den gpio-keys-Treiber und verknüpft die angeschlossenen Taster mit den entsprechenden Tastaturcodes.
Nun muss der Device Tree kompiliert werden. Das erledigen wir direkt auf dem Raspberry mit dem Befehl
sudo dtc -I dts -O dtb -o /boot/overlays/mydevice.dtbo mydevice.dts
Der kompilierte Tree wird dabei unter /boot/overlays/ abgelegt. Um diesen zu aktivieren fügen wir ihn noch in die Datei /boot/config.txt ein
dtoverlay=mydevice dtparam=spi=on
Außerdem wird die SPI-Schnittstelle aktiviert, die für das Display benötigt wird.
Damit die Konsole des Pi beim Booten auf dem Display erscheint, erweitern wir die Kommandozeile des Linux Kernels um die folgenden Parameter:
sudo echo " fbcon=map:10 fbcon=font:MINI4x6" >> /boot/cmdline.txt
Hifiberry MiniAmp
Ich nutze ein Hifiberry MiniAmp Aufsteckmodul zur Audioausgabe. Um die zugehörigen Treiber zu laden, aktivieren wir das, bereits im Raspbian hinterlegte, Overlay in der /boot/config.txt
dtoverlay=hifiberry-dac dtparam=audio=on
Das Aufsteckmodul verfügt nicht über einen Hardwarelautstärke-Regler. Daher definiere ich mir einen virtuellen, Software-Regler. Dazu wird die Datei /etc/asound.conf angelegt und mit dem folgenden Inhalt versehen:
pcm.!default { type plug slave.pcm "softvol" } pcm.softvol { type softvol slave.pcm "hw:1,0" control.name "PCM" control.card 0 }
Anschließend müssen wir den Raspberry einmal neu starten.
Steuerung
Fürs erste habe ich ein einfaches Shell-Skript geschrieben, um das Gerät zu bedienen. Mittels eines whiptail-Menu kann man durch die Ordnerstruktur navigieren. Einzelne Dateien oder ganze Ordner können dann über den Konsolen-Player “mplayer” abgespielt werden.
Da die Gerätetasten auf die entsprechenden Tastaturcodes gemapped sind, können diese direkt zur Bedienung genutzt werden.
#!/bin/bash START_DIRECTORY="/media/samba" cd "$START_DIRECTORY" shopt -s dotglob shopt -s nullglob while [[ -z $selectedFiles ]]; do array=() if [[ ! $PWD == "$START_DIRECTORY" ]]; then array+=(".." "#") fi #folders for dir in */; do array+=("$dir" "#"); done plyall="" for i in *.mp3; do if [[ -f "$i" ]]; then if [[ -z $plyall ]]; then array+=("-play all-" "#") plyall="done" fi array+=("$i" "+") fi done selection=$(whiptail --title "Music Menu" --menu "Select file" 17 42 11 "${array[@]}" 3>&1 1>&2 2>&3) unset array if [[ "${selection}" == *".mp3" ]]; then mplayer "${selection}" elif [[ "${selection}" == "-play all-" ]]; then mplayer *.mp3 elif [[ -d "$selection" ]]; then cd "${selection}" else exit 1; fi; done
Natürlich muss der Player noch installiert werden:
sudo apt install mplayer
Um das Skript automatisch nach dem Einloggen zu starten, trage ich dieses in die .bashrc ein:
echo /usr/local/bin/mediamenu.sh >> ~/.bashrc
Hinweis: Da das Skript so auch beim Einloggen über SSH automatisch gestartet wird, muss zwingend eine Abbruchbedingung rein!
Displaybeleuchtung und Shutdown-Button
Ich möchte, dass die Displaybeleuchtung nur während der Bedienung des Gerätes aktiv ist. Dazu starte ich folgendes Skript auf dem Raspberry:
#!/bin/bash sleep 30 #switch off display backlight 15 sec after last key event set_light_off(){ sleep 15 echo 1 >/sys/class/backlight/fb_ili9341/bl_power } #sleep timer: shutdown, if no player is running during 300 sec shutdown_timer(){ i=30 while : do sleep 10 if [[ -z $(pgrep mplayer) ]]; then i=$((i-1)) else i=30 fi if [[ $i -le 0 ]]; then shutdown -P now fi done } while : do shutdown_timer & set_light_off & child_pid=$! evtest /dev/input/event0 | while read line; do if [[ "$line" == *"(EV_KEY), code 44 (KEY_Z), value 2"* ]]; then kill $child_pid 2>/dev/null echo 0 >/sys/class/backlight/fb_ili9341/bl_power shutdown -P now fi kill $child_pid 2>/dev/null echo 0 >/sys/class/backlight/fb_ili9341/bl_power set_light_off & child_pid=$! done done
Das Skript startet die Funktion “set_light_off” in einem eigenen Prozess und merkt sich die PID. Wird nun innerhalb von 15 Sekunden ein Key_event ausgelöst, wird der alte Prozess beendet und eine neue Instanz gestartet. Drückt man hingegen länger als 15 Sekunden keine der Tasten, dann schaltet der Hintergrundprozess das Licht aus und beendet sich.
Außerdem prüft das Skript, ob es sich bei der gedrückten Taste um die Z-Taste (Code 44) handelt. In diesem Fall fährt das Raspberry herunter.
Damit das Skript richtig ausgeführt werden kann, muss noch das Paket evtest installiert werden:
sudo apt install evtest
Das Skript wird als Dienst gestartet. Dazu wird das folgende systemd-Service File unter /etc/systemd/system/key-monitor.service abgelegt.
[Unit] Description=Player Key Monitor [Service] Type=simple ExecStart=/bin/bash /usr/local/bin/key-watch.sh Restart=always [Install] WantedBy=multi-user.target
Aktiviert wird der Dienst mit
sudo systemctl enable key-monitor.service
Samba automatisch mounten
Ich habe keine Musikdateien auf dem Player gespeichert, sondern streame die Dateien von einem gemounteten NAS-Laufwerk.
Um das NAS automatisch einhängen zu lassen nutze ich systemd. Hierzu sind folgende zwei Dateien nötig:
/etc/systemd/system/media-samba.mount
[Unit] Description=cifs mount script [Mount] What=//192.168.0.1/music Where=/media/samba Options=username=xXxXx,password=xXxXx,workgroup=xXxXx,ro,vers=1.0 Type=cifs [Install] WantedBy=multi-user.target
/etc/systemd/system/media-samba.automount
[Unit] Description=cifs mount script Requires=network-online.target After=network-online.service [Automount] Where=/media/samba TimeoutIdleSec=0 [Install] WantedBy=multi-user.target
Die Namensgebung muss entsprechend an das Ziel angepasst werden, wo das Laufwerk gemountet werden soll. Der Ordner, in diesem Fall /media/samba muss zuvor angelegt werden.
Die Dienste müssen über “systemctl enable …” aktiviert werden, dann wird das Laufwerk gemountet, sobald man versucht auf das Verzeichnis zuzugreifen.
Daniel Hassel
PROJECTS
raspberry music hifiberry