Accelstepper encoder nema 17 1:1

833 views
Skip to first unread message

Edgars Bellins

unread,
Mar 8, 2015, 8:34:24 PM3/8/15
to accels...@googlegroups.com
Hello
I would like make rotary encoder and nema 17 1:1, but have problem when encoder rotate fast lose increments. And motor not rotate with the same speed when turn slowly/normal then works good.
Encoder increments per revolution 2000
Stepper Nema 17 1.8 degrees
Sorry for my english :)

Sketch which i use:
////////////////////////////////////////
//Arduino + Optical Encoder + Stepper //
////////////////////////////////////////
// Adi Soffer 2013 //
// for more info visit //
// http://adisoffer.tumblr.com //
#include <AccelStepper.h>
//encoder/motor/driver setup
int easyDriverMicroSteps = 4; //2,4,8
int rotaryEncoderSteps = 75; //setps per rev on your encoder
int motorStepsPerRev = 200; //steps per rev on the motor
int MinPulseWidth = 50; //too low and the motor will stall, too high and it will slow it down
int easyDriverDirPin = 16;
int easyDriverStepPin = 15;
int enablePin = 17;
//Sleep Function - to diable ED when not active
long previousMillis = 0;
int sleepTimer = 5000;
int val;
int encoder0PinA = 3;
int encoder0PinB = 4;
int encoderValue = 0;
long lastencoderValue = 0;
int encoder0PinALast = LOW;
int n = LOW;
AccelStepper stepper(1, easyDriverStepPin, easyDriverDirPin);
void setup() {
pinMode (encoder0PinA,INPUT);
pinMode (encoder0PinB,INPUT);
pinMode(enablePin, OUTPUT);
stepper.setMinPulseWidth(MinPulseWidth); //may need to play with this, but I think this is good.
stepper.setMaxSpeed(50000);
stepper.setAcceleration(1000000000); //try 100, or 1000 and see if it works
stepper.setSpeed(50000); //play with this to see if it makes a diff (1 to 1000)
}
void loop() {
int stepsPerRotaryStep = (motorStepsPerRev * easyDriverMicroSteps) / rotaryEncoderSteps;
stepper.moveTo(encoderValue * stepsPerRotaryStep);
stepper.run();
if(encoderValue != lastencoderValue)
{
lastencoderValue = encoderValue;
digitalWrite (enablePin, LOW);
previousMillis = millis();
}
else
{
//Stepper sleep after 5sec of no data
unsigned long currentMillis = millis ();
if (currentMillis - previousMillis>sleepTimer)
digitalWrite (enablePin, HIGH);
}
n = digitalRead(encoder0PinA);
if ((encoder0PinALast == LOW) && (n == HIGH)) {
if (digitalRead(encoder0PinB) == LOW) {
encoderValue--;
}
else {
encoderValue++;
}
}
encoder0PinALast = n;
}

Mike McCauley

unread,
Mar 9, 2015, 2:58:45 AM3/9/15
to accels...@googlegroups.com
Hello,

Im not sure I understand your problem, but if you try to drive a stepper too
fast, it will lose steps. You can improve this by reducing the inertia of the
mechanical load, or by increasing the current sent to the stepper.

Cheers.


