Multiroom Audio using MPD, Pulseaudio and Bluetooth

Motivation

Almost all my activities (for example cooking, working on projects or playing games) have one thing in common: Theres music running in the background.

Usually this is some sort of FM radio running a local radio channel. The drawbacks are obvious: You have no control over what music is runing and the audio quality is far from great.

I started to ponder what to do about it. My requirements were simple:

  • Choose the music myself: These days, most of what i listen to is either internet radio, or a music streaming service like Spotify or Google Play Music
  • Reasonable quality: I am not an audiophile, but at least i do not want to have audible artifacts or noise when i walk past an antenna or something
  • Easy installation: I have no intent to run speaker wires to every room
  • Price: I dont want to spend hundreds of euros and for example buy Sonos speakers for all my rooms. At 230 Euros a piece, this would become really expsive really fast

Hardware

My system is built on the basis of bluetooth speakers.

I chose to use the Xiaomi Square Box for this, because it is quite cheap. But any bluetooth speaker would do. Of course the Xiaomi Box can not compete with expensive speakers like the Sonos or Bose systems, but at around 20 Euros incl. shipping, it is not bad. I even got one of these on a sale for 13 Euros incl. shipping.

The speakers have a builtin battery, so you could carry it around the house. But since i planned on just throwing this on a cupboard, i run it of a cheap phone charger.

Next thing i bought was a bluetooth dongle, which i plugged into my home server. In my case, the home server is an HP Microserver Gen8. But you could easily run this of a Rasperry Pi.

Software

For this to work, you need any current linux distribution. I am running Archlinux, because i like to tinker with new stuff, receive updates fast and take care of my system.

If you run some form of Ubuntu or Debian, for example Raspbian on a Raspi, the steps will be roughly the same. But since Ubuntu uses upstart where archlinux is based on systemd, you will have to improvise on that point.

To play my music, i currently use the Music Player Deamon, short MPD. The get support for spotify and the likes, i plan to change to mopidy. I just have not gotten around to it yet.

I use openHAB for my home automation, so the controls for the music will also be implemented there.

Steps to make it happen

Connecting bluetooth devices

Use bluetoothctl to turn on your bluetooth controller, pair, trust and connect to the speakers. Make sure to replace the mac address of your speakers.

% bluetoothctl
[bluetooth]# power on
Changing power on succeeded
[bluetooth]# scan on
Device A0:E9:DB:51:3B:C5 Mi Bluetooth Speaker
[bluetooth]# pair A0:E9:DB:51:3B:C5
...
[bluetooth]# trust A0:E9:DB:51:3B:C5
...
[bluetooth]# pair A0:E9:DB:51:3B:C5
...

 

Make sure your bluetooth controller is powered on after a reboot. On archlinux, this is done using a simple udev rule in /etc/udev/rules.d/20-bluetooth.rules containing this:

# Set bluetooth power up
ACTION=="add", SUBSYSTEM=="bluetooth", KERNEL=="hci[0-9]*", RUN+="/usr/bin/hciconfig %k up"

Make sure everything after ACTION is one line.

Pulseaudio

Enable pulseaudio as a permanently running service (not socket activated) and start it.

% systemctl --user enable pulseaudio.service
% systemctl --user start pulseaudio.service

Also make sure your user session is started on boot.

% sudo systemctl enable user@1000.service

Where 1000 is the user id of your user. You can get this id by running the program “id” as the corresponding user:

% id
uid=1000(tlan) gid=1000(tlan) groups=1000(tlan),7(lp),10(wheel),91(video),92(audio),1001(download)

(If groups does not contain audio)
% sudo gpasswd -a <username> audio

This is also the time to make sure your user is part of the group “audio” and if not, add him.

If everything went according to plan, pulseaudio should have automatically added your bluetooth devices as sinks. You can check using “pacmd”

