[PLUGIN] RotaryEncoder II

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

2 Likes

thanks for the guidelines, i will try and let you know.

I am using your plugin under Buster

  1. I am on volumio3
  2. I get the expected response

3.6) I get gibberish output on both terminals:

volumio@basscrab:~$ dtoverlay -l
Overlays (in load order):
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
volumio@basscrab:~$ dtoverlay -l
No overlays loaded
volumio@basscrab:~$ dtoverlay -l
No overlays loaded
volumio@basscrab:~$ sudo /usr/bin/dtoverlay rotary-encoder pin_a=5 pin_b=6 relative_axis=true steps-per-period=
volumio@basscrab:~$ sudo /usr/bin/dtoverlay rotary-encoder pin_a=8 pin_b=9 relative_axis=true steps-per-period=
volumio@basscrab:~$ dtoverlay -l
Overlays (in load order):
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
volumio@basscrab:~$ cat /dev/input/by-path/platform-rotary@5-event
�f�a
�f�a
�f�a�������f�a���f�a3�
�����f�a3�
�f�aߔ�����f�aߔ�f�aa>�����f�aa>�f�a�������f�a���f�a�~�����f�a�~�f�a�������f�a���f�a�������f�a���f�a�F �����f�a�F ug�ug�ug�a�▒ug�a�▒

volumio@basscrab:~$ cat /dev/input/by-path/platform-rotary@8-event
sg�a/�
sg�a/�
tg�a�����tg�a�tg�a�%
����tg�a�%
tg�a��
����tg�a��
vg�a<�����vg�a<�wg�a������wg�a��wg�a�q����wg�a�qwg�a7����wg�a7

Ok, looks good so far - so the dtoverlay behaves the same as in Jessie, it seems.
I also have built some hardware to test it (unfortunately have no loose rotaries flying around and do not want to dismantle my tube-radio, so I had to build something from some buttons I had).
I could only do some testing of the makeshift setup so far, but the command line response works with my test-setup. I will try to debug my code with it on Volumio 3 on the weekend.

If you want to do another test, you can decode the gibberish:
If you install the overlays as described above and pipe the cat output through od (octal dump) it becomes human-readable. But I expect you get similar results as mine below.
To pipe the output through od:

cat /dev/input/by-path/platform-rotary\@5-event | od -An -vx

When you turn the rotaries, you should get the event in hex format like this:

9848 61a6 35e2 000b 0002 0000 ffff ffff
9848 61a6 35e2 000b 0000 0000 0000 0000
9849 61a6 3515 000c 0002 0000 0001 0000
9849 61a6 3515 000c 0000 0000 0000 0000

The last four bytes are an unsigned representation for the direction: 0x0001 0000 = CW, 0xFFFF FFFF = CCW. (If you interpret it as a signed value, then it is -1, but the kernel docs list it as an unsigned - and who am I to argue with the Kernel docs :wink:.)
The 9-10th byte is the type of event, in this case 0x0002 means a turn of the rotary.
The first 8 bytes are a timeval, but this is not needed.
That all comes out correctly, so I suppose, something goes wrong in my interpretation code in V3 Buster, that worked in V2 Jessie.

Hi @T0MR0

I am too trying to get your plugin working with Volumio3.

Ive tried what Darmur suggest with recompiling the node modules and it all installs and activates OK from within Volumio 3

I set it up with the same settings which work perfectly in Volumio 2
In Volumio 3 the rotary does not work but the button does as it should.

Live log of the plugin being activated, settings entered, then saved.
I dont know if this has a bearing on the issue, but at the bottom of the log each time I save the settings an exit code 1 error appears. But this error wasnt there in Volumio 2 and the plugin works fine

