Coding of a rotary encoder switch

550 views
Skip to first unread message

Mark de Reeper

unread,
Mar 7, 2014, 7:21:29 PM3/7/14
to pi...@googlegroups.com
Hi,

Having some good success with the pi4j library and have created a couple of working examples using temperature and motion sensors, thanks for all the work done on it to date.

My latest bit of code is around using a rotary encoder. Based on some C code found in http://bildr.org/2012/08/rotary-encoder-arduino/ I have been able to get some reasonably reliable readings and have been impressed with the very low overhead the whole thing requires when running a couple of listeners to track changes in the state of the two encoder inputs (much better than a python library I used to prove things were working).

Was wondering if anyone else has attempted to code something similar using the pi4j library as I would like to compare notes, mostly because whilst the results are reliable, the value tends to increase by 2 on ever turn which I could live with if every now and again it didn't increase by just 1 which is the expected increment. This might just been a factor of how the two inputs are being triggered or the low-level native access being used to monitor the GPIO.

The crux of the code is the following method which is called by a single listener added to both inputA and inputB. The encoderValue provides the current reading of the encoder at any point in time:

    /**
     */
    private void updateEncoder() {
        
        int MSB = inputA.getState().getValue(); //MSB = most significant bit
        int LSB = inputB.getState().getValue(); //LSB = least significant bit

        int encoded = (MSB << 1) | LSB; //converting the 2 pin value to single number
        int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value

        switch (sum) {
            case 2:
            case 4:
            case 11:
            case 13: 
                encoderValue++;
                break;                
            case 1:
            case 7:
            case 8:
            case 14: 
                encoderValue--;
        }
       
        lastEncoded = encoded; //store this value for next time        
    } 


Thanks

Mark

Mark de Reeper

unread,
Mar 9, 2014, 12:09:56 AM3/9/14
to pi...@googlegroups.com

So after some more experimentation, found that I could achieve what I wanted by simply listening for changes on one of the inputs rather than both (listening on both was part of my original problem) and ended up with something a bit more simple and works in all but the cases where the rotations are happening very quickly and you see the odd -1 on an increase cycle and a +1 on a decrease cycle. The calcEncoderValue() is being called from a listener attached to inputA.

    // based on [lastEncoded][encoded] lookup
    private static final int stateTable[][]= {
        {0, 1, 1, -1},
        {-1, 0, 1, -1},
        {-1, 1, 0, -1},
        {-1, 1, 1, 0}
    };

    private void calcEncoderValue(int stateA, int stateB) {
        
        // converting the 2 pin value to single number to end up with 00, 01, 10 or 11
        int encoded = (stateA << 1) | stateB;
        
        if (firstPass) {
            firstPass = false;
        } else {
            // going up states, 01, 11
            // going down states 00, 10
            encoderValue += stateTable[lastEncoded][encoded];      
        }
        
        lastEncoded = encoded;
    }


Next thing is to see how it copes with more than one rotary encoder...


Mark

Matthias Faust

unread,
Oct 12, 2014, 11:32:39 AM10/12/14
to pi...@googlegroups.com

Hello Mark,

I tried to google some examples how to use the pi4j API to connect a rotary encoder. Your message here seems to be the only post about it. So I would be happy if you could post a complete working example here.
I tried to setup a rotary encoder by connecting the left and right pin to the raspi GPIO pins and connected the mid to ground.

I implemented a Java class with pi4j using:
    inputA = GpioFactory.getInstance().provisionDigitalInputPin(raspiPinA, PinPullResistance.PULL_DOWN);
    inputB = GpioFactory.getInstance().provisionDigitalInputPin(raspiPinB, PinPullResistance.PULL_DOWN);

to read from the rotary encoder and used your code to decode the encoder. Unfortunatly the "GpioPinListenerDigital" I added fire no event. Since I am that deep in GPIO, I'm hoping your example solves my problem.

Best regards,
Matthias 

Mark de Reeper