On Sunday, March 08, 2015 05:34:24 PM Edgars Bellins wrote:
> Hello
> I would like make rotary encoder and nema 17 1:1, but have problem when
> encoder rotate fast lose increments. And motor not rotate with the same
> speed when turn slowly/normal then works good.
> Encoder increments per revolution 2000
> Stepper Nema 17 1.8 degrees
> Sorry for my english :)
>
> Sketch which i use:
> > //////////////////////////////////////////Arduino + Optical Encoder +
> > Stepper ////////////////////////////////////////// // Adi Soffer 2013 ////
> > for more info visit //// http://adisoffer.tumblr.com // #include
> > <AccelStepper.h> //encoder/motor/driver setupint easyDriverMicroSteps =
> > 4; //2,4,8int rotaryEncoderSteps = 75; //setps per rev on your encoderint
> > motorStepsPerRev = 200; //steps per rev on the motor int MinPulseWidth =
> > 50; //too low and the motor will stall, too high and it will slow it down
> > int easyDriverDirPin = 16;int easyDriverStepPin = 15;int enablePin = 17;
> > //Sleep Function - to diable ED when not activelong previousMillis =
> > 0;int sleepTimer = 5000; int val; int encoder0PinA = 3;int encoder0PinB =
> > 4;int encoderValue = 0;long lastencoderValue = 0; int encoder0PinALast =
> > LOW;int n = LOW;AccelStepper stepper(1, easyDriverStepPin,
> > easyDriverDirPin); void setup() { pinMode (encoder0PinA,INPUT); pinMode
> > (encoder0PinB,INPUT); pinMode(enablePin, OUTPUT);
> > stepper.setMinPulseWidth(MinPulseWidth); //may need to play with this,
> > but I think this is good.
> > stepper.setMaxSpeed(50000); stepper.setAcceleration(1000000000); //try
> > 100, or 1000 and see if it works stepper.setSpeed(50000); //play with
> > this to see if it makes a diff (1 to 1000)} void loop() { int
> > stepsPerRotaryStep = (motorStepsPerRev * easyDriverMicroSteps) /
> > rotaryEncoderSteps; stepper.moveTo(encoderValue * stepsPerRotaryStep);
> > stepper.run(); if(encoderValue != lastencoderValue) { lastencoderValue =
> > encoderValue; digitalWrite (enablePin, LOW); previousMillis = millis(); }
> > else { //Stepper sleep after 5sec of no data unsigned long currentMillis
> > = millis (); if (currentMillis - previousMillis>sleepTimer) digitalWrite
> > (enablePin, HIGH); } n = digitalRead(encoder0PinA); if ((encoder0PinALast
> > == LOW) && (n == HIGH)) { if (digitalRead(encoder0PinB) == LOW) {
> > encoderValue--; } else { encoderValue++; } } encoder0PinALast = n;}

--
Mike McCauley VK4AMM mi...@airspayce.com
Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia
http://www.airspayce.com
Phone +61 7 5598-7474

gregor

unread,
Mar 9, 2015, 10:40:36 AM3/9/15
to accels...@googlegroups.com
Hi,

i'm not sure i understand your problem correctly, but given this:

Encoder increments per revolution 2000
and the fact you use digialRead() to read the state of the encoder my guess is that - at higher speeds - digitalRead() is too slow to pick up the signal from the encoder correctly.
on an arduino uno, digitalRead() can be called 220x / millisecond at best: http://jeelabs.org/2010/01/06/pin-io-performance/
in your loop code you have 1 digitalWrite() and at least 1 digitalRead(), limiting your code to about 10.000 digitalRead() calls per second (not considering interrupts, the second digitalRead() and the rest of the code)
if this really is the source of the problem you should be able to fix it using one of 2 methods:
direct port reads are a lot faster than using digitalRead() (see link above), here's a library for that: https://code.google.com/p/digitalwritefast/
external encoders, like here: http://astro.neutral.org/arduino/arduino-telescope-control.shtml section "What is the hardware?".

Edgars Bellins

unread,
Mar 9, 2015, 4:19:45 PM3/9/15
to accels...@googlegroups.com
Try this but without changes



////////////////////////////////////////
//Arduino + Optical Encoder + Stepper //
////////////////////////////////////////
 
//      Adi Soffer  2013       //
//    for more info visit      //

#include <AccelStepper.h>
#include <digitalWriteFast.h>


//encoder/motor/driver setup
int easyDriverMicroSteps = 16; //2,4,8
int rotaryEncoderSteps = 2000; //setps per rev on your encoder
int motorStepsPerRev = 200; //steps per rev on the motor




int easyDriverStepPin = 5;
int enablePin = 17;

//Sleep Function - to diable ED when not active
long previousMillis = 0;
int sleepTimer = 5000;