Starting Live Log...
process exited with code nullinfo: [ROTARYENCODER2] deactivateButtons: button 1 is disabled.
info: [ROTARYENCODER2] getI18nString("TOAST_STOP_SUCCESS"):Plugin successfully stopped.
info: [ROTARYENCODER2] onStop: Plugin successfully stopped.
info: Disabling plugin rotaryencoder2
info: Done.
info: CALLMETHOD: system_controller system enableLiveLog true
info: CoreCommandRouter::executeOnPlugin: system , enableLiveLog
info: Launching a new LiveLog session
info: Killing previous LiveLog session
info: Live Log process terminated: null
info: CoreCommandRouter::executeOnPlugin: my_volumio , getMyVolumioStatus
info: CoreCommandRouter::executeOnPlugin: my_volumio , getMyVolumioToken
info: Enabling plugin rotaryencoder2
info: Loading plugin "rotaryencoder2"...
info: PLUGIN START: rotaryencoder2
info: [ROTARYENCODER2] loadI18nStrings: /data/plugins/user_interface/rotaryencoder2/i18n/strings_en.json
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'!"}}
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":"7"},"pinB0":{"type":"string","value":"8"},"dialAction0":{"type":"number","value":1},"socketCmdCW0":{"type":"string","value":""},"socketDataCW0":{"type":"string","value":""},"socketCmdCCW0":{"type":"string","value":""},"socketDataCCW0":{"type":"string","value":""},"pinPush0":{"type":"number","value":15},"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":0},"socketCmdLongPush0":{"type":"string","value":""},"socketDataLongPush0":{"type":"string","value":""},"enabled1":{"type":"boolean","value":false},"rotaryType1":{"type":"number","value":1},"pinA1":{"type":"string","value":"24"},"pinB1":{"type":"string","value":"26"},"dialAction1":{"type":"number","value":2},"socketCmdCW1":{"type":"string","value":""},"socketDataCW1":{"type":"string","value":""},"socketCmdCCW1":{"type":"string","value":""},"socketDataCCW1":{"type":"string","value":""},"pinPush1":{"type":"number","value":0},"pinPushDebounce1":{"type":"number","value":0},"pushState1":{"type":"boolean","value":false},"pushAction1":{"type":"number","value":0},"socketCmdPush1":{"type":"string","value":""},"socketDataPush1":{"type":"string","value":""},"longPushAction1":{"type":"number","value":0},"socketCmdLongPush1":{"type":"string","value":""},"socketDataLongPush1":{"type":"string","value":""},"enabled2":{"type":"boolean","value":false},"rotaryType2":{"type":"number","value":1},"pinA2":{"type":"string","value":"7"},"pinB2":{"type":"string","value":"8"},"dialAction2":{"type":"number","value":1},"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"}
info: [ROTARYENCODER2] activateRotaries: 1,2,3
info: [ROTARYENCODER2] activateRotaries: 1,2
info: [ROTARYENCODER2] activateRotaries: 1
info: [ROTARYENCODER2] activateRotaries:
info: [ROTARYENCODER2] activateRotaries: end of recursion.
info: [ROTARYENCODER2] addOverlay: 7 8 1

 volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay rotary-encoder pin_a=7 pin_b=8 relative_axis=true steps-per-period=1
