Microsoft Surface Dial or Other Physical BlueTooth Control

So, I’m at the “wish list” stage of my project.

I’d really love to have a physical volume control of some kind; either hardwired in or wirelessly connected somehow. And not phone based.

I managed to get the MS Dial connected via Bluetooth on my Pi5 running Volumio but not surprisingly it doesn’t do anything.

Any other creative solutions that don’t require an MS in EE?

Maby this.
You only can not connect long cables.

I think that requires actually connecting a physical dial to the GPIO pins on the Pi (which I could probably figure out) and then a bunch LINUX code magic, which I cannot :slight_smile:

I tried the PlugIn with the MS Surface Dial connected via BT but no joy.

1 Like

Correct… a rotary encoder and the relevent componants connected to the GPIO pins.

No none of that is needed, all you have to do is configure the rotary encoder II plugin with the correct details.

Not 100% sure what you mean by this, but I have a external volume control that sits on the arm of my chair with a 1.2 meter cable running to my Volumio box with zero issues

1 Like

So hopefully I ordered the correct components needed:

Yeah not a big fan of these rotary encoders, I ended up using a BT remote.

import asyncio
import struct
import time
import subprocess
import os

from evdev import InputDevice, categorize, ecodes

EV_VAL_PRESSED = 1
EV_VAL_RELEASED = 0
# mousea:
KEY_ENTER =      28
KEY_LEFT =       105
KEY_RIGHT =      106
KEY_UP =         103
KEY_DOWN =       108
KEY_COMPOSE =    127
# mouseb:
KEY_HOMEPAGE =   172
KEY_BACK =       158
KEY_VOLUMEDOWN = 114
KEY_VOLUMEUP =   115

mousea = InputDevice('/dev/input/event0')
mouseb = InputDevice('/dev/input/event1')
mousec = InputDevice('/dev/input/event2')
moused = InputDevice('/dev/input/event3')

print(mousea)
print(mouseb)
print(mousec)
print(moused)
print('=== Start ===')

async def print_events(device):
    global  STATUS            
    async for event in device.async_read_loop():
        if event.type == ecodes.EV_KEY:
            if event.value == EV_VAL_PRESSED:
                if event.value == EV_VAL_PRESSED:
                    if event.code == KEY_ENTER:
                        print('<ENTER> Pressed')
                        command = 'volumio toggle'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)                        
                    elif event.code == KEY_LEFT:
                        print('<LEFT> Pressed')
                        command = 'volumio previous'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)                         
                    elif event.code == KEY_RIGHT:
                        print('<RIGHT> Pressed')
                        command = 'volumio next'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)                         
                    elif event.code == KEY_UP:
                        print('<UP> Pressed')
                        command = 'volumio seek plus'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)                         
                    elif event.code == KEY_DOWN:
                        print('<DOWN> Pressed')
                        command = 'volumio seek minus'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)     
                    elif event.code == KEY_COMPOSE:
                        print('<COMPOSE> Pressed')
                        command = 'node /data/plugins/user_interface/randomizer/randomTracks'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) 
                    elif event.code == KEY_BACK:
                        print('<BACK> Pressed')
                        command = 'node /data/plugins/user_interface/randomizer/randomAlbum'                        
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) 
                    # elif event.code == KEY_HOMEPAGE:
                        # print('<HOME> Pressed')

                    elif event.code == KEY_VOLUMEDOWN:
                        print('<VOLUMEDOWN> Pressed')
                        command = 'volumio volume minus'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)      
                        
                    elif event.code == KEY_VOLUMEUP:
                        print('<VOLUMEUP> Pressed') 
                        command = 'volumio volume plus'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) 
           
                print(device.path, categorize(event), sep=': ')          
                print('---')

for device in mousea, mouseb, mousec, moused:
    asyncio.ensure_future(print_events(device))

loop = asyncio.get_event_loop()
loop.run_forever()

The 1/2 watt resistors are a little excessive but they will do.
And ideally a 100nf capacitor between ground and each of the gpio’s. Just to make the action acurate and stable.

If you are also using the button on the rotary bear in mind there is no pull up resistor on the CYT1062’s for the switch.

That’s EXACTLY what I wanted to do. I got a Microsoft Surface Dial, got it connected to the device but couldn’t figure out where to go from there. Would what you have above work or does it need to be customized for each device?

Not the buttons, but yo need to find the events belonging to your dial.
All available keys can be found here:

Look at /dev/input/ and check the events while you press every combination of the thingy.
like:

cat /dev/input/event0
cat /dev/input/event1
cat /dev/input/event2
cat /dev/input/eventx

And add these events belonging to your thingy to the script.

Thanks… that level of code awareness is beyond me - I’m hardware guy with great Photo editing skills and zero coding ability. If I could hire someone to write it for me - that’s another skill :slight_smile:

All you need to do is finding the events belonging to the dail.
And update the event numbers:

