Nonsensical numbers when negatively incrementing rotary encoder pulse count

Hi all,

I am currently using a optical incremental rotary encoder (S5-1024, US Digital) to detect the number of pulses and directionality using an Arduino Uno R3 and interrupts. However I noticed that when I negatively increment my pulse count the value sometimes increases to nonsensically high positive values, before switching back to plausible ones. First I thought it was about the calculations I was doing in my original script, but this also kept happening in the simplified sample below:

 /*
  Rotary Encoder Test
  Read pulses from an optical incremental rotary encoder to calculate rpm (unidirectional rotation only)
  Displays results on Serial Monitor
  Adapted from https://dronebotworkshop.com/rotary-encoders-arduino/
*/

// Encoder output pulse per rotation (change as required)
#define ENC_COUNT_REV 4096

// Encoder output to Arduino Interrupt pin
#define ENC_IN_A 2 
#define ENC_IN_B 3

// Pulse count from encoder
volatile long encoderValue = 0;

// Interval for measurements
int interval = 8;

// Counters for milliseconds during interval
long previousMillis = 0;
long currentMillis = 0;

// Variable for RPM measuerment
int rpm = 0;

void setup()
{
  // Setup Serial Monitor
  Serial.begin(9600); 
  
  // Set encoder as input with internal pullup  
  pinMode(ENC_IN_A, INPUT_PULLUP); 
  pinMode(ENC_IN_B, INPUT_PULLUP); 
  
  // Attach interrupt 
  attachInterrupt(digitalPinToInterrupt(ENC_IN_A), updateEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENC_IN_B), updateEncoder, CHANGE);
  
  // Setup initial values for timer
  previousMillis = millis();
}

void loop()
{
  // Show Pulses at specified frequency, if changes are detected
  currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;

    if (encoderValue != 0) {
      Serial.print(" PULSES: ");
      Serial.println(encoderValue);
    }

    encoderValue = 0;
  }
}

void updateEncoder()
{
  // Increment value for each pulse from encoder
  encoderValue--;
}

This sometimes gives outputs like this:
" PULSES: -30
PULSES: -30
PULSES: -30
PULSES: -30
PULSES: 65506
PULSES: -29
PULSES: -30
PULSES: -29
PULSES: -29
PULSES: -29"

When encoderValue-- is changed to encoderValue++ this phenomenon disappears, which led me to the assumption, that there is a problem with handling negative numbers? But somehow I doubt this should be the case. Also the numbers I am dealing with are not very high (counts of up to 400).

Does anybody know what might be the problem here?

Thanks for your help!

Yes, you are using a very poor algorithm.

You need a state machine to implement proper reading.

I find this library very good.
http://www.pjrc.com/teensy/td_libs_Encoder.html

1 Like

Thanks for the suggestion! Do you know why the problem only comes up when using encoderValue-- ?

Hi @jpd1337 ,

Welcome to the forum..

used an encoder for paddle control..
Play Uno Pong..

maybe it helps..

good luck.. ~q

1 Like

The code you posted is not the correct way to interpret pulses from a quadrature encoder. I also recommend using the Arduino encoder library.

In addition, you fail to protect the multi-byte encoder value from corruption by an interrupt while being accessed by the main program. Most people use an approach similar to this:

  noInterrupts();
  long value_copy = encoderValue;
  interrupts();
  Serial.println(value_copy);
4 Likes

That's important for modifying it too:

  noInterrupts();
  long value_copy = encoderValue;
  encoderValue = 0;
  interrupts();
  Serial.println(value_copy);
1 Like

Thanks for the suggestion! Including this in my loop got rid of the rarely occuring values >65000 when incrementing negatively. Although I am still wondering why this did not even happen when incrementing positively, I accept this as the solution. If you have any idea why only encoderValue-- was causing this, I would still be interested to know!

Small positive numbers fit in the word size of an AVR, while negative numbers take more bytes.

2 Likes

A long variable contains 4 bytes, and an Uno can only update one of these bytes in a single instruction.

When you increment upwards from zero, mostly only one of those bytes actually changes. The other bytes only rarely change. So if your code reads the variable in an unprotected way, chances are slim that anything bad will happen.

When decrementing just for the first time, from zero, all 4 bytes change, which will take at least 4 instructions on the Uno. So there is a much higher chance of your unprotected read happening during that vulnerable moment.

4 Likes