unread,
Oct 13, 2014, 4:56:37 AM10/13/14
to pi...@googlegroups.com
Hi Matthias,

I have just posted the code to https://gist.github.com/markadr/5857df614b0603b9d710, feel free to use as you like. I use it daily to skip tracks on a little Volumio based setup - https://plus.google.com/112187975142637731649/posts/44ixkU7szKC.

Known issue is that first ever turn (left or right) is not picked up but I can live with that, happy to hear if you find a way to resolve it :-)


Thanks

Mark

Matthias Faust

unread,
Oct 13, 2014, 5:32:30 AM10/13/14
to Mark de Reeper, pi...@googlegroups.com
Thank you for the quick response!!! I'm gonna try the code this week. Also, I haven't heard about the Volumio project which sounds interesting too.
The "first turn" issue doesn't bother me, so I'm looking forward seeing the code in action...

Best regards,
Matthias

--
You received this message because you are subscribed to a topic in the Google Groups "Pi4J" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pi4j/uzuGWRAk7t4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pi4j+uns...@googlegroups.com.
Visit this group at http://groups.google.com/group/pi4j.
To view this discussion on the web visit https://groups.google.com/d/msgid/pi4j/034e1ab3-2e41-4556-b461-351a58f27be0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Júnior Mascarenhas

unread,
Nov 28, 2014, 3:00:47 PM11/28/14
to pi...@googlegroups.com

Hi Mark, hope you are doing well!

Did you post your codes besides Rotary Encoder (as you mentioned temperature and motion sensors, for example) in any public directory?
I'm starting to create examples using sensors and pi4j and I really appreciate if I could see what you have done.


Best regards,

Junior.

Mark de Reeper

unread,
Nov 29, 2014, 5:27:05 AM11/29/14
to pi...@googlegroups.com
Hi,

I never had much success with the motion sensor but here is a link to the code I used to read a TMP102, feel free to use as you like - https://gist.github.com/markadr/63b8eaf4248f0054c241


Thanks

Mark

Marc Andreu Fernandez

unread,
Apr 4, 2015, 2:34:28 PM4/4/15
to pi...@googlegroups.com
Hello guys, 

I implemented few sample exercises on Rotary Encoder in Raspberry Pi. There are different approaches flavours. 

You can check this out here. The Ex09 are all the rotary encoder related exercises. 

The solution from Mark did not work very well on my cheap rotary encoder from Keyes. I guess the specs are different from the RE used by Mark. 

A good source that I found is the Rotary library from Ben Buxton for Arduino. I translated to JAVA in one the exercise "Ex09_RotaryEncoder_SMPC". Its the best solution I could adapt for the Keyes rotary encoder. I guess I will need to buy a better R.E for better performance ;-)

Anyway, I hope this helps, 
Best regards, 
Marc Andreu

Mark de Reeper

unread,
Apr 4, 2015, 4:46:55 PM4/4/15
to pi...@googlegroups.com
Hi Marc,

Great job, I had looked at the code from Ben a while ago and had several failed attempts at porting it. I just tried your version with my encoder and it worked first time. Your comment about your encoder getting double results on my posted source page (sorry missed your question), looks to be related to Ben's comment about some encoders working in half-step mode. Using your code with my encoder results in event ever 2nd turn so I added the additional stateTable for the half-step mode and it behaved better. I also found that it worked okay without the need for the Queue but it does not hurt to have it.

We should look to add this to the Pi4J project so others can benefit.


Thanks

Mark

Marc Andreu Fernandez

unread,
Apr 8, 2015, 6:44:56 AM4/8/15
to pi...@googlegroups.com
Hello Mark, 

I am glad that it worked :-)

If you add your modifications by pull request to my repo I will contact the guys from pi4j and and let them know about it and see if we can add this solution as another example. 

Yes the Queue is not really necessary but I think it helps a little bit when doing fast rotations. 

Thanks Mark, 
Best regards
Marc, 
Reply all
Reply to author
Forward
0 new messages