Trying to read a motor integrated optical incremental encoder data

Hi Everyone,

I am new to Arduino and am looking to read correctly the input from a optical incremental encoder that comes attached to the motor. I use this closed loop stepper motor for reading the integrated encoder from it.

The encoder has 4 outputs EA+, EA-, EB+ and EB-. I am making use of the EA+ and EB+ outputs as read on multiple output forums for reading the output.

Below is the code I am trying to run where I define the home position(using an end stop) of the slider attached on a ball screw which is connected to the above mentioned motor. Then I make it move 2200 steps and then back to the 0 position. I continue this infinitely just for testing. However I don't get an equivalent number from the encoder suggesting 2200 steps have been moved. The encoder resolution is 4000 Counts per Revolution and the motor is rated at 200 steps per revolution (no micro stepping being used). So theoretically this means 1 motor step is equal to 20 encoder counts. However, I am not getting the right output numbers. Your help in this regard will be highly appreciated.

#include <digitalWriteFast.h>

// Pin Definitions
#define dirPin 9          // Direction pin for the DM542
#define pulsePin 6       // Pulse pin for the DM542
#define limitSwitchPin 5  // Limit switch pin
#define ENCODER_A_PIN 2  // EA+
#define ENCODER_B_PIN 3   // EB+
//#define LeftEncoderIsReversed

volatile float count = 0;
float grades=0; /*variable to store converted value*/

// Motor and screw parameters
const float screwPitch = 5.0;        // Ball screw pitch in mm/revolution
const int stepsPerRevolution = 200; // Motor steps per revolution
const int microstepping = 1;        // Microstepping factor (e.g., 8x, 16x)
const int stepsPerMM = (stepsPerRevolution * microstepping) / screwPitch;

// Variables for position tracking
long currentPosition = -1;    // Current position in steps (-1 indicates uncalibrated)
long targetPosition = 0;      // Target position in steps
//volatile long EncoderCurrentPosition = 0; // Current position from encoder feedback

// Define constants based on encoder and motor specs
#define ENCODER_PPR 1000       // Counts per revolution (from encoder datasheet)

// Calculate derived constants
#define ENCODER_COUNTS_PER_REV (ENCODER_PPR * 4) // Quadrature decoding
#define STEPS_PER_REV (stepsPerRevolution * microstepping)
#define ENCODER_COUNTS_PER_STEP (ENCODER_COUNTS_PER_REV / STEPS_PER_REV)

// Loop count
int counter = 1;

// Motor control parameters
unsigned int stepDelay = 500;      // Microseconds between pulses (adjust for speed)
const unsigned long holdTime = 15000; // Time to hold at target position in ms (15 seconds)
const unsigned long HomeHoldTime = 7000; // Time to hold at home position in ms (7 seconds)

