$50 DIY ESP32-S3 Knob as Roon Desk Controller

That or a custom integration but, minimally, if he’s interested in including the bridge that might open the door to additional users. Some of us have home-lab type setups with many containers already deployed and familiarity with Docker. For others, “Docker” means “this is not for you”.

1 Like

You are absolute right. I have never played with Docker, Github or ESP32. So it was a big journey to tackle, but finally I arrived. And I am someone who is quite handy with PCs, Raspberry Pi, Linux, networking, … So I can imagine that someone who has not a technical background will just skip this nice tool, which is a pity.

2 Likes

@Muness
I have a sugestion to improve your knob even somewhat further.
When I turn the volume knob, the update of the digit of the level is changing approx. once every second. But when I change the volume knob drasticaly, it is not so handy that you see the digits updated only a second later, as it always over- or ondershoot the disired volumelevel.
I don’t know if it is possible, but when turning the volume knob, can the update of the volume display be changed to ex. each tenth of a second? That would, according to me, impove the functionality even further.
Kind regards, Frank.

1 Like

@gTunes : you helped me with the great velocity sensitive volume. Any thoughts on the desired behavior for the vol display? Thank you!

Hopefully I can get it on the Extension Repository. Do you think that would do the trick?

With this project there’s the first time firmware flash hurdle anyway. I’ll see if there’s a tool out there to make it easy for folks. :crossed_fingers:

Oh! There’s a web flasher: ESP Tool Will try to integrate this into the bridge.

This is interesting. I think I see the behavior that @Frank_M is referring to.

For me, if I ignore the volume display on the knob itself, volume control works very well. If I rotate slowly, each detente on the knob results in one volume change on my playback device. Rotating more quickly results in larger volume steps. When I stop rotating, no additional volume changes are made.

I’m testing this with a FiiO K17 on my desk and if i just watch the volume display on the device, which is what I do when I’m using the knob, it’s all good. @Frank_M didn’t experience the volume behavior before you implemented this current strategy - in the previous behavior, the volume on the device continued to change after the user stopped rotating the knob until the device caught up. That was unpredictable and led to unexpected high/low volume. So the current behavior is pretty good.

So what about the knob itself? I do see that it’s very latent but it does ultimately catch up. How and when are you updating the knob display? That’s where your current issue is. At some interval, you’ve collected enough rotational information to decide that you want to step the volume up or down and you’ve figured out the increment. You call a Roon API. Then what? Do you wait for a response and does the response have the new volume value? Do you wait for a callback from Roon telling you that the volume has changed (which would have to traverse through the bridge)?

I can think two approaches. One approach is to try to do it all on the knob. After writing this post, I think this is the less preferable of the two but here it is:

Knob Based

This is what I would consider in order of “fixes” but it may take some tuning:

  • If the call to change the volume is async and you don’t wait for it to complete before figuring out what to do next, try handling it as a sync call (ya know…just wait :slight_smile: )

  • if the call returns the new volume, set the display immediately. if not, then make an additional sync call to get the actual new volume and set that. it’s another call, but hopefully that doesn’t impact the user’s experience with changing the actual volume

  • if you are getting notifications from the core about volume changes, and that’s what the knob is responding to and displaying, then you’ve got a race condition to deal with because there’s the potential that those values are going to be stale by the time you try to set them as the display value. That’s a bit of a tricky problem. One approach to this might be to try something on the bridge like this: each time you get a volume notification on the bridge you make a call to the core to check the current volume. If the current volume isn’t what you got notified about, you drop the notification (don’t forward it to the device).

Bridge Based

After writing this, this is the one I think I’d play with. Maybe paste this into Claude and see what it says :slight_smile:

If you think about all of this in old-school model-view-controller terms, I think your bridge holds the model and the knob is control and view. So the actual volume truth is on the model (bridge) for each controller. The knob, as control, says to the model “move the volume up 3 or down 5 or whatever”. The model does the work and, when appropriate, tells the control what to display. The control only displays what the model tells it to through notifications from bridge → control or function results for better perf.

The model is getting control calls from the knob and is also getting notifications from the Roon core.

