I’m not proud of curl XYZ | bash but desperate times call for desperate measures.
There are reasons why we do not want to use the on-board sound output of the Raspberry Pi. First of all we have to distinguish between the digital audio out via HDMI and PWM driven pseudo analog voltages passed through a few passive components (mainly resistors and capacitors).
HDMI is quickly dismissed. I have no idea on how to convert digital HDMI audio signals into analog voltage levels suitable for an amplifier. The only out-of-the-box solution that comes to my mind is an HDMI to A/V converter. I’ve used such a device in a DIY ambilight project for my TV. They are too expensive (well, ~6€) and especially too big for our purpose. In order to make it smaller, we’d have to disassemble the device, unsolder the big connectors, which is always error-prone and time consuming, and then use the board directly.
The second option, namely analog audio out, is not readily available on the Pi Zero W. In contrast to the bigger Raspberry Pis, the whole circuit is just missing on the board. The device itself is able to produce PWM signals for audio applications. To make PWM usable, we’d have to rebuild the A/V circuit of the Raspberry Pi 3B for example.
The quality of this audio source would still not be sufficient. It’s always a bit noisy in my experience which may be caused by other components or the power supply. The latter problem may be solved by using a DC power source like a battery. Anyhow, we will not be utilizing the PWM pins for that purpose. We need them for our LED strip setup.
Instead we are going to use the I²S protocol to speak to a stereo digital to analog converter. The proposed board uses the PCM5102A IC to produce analog voltages suitable to be fed into a stereo amplifier that eventually drives a pair of speakers.
Although one could think so, I²S is not related to I²C (the protocol we use to communicate with the MPU6050 board). Instead, it is a way of transferring PCM audio data from one IC to another using a serial bus (on the same PCB at best).
The Raspberry Pi Zero W uses the Broadcom SOC BCM2835. Having a look at the documentation on page 102 reveals the I²S pins. GPIO 18 is used as continuous serial clock (SCK, BCLK or BCK [bitclock] or simply CLK). GPIO pin 19 selects which of the two channels (remember: stereo) is being transmitted (FS [frame select], WS [word select], LRCK or LRCLK [Left-Right-Clock]). Pins 20 and 21 are data pins (input and output). We only transfer data from our micro computer to the DAC and thus only connect pin 21 (DOUT [data out] aka SDATA [serial data], SDOUT, DACDAT, …).
The next link in our sound chain is an amplifier. I tried out several boards before I determined the PAM8406 as the one I want to include on the PCB.
A TEA2025B board is simply too big and bulky, although the sound was very good. Not only did I test it as a stereo amplifier but also in mono configuration (that board is not available anymore). The XTP8871 is super cheap and worked out fine but we’d have to include two of the boards for two channel audio output. That makes it too big again.
The PAM8406 IC is cheaper, supports two channel amplification and works out just flawlessly. It may be assumed that all the effort we put into crystal clear audio via the PCM5102A is ridiculed by this amplifier. Nevertheless, I’m more than happy with the results. Two on-board potentiometers allow volume control for both channels independently.
The only missing components for our audio setup are some suitable loudspeakers. I connected a pair of 3″ full range speakers with an impedance of 4Ω and a power rating of 5W. That worked out really well and I was satisfied with the sound results. Unfortunately the quality differs depending on the enclosure. We’d have to design a nice box that is big enough to fit the already large speakers. I rather chose another option, namely a completely enclosed set of loudspeakers. By doing so, we don’t have to deal with any enclosure related sound issues. The speakers have similar specs but an internal impedance of 8Ω. I’m again very satisfied with the sound. The form factor is even more appropriate for our purpose than the first bare speaker solution.
Wiring up all the components is easy. Connect PCM5102A bitclock (BCK) to RPi GPIO 18, LRCK (word select) to pin 19 and the PCM data in (DIN) to pin 21. Then link the DAC to the amplifier by hooking up the right and left channel output to the PAM8406’s input. Finally connect the speakers to the amplifier’s stereo output and you are good to go.
Now that all the hardware is connected, we have to configure the software part.
I had a really hard time trying to get the DAC up and running. You have to enable I²S, disable the on-board sound, disable drivers, change the default sound device, enable other drivers and so on. I had problems speaking to the device at all, bad sound, sometimes noise and so on. Then I found out it is some kind of clone of the Pimoroni pHAT DAC at only a fraction of the costs. From that moment on, everything went smooth. There’s a nice tutorial with a step by step guide on how to enable the device. I highly recommend the one line installer AFTER having a look at the shell script and (possible) dependencies from the web.
pi@raspberrypi $ curl https://get.pimoroni.com/phatdac > phatdac.sh pi@raspberrypi $ less phatdac.sh #!/bin/bash : <<'DISCLAIMER' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, [...] pi@raspberrypi $ bash phatdac.sh This script will install everything needed to use pHAT DAC Always be careful when running scripts and commands copied [...]
At the end of the script it should ask for a reboot and that’s what we do. You should now be able to test the new sound output device. Push a song onto the Raspberry Pi, install mpg123 and play the file.
marta@pc $ scp song.mp3 pi@raspberrypi:song.mp3 marta@pc $ ssh pi@raspberrypi pi@raspberrypi $ sudo apt-get install mpg123 pi@raspberrypi $ mpg123 song.mp3
The next thing we wanna do is to use our new ability and play an audio file from python. The mpg123 wrapper I wrote includes a main function that asks for a file and plays it back.
pi@raspberrypi $ python /home/pi/mmm/marta/MPG123.py 19:59:22.064 | MPG123 | > SILENCE 19:59:22.070 | MPG123 | waiting for max 10 seconds 19:59:22.181 | MPG123 | < @R MPG123 (ThOr) v8 19:59:22.184 | MPG123 | mpg123 startup 19:59:22.188 | MPG123 | < @silence 19:59:22.199 | MPG123 | > V 50 19:59:22.203 | MPG123 | waiting for max 1 seconds 19:59:22.208 | MPG123 | < @V 50.000000% 19:59:22.214 | MPG123 | volume: 50.000000 19:59:22.229 | MPG123 | mpg123 initialized 19:59:22.233 | MPG123 | enter song file path /home/pi/song.mp3 19:59:30.794 | MPG123 | CTRL + C to interrupt 19:59:30.798 | MPG123 | > LP /home/pi/song.mp3 19:59:30.802 | MPG123 | waiting for max 1 seconds 19:59:30.814 | MPG123 | < @I ID3:Never Gonna Give You Up Rick Astley Rickrolled Unknown 19:59:30.822 | MPG123 | < @I ID3.genre:255 19:59:30.830 | MPG123 | < @I ID3v2.title:Never Gonna Give You Up 19:59:30.836 | MPG123 | < @I ID3v2.artist:Rick Astley 19:59:30.841 | MPG123 | < @I ID3v2.comment:Rickrolled 19:59:30.848 | MPG123 | < @P 1 19:59:30.855 | MPG123 | state=PAUSED 19:59:30.893 | MPG123 | > SAMPLE 19:59:30.898 | MPG123 | waiting for max 1 seconds 19:59:30.903 | MPG123 | < @SAMPLE 0 9583535 19:59:30.909 | MPG123 | current position: 0 19:59:30.920 | MPG123 | > V 0 19:59:30.924 | MPG123 | waiting for max 1 seconds 19:59:30.930 | MPG123 | < @V 0.000000% 19:59:30.936 | MPG123 | volume: 0.000000 19:59:30.945 | MPG123 | > P 19:59:30.956 | MPG123 | waiting for max 1 seconds 19:59:30.961 | MPG123 | < @P 2 19:59:30.968 | MPG123 | state=PLAYING 19:59:30.976 | MPG123 | < @S 1.0 3 44100 Joint-Stereo 2 626 2 0 0 0 192 0 1 19:59:30.984 | MPG123 | > P 19:59:30.989 | MPG123 | waiting for max 1 seconds 19:59:30.991 | MPG123 | track length: 217314 19:59:31.004 | MPG123 | > V 50.0 19:59:31.007 | MPG123 | waiting for max 1 seconds 19:59:31.011 | MPG123 | < @P 1 19:59:31.019 | MPG123 | state=PAUSED 19:59:31.026 | MPG123 | > K 0 19:59:31.025 | MPG123 | < @V 50.000000% 19:59:31.038 | MPG123 | volume: 50.000000 19:59:31.035 | MPG123 | waiting for max 1 seconds 19:59:31.046 | MPG123 | < @K 0 19:59:31.049 | MPG123 | > P 19:59:31.064 | MPG123 | waiting for max 1 seconds 19:59:31.070 | MPG123 | < @P 2 19:59:31.074 | MPG123 | state=PLAYING 19:59:31.570 | MPG123 | > SAMPLE 19:59:31.573 | MPG123 | waiting for max 1 seconds 19:59:31.616 | MPG123 | < @SAMPLE 28847 9583535 19:59:31.621 | MPG123 | current position: 28847 19:59:31.642 | MPG123 | position: 654 ms 19:59:32.146 | MPG123 | > SAMPLE 19:59:32.150 | MPG123 | waiting for max 1 seconds 19:59:32.159 | MPG123 | < @SAMPLE 53039 9583535 19:59:32.166 | MPG123 | current position: 53039 19:59:32.174 | MPG123 | position: 1203 ms 19:59:32.679 | MPG123 | > SAMPLE 19:59:32.682 | MPG123 | waiting for max 1 seconds 19:59:32.692 | MPG123 | < @SAMPLE 76079 9583535 [...] 19:59:36.098 | MPG123 | current position: 226991 19:59:36.110 | MPG123 | position: 5147 ms 19:59:36.614 | MPG123 | > SAMPLE 19:59:36.618 | MPG123 | waiting for max 1 seconds 19:59:36.625 | MPG123 | < @SAMPLE 251183 9583535 19:59:36.633 | MPG123 | current position: 251183 19:59:36.640 | MPG123 | position: 5696 ms ^C19:59:36.968 | MPG123 | MPG123 terminating... 19:59:36.974 | MPG123 | > Q 19:59:36.980 | MPG123 | < 19:59:36.985 | MPG123 | mpg123 died 19:59:36.990 | MPG123 | waiting for checker thread. 19:59:36.999 | MPG123 | ok, finished. 19:59:37.003 | MPG123 | good bye