[PLUGIN] RotaryEncoder II

RotaryEncoder II

Description:
Alternative implementation of Rotary Encoder controls using the Linux Kernel driver for Rotaries. It also offers additional features compared to the original RotaryEncoder Plugin.

Documentation including tips for hardware debouncing is available here.

Author: 7h0mas-R

Tested with:

  • Volumio2 v2.882, v2.907
  • Arm (Rpi 2 and RPi 3)

Plugin status:

  • Functionality for dial actions and push button actions has been tested
  • Volume control running stable for several months on our family radio (other functions not tested thoroughly by my “Beta-Testers” :family_man_woman_girl_boy:
  • The function that issues Websocket commands has been tested with default Volumio commands but not yet with other plugins

Version history:
V1.0.7 [9/23/2021]:
Fixed bug reported below

V1.0.6 [8/12/2021]:
Fixed Settings page bug after install

V1.0.0 [6/20/2021]:
Initial release. Contained a bug that prevented calling the Settings after install

2 Likes

Hi! I would like to try out but the link to github point to nowhere. Thanks!

Hi,

I fixed the bug in version 1.0.6.
A PR is created, but if you want to try it before it potentially gets released, you can try it from here.

Best regards,
Thomas

Hi,
the plugin just got published. You should be able to install via UI now.
Let me know, how it goes…

Thomas

Sorry for the long delay. Works perfectly installed from ui. Much better, not as synthetic volume control feeling than with the other plugin.

Many, many Thanks!

1 Like

Cool, which Rotary did you use and did you need any sort of hardware debouncing?
Would be happy, if we can add it to the list of verified hardware in the documentation.

Hi @T0MR0

just installed your new plugin on my RPi0 with BassCrab-uHAT

It works fine, but when I turn the encoder by one position, I always get the volume increased (or decreased) by two levels, regardless the “Periods per Tick” setting.

Any idea on how to solve this issue?

regards

Dario

Hi Dario,
thanks for the feedback. I just checked - the plugin correctly transports the “Periods per Tick” setting to the dtoverlay.
That leaves you with the following potential root causes:

  1. Wrong setting in the “Settings → Playback options” of Volumio
    The “One Click Volume Steps” setting will determine the increase/decrease of the volume per step. The rotary encoder driver reports back +1 or -1 with each tick. This will be multiplied with the value set in this setting. I observe, that this is not always completely consistent, but that is not a plugin issue (I only send vol+ or vol- commands via WebSock)
  2. Problem with the Kernel driver
    If you are familiar with how to log into Volumio with ssh you can follow instructions below to check if the responses make sense.
  3. Problem with debouncing
    If you have no hardware debouncing on your rotary, there may still be too many transitions for the driver, causing more than one response per click. This is hard to investigate by software - you could either try adding hardware debouncing to just try or hook up an oscilloscope (if you have access to one). If you follow the instructions under 2. you will observe multiple messages from addEventHandle for each click, if the Kernel is affected by bounce.

How to monitor Kernel driver:

  • Enable Debug logging in the Plugin Settings
  • Connect to Volumio via ssh
  • Start journalctl -f | grep -i encoder to start logging to the terminal
  • When you change and save the “Periods per click” setting you should see a line like
sudo[3627]: volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay rotary-encoder pin_a=24 pin_b=23 relative_axis=true steps-per-period=1
  • When you turn your rotary by one click and you have the correct “Periods per click” aka steps-per-period setting, you should see a message like this (Dir is the direction (CW: 1, CCW: -1)
volumio[1056]: info: [ROTARYENCODER2] addEventHandle received from rotary: 1 -> Dir: -1

Let me know how it goes!
Regards,
Thomas

1 Like

Hi T0MR0,

thanks for the quick and very-detailed response.

  1. The setting for “One Click Volume Step” is 5, but every time get an increase (or decrease) of 5, quickly followed by another increase (or decrease) of 5. It was working as expected with the other Rotary Encoder plugin

  2. I will test this right now

  3. The circuit has HW debouncing, implemented as indicated on the rotary encoder datasheet
    image
    With the other version of the Rotary Encoder plugin it was working as expected, I never had more than one response per click.

The issue is solved now, with the Debug logging I realized the “Periods per Tick” settings was not applied properly on with the first press of the Save button, I had to press save twice for being applied properly.

Please be aware I am using two rotary encoders, maybe with one only pressing Save once is enough

Good to hear!
I am using even 3 rotaries… did not notice this behavior so far. However, if you give me a step by step instruction on how to reproduce it, I can check if it is a bug.
If you let me know your rotary brand and model, I will add it to the list of compatible hardware.

I played a little more with my setup - disabling and enabling rotaries in random fashion and changing settings. It seems that there is indeed a problem when overlays are not removed in the reverse order they have been added in.
I assume this is what happened in your case.
My hypothesis:

  • Rotary 1 is configured and enabled
  • Rotary 2 is configured and enabled
  • If we change Rotary 2 settings, everything works fine, also if we remove it
  • If we change anything on Rotary 1 before removing Rotary 2, things break

Root cause:
When settings get updated, the dtoverlay is always removed and reinstalled.
It seems that despite the fact that the rotary is addressed via an event handler, that directly refers to the GPIO, the device handlers that were installed after the removed one seem to get messed up by the dtoverlay -R command.
I already patched it locally and tested a little bit - I now always just remove all rotaries and reinstall them after updating the settings. It seems to fix the issue. Will try it out a little more over the next days and create a PR to fix it in v1.0.7.
For the time being, the workaround is to just “build up” the settings for the rotaries one by one.

1 Like

Hi T0MR0,

unfortunately I don’t remember the exact sequence of settings I applied to trigger the issue, but I think you managed to reproduce it.

I am using your plugin together with this board, released on github

the part-number of the rotary encoder is PEC12R-4217F-S0024, but for the first samples I’ve been using a chinese copy from aliexpress: EC12SXB2

Cool HAT :+1:
I’ll post the type of Rotary in the next update of the Docs and will refer to your Nick, if you do not mind.
I have meanwhile created a PR for 1.0.7 As soon as it gets merged, you should be able to install it from the plugin store.

Hi T0MR0,

sure, please free to refer to the BassCrab-uHAT in your documentation!

when I will have some spare time I will also update the readme, with instructions on how to make use of your plugin

Hi T0MR0,

are you planning to submit this plugin under Volumio 3?

Hi,
I have no concrete plans yet. To be honest, I did not even try out Volumio 3 yet.
Also did not look into differences 2 vs 3, so no idea, how much effort it would be. Is there any compressed documentation somewhere?
My time to work on this is rather limited, so I focus on the things that are important for my personal setup.
Once v3 becomes the main stream, I‘ll definitely move it over, but not sure, if we are at that point yet?
Best regards, Thomas

Hi T0MR0, XSA,

I am using the plugin on Volumio3, it works as good as on Volumio2. It required the node modules to be re-compiled after installing the plugin, besides that no changes are required.

sudo apt -y install build-essential
cd ~
wget http://plugins.volumio.org/plugins/volumio/armhf/user_interface/rotaryencoder2/rotaryencoder2.zip
mkdir ./rotaryencoder2
miniunzip rotaryencoder2.zip -d ./rotaryencoder2
cd rotaryencoder2
volumio plugin install
## when installation is finalized, press CTRL+C ##
cd /data/plugins/user_interface/rotaryencoder2/
npm install --save onoff@6.0.0
1 Like

Hi T0MR0,

with the latest version 1.0.7 I can’t have two rotary encoders working at the same time, it used to work with the previous version.

If I enable one at the time they work, I did this test to exclude HW problem from the possible root-cause. When I enable two, the push button is recognized for both, but for the rotation only the one with higher number works, the other one doesn’t (I also don’t get any debug message with journalctl | grep -i encoder)

Here the journalctl log right after reboot

volumio@basscrab:~$ journalctl | grep -i encoder 
Nov 26 14:56:46 basscrab volumio[774]: info: Loading plugin "rotaryencoder2"...
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] loadI18nStrings: /data/plugins/user_interface/rotaryencoder2/i18n/strings_en.json
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] loadI18nStrings: loaded: {"ROTARYENCODER2":{"CONFIG":"Rotary Encoder II Plugin Configuration","SAVE":"Save","ENCODER0":"Rotary Encoder 1","D_ENCODER0":"Settings for the first rotary encoder.","ENCODER1":"Rotary Encoder 2","D_ENCODER1":"Settings for the second rotary encoder.","ENCODER2":"Rotary Encoder 3","D_ENCODER2":"Settings for the third rotary encoder.","ROTARYTYPE":"Periods per tick","D_ROTARYTYPE":"Periods per tick (Full: A and B full period, half: A and B half period, quarter: A or B half period","FULL":"Full-period mode","HALF":"Half-period mode","QUARTER":"Quarter-period mode","PINA":"Pin A GPIO","D_PINA":"GPIO pin that is connected to the first pin of the rotary.","PINB":"Pin B GPIO","D_PINB":"GPIO pin that is connected to the second pin of the rotary.","DIALACTION":"Dial Action","D_DIALACTION":"Action to be triggered by turning the rotary encocer. Option 'Emit Websocket Message' can be used to control other plugins using the Volumio Websock calls between Plugins. (see https://volumio.github.io/docs/API/WebSocket_APIs.html section 'CallMethod on Plugin' for details)","DOTS":"...","VOLUME":"Volume","SKIP":"Prev/Next title","SEEK":"Seek in title","SCROLL":"Scroll","EMIT":"Emit Websocket Message","SOCKCMDCCW":"Command CCW","SOCKDATACCW":"Data CCW","SOCKCMDCW":"Command CW","SOCKDATACW":"Data CW","SOCKCMD":"Command","SOCKDATA":"Data","D_SOCKCMD":"Command to send via Websocket","D_SOCKDATA":"Data to include with the Websocket Command","PINPUSH":"Pushbutton GPIO","D_PINPUSH":"GPIO pin that is connected to the pushbutton pin of the rotary. 0 or empty to disable.","PINPUSHDEBOUNCE":"Debounce-time (ms)","D_PINPUSHDEBOUNCE":"Debounce time for the button. If the button has hardware-debouncing, set to 0.","PUSHSTATE":"Button logic-level active low","D_PUSHSTATE":"Activate this, if pressing the button pulls the logic level on the GPIO low.","PUSHACTION":"Short Press Action","D_PUSHACTION":"Action that gets triggered, when pushbutton is pressed briefly.","LONGPUSHACTION":"Long Press Action","D_LONGPUSHACTION":"Action that gets triggered, when pushbutton is pressed longer.","SET_DEBUG":"Debug Settings","D_SET_DEBUG":"Settings for functional debugging.","PLAY":"Play","PAUSE":"Pause","PLAYPAUSE":"Play/Pause toggle","STOP":"Stop","REPEAT":"Repeat","RANDOM":"Random","CLEARQUEUE":"Clear Queue","MUTE":"Mute","UNMUTE":"Unmute","TOGGLEMUTE":"Toggle Mute","SHUTDOWN":"System Shutdown","REBOOT":"System Reboot","RESTARTAPP":"Restart Application","DUMPLOG":"Dump logfile","LOGGING":"Logging","D_LOGGING":"Switch the output of log messages on or off. Error messages are always logged.","TOAST_START_SUCCESS":"Plugin successfully started.","TOAST_STOP_SUCCESS":"Plugin successfully stopped.","TOAST_START_FAIL":"Plugins failed to start.","TOAST_STOP_FAIL":"Plugins failed to stop.","TOAST_GPIO_BLOCKED":"GPIO Pin unavailable","TOAST_MSG_OVERLAY_BLOCKING":"Blocked by another overlay.","TOAST_SAVE_SUCCESS":"Successfully saved","TOAST_MSG_SAVE":"Settings for Encoder ","TOAST_DEBUG_SAVE":"Debug Settings","TOAST_SAVE_FAIL":"Save failed","TOAST_WRONG_PARAMETER":"Error in parameters","TOAST_NEEDS_INTEGER":"GPIO Pins must be Integers.","TOAST_PINS_DIFFERENT":"GPIO Pins for A/B/Button must be different.","TOAST_PINS_BLOCKED":"Pins already used in another Encoder.","TOAST_NO_TYPE":"Please select 'Periods per tick'!"}}
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] onStart: Config loaded: {"syncSave":true,"autosave":true,"autosaveDelay":1000,"saved":true,"atomicSave":false,"data":{"enabled0":{"type":"boolean","value":true},"rotaryType0":{"type":"number","value":1},"pinA0":{"type":"string","value":"5"},"pinB0":{"type":"string","value":"6"},"dialAction0":{"type":"number","value":2},"socketCmdCW0":{"type":"string","value":""},"socketDataCW0":{"type":"string","value":""},"socketCmdCCW0":{"type":"string","value":""},"socketDataCCW0":{"type":"string","value":""},"pinPush0":{"type":"number","value":7},"pinPushDebounce0":{"type":"number","value":0},"pushState0":{"type":"boolean","value":true},"pushAction0":{"type":"number","value":3},"socketCmdPush0":{"type":"string","value":""},"socketDataPush0":{"type":"string","value":""},"longPushAction0":{"type":"number","value":12},"socketCmdLongPush0":{"type":"string","value":""},"socketDataLongPush0":{"type":"string","value":""},"enabled1":{"type":"boolean","value":true},"rotaryType1":{"type":"number","value":1},"pinA1":{"type":"string","value":"8"},"pinB1":{"type":"string","value":"9"},"dialAction1":{"type":"number","value":1},"socketCmdCW1":{"type":"string","value":""},"socketDataCW1":{"type":"string","value":""},"socketCmdCCW1":{"type":"string","value":""},"socketDataCCW1":{"type":"string","value":""},"pinPush1":{"type":"number","value":10},"pinPushDebounce1":{"type":"number","value":0},"pushState1":{"type":"boolean","value":true},"pushAction1":{"type":"number","value":10},"socketCmdPush1":{"type":"string","value":""},"socketDataPush1":{"type":"string","value":""},"longPushAction1":{"type":"number","value":11},"socketCmdLongPush1":{"type":"string","value":""},"socketDataLongPush1":{"type":"string","value":""},"enabled2":{"type":"boolean","value":false},"rotaryType2":{"type":"number","value":0},"pinA2":{"type":"string","value":""},"pinB2":{"type":"string","value":""},"dialAction2":{"type":"number","value":0},"socketCmdCW2":{"type":"string","value":""},"socketDataCW2":{"type":"string","value":""},"socketCmdCCW2":{"type":"string","value":""},"socketDataCCW2":{"type":"string","value":""},"pinPush2":{"type":"number","value":0},"pinPushDebounce2":{"type":"number","value":0},"pushState2":{"type":"boolean","value":false},"pushAction2":{"type":"number","value":0},"socketCmdPush2":{"type":"string","value":""},"socketDataPush2":{"type":"string","value":""},"longPushAction2":{"type":"number","value":0},"socketCmdLongPush2":{"type":"string","value":""},"socketDataLongPush2":{"type":"string","value":""},"logging":{"type":"boolean","value":true}},"callbacks":{"_":{}},"filePath":"/data/configuration/user_interface/rotaryencoder2/config.json"}
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] activateRotaries: 1,2,3
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] activateRotaries: 1,2
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] activateRotaries: 1
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] activateRotaries:
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] activateRotaries: end of recursion.
Nov 26 14:56:49 basscrab volumio[774]: info: [ROTARYENCODER2] addOverlay: 5 6 1
Nov 26 14:56:49 basscrab sudo[965]:  volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay rotary-encoder pin_a=5 pin_b=6 relative_axis=true steps-per-period=1
Nov 26 14:56:50 basscrab kernel: rotary-encoder rotary@5: gray
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] attachListener: /dev/input/by-path/platform-rotary@5-event
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] addEventHandle for rotary: 1
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] addOverlay: 8 9 1
Nov 26 14:56:50 basscrab sudo[998]:  volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay rotary-encoder pin_a=8 pin_b=9 relative_axis=true steps-per-period=1
Nov 26 14:56:50 basscrab kernel: rotary-encoder rotary@8: gray
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] attachListener: /dev/input/by-path/platform-rotary@8-event
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] addEventHandle for rotary: 2
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons: 1,2,3
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons: 1,2
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons: 1
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons:
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons: end of recursion.
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons: Now assign push button: 1
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] Push Button 1 now resolving.
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] activateButtons: Now assign push button: 2
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] Push Button 2 now resolving.
Nov 26 14:56:50 basscrab volumio[774]: info: [ROTARYENCODER2] onStart: Plugin successfully started.
volumio@basscrab:~$