int val; 
int encoder0PinA = 2;
int encoder0PinB = 3;
int encoderValue = 0;
long lastencoderValue = 0;

int encoder0PinALast = LOW;
int n = LOW;
AccelStepper stepper(1, easyDriverStepPin);

void setup() { 
  pinMode (encoder0PinA,INPUT);
  pinMode (encoder0PinB,INPUT);
  pinMode(enablePin, OUTPUT);


  stepper.setMaxSpeed(200000);
  stepper.setAcceleration(1555555555); //try 100, or 1000 and see if it works
  stepper.setSpeed(200000); //play with this to see if it makes a diff (1 to 1000)

void loop() { 
  int stepsPerRotaryStep = (motorStepsPerRev * easyDriverMicroSteps) / rotaryEncoderSteps;
  stepper.moveTo(encoderValue * stepsPerRotaryStep);
  stepper.run();
  if(encoderValue != lastencoderValue)
  {
    lastencoderValue = encoderValue;
    digitalWrite (enablePin, LOW);
    previousMillis = millis();
  } 
  else
  {
    //Stepper sleep after 5sec of no data
    unsigned long currentMillis = millis ();
    if (currentMillis - previousMillis>sleepTimer)
      digitalWrite (enablePin, HIGH); 
  }
  n = digitalReadFast(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalReadFast(encoder0PinB) == LOW) {

gregor

unread,
Mar 9, 2015, 4:29:49 PM3/9/15
to accels...@googlegroups.com
hi,
the speed you set is very high. 200000 steps / second, are you sure this is correct?
you do not need to call setSpeed() when you use run(), setSpeed() is for using runSpeed().
you can substitute digitalWrite() with digialWriteFast().



On Monday, March 9, 2015 at 9:19:45 PM UTC+1, Edgars Bellins wrote:
Try this but without changes

gregor

unread,
Mar 9, 2015, 6:26:03 PM3/9/15
to accels...@googlegroups.com
another thing i noticed: when you use the digitalWriteFast library the pin numbers must be known at compile time, or else it will fall back to the standard digitalRead() / digitalWrite() functions.
so, either define the pin numbers like this:
#define encoder0PinA 2
#define encoder0PinB 3
int encoderValue = 0;
 or write
n = digitalReadFast(2);

good luck

Edgars Bellins

unread,
Mar 12, 2015, 2:50:25 PM3/12/15
to accels...@googlegroups.com
Thanks for help, i have one more question i need when encoder after turn stop longer for 5sec stepper make 500steps it is possible? :) I write where is sleep timer stepper.move(500); but not make 500 steps but turns all time

gregor

unread,
Mar 13, 2015, 12:36:35 PM3/13/15
to accels...@googlegroups.com
yes, it is possible.
below is a simple example for a timeout, 5 seconds after the stepper has stopped (and the last stepper destination was set using the encoder) the stepper will move 500 steps.
not tested.
bool destination_encoder;
bool
timeout_active;
long stop_time;


void loop() {
 
int stepsPerRotaryStep = (motorStepsPerRev * easyDriverMicroSteps) / rotaryEncoderSteps;
  stepper
.moveTo(encoderValue * stepsPerRotaryStep);

 
bool moving = stepper.run();       //moving will be true when the stepper is moving towards a target.

 
if(encoderValue != lastencoderValue)
 
{
    lastencoderValue
= encoderValue;
    digitalWrite
(enablePin, LOW);
    previousMillis
= millis();
    destination_encoder = true;    //destination set by encoder.
 
}
 
else

 
{
   
//Stepper sleep after 5sec of no data
   
unsigned long currentMillis = millis ();
   
if (currentMillis - previousMillis>sleepTimer)
      digitalWrite
(enablePin, HIGH);
 
}
  n
= digitalReadFast(encoder0PinA);
 
if ((encoder0PinALast == LOW) && (n == HIGH)) {
   
if (digitalReadFast(encoder0PinB) == LOW) {
      encoderValue
--;
   
}
   
else {
      encoderValue
++;
   
}
 
}
  encoder0PinALast
= n;


 
 
if (moving) timeout_active = false;

 
//memorize the time when the stepper has stopped moving.
 
if (!moving && !timeout_active)
 
{
   
timeout_active = true;
    stop_time
= millis();
 
}

  //when the stepper has stopped, the timeout
(in this case: 5000ms) has expired,
  //and the last destination was set by the encoder, set a new stepper target.
 
if (timeout_active && (millis() - stop_time() >= sleepTimer) && destination_encoder)
 
{   
    stepper.move(500); 
   
destination_encoder = false;  //destination set by timeout code.
  }
 
}

Edgars Bellins

unread,
Mar 13, 2015, 2:38:08 PM3/13/15
to accels...@googlegroups.com
Try this but show error 
Arduino: 1.6.1 (Windows 7), Board: "Arduino Uno"

arduino_varbuut_driiz.ino: In function 'void loop()':

arduino_varbuut_driiz.ino:62:5: error: 'destination_encoder' was not declared in this scope

arduino_varbuut_driiz.ino:85:15: error: 'timeout_active' was not declared in this scope

arduino_varbuut_driiz.ino:88:19: error: 'timeout_active' was not declared in this scope

arduino_varbuut_driiz.ino:91:5: error: 'stop_time' was not declared in this scope

arduino_varbuut_driiz.ino:96:7: error: 'timeout_active' was not declared in this scope

arduino_varbuut_driiz.ino:96:47: error: 'stop_time' was not declared in this scope

arduino_varbuut_driiz.ino:96:67: error: 'destination_encoder' was not declared in this scope

Error compiling.

  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.

 

Sketch:
////////////////////////////////////////
//Arduino + Optical Encoder + Stepper //
////////////////////////////////////////
 
//      Adi Soffer  2013       //
//    for more info visit      //

#include <AccelStepper.h>

//encoder/motor/driver setup
int easyDriverMicroSteps = 16; //2,4,8
int rotaryEncoderSteps = 2800; //setps per rev on your encoder
int motorStepsPerRev = 200; //steps per rev on the motor


int easyDriverDirPin = 16;
int easyDriverStepPin = 5;
int enablePin = 9;

//Sleep Function - to diable ED when not active
long previousMillis = 0;
int sleepTimer = 5000;

int val; 
int encoder0PinA = 2;
int encoder0PinB = 3;
int encoderValue = 0;
long lastencoderValue = 0;

int encoder0PinALast = LOW;
int n = LOW;
AccelStepper stepper(1, easyDriverStepPin, easyDriverDirPin);

void setup() { 
  pinMode (encoder0PinA,INPUT);
  pinMode (encoder0PinB,INPUT);
  pinMode(enablePin, OUTPUT);


  stepper.setMaxSpeed(50000);
  stepper.setAcceleration(1000000000); //try 100, or 1000 and see if it works



bool destination_encoder;
bool timeout_active;
long stop_time;

}
void loop() { 
  int stepsPerRotaryStep = (motorStepsPerRev * easyDriverMicroSteps) / rotaryEncoderSteps;
  stepper.moveTo(encoderValue * stepsPerRotaryStep);

  bool moving = stepper.run();       //moving will be true when the stepper is moving towards a target.

  if(encoderValue != lastencoderValue)
  {
    lastencoderValue = encoderValue;
    digitalWrite (enablePin, LOW);
    previousMillis = millis();
    destination_encoder = true;    //destination set by encoder.
  } 
  else

  {
    //Stepper sleep after 5sec of no data
    unsigned long currentMillis = millis ();
    if (currentMillis - previousMillis>sleepTimer)
      digitalWrite (enablePin, HIGH); 
  }
  n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) {
      encoderValue--;
    } 
    else {
      encoderValue++;
    }
  } 
  encoder0PinALast = n;


  

gregor

unread,
Mar 13, 2015, 2:42:38 PM3/13/15
to accels...@googlegroups.com
this compiles:


 
if (timeout_active && (millis() - stop_time >= sleepTimer) && destination_encoder)

 
{    
    stepper
.move(500);  
    destination_encoder
= false;  //destination set by timeout code.
 
}  
}
the variables
bool destination_encoder;
bool timeout_active;
long stop_time;
need to be defined in global scope (not in the setup() function)
also there was a typo, the bold () are wong:

