Raspberry Music Box

Einfacher Musikplayer

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.


PROJECTS
raspberry music hifiberry

Neuer Kommentar