Volume control via Piicodev POT

I’m having trouble finding the correct Python code to get this Piicodev POT to adjust the volume of my rPi5. Either master volume or VLC volume, whichever.

So far I have:
Watched numerous videos on this site and youtube.
Practived coding from those videos to ensure the POT is working (it is).
Searched this forum but didn’t find anything useful yet.
Used AI to write code for me. It didn’t work, but figured it would help me learn maybe how to make this work (attempted many things here without luck)

I would appreciate any direction. My coding is novice, but able to read and understand how most things function. Writing ground up is not my thing. Appreciate anything here!

-Josh

Hi Joshua!

Welcome to the forums. :slight_smile:

This is one of those problems where you just have to know the right google search terms.
Here is the article you need.

Have a read through and if you have any question don’t hesitate to come back and ask.

Pix :heavy_heart_exclamation:

1 Like

Out having a drink rn, but this looks very promising…thank you so much! Can’t wait to get back and try it out.

-Josh

1 Like

Hi Josh
If you have too many drinks you may be better off leaving the trying until tomorrow. HaHa I’m jealous.
Cheers Bob

2 Likes

Nice find @Pixmusix

Seems there’s a few different ways that you can achieve this end.
If found a stack overflow topic using a separate package python-alsaaudio which looks promising if you want fine-tuned control for eg. mixing.

1 Like

Excellent, checking this out rn. thanks!

3 Likes

Couldn’t find such a package via command line.
“E: Unable to locate package python-alsaaudio”

Though, going through Add/Remove Software, I found and installed it.

In Thonny “import alsaaudio” results in:
“ModuleNotFoundError: No module named ‘alsaaudio’”

hurrrmmmm… :expressionless:

1 Like

This is a good article that I will use when I attempt to setup two buttons to skip and go back a track in the audio player. Though, nothing there for a potentiometer to do the same.

I do like the idea of the button triggering simple commands to adjust volume. If I can’t get these POT’s to do this, I may have two buttons move the volume.

1 Like

Hiya again Josh.

This is solvable :slight_smile:
I know your new to code so I’ll keep things super basic.
Apologies in advance if I dumb it down too much.

This code using buttons below is what you’re talking about

def increase_volume():
    Popen(['amixer', 'set', 'Speaker', '5%+'])

def decrease_volume():
    Popen(['amixer', 'set', 'Speaker', '5%-'])

These are called functions and you would call them like this.

if pot_is_all_the_way_to_the_right():
   increase_volume() # does everything under the "increase_volume" definition.
elif pot_is_all_the_way_to_the_left():
   decrease_volume() # does everything under the "decrease_volume" definition.

but we can do better.


### ** UNTESTED CODE ** 
##Code stolen from: 
##https://core-electronics.com.au/guides/piicodev-potentiometer-getting-started-guide/
from PiicoDev_Potentiometer import PiicoDev_Potentiometer
from subprocess import Popen

pot = PiicoDev_Potentiometer() # Initialise the potentiometer
pot.maximum = 100 # set the range of output values
pot.minimum = 0   # if minimum or maximum are ommitted, they will default to 0 and 100 respectively

while true: #infinite loop
   #prepending f in from of the string allows us to embed variables in a string
   pot_value_as_percentage = f"{pot.value}%" 
   #set the volume to be whatever the pot's value.
   Popen(['amixer', 'set', 'Speaker', pot_value_as_percentage])

You will need to expand on this code and integrate it to your OS environment. You might also consider using BASH not python just because bash is the right level above the metal for this task. That’s just taste.

I hope this gives you a flavour of how you might proceed. :coffee:

Good luck.
Pix :heavy_heart_exclamation:

P.S.

You might also like to have the code automatically run when the raspberry pi boots up.
I tackled this a few years ago and you can follow my steps towards the bottom of this article under the heading “software”.

P.P.S

@Michael can import the piicodev micropython modules in vanilla python like I have above? (or is there a better way?) :question:

2 Likes

