Vor einiger Zeit habe ich einen Raspberry Pi Modell 3 als ersten Preis in einem Hackathon bei BMW gewonnen. Ich war sehr beeindruckt von all den Schnittstellen, die der neue Raspberry mitbringt, darunter WLAN und Bluetooth. Also habe ich darüber nachgedacht, was ich mit dem netten kleinen Ding bauen könnte. Nach ein paar Wochen bin ich dann auf die Idee für ein nützliches Gadget für unsere Familie gekommen.
Die Idee
Ein paar Monate zuvor hatte unsere Stereoanlage den Geist aufgegeben. Wir haben sie durch einen Bluetooth-Lautsprecher ersetzt, wobei die Musik nun vom Smartphone oder vom Computer kommt. Doch ist es auch immer wieder schön, eine CD abzuspielen. Dies ist jetzt aber aufwändiger, weil wir dazu immer einen Computer bemühen müssen.
An dieser Stelle kam der Raspberry ins Spiel. Die Idee war, einen CD-Player zu bauen, der Musik direkt auf dem Bluetooth-Lautsprecher abspielen kann. Das Gerät sollte so einfach wie möglich zu bedienen sein. Man verbindet den Lautsprecher mit dem Player, legt die CD ins Laufwerk ein und die Musik fängt sofort an zu spielen. Ohne weitere Tasten und ohne Touchscreen.
So einfach wie diese Idee klingt hatte ich mir auch die Umsetzung vorgestellt. Wie sich aber herausstellte, war unter Linux die Umsetzung nicht ganz so einfach. Ich musste alle Einstellungen über die Kommandozeile machen, da kein Bildschirm angeschlossen sein sollte. Nach viel Experimentieren und Googeln habe ich alles zum Laufen bekommen. Es gibt zwar eine Menge Blogeinträge und Beiträge auf StackOverflow , aber die Technologie unter Linux wandelt sich ständig, so dass ein Großteil dieser Informationen schon veraltet war.
In diesem Artikel beschreibe ich, wie ich den CD-Player zum Laufen gebracht habe.
Der Aufbau
Der CD-Player besteht aus folgenden Teilen:
- Raspberry Pi 3 Model B
- Micro-SD-Karte mit 8 GB
- Betriebssystem Raspbian Jessie Light
- Samsung USB-CD-Laufwerk
- Bose Soundlink Mini-Bluetooth-Lautsprecher
Um auf die Linux-Shell zugreifen zu können, habe ich den Raspberry per WLAN mit meinem Heimnetzwerk verbunden und mich dann mit ssh eingewählt.
Zur Inbetriebnahme des CD-Players sind drei Schritte notwendig:
- Den Bluetooth-Lautsprecher mit dem Raspberry koppeln und beim Start automatisch verbinden
- Eine CD von der Kommandozeile aus abspielen
- Das Einlegen einer CD erkennen, um sie automatisch abspielen zu können.
In dieser Anleitung gehe ich davon aus, dass Sie mit Linux-Kommandos vertraut sind.
1. Bluetooth-Lautsprecher für Audioausgabe koppeln
Wenn wir Bluetooth-Lautsprecher mit dem PC oder dem Smartphone koppeln, sieht es ziemlich einfach aus. Wir öffnen das entsprechende Menü, warten, bis der Lautsprecher auftaucht und wählen ihn aus. Et voilá!
Aber wenn wir dies unter Linux auf der Kommandozeile machen müssen, dann sind plötzlich mehr Schritte nötig.
Um diesen Prozess nachvollziehen zu können, brauchen wir einiges Hintergrundwissen über das Linux-Audiosystem. Heutzutage ist ALSA das Standard-Linux-Audiosystem, das auch bei Raspbian mitgeliefert wird. Die meisten Applikationen benutzen ALSA, um ihre Audioausgabe abzuspielen. ALSA bringt Treiber für die Audio-Hardware mit. So kann es Audio über den HDMI-Anschluss oder den Klinkenstecker des Raspberry abspielen.
Doch wenn wir Musik über ein Bluetooth-Gerät abspielen wollen, kommen wir mit ALSA nicht weiter, weil es Bluetooth nicht unterstützt. Stattdessen müssen wir den Soundserver Pulseaudio benutzen. Pulseaudio kann Audio-Eingaben aus einer Quelle entgegennehmen, modifizieren und an eine Senke weiterleiten. Diese Senke kann beispielsweise ein ALSA-Treiber sein, aber auch ein Bluetooth-Gerät. Diese Fähigkeit benötigen wir hier.
Als erstes müssen wir Pulseaudio zusammen mit dem Plug-in bluez für Bluetooth-Unterstützung installieren.
apt-get install pulseaudio pulseaudio-module-bluetooth bluez
Bei der Installation von Pulseaudio wird ALSA automatisch so konfiguriert, dass es die gesamte Wiedergabe durch Pulseaudio durchleitet. Somit ist ALSA jetzt sowohl Quelle als auch Senke für Pulseaudio.
Jetzt könnten wir versuchen, einen Bluetooth-Lautsprecher zu koppeln – aber es geht nicht. Wenn Pulseaudio noch nicht läuft, dann können wir keinen Lautsprecher koppeln, wofür es aber keine aussagekräftige Fehlermeldung gibt. Deswegen müssen wir erst einmal Pulseaudio starten:
pulseaudio --start
Jetzt schalten wir den Bluetooth-Lautsprecher ein. Dann koppeln wir ihn mit Hilfe des Tools bluetoothctl . Die notwendigen Kommandos in bluetoothctl lauten:
agent on scan on - should show the MAC address of all devices nearby pair trust connect quit
Nun ist das Audiogerät gekoppelt und verbunden. Als nächstes müssen wir Pulseaudio konfigurieren, um das Bluetooth-Gerät als Senke zu verwenden. Zuerst lassen wir uns eine Liste aller verfügbaren Quellen ausgeben:
pacmd list-sinks
Hier sollten wir mindestens zwei Senken sehen – den ALSA-Treiber und das Bluetooth-Gerät. Die aktuell ausgewählte Senke ist mit einem Sternchen markiert. Als nächstes ändern wir die Standardsenke auf das Bluetooth-Gerät. Angenommen, dieses hat die Nummer 1, dann sieht das Kommando so aus:
pacmd set-default-sink 1
Pulseaudio ist damit konfiguriert, so dass wir einen ersten Versuch wagen können:
paplay /usr/share/sounds/alsa/Side_Right.wav
Jetzt sollte etwas auf dem Lautsprecher zu hören sein. Wir haben den Lautsprecher konfiguriert, aber wir wollen diese Einstellung nicht bei jedem Start des Raspberrys manuell machen. Also starten wir den Pulseaudio-Server als einen Service beim Booten. Hierfür habe ich eine nette Anleitung bei Github gefunden.
Wir machen Folgendes: Zuerst die Benutzer zur Gruppe pulse-access hinzufügen:
adduser root pulse-access adduser pi pulse-access adduser ... pulse-access - For all users that need access
Dann Pulseaudio, das unter dem Benutzer pulse laufen wird, berechtigen, das bluez-DBUS-Interface zu benutzen. Hierzu brauchen wir folgende Zeilen in der Datei /etc/dbus-1/system.d/pulseaudio-bluetooth.conf
Außerdem muss das Modul bluetooth discovery geladen werden. Wir fügen diese Zeilen am Ende der Datei /etc/pulse/system.pa ein:
.ifexists module-bluetooth-discover.so load-module module-bluetooth-discover .endif
Jetzt brauchen wir einen systemd-Dienst, der Pulseaudio im Systemmodus laufen lässt. Wir erzeugen die Datei /etc/systemd/system/pulseaudio.service mit folgendem Inhalt:
[Unit] Description=Pulse Audio [Service] Type=simple ExecStart=/usr/bin/pulseaudio --system --disallow-exit --disable-shm --exit-idle-time=-1 [Install] WantedBy=multi-user.target
Noch den Service starten
systemctl daemon-reload systemctl enable pulseaudio.service systemctl start pulseaudio.service
Bluetooth neu starten
systemctl restart bluetooth systemctl status bluetooth
Nun haben wir es. Bluetooth soll auch nach dem Neustart des Raspberrys funktionieren.
2. Eine CD von der Kommandozeile abspielen
Dieser Teil ist am einfachsten, weil es hierfür bereits fertige Tools gibt. Ich habe MPlayer verwendet, den wir mit apt-get installieren:
sudo apt-get install mplayer
Wenn die CD im Laufwerk ist, dann können wir sie mit folgendem Kommando abspielen:
mplayer -cdrom-device /dev/cdrom cdda://
Et voilá!
3. Eine CD automatisch abspielen
Die nächste knifflige Frage lautet: Wie können wir den CD-Player automatisch beim Einlegen der CD starten? Es gibt hier keine Einstellung für MPlayer.
Die Lösung ist etwas aufwändiger. Der Kniff besteht darin, dass Programm udisks zu verwenden. Es verwaltet und überwacht den Zustand aller Laufwerke im System, auch des CD-ROM-Laufwerks, und benachrichtigt andere Programme bei Änderungen. Mit einem speziellen Kommandozeilen-Programm kann man den Zustand manuell abfragen, und mit einem Daemon kann man über DBUS kommunizieren. Mehr dazu später.
Bei meinen Nachforschungen bin ich auch auf Techniken gestoßen, die HAL oder udev verwenden. HAL („hardware abstraction layer“) ist der Vorgänger von udisks und wird nicht mehr unterstützt. Udev ist ein Dienst, der von udisks verwendet wird. Udev müssen wir nicht direkt verwenden, sondern können uns auf die Abstraktion verlassen, die udisks bietet.
Ich habe eine nette Einführung in udisks und udev im Blog von Finnbarr P. Murphy gefunden. Als erstes müssen wir udisks installieren, denn anders als udev wird es nicht automatisch installiert:
sudo apt-get install udisks
Jetzt können wir uns die Liste der von udisks erkannten Geräte anschauen:
udisks --enumerate
Im Monitoring-Modus können wir auch alle Events dieser Geräte sehen:
udisks –monitor
Beim Einlegen oder Auswerfen der CD sehen wir dieses Ereignis in der Ausgabe. Nun ist bekannt, wo wir die Ereignisse für das Einlegen oder Auswerfen der CDs sehen und wie wir eine CD abspielen können.
Unsere nächste Aufgabe ist es, diese Schritte zusammenzubringen. Hierfür habe ich ein kleines Python-Skript geschrieben. Das Skript benutzt DBUS zur Interprozess-Kommunikation, um die Ereignisse aus dem udisks-Daemon mitzubekommen. Wenn das Skript erkennt, dass eine neue CD eingelegt wurde, dann startet es MPlayer in einem neuen Prozess und spielt die CD ab. Wenn es das Auswerfen der CD erkennt, dann stoppt es diesen Prozess.
So sieht der Aufbau aus:

©Stefan Heinbockel
Ich habe einen Beispielcode zur Kommunikation mit udisks via DBUS im Gentoo Linux-Wiki gefunden. Mit diesem Code war es nicht so schwer, das Skript zu schreiben.
So sieht das Skript aus:
import os import dbus from dbus.mainloop.glib import DBusGMainLoop import gobject uid = None mplayer_process = None def device_added(device): print("---added---") def device_removed(device): print("---removed---") pass def device_changed(device): print("---changed---") if is_media_inserted(device): print("Media was inserted") play_cd() else: print("Media was removed") stop_cd() pass def is_media_inserted(device): device_obj = system_bus.get_object("org.freedesktop.UDisks", device) device_props = dbus.Interface(device_obj, dbus.PROPERTIES_IFACE) try: is_media_available = device_props.Get('org.freedesktop.UDisks.Device', "DeviceIsMediaAvailable") if is_media_available: return True else: return False except: print("DeviceIsMediaAvailable is not set") return False def play_cd(): print("Starting CD Playback") mplayer_process = subprocess.Popen("mplayer -cdrom-device /dev/cdrom cdda://", shell=True) def stop_cd(): if mplayer_process is not None: print("Stopping CD Playback") mplayer_process.terminate mplayer_proces = None if __name__ == '__main__': print("Starting CD Autoplay") uid = os.getuid() DBusGMainLoop(set_as_default=True) system_bus = dbus.SystemBus() udisk_proxy = system_bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks") udisk_iface = dbus.Interface(udisk_proxy, "org.freedesktop.UDisks") udisk_iface.connect_to_signal('DeviceAdded', device_added) udisk_iface.connect_to_signal('DeviceRemoved', device_removed) udisk_iface.connect_to_signal('DeviceChanged', device_changed) loop = gobject.MainLoop() loop.run()
Wir können das Skript testen, indem wir es von der Kommandozeile starten und dann eine CD einlegen. Zum Schluss starten wir das Skript beim Start des Raspberrys. Das geht ganz einfach, in dem wir folgende Zeilen zur Datei/etc/rc.local hinzfügen:
python /path/to/your/script.py
Jetzt haben Sie auch einen CD-Player für Bluetooth, der keine Interaktion auf der Kommandozeile mehr benötigt.
Ausblick
Der CD-Spieler funktioniert zuverlässig und ist bei uns im täglichen Einsatz. Das Ziel war es, einen CD-Spieler ohne extra Tasten zu entwickeln. Die einzigen Interaktionen sind das Öffnen und das Schließen des CD-Laufwerks. Die Lautstärke können wir direkt am Bluetooth-Lautsprecher einstellen. Für meine Familie war das Projekt somit ein Volltreffer.
Natürlich habe ich noch mehr Ideen, was ich mit dem Aufbau machen könnte:
Auswerfen der CD
Eine Einschränkung des Systems ist momentan, dass das Auswerfen der CD nicht richtig ausgewertet wird. Das Problem existiert, weil sich MPlayer aufhängt, sobald eine CD ausgeworfen wird, während er sie gerade abspielt. Dies blockiert das Betriebssystem, selbst dann, wenn MPlayer in einem separaten Prozess gestartet wurde. Und weil das passiert noch bevor das Python-Skript das DBUS-Ereignis bekommt, kann es den MPlayer-Prozess auch nicht beenden. Deswegen muss vor dem Einlegen einer neuen CD der Raspberry neu gestartet werden. Für uns ist das kein Problem, da der Neustart einfach durch eine kurze Unterbrechung der Stromversorgung funktioniert und sehr schnell vonstatten geht. Vielleicht lässt sich dieses Problem in der Zukunft beheben, indem das Skript schon den Druck auf die Taste des CD-Laufwerks mitbekommt.
Benutzer-Schnittstelle
Die Benutzer-Schnittstelle ist absichtlich sehr einfach gehalten. Wer mehr Interaktionsmöglichkeiten haben möchte wie Lieder auswählen oder Wiedergabe pausieren, für den reicht MPlayer nicht mehr. Hier könnte man mit den Python Audio-Tools arbeiten, um einen vollwertigen CD-Player zu bauen.
Ich habe dies bislang nicht zum Laufen gebracht, da die Installation der Python Audio-Tools nicht ausreichend dokumentiert ist. Man benötigt die Bibliothek libcdio , um auf das CD-Laufwerk zuzugreifen. Hierzu braucht man wiederum libcdio-paranoia , das ich nach einigem Ausprobieren zum Kompilieren gebracht habe. Wenn ich die Module dann in Python laden wollte, habe ich einige Fehlermeldungen bekommen. Da der CD-Spieler mit MPlayer für mich ausreicht, habe ich es hierbei belassen.
Aber vielleicht bietet diese Information einen Startpunkt für Ihre eigenen Projekte.
Für dieses Projekt musste ich sehr viel experimentieren und Versuche verwerfen. Ich habe hier nur die Schritte beschrieben, von denen ich denke, dass sie wirklich nötig waren. Es kann passieren, dass ich etwas übersehen habe oder einige Schritte nicht nötig sind. Wenn Sie das hier ausprobieren und etwas nicht funktionieren sollte, lassen Sie es mich bitte wissen.