Edgars Bellins

unread,
Mar 13, 2015, 3:28:36 PM3/13/15
to accels...@googlegroups.com
No more error, but stepper not make steps after encoder stopping. I think timer switching on when encoder turns, because when i put timer 2000 stepper not turn 1:1 when put 15000 then turns ok. Thank you
////////////////////////////////////////
//Arduino + Optical Encoder + Stepper //
////////////////////////////////////////
 
//      Adi Soffer  2013       //
//    for more info visit      //

#include <AccelStepper.h>

//encoder/motor/driver setup
int easyDriverMicroSteps = 16; //2,4,8
int rotaryEncoderSteps = 2800; //setps per rev on your encoder
int motorStepsPerRev = 200; //steps per rev on the motor


int easyDriverDirPin = 16;
int easyDriverStepPin = 5;
int enablePin = 12;

//Sleep Function - to diable ED when not active
long previousMillis = 0;
int sleepTimer = 2000;

int val; 
int encoder0PinA = 2;
int encoder0PinB = 3;
int encoderValue = 0;
long lastencoderValue = 0;

bool destination_encoder;
bool timeout_active;
long stop_time;


int encoder0PinALast = LOW;
int n = LOW;
AccelStepper stepper(1, easyDriverStepPin, easyDriverDirPin);

