2.4 – PCM5102 Sound Board + Loudspeakers

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