Cheap quadrature encoder jumps values greatly when turned quickly

Hitachii

Active member
Using only one knob from the "TwoKnobs Encoder Test" from the PJRC encoder library to read a cheap quadrature encoder that I picked up. Got help on this forum a few months ago to constrain the value to the 0 - 127 midi standard. Of all of the interrupt-based encoder sketches that I was able to find online, this one came the closest to what I'm trying to do, which is simply count up and down by one-per-click.

The one problem with this code is that when I turn it a certain speed (not very fast at all), the value will most often lurch forward or backward by as much as 40-60. This behavior is inconsistent because sometimes it counts normally, though that is rare. Is there something about this code that I need to change in order to prevent this behavior? I'm not looking for laser accurate readings e.g. I don't mind if it doesn't read every click if I twist it quickly, what's most important for me is that the count remains sequential instead of jumping uncontrollably.

If there's a better solution for a cheap rotary encoder that increments/decrements once per click, and is relatively understandable for someone who doesn't specialize in coding, I'm all ears. Thanks!

Code:
#include <Encoder.h>

Encoder knobLeft(40, 39);

void setup() {
  Serial.begin(9600);
  Serial.println("TwoKnobs Encoder Test:");
}

int positionLeft  = 0;

void loop() {

  byte newLeft;
  byte finalVal;

  newLeft = knobLeft.read() / 2;
  finalVal = newLeft >> 1;
  if (newLeft != positionLeft) {
    positionLeft = newLeft;
    Serial.print("Left = ");
    Serial.print(finalVal);
    Serial.println();
  }
}
 
That was 2 weeks ago :)
And you still use byte and the bitshift.

There are better encoder libraries, luni wrote a good one (use the forum search).
But the best way is not to use cheap encoders.
 
:D Forgive me, time is not real at the moment. If it means anything, I did change the 'byte' part in the larger controller code, but referred back to this one since the code was mostly the same. Thanks for the tip!
 
luni wrote a good one (use the forum search).
Thanks for the shoutout. Here the link to the library: https://github.com/luni64/EncoderTool. Actually I'd be quite interested if it works with your problematic encoder (which is quite expensive by the way).

I translated your code fromn #1, so that it works with the EncoderTool. Looking at it, it seems that you have an encoder with two counts per detent (unfortunately the datasheet doesn't have much information).
I therefore set the count mode to CountMode::half. You can set it to CountMode::quarter if you have a 'nomal' 4 counts per detent encoder.
The code assumes that your encoders have a common GND and activates the internal pullups (can be switched to common VCC with pulldowns if needed).

Code:
#include "Arduino.h"
#include "EncoderTool.h"
using namespace EncoderTool;

Encoder knobLeft;

void setup()
{
    while (!Serial && millis() < 1000) {}
    Serial.println("TwoKnobs Encoder Test:");

    knobLeft.begin(39, 40, CountMode::half);  // set to CountMode::quarter if your encoder has 4 counts per detent
    knobLeft.setLimits(0, 127, false);        // limits counts to 0..127, set third param to true if you want cyclic boundaries
}

void loop()
{
    if (knobLeft.valueChanged())
    {
        Serial.print("Left = ");
        Serial.print(knobLeft.getValue());
        Serial.println();
    }
}
 
Thanks for going through the trouble to do that, Luni! I've installed the library, but the code is not compiling, and is giving me this error:

Code:
In file included from /Users/coryoleson/Documents/Arduino/libraries/EncoderTool-master/src/EncoderBase.h:2:0,
                 from /Users/coryoleson/Documents/Arduino/libraries/EncoderTool-master/src/Multiplexed/EncPlexBase.h:4,
                 from /Users/coryoleson/Documents/Arduino/libraries/EncoderTool-master/src/Multiplexed/EncPlex74165.h:5,
                 from /Users/coryoleson/Documents/Arduino/libraries/EncoderTool-master/src/EncoderTool.h:3,
                 from /Users/coryoleson/Documents/Arduino/Study/Luni_Encoder_Tool/Luni_Encoder_Tool.ino:2:
/Users/coryoleson/Documents/Arduino/libraries/EncoderTool-master/src/EncoderButton.h:19:14: error: 'bool EncoderTool::EncoderButton::readCurrentState()' marked 'override', but does not override
         bool readCurrentState() override { return curState; }
              ^