pam_unix(sudo:session): session opened for user root by (uid=0)
rotary-encoder rotary@7: gray
input: rotary@7 as /devices/platform/rotary@7/input/input5
pam_unix(sudo:session): session closed for user root
info: CoreCommandRouter::volumioGetState
info: CorePlayQueue::getTrack 0
info: [ROTARYENCODER2] attachListener: /dev/input/by-path/platform-rotary@7-event
info: [ROTARYENCODER2] addEventHandle for rotary: 1
info: [ROTARYENCODER2] activateButtons: 1,2,3
info: [ROTARYENCODER2] activateButtons: 1,2
info: [ROTARYENCODER2] activateButtons: 1
info: [ROTARYENCODER2] activateButtons:
info: [ROTARYENCODER2] activateButtons: end of recursion.
info: [ROTARYENCODER2] activateButtons: Now assign push button: 1
info: [ROTARYENCODER2] Push Button 1 now resolving.
info: [ROTARYENCODER2] onStart: Plugin successfully started.
info: Done.
Process '/usr/sbin/th-cmd --socket /var/run/thd.socket --passfd --udev' failed with exit code 1.
info: CoreCommandRouter::getUIConfigOnPlugin
info: [ROTARYENCODER2] getUIConfig: starting:
info: [ROTARYENCODER2] getUIConfig: i18nStrings{"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'!"}}
info: [ROTARYENCODER2] getUIConfig: i18nStringsDefaults{"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'!"}}
info: [ROTARYENCODER2] getUIConfig: language code: en dir: /data/plugins/user_interface/rotaryencoder2
info: [ROTARYENCODER2] getI18nString("VOLUME"):Volume
info: [ROTARYENCODER2] getI18nString("PLAYPAUSE"):Play/Pause toggle
info: [ROTARYENCODER2] getI18nString("DOTS"):...
info: [ROTARYENCODER2] getI18nString("SKIP"):Prev/Next title
info: [ROTARYENCODER2] getI18nString("DOTS"):...
info: [ROTARYENCODER2] getI18nString("DOTS"):...
info: [ROTARYENCODER2] getI18nString("VOLUME"):Volume
info: [ROTARYENCODER2] getI18nString("DOTS"):...
info: [ROTARYENCODER2] getI18nString("DOTS"):...
info: CALLMETHOD: user_interface rotaryencoder2 updateEncoder [object Object]
info: CoreCommandRouter::executeOnPlugin: rotaryencoder2 , updateEncoder
info: [ROTARYENCODER2] updateEncoder: Rotary1 with:{"enabled0":true,"rotaryType0":{"value":1,"label":"1/1"},"pinA0":7,"pinB0":8,"dialAction0":{"value":1,"label":"Volume"},"socketCmdCCW0":"","socketDataCCW0":"","socketCmdCW0":"","socketDataCW0":"","pinPush0":15,"pinPushDebounce0":0,"pushState0":true,"pushAction0":{"value":3,"label":"Play/Pause toggle"},"socketCmdPush0":"","socketDataPush0":"","longPushAction0":{"value":0,"label":"..."},"socketCmdLongPush0":"","socketDataLongPush0":""}
info: [ROTARYENCODER2] sanityCheckSettings: Rotary1 for:{"enabled0":true,"rotaryType0":{"value":1,"label":"1/1"},"pinA0":7,"pinB0":8,"dialAction0":{"value":1,"label":"Volume"},"socketCmdCCW0":"","socketDataCCW0":"","socketCmdCW0":"","socketDataCW0":"","pinPush0":15,"pinPushDebounce0":0,"pushState0":true,"pushAction0":{"value":3,"label":"Play/Pause toggle"},"socketCmdPush0":"","socketDataPush0":"","longPushAction0":{"value":0,"label":"..."},"socketCmdLongPush0":"","socketDataLongPush0":""}
info: [ROTARYENCODER2] deactivateRotaries: 1,2,3
info: [ROTARYENCODER2] deactivateRotaries: 2,3
info: [ROTARYENCODER2] deactivateRotaries: 3
info: [ROTARYENCODER2] deactivateRotaries:
info: [ROTARYENCODER2] deactivateRotaries: end of recursion.
info: [ROTARYENCODER2] detachListener: [object Object]
info: [ROTARYENCODER2] checkOverlayExists: Checking for existing overlays for Rotary: 1
 volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay -l
pam_unix(sudo:session): session opened for user root by (uid=0)
pam_unix(sudo:session): session closed for user root
info: [ROTARYENCODER2] checkOverlayExists: check pinA=7pinB=8 in Overlays (in load order):
0:  rotary-encoder  pin_a=7 pin_b=8 relative_axis=true steps-per-period=1
info: [ROTARYENCODER2] checkOverlayExists: rotary 1uses overlay 0
info: [ROTARYENCODER2] removeOverlay: 0
 volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay -r 0
pam_unix(sudo:session): session opened for user root by (uid=0)
pam_unix(sudo:session): session closed for user root
info: [ROTARYENCODER2] removeOverlay: 0 returned:
 volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay -l