void setup() { 
  pinMode (encoder0PinA,INPUT);
  pinMode (encoder0PinB,INPUT);
  pinMode(enablePin, OUTPUT);


  stepper.setMaxSpeed(50000);
  stepper.setAcceleration(1000000000); //try 100, or 1000 and see if it works





}
void loop() { 
  int stepsPerRotaryStep = (motorStepsPerRev * easyDriverMicroSteps) / rotaryEncoderSteps;
  stepper.moveTo(encoderValue * stepsPerRotaryStep);

  bool moving = stepper.run();       //moving will be true when the stepper is moving towards a target.

  if(encoderValue != lastencoderValue)
  {
    lastencoderValue = encoderValue;
    digitalWrite (enablePin, LOW);
    previousMillis = millis();
    destination_encoder = true;    //destination set by encoder.
  } 
  else

  {
    //Stepper sleep after 5sec of no data
    unsigned long currentMillis = millis ();
    if (currentMillis - previousMillis>sleepTimer)
      digitalWrite (enablePin, HIGH); 
  }
  n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) {
      encoderValue--;
    } 
    else {
      encoderValue++;
    }
  } 
  encoder0PinALast = n;


  

gregor

unread,
Mar 13, 2015, 4:17:47 PM3/13/15
to accels...@googlegroups.com
i think i see the problem.
 your code switches off the enable pin of you A4988 driver 2000 ms after the enoder has been changed the last time. my code does not switch the enable pin back on, but when you set a longer sleepTimer value (like 15000) my code has plenty of time to finish before your code switches off the enable pin.
i think this might work:
 if (timeout_active && (millis() - stop_time >= sleepTimer) && destination_encoder)
 
{    
    stepper
.move(500);  
    destination_encoder
= false;  //destination set by timeout code.

    previousMillis
= millis();    
    digitalWrite
(enablePin, LOW);
 
}


Edgars Bellins

unread,
Mar 13, 2015, 4:28:24 PM3/13/15
to accels...@googlegroups.com
I think with enable pin is ok, because stepper turn when turn encoder only not move after i stop turn encoder. 

Edgars Bellins

unread,
Mar 13, 2015, 4:43:10 PM3/13/15
to accels...@googlegroups.com
Maybe need change tihs place 
if (timeout_active && (millis() - stop_time >= sleepTimer) && destination_encoder)
because i put sleep timer 5000 
turn encoder and after 5 sec when turn encoder stepper turns but slowly
Reply all
Reply to author
Forward
0 new messages