% pacmd list-sinks
1 sink(s) available.
  * index: 1
    name: <bluez_sink.A0_E9_DB_51_3B_C5>
    driver: <module-bluez5-device.c>
    flags: HARDWARE DECIBEL_VOLUME LATENCY FLAT_VOLUME
    state: SUSPENDED
    suspend cause: IDLE
    priority: 9030
    volume: front-left: 51773 / 79% / -6.14 dB, front-right: 51773 / 79% / -6.14 dB
    balance 0.00
    base volume: 65536 / 100% / 0.00 dB
    volume steps: 65537
    muted: no
    ...
    properties:
      bluetooth.protocol = "a2dp_sink"
      device.description = "Mi Bluetooth Speaker"
      device.string = "A0:E9:DB:51:3B:C5"
      ...

(Optional, to set an upper limit for the volume)
% pacmd set-sink-volume bluez_sink.A0_E9_DB_51_3B_C5 50000

By setting the sink volume a bit under the maximum value of 65k and controlling the volume in my player software independently from pulseaudio, i effectively limit the maximum volume that can be set to not overload my speakers.

Add the line “load-module module-switch-on-connect” to /etc/pulse/default.pa to make sure pulseaudio automatically connects to your speakers after a reboot.

You should now be able to play music and hear it out of your bluetooth speakers. If you do not specify a sink, pulseaudio will output everything using the default sink.

MPD

I installed MPD and created the configuration files for each room in ~/.config/mpd:

% touch kitchen.{conf,database,log,pid}

Set up everything needed in the conf file. Make sure the files, especially the pid file, are unique for each configuration.  Change the paths to music and playlists to your needs

music_directory "/media/virtual/Music/mp3" 
playlist_directory "/media/virtual/Music/playlist"
db_file "~/.config/mpd/kitchen.database" 
log_file "~/.config/mpd/kitchen.log" 
pid_file "~/.config/mpd/kitchen.pid" 
 
port "6600" 
 
audio_output { 
 type "pulse" 
 name "Kitchen" 
 sink "bluez_sink.A0_E9_DB_51_3B_C5" 
}

Every instance of MPD has its own database. If you dont want this, see the Archwiki: Music Player Daemon – Satellite_setup.

The I created a special parameterized systemd unit in ~/.config/systemd/user/mpd@.service to run multiple MPDs with a custom configuration each

Description=Music Player Daemon
After=network.target sound.target

[Service]
ExecStart=/usr/bin/mpd --no-daemon %h/.config/mpd/%I.conf

[Install]
WantedBy=default.target

Then, have systemd reload config files, enable an instance of this configuration and start it

% systemctl --user daemon-reload
% systemctl --user enable mpd@kitchen
% systemctl --user start mpd@kitchen

You should now be able to play music using mpd to your bluetooth speakers. Use any MPD client to test this.

OpenHAB

OpenHAB comes with a binding for MPD. Bindings are plugins, that extend openHABs features for IoT devices. I use the binding to start/stop the music and control the volume.

Unfortunately, the plugin does not have features builtin to control playlists. So i decided just to use the exec-Binding, which allows you to run system commands, and the mpc client to load playlists.

Alternatively, i could have used a WebView, to display some sort of Webinterface for MPD in an iframe. But since right now, i only need to choose between two different playlists, i did not bother.

OpenHAB users will be familiar with the configuration layout, so i will just post the config files here and not go into much detail:

 % cat Music.items configurations/items raspberrypi Switch Mpd_Kitchen_StartStop "Start/Stop" (Kitchen,Radio) { mpd="ON:kitchen:play, OFF:kitchen:stop" }
Dimmer Mpd_Kitchen_VolumeControl "Volume [%d%%]" (Kitchen,Radio) { mpd="INCREASE:kitchen:volume_increase, DECREASE:kitchen:volume_decrease, PERCENT:kitchen:volume" }
String Mpd_Kitchen_Channel_Selection "Sender" (Kitchen,Radio) { autoupdate="false" }
String Mpd_Kitchen_Channel (Kitchen,Radio) { exec=">[CLEAR:mpc -h tlan-server.local -p 6600 clear] >[1LIVE:mpc -h tlan-server.local -p 6600 load 1live] >[WMW:mpc -h tlan-server.local -p 6600 load wmw]" }

 % cat default.sitemap