pam_unix(sudo:session): session opened for user root by (uid=0)
pam_unix(sudo:session): session closed for user root
info: [ROTARYENCODER2] removeOverlay: "overlay -l" returned: No overlays loaded
info: [ROTARYENCODER2] deactivateRotaries: deactivated rotary1
info: [ROTARYENCODER2] deactivateButtons: 1,2,3
info: [ROTARYENCODER2] deactivateButtons: 2,3
info: [ROTARYENCODER2] deactivateButtons: 3
info: [ROTARYENCODER2] deactivateButtons:
info: [ROTARYENCODER2] deactivateButtons: end of recursion.
info: [ROTARYENCODER2] deactivateButtons: button 1 is disabled.
info: [ROTARYENCODER2] updateEncoder: Changing Encoder 1 Settings to new values
info: [ROTARYENCODER2] activateRotaries: 1,2,3
info: [ROTARYENCODER2] activateRotaries: 1,2
info: [ROTARYENCODER2] activateRotaries: 1
info: [ROTARYENCODER2] activateRotaries:
info: [ROTARYENCODER2] activateRotaries: end of recursion.
info: [ROTARYENCODER2] addOverlay: 7 8 1
 volumio : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/usr/bin/dtoverlay rotary-encoder pin_a=7 pin_b=8 relative_axis=true steps-per-period=1
pam_unix(sudo:session): session opened for user root by (uid=0)
rotary-encoder rotary@7: gray
input: rotary@7 as /devices/platform/rotary@7/input/input6
pam_unix(sudo:session): session closed for user root
info: [ROTARYENCODER2] attachListener: /dev/input/by-path/platform-rotary@7-event
info: [ROTARYENCODER2] addEventHandle for rotary: 1
info: [ROTARYENCODER2] activateButtons: 1,2,3
info: [ROTARYENCODER2] activateButtons: 1,2
info: [ROTARYENCODER2] activateButtons: 1
info: [ROTARYENCODER2] activateButtons:
info: [ROTARYENCODER2] activateButtons: end of recursion.
info: [ROTARYENCODER2] activateButtons: Now assign push button: 1
info: [ROTARYENCODER2] Push Button 1 now resolving.
info: [ROTARYENCODER2] getI18nString("TOAST_SAVE_SUCCESS"):Successfully saved
info: [ROTARYENCODER2] getI18nString("TOAST_MSG_SAVE"):Settings for Encoder
info: [ROTARYENCODER2] updateEncoder: SUCCESS with Toast: Successfully saved Settings for Encoder 1
info: [ROTARYENCODER2] getI18nString("TOAST_SAVE_SUCCESS"):Successfully saved
info: [ROTARYENCODER2] getI18nString("TOAST_MSG_SAVE"):Settings for Encoder
Process '/usr/sbin/th-cmd --socket /var/run/thd.socket --passfd --udev' failed with exit code 1.

With the plugin enabled from an SSH window I issued the cat /dev/input/by-path/platform-rotary\@7-event | od -An -vx command and the rotary was giving an output OK. CW then ACW


 5cf1 61a7 ce51 000a 0000 0000 0000 0000
 5cf1 61a7 c286 000b 0002 0000 0001 0000
 5cf1 61a7 c286 000b 0000 0000 0000 0000
 5cf1 61a7 a179 000c 0002 0000 0001 0000
 5cf1 61a7 a179 000c 0000 0000 0000 0000
 5cf1 61a7 da0c 000d 0002 0000 0001 0000
 5cf1 61a7 da0c 000d 0000 0000 0000 0000
 5cf2 61a7 4d04 0002 0002 0000 ffff ffff
 5cf2 61a7 4d04 0002 0000 0000 0000 0000
 5cf2 61a7 226e 0003 0002 0000 ffff ffff
 5cf2 61a7 226e 0003 0000 0000 0000 0000
 5cf2 61a7 a753 0003 0002 0000 ffff ffff
 5cf2 61a7 a753 0003 0000 0000 0000 0000
 5cf2 61a7 156a 0004 0002 0000 ffff ffff

Hardware RPI4 2GB, D10 Dac

Hope this will help, any other test needed just ask, I’ll try

1 Like

