Music in the Rest Room

November 1, 2021

Overview

I’ve been to a training site [1]. When closing the toilette door music started. This is completely unnecessary, but i liked the idea. More over i found it very relaxing, which fits to the sense of the word “rest room”. Since then i pushed to have the same functionality in my home. During refurbishment i included additional cabling and added panels to the ceiling of the rest room, giving some headroom to mount a speaker and hide electronics.

Overall Setup

The training site [1] uses some prebuilt solution [2]. This for sure works, but I preferred to rely on Raspberry PI and built things more modular.

The music should start as soon as the door gets locked and should stop as soon as it gets unlocked again. For this to happen i included a switch into the strike plate [3] of the door and have cabling from there to the Raspberry PI. The PI needs to stay powered on and booted so the music will start immediately. The Switch gets connected to the GPIO and a little program is needed to read the switch state and to start the music.

Switch integrated into strike blade (source: see [2])

Also the audio should be delivered at good / well enough quality. I decided for an “in ceiling” speaker [4], which is most likely a bit oversized, overqualified for the use case. The vendor normally sells this in a set of two but i managed to get hold of only one single speaker, which is way enough, given the room size.

Audio Out

The Raspberry PI features a stereo 3.5mm socket. I’ve learned this audio out connection lacks power, when connecting a passive speaker. Moreover it does not seem to have the best quality as some electrical noise can be introduced by the design of the PI itself. Higher quality can be achieved by grabbing audio from USB port or HDMI port. I did not test any of these, though.
I decided to go with an Amplifier card “HiFiberry Amp 2” [5] to address this topic. Most likely again a little above what is needed.
This amplifier card introduces additional requirements: 18V power supply [6], when connecting 8Ω Speaker and a bigger case [7] for Raspberry Pi including the amplifier card to fit into. The 5V power supply of the Raspberry itself, can (must) be omitted. The power for the raspberry is provided via the amplifier.

Choosing an OS

I’ve also digged into which OS to go with. It would be beneficial if configuration and usage of the Hifiberry Amp 2 would be integrated out of the box. I found following options:

  • HifiBerryOS – HifiBerryOS is coming in different versions for different Raspberries and works with AMP2 without additional challenges. It can be reached from LAN / WLAN can be streamed e.g. via spotify app. While this is a nice functionality (e.g. in the kitchen or my private office), i still need to get rid of the interaction needed on the mobile and make the pushed switch start the audio. [8]
  • volumio – Is a more generic approach to play audio. I did not look into it yet. [9]
  • some plain raspberianOS and adjust the OS according to [10]

I started with the HifiBerryOS and found:

  • Out of the box i can connect from mobile device e.g spotify app as an output device
  • Cmd-line tools to play a wav (via aplay) or mp3 (via mpg123) file are available
  • python is available (tested with python --version)

But

  • I still don’t know how to define or change the volume from cmd line, in a non interactive way.
  • No git client available to persist or version my changes. I work around this by scp-ing data to my notebook and persisting/versioning there.
  • I can’t install additional packages as there is no package manager available.

I stick to HifiberryOS for now. Changing to raspberianOS will be a future task.

playing sound from cmd line

I’ve downloaded some music suiting my needs from [11]. Uploaded it to the hifiberry into the /data directory. The /data is on a different filesystem and OS updates would not delete anything stored there. As i’m new to audio commands, here is what i used:

  • To assure HifiBerry Amp2 is available as output device:
# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: sndrpihifiberry [snd_rpi_hifiberry_dacplus], device 0: HiFiBerry DAC+ HiFi pcm512x-hifi-0 [HiFiBerry DAC+ HiFi pcm512x-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
# aplay -L
null
  ......
sysdefault:CARD=sndrpihifiberry
    snd_rpi_hifiberry_dacplus, HiFiBerry DAC+ HiFi pcm512x-hifi-0
    Default Audio Device
usbstream:CARD=sndrpihifiberry
    snd_rpi_hifiberry_dacplus
    USB Stream Output
  • To play mp3
# mpg123 /data/520674__shortrecord__relaxation-music-2.mp3
High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3
version 1.25.13; written and copyright by Michael Hipp and others
...
  • to convert from mp3 to wav
# ffmpeg -i /data/520674__shortrecord__relaxation-music-2.mp3 -acodec pcm_s16le -ac 1 -ar 16000 /data/520674__shortrecord__relaxation-music-2.wav
ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
...

sensor the switch status

You need to be aware, that some of the pins are used by AMP2 already. So please review [12].

