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?

asked Jul 13, 2015 at 14:53
5
  • 1
    What does the author say? Commented Jul 13, 2015 at 15:15
  • If that comment was intended for this question then I'm stumped - can you clarify the comment? Commented Jul 13, 2015 at 15:16
  • 1
    What does the author of the software library you intend to use say? Commented Jul 13, 2015 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. Commented Jul 13, 2015 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. Commented Jul 13, 2015 at 15:42

4 Answers 4

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);
 }
 }
 }
 };
 }
 }
}
answered Jul 14, 2015 at 16:52
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).

answered Jul 13, 2015 at 18:43
1
  • 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! Commented Jul 13, 2015 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 */
}
answered Jan 10, 2016 at 19:32
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

answered Apr 23, 2016 at 0:30

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.