....
Frame label="Musik" { 
 Switch item=Mpd_Kitchen_StartStop 
 Slider item=Mpd_Kitchen_VolumeControl 
 Switch item=Mpd_Kitchen_Channel_Selection mappings=[ "1live"="Eins Live", "wmw"="WMW"]
}
....
 
 % cat Musik.rules configurations/rules raspberrypi import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.DateTime
import java.lang.Thread

rule "Change channel"
when
 Item Mpd_Kitchen_Channel_Selection received command
then
 var channel = receivedCommand

 sendCommand( "Mpd_Kitchen_StartStop", "OFF")
 Thread::sleep(50)
 sendCommand( "Mpd_Kitchen_Channel", "CLEAR")
 Thread::sleep(50)

 switch channel {
 case '1live': sendCommand( "Mpd_Kitchen_Channel", "1LIVE")
 case 'wmw': sendCommand( "Mpd_Kitchen_Channel", "WMW")
 }

 Thread::sleep(50)
 sendCommand( "Mpd_Kitchen_StartStop", "ON")

end

Some notes:

  • Since (i think) openHAB processes events asynchronously, i had to add some Thread::sleep between commands to MPD
  • MPD behaved kind of strange when just loading playlists. So to make sure i dont mix up my playlists, i first stop music playback, then clear the current playlist, load the new playlist, and turn playback back on. This happens fast enough so you do not notice it, but fixed a lot of weirdness when controlling it.

Conclusion

Right now, i only have one speaker connected. But adding more is just a matter of connecting, copying configuration files and starting services. Controlling it is easy enough, everybody on my house can handle it.

Everything works great!

Further steps

As mentioned before, to support streaming services like spotify etc. i am going to switch to mopidy, although i will have to check how different the configuration will be.

Since the server is available as a bluetooth device, you can connect your smartphone to it and have sound from your phone come out of your speaker in any room. I am still looking nito what it would take to have an easy interface to choose which input to send to which output.

Since the bluetooth speakers are just regular pulseaudio sinks, you can send other stuff to them. For example, i installed espeak, a text-to-speech software. With it, you can have the server tell you “Hey, you’ve got mail!’ whenever a new email arrives.

Or, going further down the home automation route: You could for example use the openHAB weather binding check for rain and if rain is expected, warn you to close the windows in rooms where windows are open.

If you used bluetooth speakers which also offer handsfree features and have a microphone, you could even built something like an always listening voice assistant.

Update: Fertig

Obwohl ich immer noch keine Schiebelehre habe, habe ich den Drucken inzwischen zuende aufgebaut und in Betrieb genommen.

Die Ergebnisse sind, dafür das ich noch nicht wirklich viel Feintuning betrieben habe, schon sehr gut und ich bin sehr begeistert von den Ergebnissen.

Jetzt folgt das Feintuning. Das heißt: Die diversen Zahnriehmen nochmal nachstraffen, die richtigen Druckeinstellungen finden etc.

Fast fertig

Die letzten Tage habe ich die noch fehlenden mechanischen Teile verbaut, die ganze Verkabelung soweit vorbereiten und die Software an meine Bedürfnisse angepasst.

Das Ergebnis sieht man im folgenden Video:

Es fehlt immernoch die Feinjustierung und das Hotend, weshalb man im Video nur einen Trockenlauf sieht. Man sieht aber schon wie sauber und akkurat alles läuft. Dies freut mich :)

Wellenkupplungen da, Z-Achse montiert

Endlich sind heute die Wellenkupplungen in der Post gewesen, sodass ich die Z-Achse montieren konnte.

Wie man im zweiten Bild hoffentlich erkennen kann, sind die Wellenkupplungen keine starren Körper sondern etwas flexibel. Dadurch sind sie in der Lage, leichte Winkelunterschiede zwischen Motorachse und Z-Achsen-Welle auszugleichen.

Die Motoren habe ich dann auch gleich parallel zusammengelötet, der Stecker angebracht und das Ganze ausprobiert. Das Ergebnis gibt es im folgenden Video zu sehen:

Das Quietschen, welches beim Herunterlassen der Z-Achse zu hören ist, ist inzwischen auch verschwunden, nachdem ich die unteren Lager für die Z-Achsen-Welle angebracht habe.

Heatbed: Fertig