Excellent, thank you. In my mind I was going to take the Piicodev code for the led flash rate (which works) and kinda convert that to deal with volume instead, didn’t think it would be so difficult. Excited to conquer it though! (oh also! I do understand the code and what you are saying it does <3 )

3 Likes

Also, Simulated Parisians: Little Autonomous Pedestrians is neat! \m/

2 Likes

Sure! PiicoDev is compatible with Raspberry Pi.

1 Like

3+ hours in playing with the scripts from all the links. Used Ai to fill in some gaps, or at least learn more of what the code is doing. Using THONNY Assistant to identify where errors are and corrected a few. I feel closer, but no dice quite yet. (got working the LED code to change flash rate and also a basic POT read printouts…so at least the hardware is in a working condition)

Currently sitting at this code, which seems quite straight forward in what it’s to be performing. Only seems a few things it’s not understanding due to defining, it appears.

# Read the PiicoDev Potentiometer value
from PiicoDev_Potentiometer import PiicoDev_Potentiometer
from PiicoDev_Unified import sleep_ms
from subprocess import Popen

pot = PiicoDev_Potentiometer() # Initialise the potentiometer
pot.maximum = 100 # set the range of output values
pot.minimum = 0   # if minimum or maximum are ommitted, they will default to 0 and 100 respectively

# while True:
#    print(pot.value) # read the pot and print the result
#    sleep_ms(100)
    
DEBUG = True
MIN_VOLUME = 1
MAX_VOLUME = 100
VOLUME_INCREMENT = 5

def increase_volume():
    current_volume = int(get_current_volume())
    new_volume = min(current_volume + VOLUME_INCREMENT, MAX_VOLUME)
    set_volume(new_volume)

def decrease_volume():
    current_volume = int(get_current_volume())
    new_volume = max(current_volume - VOLUME_INCREMENT, MIN_VOLUME)
    set_volume(new_volume)

def get_current_volume():
    return Popen(['amixer', 'get', 'Master'], stdout=subprocess.PIPE).communicate()[0].split(' ')[5].strip('[]%')

def set_volume(volume):
    Popen(['amixer', '-D', 'pulse', 'set', 'Master', str(volume) + '%'])

while True:
    pot_value = pot.value
    if pot_value == pot.maximum:
        increase_volume()
    elif pot_value == pot.minimum:
        decrease_volume()

THONNY Assistant only gives me this last error:
NameError: name ‘subprocess’ is not defined
read.py, line 30
Python doesn’t know what subprocess stands for.