Error compiling for board Teensy 4.1.

As the error states, I'm using a Teensy 4.1. I tried changing the count mode to quarter, and changed to true for cyclic boundaries, but the error persists.
 
Comment #19 from this thread was the answer that I needed. As the person said, it was as simple as removing the word "override" from EncoderButton.h. This is by far the best encoder code that I've come across, works like magic. Thanks Luni! Thanks Frank!
 
Comment #19 from this thread was the answer that I needed. As the person said, it was as simple as removing the word "override" from EncoderButton.h. This is by far the best encoder code that I've come across, works like magic. Thanks Luni! Thanks Frank!

Glad it works, and good to know that it handles problematic encoders as well.

The "fix" you did with removing "override" is OK if you don't use the button features of the library. Actually, it just cheats to the compiler so that it accepts the underlying fault. The root cause of the issue is an old version of Bounce2 which ships with Teensyduino < 1.54. You can easily fix this by either installing the current (beta) version 1.54 of Teensyduino or install a current version of Bounce2 with the library manager of the Arduino IDE. Let me know if you need further support using the lib.
 
Those CNC pulser handwheel ecoders from China are optical (!) 100PPR and the 5V version works perfectly on 3.3V as well. The detent can be removed to have a completely smooth flow, with 400 transitions per round!
They sell for as low as 5€ each if you are lucky, completely with nicely machined aluminum knob, that could as well be connected to a touch pin for touch sensing, if the encoder is mounted electrically isolated.

So where is the use of "cheap" encoders, if you can have this professional solution for the same or little extra money?
 
Those CNC pulser handwheel ecoders from China are optical (!) 100PPR
Of course these encoders are perfect for some applications but they are large and the price difference is significant. A standard mech encoder is about 50ct in single quantities. People here are doing projects using 32+ Encoders per whatever device. That makes a difference of 16EUR to 160EUR for the encoders alone. Not exactly little extra money for a lot of hobby users.

The notion of cheap, bouncing encoders most often comes from bad read out algorithms. Sometimes the algorithms are good but are done for different types of encoders. Actually, I never stumbled over a (not broken) "cheap" encoder which generated bounces or behaves strangely when read out correctly.
 
The notion of cheap, bouncing encoders most often comes from bad read out algorithms. Sometimes the algorithms are good but are done for different types of encoders. Actually, I never stumbled over a (not broken) "cheap" encoder which generated bounces or behaves strangely when read out correctly.

If I may chime in here. I am using the first version of Luni's encoder library(now somewhat modified by myself) with ultra cheap Chinese encoders. Luni's library works perfectly for me. It counts reliably, with no bounces or skipping, no matter how fast I turn the encoder. The real problem is the poor mechanical durability of the cheap Chinese encoders. The shaft bush, detents and contacts wear quickly with sustained use, and the whole thing becomes increasingly sloppy over time. But then I must admit to having driven my encoders hard!

I think it all comes down to durability and 'feel'. Are they important enough to you to warrant spending more money?
 
Agreed - I had some cheap mice with a worn out wheel-encoder. The work good when they are new. After a year they begin to fail and begin to count back and forth or miss steps. I'm pretty sure they have a good algorithm inbuilt, too.
 
Yep. Optical encoders on computer-mice do not wear, if built properly. Bearings or the plasic itself are then the weakest parts.
The CNC encoders are optical, built like a tank, especially useful for rough use on a MIDI controller.
One detent marks A=B=HIGH, so if the detent is removed, one gets 400 steps per round using encoder library, but with the detent, a factor 4 is needed.
It is done here mathematically, to forget about endianness, but there has to be that remaining two bits to count in half movements that happened during relative readout with readAndReset(), so giving a non-missing readout.

View attachment 23000

Edit: Made this little MIDI controller thing during the Holidays lockdown out of a Teensy 4.1 (to be replaced later) and four of the handwheels with detents removed and CD tray. And it is fantastic. I really can not figure out why I did not make it earlier. It gives possibility to modify a synth with fingertips, four parameters at once.

View attachment 23001

And the source of that dirty hack:

View attachment 23003

It is MIDI Relative CC.
Maybe I make a more sophisticated and better coded version later, but for now (and the days of lockdown) it works perfectly as-is.
 
Last edited:
Back
Top