Aufbau

Gestern wurde mir Abends unerwartet von den Nachbarn das Keptop-Tape “geliefert”. Ich habe mich also heute dran gemacht das Heatbed abzuschließen.

Angefangen habe ich damit, ein Kabel an den Thermistor zu löten. Das stellte sich als unerwartet schwierig raus, da der Thermistor unfassbar nervig dünne Abschlüsse hat und das Kabel, ein ehemaliges USB-Kabel, auch nicht viel besser war. Dann habe den Thermistor in das Loch in der Mitte des Heatbeds gesteckt und das ganze mit je einem Streifen Kapton-Tape darüber und darunter am Heatbed befestigt.

Kapton ist ein Spezial-Kunststoff, der sehr hitzebeständig ist. Das Klebeband welches ich hier verwende ist bis zu 350°C angegeben. Das sollte also auch bei ABS-Temperaturen von 110°C noch halten.

Sorge machte ich mir bzgl. der Hitzebeständigkeit des USB-Kabels. Aber ein Problelauf zeigte, dass es hier keine direkten Probleme zu geben scheint. Ich werde über die ersten paar Druckvorgänge mal beobachten, wie es sich verhält wenn das Heatbed auch mal länger heiß ist.

Zuletzt kann man auf den Bildern sehen wie ich teilweise improvisieren musste:

  • Der Regler für den Heatbed-Strom muss zeitweise um die 11A Regeln, wobei er gerne warm wird. Da der Kühlkörper, der da drauf soll, allerdings noch in auf dem Weg zu mir ist, habe ich einfach einen PC-Lüfter davor gestellt. Damit blieb der Regler laut meinem Multimeter gut unter 30°C.
  • Der Sangionololu verweigert den Dienst, wenn der Thermistor des Hotend zu niedrige Temperaturen meldet, sodass man nichts verstopfen kann. Was prinzipiell keine schlechte Idee ist, ist beim Testen eher hinderlich. Statt umständlich die Software umzupatchen, habe ich den erstbesten Widerstand (620 Ohm) genommen und an die Pins geklemmt. Damit meldet die Software eine Hotend-Temperatur von gemütlichen 195°C meldet.

Ergebnisse

Im Testlauf zeigte sich, dass das Heatbed verdammt schnell auf Temperatur kommt und es nur eine Frage der Zeit ist, bis ich mir daran tierisch die Finger verbrenne. Die LED geht fleißig an und aus gemäß der Heizphasen, die Temperaturen werden korrekt an die Software zurückgeliefert.

Interessanterweise kann man sogar den Lüftern anhören, wenn die Heizphasen einsetzen und die Spannung einbricht. Hier muss ich mir evtl. in näherer Zukunft noch mal ein gescheites Netzteil zulegen.

Heatbed-Träger montiert

Heute habe ich mir mal die Zeit genommen, die MDF-Platte auf die Lager zu montieren. Dabei kommt es auf Präzision an, da die beiden Lager statisch an der Platte montiert ist und exakt parallel über die zwei Achsen laufen müssen. Je ungenauer man hier arbeitet, desto schwerläufiger ist die Y-Achse später, was dazu führt das der Motor mehr arbeiten muss und die Druckgenauigkeit sinkt.

Ich habe zu Hilfsmitteln gegriffen: Beim Bohren der Löcher für die Lagerhalter, habe ich durch die Halter hindurch angebohrt, sodass die Löcher exakt sitzen. Dann habe ich ein Reststück der Achse schon beim Setzen der Löcher durch die Lager geführt, damit sie genau auf einer Flucht sind. Zu guter letzt noch klassisch mit dem Zollstock dafür gesorgt, das die zwei Achsen parallel sind.

Bei der Montage habe ich dann die Befestigungen der Achsen gelöst, so dass sich nach ein paar mal Vor- und Zurückbewegen des Heatbeds alles schön eingerappelt hat und super flüssig läuft.

Bonus: Heatbed Probeliegen

Nachdem man mir gerade die MDF-Platte brachte, welche die Basisplatte für das Heatbed wird, habe ich das Heatbed direkt mal draufgeschraubt und das Ganze testweise auf die Y-Achse probegelegt.

