Rotary volume control for the Raspberry Pi

If you have checked out my previous post, you know I want to create a clock radio powered by the Raspberry Pi with the audio coming out of a JustBoom AMP Zero pHAT.  One of the things that I thought would be quite handy (probably necessary) would be volume control.  Ideally that would be some kind of hardware rotation controller, but looking around online on how one might control the JustBoom amp with a rotary encoder really only brought back results regarding full OS solutions such as Moode Audio.

As it turns out, controlling the volume for the JustBoom when using just Raspbian is actually pretty simple.

The hardware

We’re using a simple rotary encoder (this particular one was purchased from ModMyPi) which also has a momentary push button functionality.

ModMyPi has a great article on just what a rotary encoder is and how it works, so I won’t bother repeating it all here.  Suffice to say, you can determine which way the rotary encoder is rotating and from there you can determine if you want to move the volume up or down.

The encoder has five pins; a positive and ground, one for the button and two for the rotary encoding.  I hooked those up to pins higher up on the end of the GPIO because I knew that JustBoom weren’t using them (nor would be the screen I want to use).  The pins I used were:

  • GND: Physical pin 39
  • +: Physical pin 2
  • SW: Physical pin 37/BCM 26
  • DT: Physical pin 31/BCM 6
  • CLK: Physical pin 29/BCM 5

Setting the volume

The first thing I needed to do was just get a little information about my audio card, such as what is the number of the audio card and what is the maximum volume.

In order to do that we first want to check out the proc system so that we can see the audio card information.  That can be done with the command line:

 cat /proc/asound/cards

With that I get the output:

 0 [sndrpijustboomd]: snd_rpi_justboo - snd_rpi_justboom_dac
                      snd_rpi_justboom_dac

So it can be seen that the JustBoom card is number 0.  This was going to be pretty obvious as it’s the only card that I have attached to the Raspberry Pi, but it’s good to see it written down.

The audio amp card is an ALSA-based card and as such the amixer command can be used to get the information or set the value of audio controls.  I’ve tried to use the name of the control but that never worked well for me, but using the id of the control worked well.  To get the list of controls and their respective ids, I just run the command:

 amixer -c 0 controls

That brought back the results:

numid=6,iface=MIXER,name='DSP Program'
numid=3,iface=MIXER,name='Analogue Playback Boost Volume'
numid=2,iface=MIXER,name='Analogue Playback Volume'
numid=10,iface=MIXER,name='Auto Mute Mono Switch'
numid=11,iface=MIXER,name='Auto Mute Switch'
numid=8,iface=MIXER,name='Auto Mute Time Left'
numid=9,iface=MIXER,name='Auto Mute Time Right'
numid=7,iface=MIXER,name='Clock Missing Period'
numid=5,iface=MIXER,name='Deemphasis Switch'
numid=4,iface=MIXER,name='Digital Playback Switch'
numid=1,iface=MIXER,name='Digital Playback Volume'
numid=20,iface=MIXER,name='Max Overclock DAC'
numid=19,iface=MIXER,name='Max Overclock DSP'
numid=18,iface=MIXER,name='Max Overclock PLL'
numid=16,iface=MIXER,name='Volume Ramp Down Emergency Rate'
numid=17,iface=MIXER,name='Volume Ramp Down Emergency Step'
numid=12,iface=MIXER,name='Volume Ramp Down Rate'
numid=13,iface=MIXER,name='Volume Ramp Down Step'
numid=14,iface=MIXER,name='Volume Ramp Up Rate'
numid=15,iface=MIXER,name='Volume Ramp Up Step'

The volume control I want to use is the ‘Digital Playback Volume’ which has the numid of 1.

That numid can now be used to get information about that control.  To do that, the command is:

 amixer cget numid=1

Which gives the result:

 numid=1,iface=MIXER,name='Digital Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=207,step=0
  : values=0,0
  | dBscale-min=-103.50dB,step=0.50dB,mute=1

With the above, the minimum audio value is 0 and the maximum value that can be set is 207.  The values=0,0 shows that the volume value is currently at 0 (for left and right channels, presumably).

The volume can easily be adjusted with the command like:

 amixer -c 0 cset numid=1 175

The value that returns from the cget lookup is now:

numid=1,iface=MIXER,name='Digital Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=207,step=0
  : values=175,175
  | dBscale-min=-103.50dB,step=0.50dB,mute=1

Hooking it all up

Now that the hardware is attached and we know a little more information about the card we can write a little code:

I saved that into a file called volume.py and ran it with the command:

python volume.py

(You could put it in the background if you want, but I had another terminal window up where I start some music playing with mpg321).

And it works!  Pretty simple, but the results are effective.

What next?

The rotary encoding is just determined by a simple check of the clk and dt values being different and using those to determine which way it’s rotating.  However, it’s a little susceptible to bounce and can quite often go down when it’s meant to go up, and it’s certainly not very good at rotating at high speeds.  So something about that needs to be sorted out.  However, it’s not a bad first step.

Did you like this? Share it:

Leave a Reply