Hi M1ck!
Thanks! I already figured out, that it is related to the child process which handles the rotary device. It does not work anymore in Buster.
It is probably easy to fix, but I need a few hours to sit down, Test and implement. Hope to be able to do that on the weekend.
I‘ll post a Link to a fixed package for Testing asap.
Please be patient!

1 Like

another update - there seems to be a thing called “trigger happy daemon” which somehow attaches to all devices - when I kill that process, it partly works again. But I still need some more cleaning up and need to figure out, what trigger happy is used for and how to keep it from grabbing the rotary

thd is utilised for input handling for remotes/keyboards etc.
You might be able to simplify your plugin by hooking into it directly, once the encoders are configured properly.

Faster alternative - you could add your encoders to it’s avoid list

--ignore eventname
           Ignore key and switch event labeled eventname. This can be used to suppress the FN key
           on some notebooks which only generates events sometimes and might screw up key
           combinations.

EDIT:
I looked (very quickly) at your plugin, and see that you spawn a process to read /dev/input/by-path/platform-rotary\@<>-event - have a look at Node’s readstream that can do this for you, without the overhead of the process spawning :slight_smile:

For usual NEXT/PREV/Volume, you can hook into triggerhappy directly. For the generic generic websocket event, you could also do the same, but would need to be a bit more creative. Not sure if it yields any benefits though.

1 Like

Thanks, I’ll explore thd a bit the next days.

My first version of the plugin was initially using readStream but I had to abandon that, because I could not get the handle properly released, which created issues with stopping and restarting the plugin. I even posted to the node issues on Github about it but in the end followed the workaround proposed at the end of this thread. After weeks of debugging, that was the only option that allowed a proper release and subsequent removal of the overlay.

When I dug out my old post up now, I see that this problem is mentioned in the documentation of fs now and there is a new proposal to close the stream with stream.push(null). I’ll try that the next days and see, if I can move back to the readStream and abandon the spawn-crutch.

2 Likes

Hi Darmur

I tried to install the rotary II plugin on Volumio 3 according to your proposal…
Once i get to the “npm install --save onoff@6.0.0” get the following:

In the end the pushbutton is working but the rotary encoder is not. With Volumio 2.X it did work…
Can you tell me what is the problem?
Thanks for your help

This is exactly what happens to me.
The author T0MR0 is looking into why it no longer works with V3
We need to be patient

Hi all,
sorry - still very busy times for me. Had a little time to dig and play and build a first fix.
Here is my current hypthesis:

  • In buster, we have the triggerhappy-daemon thd, which is watching the /dev/input folder for new devices. However, triggerhappy is not aware of rotary-encoders, so it does not act on the new device but seems to slow the udev process a bit.
  • I will not move to triggerhappy as was suggested, because it currently does not support rotary encoders and I do not like the idea of building on code, that seems to be abandoned since 2016. There is an open pull-request to include rotaries here which seems to be merged into another package in the meantime - but overall I trust the approach to build on Linux base-functionality more, as it works and is easier to maintain.
  • I also noticed, that dtoverlay returns before the by-path reference to the rotary is available to the system, because it is made with some delay by udev. Therefore the event-handle is not properly configured, which leads to the malfunction of one of the encoders.
  • As a first quick fix, to give all of you a work-around, I have installed a timeout to delay the activation of the events. This solves the issue on my test-system, giving me two functional encoders.
  • I uploaded a package in a branch on my personal repo. It was made with volumio plugin package on my Buster test-system. You should be able to use it and do a test. Let me know, how it goes. package is here
  • For a permanent fix I want to try and significantly improve the code. I think I found a way, to go back to readstream, but I need to test if it really does not crash the system anymore like in my old implementation, where it sometimes waited for data indefinitely. I also would like to insert a function that verifies, that the by-path reference is really installed, before it resolves the promise. Hope I have some time over the Holidays.
3 Likes

Hi @T0MR0

Just tested
Only using the 1 encoder as the volume, which seems to be working just fine.

You should submit it to the volumio plugin store as a beta, this will let other people have an encoder, while you work on getting it the way you want it to be.

Thanks BTW and good job :+1:

1 Like