[Blackboard] Turning rotary encoder has no effect if I don't turn it right first

Hi,

I realized that with Blackboard and the example sensei configuration that you provide for the Blackboard examples, the rotary encoder does not seem to work as expected. The issue is that if I turn it left I see no callback fired in the Python glue app code. The function _handle_encoder from elk_ui.py is not being called. Once I turn the encoder right once, then I see the function being called as expected, and then I can turn it left again (only once) and it will also work. If I turn right 10 times, then I can turn left 10 times.

I think the issue is that sensei stores some internal value for the encoder that starts at 0 and canā€™t go negative, so turning encoder left has no effect until if have turned it right and the internal value changes (and sensei detects that change and fires an event).

I guess the solution would be to add some sort of ā€œinitialā€ or ā€œdefaultā€ parameter to sensei config for the encoder.

Ideas?

thanks

Hi frederic,
setting a default value in the middle would most probably be a workaround.

I kinda remember we did something similar when working on examples, maybe @Ruben knows more.

thanks, in sensei docs I did not find a way to set default, but I did not look into the code. maybe thereā€™s an undocummented way to do it?

thanks!

Hi and sorry for the late response. I think this issue is with the range of the encoder in the sensei config.

{
    "id" : 23,
    "enabled": true,
    "name" : "rot_enc",
    "sensor_type" : "analog_input",
    "mode" : "on_value_changed",
    "range" : [0, 10000],
    "hardware" :
    {
      "hardware_type" : "encoder",
      "polarity" : "active_low",
      "pins" : [9, 8],
      "delta_ticks" : 1
    }
},

Youā€™re right with the internal value starting at zero. If the range is changed to for example [-5000, 5000] it should work as you expect it to work. Iā€™ll make a change in the examples repository asap!

Hi, I tried setting the range as suggested and it does not seem to work. It might me initialized at the lower end of the range instead of 0 (which makes sense because you might have ranges that do not cross 0). Isnā€™t there a way to provide a default in sensei?

Hi @frederic,
have you tried to just send a value to SENSEI through OSC to initialize it?

IIRC that is how we did it in the past.

Hi @Stefano, thanks for the tip but Iā€™m afraid I did not find how to do this.
Iā€™m sending OSC messages to Sensei port (23024), and I can indeed, eg, control leds with ā€˜/set_outputā€™, but I donā€™t know what OSC message I need to set the value of the rotary encoder. There seems to be no documentation for that, I searched in Senseiā€™s source code and I tried some things but so far Iā€™ve not been able to initialize the encoder with any value. What OSC address/params should I use?

Hi @frederic,
I checked and we did that in the past but it might have been with an older SENSEI implementation where there was an unique id space between inputs and outputs.

Even in the current examples, we use the encoder in a relative way but it wonā€™t wrap to the left of the initial position.

But indeed it wonā€™t work now since SENSEI has different address spaces for idxs and outputs.

@Sharan could you confirm this?

Weā€™ll look if itā€™s possible to hack around the sensei codebase to introduce a workaround.

Hey @frederic

Sorry for the late reply.

Essentially, when you configure the encoder, it is set to range that you specify and the initial value is set to 0. Currently, the enoder does not provide ā€œtick infoā€ i.e how many ticks the encoder has moved either direction. It simply acts as a counter. We have plans in the future to add that support in sensei and gpio_protocol_lib,

However, we also have the option to set the value of an input sensei controller using the same method as that for setting LEDs. While you cannot set the initial value in the configuration, you can set it at the start of your python prog. Apologies if this is not shown in the documentation. We can get right to it,

Shown below is an example to set the encoder value.

Normalized encoder values

My configuration for the encoder is as shown below. I have set it up as as an analog_input which means that sensei normalizes the range to a value between 0 and 1.0:

  {
    "id" : 23,
    "enabled": true,
    "name" : "rot_enc",
    "sensor_type" : "analog_input",
    "mode" : "on_value_changed",
    "range" : [0, 10000],
    "hardware" :
    {
      "hardware_type" : "encoder",
      "polarity" : "active_low",
      "pins" : [9, 8],
      "delta_ticks" : 1
    }
  },

To set the initial value to 0.2, i send an osc command (through cmdline) to path /set_output to set the value of id 23 to 0.2. You can do the same through your python program as well.

oscsend localhost 23024 /set_output if 23 0.2


