1

I'm using Mono and the Raspberry Sharp library to try and decode the output of a rotary encoder.

Raspberry sharp: https://github.com/raspberry-sharp/raspberry-sharp-io

Rotary encoder data sheet: http://www.bourns.com/data/global/pdfs/PEC11R.pdf

So far I've tried using the library's PinStatusChanged event to trigger a simple Console.WriteLine() when either one of the pins changes value:

private static InputPinConfiguration pinA = ConnectorPin.P1Pin24.Input().PullUp();
private static InputPinConfiguration pinB = ConnectorPin.P1Pin26.Input().PullUp();

static void Main(string[] args)
{
    var pinAConnection = new GpioConnection(pinA);
    pinAConnection.PinStatusChanged += (sender, eventArgs) =>
        {
            Console.WriteLine(eventArgs.Enabled.ToString());
        };

    var pinBConnection = new GpioConnection(pinB);
    pinBConnection.PinStatusChanged += (sender, eventArgs) =>
        {
            Console.WriteLine(eventArgs.Enabled.ToString());
        };
}

If I run this on the Pi I can see things happening with the above code. The problem I'm facing is that the Raspberry Sharp library doesn't appear to have functionality for monitoring multiple pins at the same time. To the best of my knowledge, correctly assessing the state of the rotary encoder requires interpreting the values of two of its pins simultaneously. Does anyone have any suggestions as to how to go about this using the Raspberry Sharp library?

  • 1
    What does the author say? – joan Jul 13 '15 at 15:15
  • If that comment was intended for this question then I'm stumped - can you clarify the comment? – goobering Jul 13 '15 at 15:16
  • 1
    What does the author of the software library you intend to use say? – joan Jul 13 '15 at 15:26
  • Very little on this subject. The library in question is sparsely documented, and what documentation there is contains no examples specific to rotary encoders or simultaneously reading multiple pins. – goobering Jul 13 '15 at 15:32
  • If you don't get an answer here your best bet may be to raise a github issue on the library. The author will have visibilty of raised issues. – joan Jul 13 '15 at 15:42
1

I figured this out. I had done some exceedingly stupid things first time round. Ultimately I ported the code posted here: http://abyz.me.uk/rpi/pigpio/ex_rotary_encoder.html, which provided a really useful starting point.

With the rotary encoder pins hooked to connector pin 24 (referenced by the library as Pin08), ground and connector pin 26 (referenced by the library as Pin7):

using System;
using Raspberry.IO.GeneralPurpose;

namespace GPIOTesting
{
    class Program
    {
        //State of Pin08
        private static bool levA = false;
        //State of Pin7
        private static bool levB = false;
        //The name of the last GPIO pin to fire a PinStatusChanged event
        private static string lastGpio = String.Empty;

        static void Main(string[] args)
        {
            //Declare our pins (connector 24 and 26 / processor 08 and 7) as INPUT pins, and apply pull-up resistors
            var pin1 = ConnectorPin.P1Pin24.Input().PullUp();
            var pin2 = ConnectorPin.P1Pin26.Input().PullUp();

            //Create the settings for the connection
            var settings = new GpioConnectionSettings();

            //Interval between pin checks. This is *really* important - higher values lead to missed values/borking. Lower 
            //values are apparently possible, but may have 'severe' performance impact. Further testing needed.
            settings.PollInterval = TimeSpan.FromMilliseconds(1);

            //Create a new GpioConnection with the settings per above, and including pin1 (24) and pin2 (26).
            var connection = new GpioConnection(settings, pin1, pin2);

            //Integer storing the number of detents turned - clockwise turns should increase this and vice versa.
            var encoderPos = 0;

            //Add an event handler to the connection. If either pin1 or pin2's value changes this will fire.
            connection.PinStatusChanged += (sender, eventArgs) =>
                    {
                        //If pin 24 / Pin08 / pin1 has changed value...
                        if (eventArgs.Configuration.Pin == ProcessorPin.Pin08)
                        {
                            //Set levA to this pin's value
                            levA = eventArgs.Enabled;
                        }
                        //If any other pin (i.e. pin 26 / Pin7 / pin2) has changed value...
                        else
                        {
                            //Set levB to this pin's value
                            levB = eventArgs.Enabled;
                        }

                        //If the pin whose value changed is different to the *last* pin whose value changed...
                        if (eventArgs.Configuration.Pin.ToString() != lastGpio)
                        {
                            //Update the last changed pin
                            lastGpio = eventArgs.Configuration.Pin.ToString();

                            //If pin 24 / Pin08 / pin1's value changed and its value is now 0...
                            if ((eventArgs.Configuration.Pin == ProcessorPin.Pin08) && (!eventArgs.Enabled))
                            {
                                //If levB = 0
                                if (!levB)
                                {
                                    //Encoder has turned 1 detent clockwise. Update the counter:
                                    encoderPos++;
                                    Console.WriteLine("UP: " + encoderPos);
                                }
                            }
                            //Else if pin 26 / Pin7 / pin2's value changed and its value is now 1...
                            else if ((eventArgs.Configuration.Pin == ProcessorPin.Pin7) && (eventArgs.Enabled))
                            {
                                //If levA = 1
                                if (levA)
                                {
                                    //Encoder has turned 1 detent anti-clockwise. Update the counter:
                                    encoderPos--;
                                    Console.WriteLine("DOWN: " + encoderPos);
                                }
                            }
                        }
                    };
        }
    }
}
0

