Whilst I understand the need to have a ‘bit perfect’ output from the DigiOne, I’d like to vary the volume signal being sent from the Pi to my external DAC. I have Volumio configured for software volume control and this works as expected but I would like the tactile feel of a proper volume control.
Has anyone used (or can recommend) a suitable rotary control for this task?
I would suggest you look into using a digital rotary encoder wired to GPIO pins on the Pi and then add some python code to detect the motion as the wheel is turned in either direction.
To flesh out that example to drive volume changes, you’d need to add range limits on the value preventing it going above 100 and below 0 and then simply calling into the volumio RESTful API for volume changes or running an OS command to call "volumio volume "
If your DAC is a hat, then you will need to see if any of the GPIO pins are available. I know some of the boards, do offer access to unused pins.
Also, check out the Volumio plugins section, there is already rotary encoder. I noticed it last night and assume its in the general area you’re trying to tackle.
Thanks for that link, it would work, although I might have to cut some more plastic away from my 7" touchscreen case.
I’d noticed the rotary encoder plugin, but never installed it to see what options there were available. Just installed and checked and there’s loads to play with there, I will have to give it a go!
Rotary encoder (KY040) connected up and it really needs debouncing, it’s hardly working. I’ve tried soldering 0.1uf caps to the CLK and DATA pins and still no better.
I’ve written a python routine to poll the rotary and send the correct volumio commands and that works although the volumio GUI is a little sluggish to respond.
Time to have a look at the plugin code and see what the issue is.
The GUI does have a delay as the websocket API syncs. As long as you feel there is a fast response with the volume updates, tuen you’re likely in the right ballpark.
Just do a top -d 1 on the pi to see if the python code is hammering CPU. Also post the code if you wish and I might be able to make some suggestions.
I’ve taken Martin O’Hanlon’s code (github.com/martinohanlon/KY040) and used his example, using the subprocess module so I can ‘call’ volumio.
I’m playing with the rotaryBouncetime and the default loop sleep value (to reduce CPU time), although hardly uses any CPU time! I’ve added this to my rc.local and it’s working well.
You haven’t posted your code, so I’ll assume that in the rotarychange callback is where you are making a subprocess call to run volumio.
What I’d suggest is to make a direct HTTP request against the internal API rather than call a subprocess to run “volumio” as that command is itself a script that makes an internal web API request to change the volume. So you’ll save yourself the overheads of running a command shell and invoking python again each time to make the volume change.
So taking that baseline example from Martins code, I’ve pulled in the requests module and put some globals to track the API session and running volume value.
[code]from time import sleep
import RPi.GPIO as GPIO
from ky040.KY040 import KY040
import requests
CLOCKPIN = 5
DATAPIN = 6
SWITCHPIN = 13
def rotaryChange(direction):
global api_session
global volume
if (direction == 0):
# clockwise up
volume += 1
if (direction == 1):
# anti-clockwise down
volume -= 1
resp = api_session.get('http://localhost:3000/api/v1/commands/?cmd=volume&volume=%d' % (volume))
print("turned - " + str(direction))
def switchPressed():
print(“button pressed”)
REST API session
api_session = requests.session()
inits for volume
set both tracking value and actual volume to same initial value
Another approach is to let the GPIO activity drive the volume variable and then use a timed interval to sync volume. That longer that timed interval becomes, the more it will produce both a lag in responding and tendency to cause jumps if you spin the control fast. But you may be able to find a good balance between the two.
So notionally, that approach would be something like this. I’m using .5 second on the main loop as the default interval. I’d hesitate to use a smaller interval
[code]from time import sleep
import RPi.GPIO as GPIO
from ky040.KY040 import KY040
import requests
CLOCKPIN = 5
DATAPIN = 6
SWITCHPIN = 13
def rotaryChange(direction):
global api_session
global volume
if (direction == 0):
# clockwise up
volume += 1
if (direction == 1):
# anti-clockwise down
volume -= 1
print("turned - " + str(direction))
def switchPressed():
print(“button pressed”)
REST API session
api_session = requests.session()
inits for volume
set both tracking value and actual volume to same initial value
try:
while True:
sleep(0.5) # This value controls the sync frequency
if (volume != last_volume):
resp = api_session.get(‘http://localhost:3000/api/v1/commands/?cmd=volume&volume=%d’ % (volume))
last_volume = volume
finally:
ky040.stop()
GPIO.cleanup()[/code]