Encoder library and rotary encoder

I thought using a rotary encoder (such as https://www.allelectronics.com/item/re-66/20-detent-rotary-encoder/1.html) to be able to increment and decrement setpoints would be a neat idea over having to repeatedly press up and down buttons.

I'm starting simple with the Stoffregen Encoder library and the basic example.

/* Encoder Library - Basic Example
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 *
 * This example code is in the public domain.
 */

#include <Encoder.h>
#define ENCODER_USE_INTERRUPTS

// Change these two numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder myEnc(2,3);
//   avoid using pins with LEDs attached

void setup() {
  Serial.begin(57600);
  Serial.println("Basic Encoder Test:");
}

long oldPosition  = -999;

void loop() {
  long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    Serial.println(newPosition);
  }
}

The output increments by 2 in both directions. Yes, I mean what I said. Increments by 2 in both directions. Turning CW, increments 2-4-6-8-10, turn CCW and increments 12-14-16-18.

As I understand the library, CCW should decrement the position. I don't know why it increments by two per notch. I have tried a few other libraries with differing issues and some with similar results (one library returns CW motion for both CW and CCW).

If there is a better library available, or what the best library is available, please also let me know.

Try

 long newPosition = myEnc.read() / 2;

Do you have external pullups on the encoder pins?

The library uses the internal pullups.

Why is the library returning 2x the actual result?

Not sure how to explain it. It has to do with the state machine to read the encoder. Have a look at the source code. I have to use /4 with my encoders.

Why does the library only increment, or increment in both CW and CCW directions?

I do not know what you mean.

Here is the library documentation.

Can you show how your encoder is wired? Photos or schematic.

Turn one notch right (CW), result increments 2. Turn one notch left (CCW), result increments 2 (now position is 4). Turn one notch right, increments 2 and result is now 6. Turn one notch left, increments 2 and result is now 8.

Show how the encoder is wired, please.

image

Pin A is connected to UNO Pin2, Pin B connected to UNO Pin 3. Pin C to UNO GND.

You got me. I have used that library on several projects and always had to divide by 4 and all of the encoders would increment one way and decrement the other way. However, I use bare encoders, not the ones on breakout boards. Wired like so (caps optional).

Not that it matters much, but no break out board is involved. Just the part purchase from the link in post #1. Other than the notches in the shaft, this is as bare an encoder as I have seen in 25 years of industrial automation. :slight_smile:

Some encoders do not make a connection in the detent position, and give one or two pulses when moved from one detent to another. They will not work correctly with that library.

Yours does not seem to have any documentation, so check with a multimeter.

Documentation:

Yep.


volatile uint8_t enc=4;
volatile uint8_t state=0;

void setup()
 {
  Serial.begin(115200);
  Serial.println("Ready");
  
  pinMode(13, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), ISR_P2, CHANGE);
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(3), ISR_P3, CHANGE);
 }

void ISR_P2()
 {
  enc = (enc & 0x06) | digitalRead(2);
  state = !state;
  Serial.println(enc, BIN);
 }

void ISR_P3()
 {
  enc = (enc & 0x05) | (digitalRead(2)<<1);
  Serial.println(enc,BIN);
 }

void loop()
 {
  digitalWrite(13, state);
 }

gives the output

110
100
101
111

in both directions.

Here is my encoder test code. It does not use interrupts. It counts by 2, incrementing in one rotational direction and decrements in the other. This works with a bare encoder with no caps.

const byte aPin = 4;
const byte bPin = 5;
const byte swPin = 3;
const byte ledPin = 13;

long encoderCnt = 0;

void setup()
{
   Serial.begin(115200);
   pinMode(aPin, INPUT_PULLUP);
   pinMode(bPin, INPUT_PULLUP);
   pinMode(swPin, INPUT_PULLUP);
   pinMode(ledPin, OUTPUT);
}

void loop()
{

   if (readEncoder()) // count changed?
   {
      Serial.print("Encoder count =  ");
      Serial.println(encoderCnt);
   }
   readSw();
}

void readSw()
{
   static bool lastSwState = HIGH;
   bool swState = digitalRead(swPin);
   if (swState != lastSwState)
   {
      if (swState == LOW)
      {
         digitalWrite(ledPin, !digitalRead(ledPin));
      }
   }
   lastSwState = swState;
}


boolean readEncoder()
{
   boolean newEncode = false;
   static boolean last_aState = 0;
   boolean aState = digitalRead(aPin);
   boolean bState = digitalRead(bPin);
   if (aState != last_aState)
   {
      if (aState != bState)
      {
         encoderCnt++;
         //Serial.println("clockwise");
      }
      else
      {
         encoderCnt--;
         //Serial.println("counter clockwise");
      }
      last_aState = aState;
      newEncode = true;
   }
   return newEncode;
}
const byte aPin = 2;
const byte bPin = 3;
const byte swPin = 4;
const byte ledPin = 13;

long encoderCnt = 0;

void setup()
{
   Serial.begin(115200);
   pinMode(aPin, INPUT_PULLUP);
   pinMode(bPin, INPUT_PULLUP);
   pinMode(swPin, INPUT_PULLUP);
   pinMode(ledPin, OUTPUT);
}

void loop()
{

   if (readEncoder()) // count changed?
   {
      Serial.print("Encoder count =  ");
      Serial.println(encoderCnt);
   }
   readSw();
}

void readSw()
{
   static bool lastSwState = HIGH;
   bool swState = digitalRead(swPin);
   if (swState != lastSwState)
   {
      if (swState == LOW)
      {
         digitalWrite(ledPin, !digitalRead(ledPin));
      }
   }
   lastSwState = swState;
}


boolean readEncoder()
{
   boolean newEncode = false;
   static boolean last_aState = 0;
   boolean aState = digitalRead(aPin);
   boolean bState = digitalRead(bPin);
   if (aState != last_aState)
   {
      if (aState != bState)
      {
         encoderCnt++;
         //Serial.println("clockwise");
      }
      else
      {
         encoderCnt--;
         //Serial.println("counter clockwise");
      }
      last_aState = aState;
      newEncode = true;
   }
   return newEncode;
}

has the following output

Encoder count =  -1
Encoder count =  -2
Encoder count =  -3
Encoder count =  -4
Encoder count =  -5
Encoder count =  -6
Encoder count =  -7
Encoder count =  -8
Encoder count =  -9
Encoder count =  -10
Encoder count =  -11
Encoder count =  -12
Encoder count =  -13
Encoder count =  -14
Encoder count =  -15
Encoder count =  -16
Encoder count =  -17
Encoder count =  -18
Encoder count =  -19
Encoder count =  -20

10 notches right, then 10 notches left.

That is weird. Its like the encoder is not quadrature. I can duplicate what you are seeing by wiring the encoder as normal, but also connecting the A and B terminals together with the A still connected to Uno pin 4 and B to Uno pin 5. Like A and B are shorted.

The output from the code in post 15 indicates A and B terminals are independent (ie., not shorted) as b0 and b1 change in the quadrature count (Gray code).

I ran the code from post #15 (changed pins) and got the following with A and B shorted:

110
100
101
111
110
100
101
111

I can't explain it but there it is. It seems that if I short A and B I get the behavior that you are seeing.

Wired like so:

shorted encoder

I think @jremington has a point. You may need to look for a different encoder.