RPI3B+, Allo DigiOne and .......

…I need a suitable rotary pot./volume control.

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?

The orchardaudio.com/gala DAC has such a volume knob, it was formerly sold on this volumio shop.

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.

There’s an article on using such an encoder below with a link to python code on how to implement the feature.
modmypi.com/blog/how-to-use … spberry-pi

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.

Thanks for the responses.

I’m not looking at an another external DAC I want the Pi to be the transport to my existing DACs with the Pi being the preamp.

The DigiOne isn’t a HAT so I’ll have to solder the necessary connectors directly to the header.

There’s also breakout interceptors that can sit between the two, saving the need to try and solder pins etc.

such as:
shop.4tronix.co.uk/products/gpi … spberry-pi

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.

Just an update…

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.

Pros:

  • works better than the plugin (at the moment)

Cons:

  • 3rd party Python code
  • sluggish volume response in the volumio GUI
  • wish the rotary had better weight and inertia

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

volume = 0
resp = api_session.get(‘http://localhost:3000/api/v1/commands/?cmd=volume&volume=%d’ % (volume))

GPIO.setmode(GPIO.BCM)

ky040 = KY040(CLOCKPIN, DATAPIN, SWITCHPIN, rotaryChange, switchPressed)

ky040.start()

try:
while True:
sleep(0.1)
finally:
ky040.stop()
GPIO.cleanup()[/code]

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

volume = 0
resp = api_session.get(‘http://localhost:3000/api/v1/commands/?cmd=volume&volume=%d’ % (volume))

That is the volume last time we synced

last_volume = volume

GPIO.setmode(GPIO.BCM)

ky040 = KY040(CLOCKPIN, DATAPIN, SWITCHPIN, rotaryChange, switchPressed)

ky040.start()

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]

Sorry, I’ve posted it in the How-to, on the other big touchscreen thread.

CLOCKPIN = 27
DATAPIN = 25
SWITCHPIN = 0

def rotaryChange(direction):
    if direction==1:
        call(["volumio", "volume", "plus"])
    else:
        call(["volumio", "volume", "minus"])

def switchPressed():
    print "button pressed"

GPIO.setmode(GPIO.BCM)
ky040 = KY040(CLOCKPIN, DATAPIN, SWITCHPIN, rotaryChange, switchPressed, rotaryBouncetime=250)
ky040.start()

try:
    while True:
        sleep(0.1)
finally:
    ky040.stop()
    GPIO.cleanup()

I like the API call method, nice one! I’ll give it go and let you know.

Response is a lot better, I’ve taken your ‘starter for 10’ and it’s now this:

def rotaryChange(direction):
    global api_session
    resp = api_session.get('http://localhost:3000/api/v1/commands/?cmd=volume&volume=%s' % ('plus' if direction==1 else 'minus'))

def switchPressed():
    print "button pressed"

api_session = requests.session()

GPIO.setmode(GPIO.BCM)
ky040 = KY040(CLOCKPIN, DATAPIN, SWITCHPIN, rotaryChange, switchPressed, rotaryBouncetime=250)
ky040.start()

try:
    while True:
        sleep(0.1)
finally:
    ky040.stop()
    GPIO.cleanup()

I’m using ‘plus’ and ‘minus’ so I don’t have to ‘hold’ a volume value and then worry about this changing through a browser or other client etc.

Thanks!