I'm not a mono user and hopefully you get a more detailed answer, but there is a bit of a logical caveat here:

interpreting the values of two of its pins simultaneously

In a single threaded process, this is obviously not possible; you can get the state of one pin, then you can immediately get the state of the other one, but you cannot get them both at exactly the same time.

In a multi-threaded process, on a system with multiple cores, it might in theory be possible but it is still probably not a useful way to think about it.

The example you have looks to be using callbacks. You want to work those into a broader (e.g. global) context so that when one pin changes state you can check what/when the last reported change took place for the other pin.

the Raspberry Sharp library doesn't appear to have functionality for monitoring multiple pins at the same time.

If those are callbacks, it obviously does in the sense that your example code presumably waits and reports on both pins. If you want to coordinate the meaning of these events, that's a higher level conceptually and you need to do it yourself. I doubt any GPIO library will provide such arbitrary functionality (because that is not their purpose).

  • This is helpful, and I would upvote it if it were a comment. I could probably have phrased the 'simultaneously' sentence a little more clearly - getting both pin values at very close to the same time would, of course, do the job just fine. I've done some more work since my original post and think I've just about gotten my head around the problem. The Raspberry Sharp library approach to reading a pin value is more complicated than I'd like it to be! – goobering Jul 13 '15 at 19:03
0

If your only option is polling the inputs, then the strategy is limited to:

  • read first pin, then immediately read second pin, storing both in variables
  • send both variables to a function to compute the count and direction, based on the previous values

Example in Python (algorithm taken from this blog post):

# Globals:
state = 0
counter = 0
direction = 1

while True:
    # get raw pin values
    ra = GPIO.input(22)
    rb = GPIO.input(23)

    # update encoder count and direction
    ns = (ra ^ rb) | rb << 1
    if state == ns: # no change since last time
        return
    dt = (ns - state) % 4
    if dt == 3:
        direction = -1
    elif dt == 1:
        direction = 1
    counter = counter + direction
    state = ns

    # do something with counter and/or direction

I don't know C#, but here's a C version of the above code:

long counter;
char state;
int direction = 1;

while(1) {
    /* get pin values */
    char ra = gpio_input_read(22); /* pseudocode library function */
    char rb = gpio_input_read(23);

    /* update encoder */
    int ns = (ra ^ rb) | (rb << 1);
    if (state == ns) { return; }
    int dt = (ns - state) % 4;
    if (dt < 0) { dt = dt * -1; }
    if (dt == 3) {
        direction = -1;
    if (dt == 1) {
        direction = 1;
    }
    counter = counter + direction;
    state = ns;

    /* do something with counter and/or direction */
}
0

Raspian comes with driver that does events like someone suggested. All you need to do is write and compile a Device Tree overlay that loads the driver and hooks up the GPIOs. The text fie for the driver is here: https://www.kernel.org/doc/Documentation/input/rotary-encoder.txt

I posted my overlay here: Making input device for Raspberry Pi

The nice thing I forgot to mention is that the SDL game developement library has that driver interface built in as Joystricks and/or JoyBalls

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.