Optimize code to read an optical incremental encoder

Hi everyone, hope you're doing well. I'm new to Arduino and got the homework to read an optical incremental encoder. I already saw many videos and did a sample code in 1X mode (just checking the rising edges of Channel A).

The encoder is a TAISS E38S6-600-24G, which has 600 PulsesPerRevolution.

The problem here is I'm afraid Arduino will miss some data because the process will be very slow. Many of the data types I used were int's or double's. Although I did this to understand the logic behind position and velocity estimation, I'm looking for a way to optimize the code.

#include <digitalWriteFast.h>         

#define pinA 2
#define pinB 3

  // Position and Direction
int16_t firstCounter = 0;                   // First counter
volatile int16_t secondCounter = 0;         // Second counter
int16_t previousSecondCount = 0;            // Previous count of the secondCounter (previous loop)

int8_t currentDirection = 0;              
int8_t previousDirection = 0;             

const int pulsesPerRev = 600;               // Pulses per Revolution [PPR](Check YOUR encoder)

  // Velocity
uint32_t lastPulseTime = 0;                 // Time of the last pulse (in microseconds)
uint32_t periodTime = 0;                    // Period between two pulses
double RPM = 0;                             // Estimated velocity in RPM

volatile bool pulseDetected = false; 

double convert_Pulses2Degs (int x) {
   float degs = x * 360 / pulsesPerRev;
   return degs;
}

void setup() {
  Serial.begin(614400);
  
  // Config pins as inputs
  pinModeFast(pinA, INPUT_PULLUP);
  pinModeFast(pinB, INPUT_PULLUP);
  
  // FLAG to trigger
  enableInterrupt(pinA, checkDirection, RISING);
}

void loop() {

  // Counts Periods when flag/interrupt is activated
  if (pulseDetected){
    pulseDetected = false;
    uint32_t currentPulseTime = micros();
    if (lastPulseTime != 0) {
      periodTime = currentPulseTime - lastPulseTime;
    }
    lastPulseTime = currentPulseTime;
  }

  // Position and Direction
  // 
  // Pulses 2 Degs
  degsA = convert_Pulses2Degs(secondCounter);
  Serial.println((String)"Pulses: " + secondCounter + ", Degrees: " + degsA);

  // Update direction (with the change in the Count of Pulses)
  if (secondCounter > previousSecondCount) {
    currentDirection = 1;
    Serial.println("Direction: CCW");
  } else if (secondCounter < previousSecondCount) {
    currentDirection = -1;
    Serial.println("Direction: CW");
  } else {
    currentDirection = 0;
    Serial.println("Direction: Stopped");
  }

  // FIRST COUNTER:
      // Counts until direction changes and reset the counter
  if (currentDirection != previousDirection) {
    Serial.println((String)"CHANGE!!! Pulses: " + firstCounter + ", Degrees: " + convert_Pulses2Degs(firstCounter));
    firstCounter = 0;
  }

      // Counts only if there's movement
  if (currentDirection != 0) {
    firstCounter++;
  }

  // Update the previous count and direction
  previousSecondCount = secondCounter;
  previousDirection = currentDirection;

  // Velocity
  //
  // Estimates RPM from counting periods
  if (periodTime > 0) {
      RPM = (1.0 / periodTime) * 1000000.0 * 60.0 * currentDirection / pulsesPerRev;
      Serial.println((String)"Estimated Velocity: " + RPM);
  }

  /*
  Serial.print(firstCounter);
  Serial.print("\t");
  Serial.print(secondCounter);
  Serial.print("\t");
  */
}

void checkDirection() {
  pulseDetected = true;
  if (digitalReadFast(pinB) == HIGH) {
    secondCounter++;                        
  } else {
    secondCounter--;
  }
}

Which Arduino are you using?

Since that is a standard quadrature encoder, have you tried using the Arduino Encoder library, which can use external interrupts on both channels?

1 Like

Hi. I'm using Arduino Uno. I donn't know there is a Arduino Encoder library. I will check it. Do you think two interrupts will give better performance than the sample code? I really want to optimize that haha.

  • What turns the encoder ? :thinking:

Yes, the encoder library can use the two external interrupts on the Uno.

For higher speed in the calculations, use a faster processor. The Uno is quite slow and old!

What are you looking for when you write about "optimize" the code? Based on your comments, you want to optimize the speed of the code. Is this correct?

Thanks a lot. My university only have Uno's. I will see if I can buy another board for my own testing. Hey, and thanks for the library! I will definitely check it out

Exactly. Speed and if it's possible, storage. Lighter and faster!

What turns the encoder ? :thinking:

For now, I move it by hand just to check if the monitor is receiving data. Later, i will use a servomotor.

The problem I see is by not using an interrupt on the second encoder pin, your reading of that pin will very rarely catch the pin at exactly the time is goes low.

  • At first thought, I would try to just pole the encoder checking for a change in state.
    The Sketch would be written in a non-blocking fashion.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.