You’re in Python on the bridge, right? If I were building this, I’d use a mutex per knob on the bridge. It’s per knob so knobs don’t contend with one another. When a volume control call comes from the knob, grab the mutex for that knob. Change the volume on the core, get the current volume (either as the return value from that change call or make an additional call). Store this current value in the core as the knob’s volume. Then release the mutex and return the result (or within the mutex, send a display volume notification to the knob and then return no result).

When the bridge receives a volume notification from the core, grab the mutex. If the value you received in the notification is the same as what you’ve stored for the knob, drop the notification. If it’s not the same as what you’ve stored, then call the core to get the current value since the notification is in a possible race with volume changes. Call the core to get the current value. If the value is the same as what’s in the notification, store it on the core and then send a notification to the knob (still in the mutex). Then release the mutex. If when you call the core you get a different value than what was in the notification, just drop that notification, too and release the mutex.

There are probably issues with what I wrote but it’s an interesting starting point. In the extension based approach, you still have to make the calls from the knob to the extension sync. I assumed that was obvious but maybe not.

Maybe this helps :slight_smile:

1 Like

I don’t think so. I believe Extension Manager is supported on DietPi, rooExtend, and a number of other platforms but I don’t think it’s supported on RoPieee (https://ropieee.org/). You’d need to reach out to Harry/Spockfish. He’s easy to find around here.

If you’re not familiar with RoPieee - it’s popular because it can act as a streamer, it supports Raspberry Pi displays, supports some simple volume controls, supports on-board HATs, etc. Your knob would make a nice addition to any RoPieee setup.

1 Like

I’m new to the whole hifi world, and Roon (only picked up a license 2 months ago)! Thanks for helping me navigate, as I had no idea RoPieee was the most popular path. Will explore what it would take to add the bridge + knob in the remote control section with Harry’s guidance.

Wow! You should get an award for “Most Significant Contribution by a New User”. Seriously.

I’m not sure it’s the most popular path, but it’s a very popular option. A RoPieee box can act as a zone, it can be a “Now Playing” display, and more.

RoPieee isn’t open source - it’s Harry’s project but he’s active here and it would be just a direct question to him (probably a DM) asking if he’s open to/interested in supporting your knob. If he says “no”, the conversation is pretty much over :slight_smile:

1 Like

Huge Update! Getting Started with Roon Knob Just Got a Lot Easier in 2.0.1. Two Big Improvements

Setup now:

  1. Flash the knob → Web flasher
  2. Run the bridge → Extension Manager or Docker
  3. Authorize in Roon → Settings → Extensions → Enable
  4. Connect knob to WiFi → Join “roon-knob-setup” network, enter credentials.
  5. (Hopefully not needed if mDNS works on your network) Connect to the knob (http://knob-ip) to set the bridge address (http://:8088)

Next up I’ll try to make step 5 (when needed) easier and add info in the Roon > Extension description + settings there, and RoPieee support if Harry is open to it.

1 Like

Done as of this morning! Thanks for the nudge!

1 Like

Sent a DM to Harry. :crossed_fingers:

Not positive but I think I left my “production” roon knob on overnight (with all the battery savings settings enabled) and it still worked this morning!

Fixed as of 2.0.1. Thank you for the suggestion!

1 Like

This was a tough one! Progress bar was working but only sometimes. Turns out I was only updating the track progress from Roon on some conditions (new track, volume change, pause, play). Now I handle the zones_seek_changed event from Roon correctly and pass it on to the knob.

3 Likes

What an immense improvement! very nice work!!
Congratulations, this is very handy for non-technical users.

@Muness

Thank you for investigating and solving it.

I updated the bridge, but how can I see the actual version of the bridge? In the GUI, I see still “Version: 0.1.0 (unknown)”, so this is not implemented (not an issue for me). Is there a command to see the real version of the bridge? I thought I would see the version in ”docker compose ps”, but the info of the image tells “muness/roon-extension-knob:latest” and that before and after the upgrade. So this output is not the reliable to check the version.

I also tried to do an update of the knob via selecting the zone and going to Settings", than “check for update”. Then I see “checking …” appearing, but it is not going out of that state. So this is not working. How must the knob be updated, or checked it is on the latest version?

I can confirm that the issue of the inner circle is now working perfectly. Nice work.

Kind regards, and thank you for all the efforts you are putting in this knob. Much appreciated.