Adjust the volume or mute the active audio device on your Raspberry Pi using a rotary encoder and a few GPIOs. This project was inspired by the Orthopi mechanical keyboard and savetheclocktower rotary encoder volume control. Holding down the knob launches PulseAudio Volume Control. Tested on Raspberry Pi models 3B+, 4, and 400 with HDMI audio output, analog out (on the 3B+), and with a wireless headset via USB dongle. It works with Raspbian Bullseye 32-bit and 64-bit and Retropi--possibly older releases but I haven't tested.
Hardware requirements for this project are a Raspberry Pi, a cheap rotary encoder, jumper wires, and 3 free GPIO pins.
This is a simple high-level implementation using the well documented gpiozero library that I learned to use in The Official Raspberry Pi Beginner's Guide. Specifically, this project uses gpiozero RotaryEncoder and Button. Instead of alsa, as the OS level command for muting and setting volume, I'm using the pulse audio utility pactl
which seems to be installed out of the box on my Raspberry Pi 3, 4, and 400 all running Raspbian. Currently, this script is less than 100 lines and doesn't require admin privileges or creating a systemd service. To install I created a symlink to the script in $HOME/.local/bin
. I use BASH as my shell, so I appended the script name to my .bashrc. You can also simply run the script.
Download the Python script below from Github into your .local/bin/
folder
cd $HOME/.local/bin
wget https://gist.githubusercontent.com/brucesdad13/4ac8ceeb904903b9346fc1fa4d579c73/raw/6bfa66ce287caae835b9b871855425d328e8fdab/gpiozero_volknob.py
chmod 755 gpiozero_volknob.py
sudo apt update
sudo apt install pulseaudio pulseaudio-utils pavucontrol python3 python3-gpiozero python3-pigpio python3-rpi.gpio
Note: on RetroPi I had to reboot after installing the above packages which seemed to fire up the pulseaudio server. I also futzed with some of the audio settings on the RetroPi and TBH am not sure what I changed compared with the stock install. I'll have to do a diff compare someday...
If you want the knob to automatically work as soon as you login, append this snippet to your .bashrc or equivalent shell rc file. This will launch gpiozero_volknob.py in the background. If you need this to work for multiple users see the systemd example here and adapt:
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi
GPIOZERO_VOLKNOB_PID="$HOME/.gpiozero_volknob.pid"
if ! ps up `cat $GPIOZERO_VOLKNOB_PID 2>/dev/null` &> /dev/null; then
nohup gpiozero_volknob.py &> /dev/null &
echo $! > $GPIOZERO_VOLKNOB_PID
fi
Running these commands will give you an idea of whether or not everything is in place for the script to function. First run pactl list sinks
and make a note of which sink # is "RUNNING". With some audio playing, try muting it by running pactl set-sink-mute 0 toggle
(substituing your active sink # for 0). Run the command again to unmute. If that worked you should be ready. You can also play with setting your volume level pactl set-sink-volume 0 60%
(substituting your active sink # for 0).
- Nothing happens when the knob is turned or button pushed:
- Ensure that gpiozero_volknob.py is running (ps -ef|grep volknob)
- Check that your wiring matches the pins in the code and or edit the code
- GPIO BCM pin 22 goes to the encoder push button switch pin
- GPIO BCM pin 18 goes to rotary encoder CLK pin
- GPIO BCM pin 17 goes to rotary encoder DT pin.
- Volume goes up when you want it to go down or vice versa
- Switch the CLK and DT wires or the pin numbers in RotaryEncoder()
I'm just testing now if I can disable the auto idle and see if that helps... Well Its now sitting at IDLE instead... Progress i guess!
Sink #1
State: IDLE
Name: bluez_sink.66_B8_89_0C_A8_B0.a2dp_sink
Description: XFW-BT
Driver: module-bluez5-device.c
Sample Specification: s16le 2ch 44100Hz
Channel Map: front-left,front-right
Owner Module: 24
Mute: no
Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB
balance 0.00
Base Volume: 65536 / 100% / 0.00 dB
Monitor Source: bluez_sink.66_B8_89_0C_A8_B0.a2dp_sink.monitor
Latency: 43108 usec, configured 39512 usec
Flags: HARDWARE DECIBEL_VOLUME LATENCY
Properties:
bluetooth.protocol = "a2dp_sink"
device.description = "XFW-BT"
device.string = "66:B8:89:0C:A8:B0"
device.api = "bluez"
device.class = "sound"
device.bus = "bluetooth"
device.form_factor = "headset"
bluez.path = "/org/bluez/hci0/dev_66_B8_89_0C_A8_B0"
bluez.class = "0x240404"
bluez.alias = "XFW-BT"
device.icon_name = "audio-headset-bluetooth"
device.intended_roles = "phone"
Ports:
headset-output: Headset (type: Headset, priority: 0, available)
Active Port: headset-output
Formats:
pcm