electronics

To have a normal switch work as an input device for some digital system (like computer or Raspberry PI) you normally add 2 resistors. One pull-up (or pull-down) resistor to grant defined state while the switch is open and a second resistor which limits the current in case the closed switch creates a short circuit (e.g. when connected to output pin instead of input pin). This is nicely explained here [13]. If a switch is closed the electronic senses a lot of closing/open/closing till the switch is firmly shut. This behaviour is common and called bouncing. You level this away with a capacitor.
The Raspberry allows to address pull-up / pull-down and the bouncing via SW. At the top of the program you initialise the GPIO port accordingly. The bouncing should be addressed by adding a bouncetime to the event handler:

# Pulldown-Resistor
GPIO.setup(XX, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
# Pullup-Resistor
GPIO.setup(XX, GPIO.IN, pull_up_down = GPIO.PUD_UP)
# Event detection with bounce time
GPIO.add_event_detect(channel, GPIO.BOTH, callback=antwort, bouncetime=300)

The internal pull-resistor is so oversized [14], that a short loose wire is sufficient to change the input state due to cosmic. So i added resistors anyway. I’ve only managed to get reliable sensoring in place after also adding a capacitor. Maybe bouncetime=300 was just a to low value [15]. With the next two images you find the circuit diagram and the layout diagram. Please be aware that the white/green wires are connected to the switch integrated in the strike blade, while the other three connection points (3.3V, GPIO15 and GND) connect to the Raspberry PI. The colours chosen for cabling are not refering to any standard and are based on my well fare.

Circuit Diagram
Layout Diagram

I tried to keep the layout small so that it will still fit into the Case with Raspberry and AMP2.

The python scripts

As mentioned earlier on, i put all scripts into the /data directory as this does not get overwritten when doing an OS update.

Pin naming

There are 2 different ways to name the pins of the GPIO [16]. On most diagrams you find the pins numbered from 1 to e.g. 40. This is the circuit board numbering. You will also see naming like GPIO14 and similar. This is the broadcom naming, which has changed with some Raspberry versions. It is important to know which pin you wired and how it is called.

The easiest way to control the GPIO pins is using the RPi.GPIO Python library and it is available within the HifiberryOS. You need some initializing:

import RPi.GPIO as GPIO 
# Use GPIO numbers not pin numbers
GPIO.setmode(GPIO.BCM) 
# set up the GPIO channels - one input and one outputGPIO.setup(7, GPIO.IN)

Sensoring

To understand the status of the switch you can either read the status of the pin in an endless loop:

#!/usr/bin/env python
#coding: utf8

import RPi.GPIO as GPIO
import time

channel = 10

## GPIO.setmode (GPIO.BCM)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(channel, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

while 1:
    time.sleep(0.1)
    if GPIO.input(channel) == GPIO.HIGH:
        print ("Input " + str(channel) + " HIGH")
    else:
        print ("Input " + str(channel) + " LOW")

Or, a bit nicer you can wait for an change-event on that pin. And react according to the status found:

#!/usr/bin/env python
#coding: utf8

#### http://www.netzmafia.de/skripten/hardware/RasPi/RasPi_GPIO_int.html

import RPi.GPIO as GPIO
import time

## GPIO.setmode (GPIO.BCM)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(10, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

# event functions defined
def doHigh(channel):
        print ("Eingang " + str(channel) + " HIGH")
def doLow(channel):
        print ("Eingang " + str(channel) + " LOW")

# declare event
channel = 10
GPIO.add_event_detect(channel, GPIO.RISING, callback = doHigh, bouncetime = 200)
GPIO.add_event_detect(channel, GPIO.FALLING, callback = doLow, bouncetime = 200)


# Still need an endless loop, doing nothing till Strg-C  (keyboard interrupt) is pressed, cleaning up then.
try:
  while True:
    # do nothing
    time.sleep(1)
except KeyboardInterrupt:
  GPIO.remove_event_detect(channel)
  GPIO.cleanup()

If these scripts do not work reliably, then your resistors and capacitor is not in place. Figuring this out took me ages as the gpio python library gives the impression this can be handled via software.
I will use the event based script moving forward.

playing Music

We know how to play music from cmd line. We will call this cmd-line tool from within the python script. The way we call it creates a second process which runs unrelated to the python process. But it can easily be killed from the same python process at a later point in time.
Maybe there are more sophisticated ways with python audio libraries, but i did not see how to add python libraries easily, with the OS chosen.
So the python script which starts and stops playing music looks like this:

#!/usr/bin/env python

import time
import subprocess

file = "/data/520674__shortrecord__relaxation-music-2.mp3"
args = ['/bin/mpg123', '--mono', '--continue', file ]
p = subprocess.Popen(args, shell=False, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,)
time.sleep(8)

b = bytes("q", 'utf-8')
# p.communicate(b)
p.terminate()

putting it together

Combining both functionalities into one python script will look like this:

# cat sensor_audio.py
#!/usr/bin/env python
#coding: utf8

import RPi.GPIO as GPIO
import time
import subprocess

channel = 10
file = "/data/524342__geo-skango__listen-to-the-nature.mp3"   args = ['/bin/mpg123', '--mono', '--continue', file ]

GPIO.setmode(GPIO.BOARD)
GPIO.setup(channel, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

# function for input signal change event 
def doHighLow(channel):
    global p
    if GPIO.input(channel) == GPIO.HIGH:
        p = subprocess.Popen(args, shell=False, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,)
    if GPIO.input(channel) == GPIO.LOW:
        # b = bytes("q", 'utf-8')
        # p.communicate(b)
        p.terminate()

# declare the event
GPIO.add_event_detect(channel, GPIO.BOTH, callback = doHighLow, bouncetime = 200)

# endlessloop
try:
  while True:
    time.sleep(1)
except KeyboardInterrupt:
  GPIO.remove_event_detect(channel)
  GPIO.cleanup()

Auto-Starting the scripts

The last task is to assure this python script gets started, when the Raspberry starts. In the given OS systemd mechanisms are used so i wrote a systemd unit file and placed it into the appropriate directory

# cat /etc/systemd/system/restroom.service
[Unit]
Description=Script which plays music in the restroom
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /data/sensor_audio.py

[Install]
WantedBy=multi-user.target

Thereafter you need to enable and start the newly created service:

# systemctl daemon-reload
# systemctl enable restroom.service
# systemctl start restroom.service

Conclusion

Now each time you lock the door of the rest room, the switch gets closed, which triggers the event to start the music. Unlocking the door releases the switch, which once again triggers an event. This time the playback process gets killed, which stops the music. I love this gimmick even though it is not much use.

I needed to dig into details for most of the topics i strived with this project. And here are the links i found most relevant and useful for the work done. Thank you to each of the linked content providers for the knowledge i could found this project on. I hope these links are still valid.

[1] Training Center
Lexher Training Center, Isafjordsgatan 15,, , Kista, Stockholm, Sweden, 16440

[2] turnkey solution
http://www.arbitrary-precision.com/products/cfsound/CFSound_II_Main.htm

[3] switch
https://de.elv.com/riegel-umschaltkontakt-099787

[4] InCeiling speaker
https://www.canton.de/de/pro-house/inceiling/inceiling-443

[5] HiFiBerry AMP2
https://www.hifiberry.com/shop/boards/hifiberry-amp2/

[6] Power Supply:
https://www.meanwell-web.com/en-gb/ac-dc-industrial-desktop-adaptor-with-3-pin-iec320-gs60a18–p1j (or similar)

[7] Case:
https://www.hifiberry.com/shop/cases/universal-case-black/

[8] HifiBerryOS
https://www.hifiberry.com/hifiberryos/

[9] volumio
https://volumio.org/get-started/

[10] adjust e.g. raspberianOS for use with HifiBerry
https://www.hifiberry.com/docs/software/configuring-linux-3-18-x/

[11] download some audio files
https://freesound.org/search/?q=relax&f=created%3A%5BNOW-7DAY+TO+NOW%5D&s=num_downloads+desc&advanced=0&g=

[12] GPIO usage of hifiberry boards
https://www.hifiberry.com/docs/hardware/gpio-usage-of-hifiberry-boards/

[13] resistors to attach the switch sensor:
https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/robot/buttons_and_switches/

[14] German site explains coding, issues with internal resistors
http://www.netzmafia.de/skripten/hardware/RasPi/RasPi_GPIO_int.html

[15] suggesting 100 nf capacitor as well:
https://raspi.tv/2014/rpi-gpio-update-and-detecting-both-rising-and-falling-edges
or: https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/

[16] GPIO pin numbering
https://www.raspberrypi-spy.co.uk/2012/06/simple-guide-to-the-rpi-gpio-header-and-pins/

Leave a Reply

close

Subscribe to our newsletter.

Please select all the ways you would like to hear from Open Sourcerers:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our newsletter platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.