Non normalized encoder values

If you prefer to have represent the encoder with the whole range of values instead of a normalized range, you can set the senor type as range_input as shown below. This will enable you to read or write the encoder values to anything between [0, 10000]. Note that this will result in sensei outputting the encoder values in the osc path /sensors/range/rot_enc

  {
    "id" : 23,
    "enabled": true,
    "name" : "rot_enc",
    "sensor_type" : "range_input",
    "mode" : "on_value_changed",
    "range" : [0, 10000],
    "hardware" :
    {
      "hardware_type" : "encoder",
      "polarity" : "active_low",
      "pins" : [9, 8],
      "delta_ticks" : 1
    }
  }

To set the value to say 400:

oscsend localhost 23024 /set_output if 23 400


A simple python example:

#!/usr/bin/env python3
import liblo
import time


SENSEI_TO_TEST_PORT = 23023
TEST_TO_SENSEI_PORT = 23024

TEST_BRIDGE_PATH = "/test_bridge/result"

ROT_ENC_ID = 23

# if set up as analog input
ROT_ENC_ANALOG_PATH = "/sensors/analog/rot_enc"

# if set up as range input
ROT_ENC_RANGE_PATH = "/sensors/range/rot_enc"

sensei_address = ('localhost', TEST_TO_SENSEI_PORT)

def handle_encoder(path, args):
    encoder_val = args[0]
    print (encoder_val)


def unhandled_msg_callback(path, args, types, src):
    print('Unknown OSC message %s from %s' % (path, src.url))


# set some initial val to the rot enc
def set_rot_enc_initial_val():
        osc_msg = liblo.Message('/set_output')
        osc_msg.add(('i', ROT_ENC_ID))
        
        # if set to analog input
        osc_msg.add(('f', 0.2))
        
        # if set to range input
        #osc_msg.add(('f', 500))

        liblo.send(sensei_address, osc_msg)

if __name__ == "__main__":
    osc_server = liblo.ServerThread(SENSEI_TO_TEST_PORT)

    #osc callback reg in case rot enc is setup as analog input
    osc_server.add_method(ROT_ENC_ANALOG_PATH, 'f', handle_encoder)
    
    #osc callback reg in case rot enc is setup as range input
    #self._osc_server.add_method(ROT_ENC_RANGE_PATH, 'f', self._handle_encoder)

    osc_server.add_method(None, None, unhandled_msg_callback)
    osc_server.start()

    set_rot_enc_initial_val()

    while True:
        time.sleep(1)

Hi @Sharan, thanks for your detailed answer.
I tried your script as is (copy paste) and unfortunately I canā€™t get the encoder to work properly. Also the sensei configuration for the encoder is the same you pasted (set as analog input). Running your script prints values when I rotate the encoder, but even if I send ā€œ/set_outputā€, the values donā€™t change so I canā€™t ā€œrotate leftā€ if I donā€™t ā€œrotate rightā€ first. Does the script work for you as is?

Thanks,

Hey @frederic

Sorry my bad, the gpio_protocol i used has not yet been merged to to github, I shall do it shortly and let you know. After this, you can compile sensei either usiong the sdk or the board.

1 Like

Hey @frederic

I have pushed the latest version of sensei 0.3.0 which fixes these issues. Now you can set the value of input/analog controllers. You can use the SDK to build it or you can build it on the board.

You can replace the systemwide sensei binary which is in the path /usr/bin.

Do let me how things go.

Hi,

I was able to compile sensei but I donā€™t seem to be able to initialize it with the same blackboard configuration file I had. I get this error:

[2021-02-05 15:25:18.515] [warning] #############################
[2021-02-05 15:25:18.515] [warning]    Started Sensei Logger!
[2021-02-05 15:25:18.515] [warning] #############################
[2021-02-05 15:25:18.515] [info] [config] Reading configuration file
[2021-02-05 15:25:18.516] [info] [Shiftreg Gpio] Initializing Gpio Frontend with Elk Pi hw backend
[2021-02-05 15:25:18.516] [error] [Shiftreg Gpio] Cannot Init Shiftregister Hw Backend. Its not enabled!
[2021-02-05 15:25:18.516] [error] [Shiftreg Gpio] Failed to initialize hw backend

Ideas?

thanks!

Hi @Sharan did you have any chance to look into this? Looks like I need a different sensei configuration file maybe?