1

The plan is to control a NEMA 17 stepper motor with an endless rotary encoder.

First, I found a code to read the rotary encoders data on www.HowToMechatronics.com by Dejan Nedelkovski. It basically manipulates an integer and outputs it on the Serial Monitor.

Works almost perfectly except that it actually doubles the count of steps the rotary encoder does.
So a clockwise turn subtracts 1, outputs it in the SM, subtracts 1 and outputs it in the SM again. Same in the other direction with adding 1. Although being not a big deal for intended use of the rotary encoder data, I think this has something to do with the problem.

In the code down below you will find a modified version of the code from Mr. Nedelkovski. I added a few lines to move the motor a few steps in each direction.

Clockwise rotation works as I expected.
One step on the rotary equals subtract, output, movement, subtract, output, movement.

But counterclockwise rotation now adds 1 to the rotary position and subtracts it immediately, leaving the motor in moving back and forward.
But if I move the rotary very very slowly it will work.

Why are the rotary values jumping around once I added the lines?
Is there a way to solve this?

The code:

/*     Arduino Rotary Encoder Tutorial
 *      
 *  by Dejan Nedelkovski, www.HowToMechatronics.com
 *  
 *  to make it work with a stepper
 *  modified by me
 *  
 */

#include <Stepper.h>


#define outputA 52
#define outputB 50

int counter = 0; 
int dir = 0; 
int aState;
int aLastState;

Stepper myStepper(200, 47, 49, 51, 53);

void setup() { 

  pinMode (outputA,INPUT);
  pinMode (outputB,INPUT);

  Serial.begin (9600);
  // Reads the initial state of the outputA
  aLastState = digitalRead(outputA);

  // set the speed at 60 rpm:
  myStepper.setSpeed(60);

} 

void loop() { 

  aState = digitalRead(outputA); // Reads the "current" state of the outputA
  // If the previous and the current state of the outputA are different, that means a Pulse has occured

  if (aState != aLastState){     
    // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise

    if (digitalRead(outputB) != aState) { 

      counter ++;
      myStepper.step(50);

    } else {

      counter --;
      myStepper.step(-50);

    }

    Serial.print("Position: ");
    Serial.println(counter);
  } 

  aLastState = aState; // Updates the previous state of the outputA with the current state
}

Thanks for taking your time to read this and help me. I hope that a detailed explanation will help solving the problem.

3
  • 1
    most rotary handler only fire on the leading or trailing edge, you fire on both...
    – dandavis
    Aug 1, 2017 at 17:09
  • @dandavis Does it mean that the encoder is broken?
    – Norman
    Aug 2, 2017 at 15:38
  • 1
    no, it means you need to check not just that the gpios are different, but that they are 0 (or 1) and have changed, but not the other. something like if (digitalRead(outputB) != aState && aState==1)
    – dandavis
    Aug 2, 2017 at 22:47

1 Answer 1

1

Unfortunately, I couldn't find the issue.
BUT I used another approach which worked fine.

I used the ClickEncoder library.
This time, my rotary encoder triggered 4 events per notch.

After a little research I found out, that it's possible to set the steps per notch with this library. I used this code to initiate the ClickEncoder:

encoder = new ClickEncoder(A15, A14, A13, 4);

The first three arguments define the pins the encoder is connected to. The last argument sets the steps per notch. Now it works like a charm!

Full code:

#include <ClickEncoder.h>
#include <TimerOne.h>

ClickEncoder *encoder;
int16_t last, value;

void timerIsr() {
  encoder->service();
}

void setup() {
  Serial.begin(9600);
  encoder = new ClickEncoder(A15, A14, A13, 4);

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 

  last = -1;
}

void loop() {  
  value += encoder->getValue();

  if (value != last) {
    last = value;
    Serial.print("Encoder Value: ");
    Serial.println(value);
  }

  ClickEncoder::Button b = encoder->getButton();
  if (b != ClickEncoder::Open) {
    Serial.print("Button: ");
    #define VERBOSECASE(label) case label: Serial.println(#label); break;
    switch (b) {
      VERBOSECASE(ClickEncoder::Pressed);
      VERBOSECASE(ClickEncoder::Held)
      VERBOSECASE(ClickEncoder::Released)
      VERBOSECASE(ClickEncoder::Clicked)
      case ClickEncoder::DoubleClicked:
          Serial.println("ClickEncoder::DoubleClicked");
          encoder->setAccelerationEnabled(!encoder->getAccelerationEnabled());
          Serial.print("  Acceleration is ");
          Serial.println((encoder->getAccelerationEnabled()) ? "enabled" : "disabled");
        break;
    }
  }    
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.