Hi Darmur,

the logs look perfectly normal. Beside the different IO pins, they are the same as for my setup, where everything works perfectly.

I currently have limited time, but there are some things not clear for me. Maybe you can check them in the meantime:

  1. are you on Volumio 2 or 3 now? As mentioned, I did no testing on Buster so far and can only give limted support (but I can announce, that I found the time to setup a test-system with Buster in the meantime - only need time to play with it)
  2. If I get your point correctly, the push-buttons are working but the rotation is only working for one rotary at a time and not both simultaneously. This might be related to some issue with the dtoverlay or to the handles to the devices for the rotary. You could check the following:
  • ssh into volumio
  • issue dtoverlay -l
  • the response in your case should be
0:  rotary-encoder  pin_a=5 pin_b=6 relative_axis=true steps-per-period=1
1:  rotary-encoder  pin_a=8 pin_b=9 relative_axis=true steps-per-period=1

If this is the case, the overlays are properly installed. The next test could be, to check if the dtoverlays are working.
Step by step:

  1. disable the plugin
  2. ssh into volumio
  3. Install both overlays by issuing:
    sudo /usr/bin/dtoverlay rotary-encoder pin_a=5 pin_b=6 relative_axis=true steps-per-period=1
    sudo /usr/bin/dtoverlay rotary-encoder pin_a=8 pin_b=9 relative_axis=true steps-per-period=1
    (response for both commands should be pre post)
  4. Open two terminal windows and ssh into Volumio
  5. Issue one cat command into each of them:
    cat /dev/input/by-path/platform-rotary\@5-event
    cat /dev/input/by-path/platform-rotary\@8-event
    (attention, the number is the pin A number in hex, so 10 would become A, 18→12 and so on)
  6. Now, when you turn the rotaries, there should be some gibberish output on the two terminals
  7. after the test, remove the overlays with sudo dtoverlay -r (2x)

If everything works fine up to 6), then the dtoverlay configuration is working and the problem is the handle in the plugin. In that case, I will prepare a version with more log info so we can find the root cause.
In my setup the thing is working with 3 rotaries on Volumio 2 with Debian Jessie. I expect that we get it working… maybe some issue, that is somehow not visible in my setup.

Best regards,
Thomas

1 Like