Hello everyone,
I was wondering If anyone could help me out with controlling the system volume with some buttons. What I'm doing is running a video looper with omxplayer, but after every video the volume setting inside of omxplayer reverts to the default before starting the next video. Since this happens the best way to control volume for the looper is the volume setting inside of alsamixer.
From SSH I can run:
amixer set Speaker 5%+
amixer set Speaker 5%-
These commands control the volume perfectly, but now I want to set up two GPIO buttons to be volume up and down buttons. Can this be done with a simple Python script? Any help?
Thanks!
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
alsamixer volume control via GPIO buttons
Last edited by dirtyhorror on Sat Sep 22, 2018 10:41 am, edited 1 time in total.
Re: alsamixer volume control via GPIO buttons
In python you can run terminal commands quite easily using the subprocess module. Best look up the documentation but you might do something like
Have a look at the gpiozero docs for how to connect your gpio pins to enable this to happen
Code: Select all
import subprocess
...
def change_volume_function(percent_change):
if percent_change < 0:
sign_char = '-'
else:
sign_char = '+'
change_str = '{}%{}'.format(abs(percentage_change), sign_char)
subprocess.Popen(['amixer', 'set', 'Speaker', change_str]
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
Thanks for the reply. I was having a hard time following the gpiozero doc to get this working, so what I did was edit a shutdown button script I had to run the volume command instead and it works! However my button only runs the script once and then it stops working. Here is the code:
I also tried to add the volume down button into the script like this:
This works, but it will only let me trigger gpio 2 after triggering gpio 1.
Can someone please help me fix the code to keep running and to be able to trigger either command at any time?
Thanks!
Code: Select all
#!/bin/python
import RPi.GPIO as GPIO
import os
gpio1=38
GPIO.setmode(GPIO.BOARD)
GPIO.setup(gpio1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
try:
GPIO.wait_for_edge(gpio1, GPIO.FALLING)
os.system("amixer set Speaker 5%+")
except:
pass
GPIO.cleanup()
Code: Select all
#!/bin/python
import RPi.GPIO as GPIO
import os
gpio1=38
gpio2=40
GPIO.setmode(GPIO.BOARD)
GPIO.setup(gpio1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
try:
GPIO.wait_for_edge(gpio1, GPIO.FALLING)
os.system("amixer set Speaker 5%+")
GPIO.wait_for_edge(gpio2, GPIO.FALLING)
os.system("amixer set Speaker 5%-")
except:
pass
GPIO.cleanup()
Can someone please help me fix the code to keep running and to be able to trigger either command at any time?
Thanks!
Re: alsamixer volume control via GPIO buttons
If you want to keep on triggering increase or decrease then you will need to keep your program running in a loop. In gpiozero (or GPIO using callback functions) then the loop can just be a pause as in the code below. Alternatively it could be a `while True:` kind of thing.
It might not matter most of the time but it's a good idea to stop using os.system. Look at the python documentation to see why.
It might not matter most of the time but it's a good idea to stop using os.system. Look at the python documentation to see why.
Code: Select all
#!/bin/python
from gpiozero import Button
from signal import pause
from subprocess import Popen
def increase_volume():
Popen(['amixer', 'set', 'Speaker', '5%+'])
def decrease_volume():
Popen(['amixer', 'set', 'Speaker', '5%-'])
button_up = Button(20) # bcm20 is board38 (I think, better check)
button_up.when_pressed = increase_volume
button_down = Button(21) # bcm21 is board40
button_down.when_pressed = decrease_volume
pause()
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
Thanks Paddy! That works beautifully!
I'm pretty happy with just being able to control the volume up and down, because it stays put afterwards, but is there an easy way to add to the code so that if you hold one of the buttons something else would happen?
Instead of setting up a 3rd button, I'd like to make the audio mute when gpio 21 is held.
The terminal command to mute the alsamixer is:
amixer set Speaker toggle
I tried this but it doesn't work and breaks script.
Thank you so much!
I'm pretty happy with just being able to control the volume up and down, because it stays put afterwards, but is there an easy way to add to the code so that if you hold one of the buttons something else would happen?
Instead of setting up a 3rd button, I'd like to make the audio mute when gpio 21 is held.
The terminal command to mute the alsamixer is:
amixer set Speaker toggle
I tried this but it doesn't work and breaks script.
Code: Select all
#!/bin/python
from gpiozero import Button
from signal import pause
from subprocess import Popen
def increase_volume():
Popen(['amixer', 'set', 'Speaker', '10%+'])
def decrease_volume():
Popen(['amixer', 'set', 'Speaker', '10%-'])
def mute_volume():
Popen(['amixer', 'set', 'Speaker', 'toggle'])
button_up = Button(20) # bcm20 is board38 (I think, better check)
button_up.when_pressed = increase_volume
button_down = Button(21) # bcm21 is board40
button_down.when_pressed = decrease_volume
button_mute = Button(21, hold_time=2)
button_mute.when_held = mute_volume
pause()
Thank you so much!
Re: alsamixer volume control via GPIO buttons
Hi, I don't think you need to set up a different `Button` as you're not using an additional physical button. You just need to add to the existing behavior of your existing button(s). I would try something like
PS you might want to include some extra logic so that you keep track (say in a global variable) on whether the sound is muted then unmute if either button is pressed but not held.
Code: Select all
...
def mute_volume():
Popen(['amixer', 'set', 'Speaker', 'toggle'])
button_up = Button(20)
button_up.when_pressed = increase_volume
button_down = Button(21, hold_time=2)
button_down.when_pressed = decrease_volume
button_down.when_held = mute_volume
pause()
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
Thanks again, Paddy! That code works great! And I agree about setting up some extra logic to unmute if either volume buttons are pressed. That would definitely make the experience more intuitive. I will do some research and hopefully be able to get it working without bugging you some more
Thank you so much for you help

Thank you so much for you help
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
I managed to break the code again.
I tried to add a new process that would toggle the mute by a button press if the mute had previously been executed by holding the button. This obviously is not the right way to do this..
Please lend me some guidance.
Code: Select all
def unmute_volume():
if mute_volume == True
Popen(['amixer', 'set', 'Speaker', 'toggle'])
button_up = Button(20)
button_up.when_pressed = increase_volume
button_down = Button(21, hold_time=1)
button_down.when_pressed = decrease_volume
button_down.when_held = mute_volume
button_up.when_pressed = unmute_volume
button_down.when_pressed = unmute_volume
pause()

Please lend me some guidance.
Re: alsamixer volume control via GPIO buttons
Ah, you need to set the global variable and check it in the other functions so something along the lines of: (not checked or tested)
As you can see there is much duplication between increase_volume and decrease_volume so I would look at combining them but it's a bit messy with gpiozero callbacks (using globals isn't ideal either, which you have to do in order for this to be relatively simple code - look up on google why globals not ideal and things that can go wrong with python globals in functions)
Code: Select all
#!/bin/python
from gpiozero import Button
from signal import pause
from subprocess import Popen
def increase_volume():
global is_muted
if is_muted:
toggle_volume()
Popen(['amixer', 'set', 'Speaker', '5%+'])
def decrease_volume():
global is_muted
if is_muted:
toggle_volume()
Popen(['amixer', 'set', 'Speaker', '5%-'])
def toggle_volume():
global is_muted
Popen(['amixer', 'set', 'Speaker', 'toggle'])
is_muted = not is_muted
def mute_volume():
global is_muted
if not is_muted:
toggle_volume()
is_muted = False #set it off to begin with
button_up = Button(20)
button_up.when_pressed = increase_volume
button_down = Button(21)
button_down.when_pressed = decrease_volume
button_down.when_held = mute_volume
pause()
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
Brilliant! Works perfectly! Thank you so much, Paddy!
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
After some testing, everything works great except if you shutdown the Pi with the mute engaged. When it powers back up the behavior of the code is reversed and the volume controls no longer work. It just allows for one touch mute, and hold to unmute.
The cure was to unmute the system and then reboot. It started to work normal again after that.
Any fix, or just avoid shutting down with mute engaged?
The cure was to unmute the system and then reboot. It started to work normal again after that.
Any fix, or just avoid shutting down with mute engaged?
Re: alsamixer volume control via GPIO buttons
hi. the obvious solution would be to use amixer to determine state rather than a variable in the python code. I will have a look at some stage.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d
Re: alsamixer volume control via GPIO buttons
OK, after a quick google https://linux.die.net/man/1/amixer I would suggest using 'amixer set Speaker mute' and '... unmute' instead of toggling. i.e. something like (again untested. And you see how I've tried to reduce the duplication in the increase and decrease functions by having a change function)
Code: Select all
#!/bin/python
from gpiozero import Button
from signal import pause
from subprocess import Popen
def increase_volume():
change_volume('+')
def decrease_volume():
change_volume('-')
def change_volume(ch_sign):
Popen(['amixer', 'set', 'Speaker', 'unmute'])
Popen(['amixer', 'set', 'Speaker', '5%{}'.format(ch_sign)])
def mute_volume():
Popen(['amixer', 'set', 'Speaker', 'mute'])
button_up = Button(20)
button_up.when_pressed = increase_volume
button_down = Button(21)
button_down.when_pressed = decrease_volume
button_down.when_held = mute_volume
pause()
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d
-
- Posts: 11
- Joined: Sun Sep 16, 2018 10:00 am
Re: alsamixer volume control via GPIO buttons
Thanks Paddy! That did the trick!
BTW, Would you happen to know of any solution for an OSD that will overlay omxplayer and show the volume level from amixer? Since I am adjusting the master system volume I have lost my volume OSD. It is not super important, but would be very nice to have!
BTW, Would you happen to know of any solution for an OSD that will overlay omxplayer and show the volume level from amixer? Since I am adjusting the master system volume I have lost my volume OSD. It is not super important, but would be very nice to have!
Re: alsamixer volume control via GPIO buttons
Hi both,
excellent initiative! I am keen on trying the same on mu RPI4 with some push buttons on top (VOL+; VOL- & Mute). Can I use the code with relevant GPIO pin?
Any piece of advice appreciated, thanks
excellent initiative! I am keen on trying the same on mu RPI4 with some push buttons on top (VOL+; VOL- & Mute). Can I use the code with relevant GPIO pin?
Any piece of advice appreciated, thanks