IMG_3553

Noch ist nichts fest montiert, da ja noch erst der Zahnriemen angebracht werden muss, aber schön zu sehen wie alles passt.

Extruder fertig, Heatbed gelötet

Babyschritte: Heute habe ich die Stromleitungen an das Heatbed gelötet sowie den Extruder zusammengesetzt.

Extruder

Da heute die Filmentschraube in der Post war, habe ich den Extruder weitestgehend zusammengesetzt.

In den folgenden Bildern sieht man zum einen das Getriebe, welches einen langsamen, stabilen Vorschub des Filaments in das Hotend garantiert.

Zum anderen kann man im zweiten Bild (hoffentlich) erkennen, wie durch die zwei Federn der vordere Teil hebelartig gegen den hinteren Teil gedrückt wird. Im Inneren des Hebels befindet sich ein weiteres Kugellager, welche das Filament in die Filamentschraube drückt. Durch die Schrauben kann die Spannung der Federn erhöht oder verringert werden.

Heatbed

Das Heatbed kann beim Aufheizen gerne mal bis zu 11A ziehen, weshalb die Zuleitung ausreichend dimensioniert sein muss. Zu dünne Kabel können sich bei dem Strom schon stark erwärmen bzw. im schlimmsten Fall ihre Isolierung verschmoren. Ich habe daher einfach ein Kaltgerätekabel geopfert und dieser angelötet. Da ich nur zwei Adern brauchte, war der Schutzleiter über. Ich habe ihn daher einfach angeknotet und somit als Zugentlastung für die anderen beiden Leitungen verwendet.

IMG_3547

Die Lötstellen sind vielleicht nicht perfekt, aber für meine Zwecke mehr als ausreichend. Alles glitzert schön und es sind keine kalten Lötstellen vorhanden.

Filament

Zum Abschluss sei erwähnt, das derweil auch meine erste Rolle Filament angekommen ist.

1kg rotes PLA
1kg rotes PLA

 

Es nimmt Form an

Einige Tage sind vergangen und ich warte immernoch auf mehr Post. Aber einige weitere Teile sind schon da und somit konnte es weiter gehen.

Z-Achse

Wellenkupplung

Leider musste ich feststellen, dass die Kupplungen, die die Motorwellen mit den Gewindestangen der Z-Achse verbinden, wohl für Motorwellendurchmesser weit über den 5mm meiner NEMA17-Schrittmotoren ausgelegt sind. Im Bild kann man außerdem noch ganz leicht sehen, wie ich versucht habe, dies mit Klebeband auszugleichen. Leider hat das nicht gehalten und unter dem Gewicht der daran hängenden Z- und X-Achse hat sich das Ganze über nacht wieder gesetzt. Statt noch lange herum zu experimentieren habe ich daraufhin einfach passende Wellenkupplungen bei eBay geshopt und hoffe, dass diese heute oder Montag ankommen.

Extruder

Der Extruder bis auf die noch fehlende Filamentschraube fertig. Die Filamentschraube ist eine M8-Schraube, bei zum Beispiel mittels eines Dremels einmal rund herum Einschnitte gemacht wurden. Diese Einschnitte sind rechtwinklig zum Gewinde uns sind dazu da, das Filament zu greifen und durch die Drehung der Schraube zu transportieren. Ein Kugellager wird mittels Federn an diese Schraube heran gedrückt, sodass immer ein konstanter Druck anliegt und das Filament sicher und  gleichmäßig in das Hotend transportiert wird.

Sonstiges

IMG_3509

Die Halterungen für die glatten Metallstangen sind leider auch zu knapp ausgelegt gewesen. Daher musste ich diese mit dem Dremel etwas auffräsen, um die Stangen durch gesteckt zu bekommen. Das war zwar wieder eine ziemliche Sauerei, aber relativ einfach und schnell gemacht.

Ansonsten habe ich inzwischen alle Motoren an ihre zukünftigen Positionen montiert und die Pulleys angebracht. Die Pulleys haben so enge Wellenlöcher, dass ich die Madenschraube nicht einmal verwenden musste, um alles Stramm genug fest zu bekommen.