That would be this line:
def get_current_volume():
return Popen([‘amixer’, ‘get’, ‘Master’], stdout=subprocess.PIPE).communicate()[0].split(’ ‘)[5].strip(’[]%')

How might I tackle this portion?

You are importing the module Popen from the package subprocess, so the Pipe() module is not available. Either import the whole subprocess package (and change your references to Popen() to include the subprocess identifier) or import Pipe from subprocess (and change the Pipe() reference to remove the subprocess identifier).

You can avoid the need to get the current volume by setting the volume to some arbitrary value before starting the loop. If you then keep track of how this value changes by adjusting it as appropriate in the increase and decrease methods, then you will always know what the current volume is without the need to query it. Your current_volume variable will need to be declared at the global level so the value is accessible to all methods.

4 Likes

Good suggestions on a cleaner approach, appreciate that.

I’ve been using https://www.blackbox.ai/ to ask questions and solve a bunch of little errors and questions I had. Here is what has been updated:

1. defined current volume globally as suggested (have had a few versions of this)

2. imported subprocess as a whole and updated Popen calls (this fixed the Popen issues)

3. Added - set the volume 30% at start (for i know the code is working and able to test using the potentiometer)

I’ve got one step further! Now it starts the volume at 30%. I can turn the POT, but the volume doesn’t change…until I hit under ~5% or over ~95%, then the volume automatically jumps to zero (which is the error return value in current_value. I asked AI about this and it gave me a few def current_volume suggestions which aren’t working quite yet.

Everyone, I appreciate the direction! I come from a time where I built beige boxes to mess with payphones, so I appreciate you bearing with me.

1 Like

That’s what the code is doing. In effect, it is “If the pot is turned to its maximum value, then increase the volume a bit. If the pot is set to its minimum value then decrease the volume a bit”. It appears to be based on switches rather than a pot. It is typical for a pot to hit the minimum and maximum values just short of the full rotation.

I would recommend adding a debug print statement into the main loop to continually show the value of the pot. Note the values you get at minimum and maximum rotation. Then change the rest of the main loop to:

  • Map the pot reading to a volume range (0 to 100)
  • IF the maped value has changed from the current volume, update the curent volume and set the volume to the new value.
2 Likes

Just got it working, sort of. It’s just as you said. If the POT is at 100%, the reading (volume) slowly climbs. And at 0% slowly goes down.

This is the return as it’s climbing/lowering

Simple mixer control ‘Master’,0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 65536
Mono:
Front Left: Playback 28836 [44%] [on]
Front Right: Playback 28836 [44%] [on]

I was just thinking maybe it had to do with mono versus stereo, but suppose not.

2 Likes

Here’s my go at it.

  • read the pot
  • compare if the current pot value is different from the last value
  • if the difference is large enough, update the volume

this won’t work flawlessly, there will be a little deadband around the current value, but it might be enough to get you over the line

# Read the PiicoDev Potentiometer value
from PiicoDev_Potentiometer import PiicoDev_Potentiometer
from PiicoDev_Unified import sleep_ms
from subprocess import Popen

pot = PiicoDev_Potentiometer() # Initialise the potentiometer
pot.maximum = 100 # set the range of output values
pot.minimum = 0   # if minimum or maximum are ommitted, they will default to 0 and 100 respectively
    
def set_volume(volume):
    Popen(['amixer', '-D', 'pulse', 'set', 'Master', str(volume) + '%'])


last_value = pot.value
while True:
    pot_value = pot.value
    if abs(pot_value - last_value) > 2: # look for changes in the volume knob
        new_volume = pot_value
        last_value = pot_value
    set_volume(new_volume)

:point_up: this code is untested… good luck everybody :sweat_smile:

aside: no mapping should be necessary, because we get to initialise the potentiometer with whatever min/max values we like :slight_smile: custom mapping is only required if you don’t want linear mapping between the max and min.

2 Likes

Got it solved!

@Michael I used your simple idea of READ, COMPARE, IF DIFFERENCE and started over with AI help. It only took two tries.

As with many things, the simplest approach works quite well, though it can take a lot of work to figure out that “simple”. That’s just how it goes.

I truly appreciate all of you helping me learn coding that I knew nothing about 2 weeks ago. Through lots of trial and error (this took me 5 hours) I got this working!

Here is a link to the Old Radio Project I’m working on that I needed this coding for. It’s a rebuilding of an old radio from my fathers childhood that auto plays old radio shows and music. Excited to complete this and gift it to him!

Thank you kindly @Michael @Pixmusix Jeff105671 and cheers to Robert93820 (I’m new and it only allows me to tag 2 users :yum: )

Here is the working code:

# Read the PiicoDev Potentiometer value
from PiicoDev_Potentiometer import PiicoDev_Potentiometer
from subprocess import Popen

pot = PiicoDev_Potentiometer() # Initialise the potentiometer
pot.maximum = 100 # set the range of output values
pot.minimum = 0   # if minimum or maximum are ommitted, they will default to 0 and 100 respectively

def set_volume(volume):
    try:
        Popen(['amixer', '-D', 'pulse', 'set', 'Master', str(volume) + '%'])
    except Exception as e:
        print(f"Error setting volume: {e}")

last_value = pot.value
while True:
    pot_value = pot.value
    if abs(pot_value - last_value) > 2: # look for changes in the volume knob
        new_volume = pot_value
        last_value = pot_value
        set_volume(new_volume)

-Joshua

3 Likes

Great result @Joshua270186 :slight_smile:
Glad you’re back on track, happy making :call_me_hand:

2 Likes