mousea = InputDevice('/dev/input/event0')
mouseb = InputDevice('/dev/input/event1')
mousec = InputDevice('/dev/input/event2')
moused = InputDevice('/dev/input/event3')

Since this depends on the USB port, what else is connected to the USB, you need to do this by yourself.

USB 2.0 ports going to 5.5 AMOLED touch screen and SMSL DAc
USB 3.0 ports going to external WiFi and External SSD

Does that matter since the Dial is BlueTooth?

please read:

You’re going to make me code, aren’t you? :wink:

I already gave you the complete code all you have to do is get the proper events as instructed.
I get the impression your better in asking questions than in reading?

I literally have no idea what do with the code or how to “get” the events. I’ve read through it but it’s just a wall of text that assumes you understand the coding and instruction set.

Sorry to be a bother and you can go ahead and consider this closed - this has skill requirements above what I have or am willing to invest the time to learn so I’ll just another option for volume control.

I gave t-you the complete code, instructions what you need to do.
Since I don’t have the hardware, you do, this is on you to determine.
ssh into Volumio: How do I enable ssh connection?
SSH Connection | Volumio Developers Documentation

type:
cat /dev/input/event0
press buttons and see if there is a response, if yes note it down and continue to the next
cat /dev/input/event1
press buttons and see if there is a response, if yes note it down and continue to the next
cat /dev/input/eventx

so if in your case event1 and event2 triggers a response update the code:
mousea = InputDevice(‘/dev/input/event1’)
mouseb = InputDevice(‘/dev/input/event2’)

and change
for device in mousea, mouseb, mousec, moused:
to
for device in mousea, mouseb:

This is where my support stops.

Thanks for your patience. I had actually tried the cat commands before but figured I was doing something wrong as what I got back was this:
image

Surface Dial Button Push then Left Turn then Right Turn…

then your script would be something like this and save it as mouse.py in /home/volumio.

import asyncio
import struct
import time
import subprocess
import os

from evdev import InputDevice, categorize, ecodes

EV_VAL_PRESSED = 1
EV_VAL_RELEASED = 0
# mousea:
KEY_ENTER =      28
KEY_LEFT =       105
KEY_RIGHT =      106

mousea = InputDevice('/dev/input/event5')

print(mousea)

print('=== Start ===')

async def print_events(device):
    global  STATUS            
    async for event in device.async_read_loop():
        if event.type == ecodes.EV_KEY:
            if event.value == EV_VAL_PRESSED:
                if event.value == EV_VAL_PRESSED:
                    if event.code == KEY_ENTER:
                        print('<ENTER> Pressed')
                        command = 'volumio toggle'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)                        

                    elif event.code == KEY_VOLUMEDOWN:
                        print('<VOLUMEDOWN> Pressed')
                        command = 'volumio volume minus'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)      
                        
                    elif event.code == KEY_VOLUMEUP:
                        print('<VOLUMEUP> Pressed') 
                        command = 'volumio volume plus'
                        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) 
           
                print(device.path, categorize(event), sep=': ')          
                print('---')

for device in mousea:
    asyncio.ensure_future(print_events(device))

loop = asyncio.get_event_loop()
loop.run_forever()
sudo apt-get update
sudo apt-get install -yf python3 python3-evdev

as service:
sudo nano /lib/systemd/system/btmouse.service

[Unit]
Description=BlueTooth Mouse Plugin
After=network.target
Wants=volumio.service
After=volumio.service
[Service]
ExecStart=/usr/bin/python3 /home/volumio/mouse.py

[Install]
WantedBy=multi-user.target

save with CTRL+o, CTRL+x

sudo chmod 644 /lib/systemd/system/btmouse.service && sudo systemctl daemon-reload && sudo systemctl enable btmouse.service && sudo systemctl start btmouse.service

Does this look like the issue?

http://logs.volumio.org/volumio/dzBfwnj.html

Aug 01 01:33:29 volumio systemd[1]: Started BlueTooth Mouse Plugin.
Aug 01 01:33:29 volumio sudo[3346]: pam_unix(sudo:session): session closed for user root
Aug 01 01:33:29 volumio python3[3349]: device /dev/input/event5, name “Surface Dial System Multi Axis”, phys “D8:3A:DD:AD:26:C5”
Aug 01 01:33:29 volumio python3[3349]: === Start ===
Aug 01 01:33:29 volumio python3[3349]: Traceback (most recent call last):
Aug 01 01:33:29 volumio python3[3349]: File “/home/volumio/mouse.py”, line 46, in
Aug 01 01:33:29 volumio python3[3349]: for device in mousea:
Aug 01 01:33:29 volumio python3[3349]: TypeError: ‘InputDevice’ object is not iterable
Aug 01 01:33:29 volumio systemd[1]: btmouse.service: Main process exited, code=exited, status=1/FAILURE
Aug 01 01:33:29 volumio systemd[1]: btmouse.service: Failed with result ‘exit-code’.

Also, are all the pcieport & nvme error messages normal?