void setup() {
  // Set pin modes
  pinMode(pulsePin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(limitSwitchPin, INPUT_PULLUP); // Enable internal pull-up for limit switch

  // Encoder pins
  pinMode(ENCODER_A_PIN, INPUT); 
  pinMode(ENCODER_A_PIN, INPUT); 
// encoder pin on interrupt 3 (pin 20)
  attachInterrupt(digitalPinToInterrupt(ENCODER_A_PIN), doA, CHANGE);
// encoder pin on interrupt 2 (pin 21)
  attachInterrupt(digitalPinToInterrupt(ENCODER_B_PIN), doB, CHANGE);  

  // Initialize outputs
  digitalWrite(pulsePin, LOW);
  digitalWrite(dirPin, LOW);

  // Debugging
  Serial.begin(9600);
  Serial.println("Stepper Motor with Calibration and Position Control Initialized.");

  // Perform calibration
  calibrateToHome();
  delay(HomeHoldTime);
}

void loop() {
  // Test run: Move to 100 mm (10 cm), hold, and return to home
  static bool taskCompleted = false;
  if (!taskCompleted) {
    moveToTargetPosition(55); // Move to 55 mm which is 2200 motor steps as the pitch of the ball screw is 5 mm
    //currentPosition += ENCODER_COUNTS_PER_STEP;
    // Confirm the final position
    Serial.print("Current Encoder Position: ");
    Serial.println(count/20.0);
    delay(holdTime);           // Wait at the target position

    moveToTargetPosition(0);   // Return to home
    //currentPosition -= ENCODER_COUNTS_PER_STEP;
    // Confirm the final position
    Serial.print("Current Encoder Position: ");
    Serial.println(count/20.0);
    delay(HomeHoldTime);               // Wait at Home position
    
    //taskCompleted = true;      // Ensure it doesn't repeat indefinitely
    Serial.println("Task " + String(counter) + " completed");
    counter = counter + 1;
  }
}

// Function to calibrate to the home position (limit switch)
void calibrateToHome() {
  Serial.println("Calibrating to home position...");

  // Move towards the limit switch until it is pressed
  digitalWrite(dirPin, LOW); // Set direction towards home
  while (digitalRead(limitSwitchPin) == HIGH) {
    // Generate step pulses
    digitalWrite(pulsePin, HIGH);
    delayMicroseconds(stepDelay);
    digitalWrite(pulsePin, LOW);
    delayMicroseconds(stepDelay);
  }

  // Stop motor and set current position to 0
  currentPosition = 0;
  count = 0;
  //EncoderCurrentPosition = 0;
  //_LeftEncoderTicks = 0;
  Serial.println("Home position reached. Position set to 0 both Arduino and Encoder.");
}

// Function to move to a specific target position in mm
void moveToTargetPosition(float positionInMM) {
  // Convert the target position to steps
  targetPosition = positionInMM * stepsPerMM;

  // Determine direction
  digitalWrite(dirPin, targetPosition > currentPosition ? HIGH : LOW);

  // Calculate the number of steps to move
  long stepsToMove = abs(targetPosition - currentPosition);

  // Generate pulses to move the motor
  for (long i = 0; i < stepsToMove; i++) {
    digitalWrite(pulsePin, HIGH);
    delayMicroseconds(stepDelay);
    digitalWrite(pulsePin, LOW);
    delayMicroseconds(stepDelay);
  }

  // Update the current position
  currentPosition = targetPosition;

  Serial.print("Moved to position (steps from Arduino): ");
  Serial.println(currentPosition); 

}

// Interrupt service routine for encoder A signal
void doA(){
  // look for a low-to-high on channel A
  if (digitalRead(ENCODER_A_PIN) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(ENCODER_B_PIN) == LOW) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(ENCODER_B_PIN) == HIGH) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
 
}
void doB(){
  // look for a low-to-high on channel B
  if (digitalRead(ENCODER_B_PIN) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(ENCODER_A_PIN) == HIGH) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(ENCODER_A_PIN) == LOW) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }

}

The output I get is as follows:

Calibrating to home position...
Stepper Motor with Calibration and Position Control Initialized.
Calibrating to home position...
Home position reached. Position set to 0 both Arduino and Encoder.
Moved to position (steps from Arduino): 2200
Current Encoder Position: -1126.25
Moved to position (steps from Arduino): 0
Current Encoder Position: -4004.35
Task 1 completed
Moved to position (steps from Arduino): 2200
Current Encoder Position: -5374.00
Moved to position (steps from Arduino): 0
Current Encoder Position: -8360.30
Task 2 completed
Moved to position (steps from Arduino): 2200
Current Encoder Position: -9593.75

These number don't make sense to me even if we consider the absolute value.

Did you try the sample sketches with the library first?

The posted code violates several guidelines for the use of interrupts, and is unlikely to work as expected.

I recommend to use the Arduino encoder library instead, as it uses advanced coding techniques and takes care of those details.

You cannot jump from standstill to 1000 steps per second (300 RPM) instantly. The motor will lose steps. Try 5000 for stepDelay.

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