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.
8 Responses
Leave a Reply
You must be logged in to post a comment.
Will this work with these type of pot?
10k B103 3-pin – https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSha2H8vu2tjdDgKgJrk_r9yOF24JH76gm3h58DSKpJzSRvepY7wA
Or
10k B103 5 pin – https://ae01.alicdn.com/kf/HTB1.eMEHVXXXXayXXXXq6xXFXXX7/Dial-potentiometer-b103-10k-double-16-2mm-gear-dianthuses-potentiometer-5.jpg_640x640.jpg
Thank you!
Honestly, I don’t know for sure – never tried. However, potentiometers only have a certain range that they can travel through – a min and max rotation before they stop. Rotary encoders, on the other hand, have infinite rotation so would allow you to scroll as much you want to either way. Also, rotary encoders produces digital signals whereas potentiometers would be analogue. So I suppose it depends on your need.
This thread may be of some info to you: https://www.element14.com/community/thread/55374/l/potentiometer-instead-of-rotary-encoder?displayFullThread=true
Awesome, easy and very well explained! Thank you so much… I ran into one Prolem though:
I get a negative Volume for minimum volume (-10239) when I do “amixer cget numid=1″. I cannot call ” amixer -c 0 cset numid=1 -175″ with a negative number, for it gets misinterpreted…. I have no soundcard, but use regular output via build in sound: “cat /proc/asound/cards”
gives
” 0 [ALSA ]: bcm2835_alsa – bcm2835 ALSA
bcm2835 ALSA”
How can I use the negative value for volume-min. so I can make the volume lower than now? Right now I can only go from 400 to 0, leaving a considerable amount of volume left…..
Hi Andy,
I was wondering if you could help to understand my problem is, as I followed your tuto and the rotary is working but not acting on sound at all.
(Raspberry 3)
Here is the output of with the above commands :
pi@raspberrypi:~ $ cat /proc/asound/cards
0 [Headphones ]: bcm2835_headphonbcm2835 Headphones – bcm2835 Headphones
bcm2835 Headphones
pi@raspberrypi:~ $ amixer -c 0 controls
numid=2,iface=MIXER,name=’Headphone Playback Switch’
numid=1,iface=MIXER,name=’Headphone Playback Volume’
pi@raspberrypi:~ $ amixer cget numid=1
numid=1,iface=MIXER,name=’Capture Volume’
; type=INTEGER,access=rw——,values=1,min=0,max=65536,step=1
: values=65536
And once I launch the script I can see the knob acting
pi@raspberrypi:~ $ python volume.py
105 (51%)
Muted
Unmuted
100 (48%)
105 (51%)
110 (53%)
115 (56%)
110 (53%)
105 (51%)
100 (48%)
95 (46%)
90 (43%)
85 (41%)
80 (39%)
I have not modify your script.
Sound is coming in the raspberry through Bluetooth (A2DP) and out via the jack plug.
Thank you for your help
cheers
@srvduplex you say you haven’t modified the script at all, but I think you may need to. In the script there is a min/max value:
which I got from the values output by using
amixer cget numid=1
.When you ran that same command, your min/max was shown as
min=0,max=65536
. So in the script, make the lines look like:and see how that works out. You may also need to adjust the line:
So that the
100
is around3200
or something, given that your max is reported as65536
.Hello!
Thank you for a very interesting site!
I have the same question as klettervirus.
I get a negative Volume for minimum volume (-10239) when I do “amixer cget numid=1″. I cannot call ” amixer -c 0 cset numid=1 -175″ with a negative number, for it gets misinterpreted…. I have no soundcard, but use regular output via build in sound: “cat /proc/asound/cards”
gives
” 0 [ALSA ]: bcm2835_alsa – bcm2835 ALSA
bcm2835 ALSA”
How can I use the negative value for volume-min. so I can make the volume lower than now? Right now I can only go from 400 to 0, leaving a considerable amount of volume left…..
Can you please help me?
Best regards
Johan
Hi Johan, thanks for posting a comment.
To be honest, it’s been a really long time since I’ve played around with this as the project I was going to use it on didn’t go anywhere, so this answer is just a guess…
I had a look at the amixer man page, and it’s possible to give a percentage value, such as:
So instead of incrementing a numeric count based on min/max values from a call to amixer, you could try just going up and down from 0 to 100%, something like (the very, very untested code as I’m just winging it right now):
No idea if it'll work, and I cannot try it out as my pi and sound card are scattered around in a box or two somewhere - no idea where right now! 😀 But hopefully it'll point you in close enough to the right direction!