Stepper with EasyDriver and POT defined delay

HI everyone, I am gaining a really good understanding of how to drive steppers here (I think).

My goal is drive a dolly along a track with variable speed using a Potentionmeter and the EasyDiver, its all wired and can run the stepper in both forward and reverse at any speed right now - If I hard Code it!

What I would like is to control the delay between steps using a Potentiomenter. WIth the Sketch below I am able to control the delay with the POT nicely, although I am having some trouble with the math - at its slowest I would like a 500th of second between steps and at its fastest almost no delay.

I am also not able to change the direction of the stepper, how would I reverse the direction with the second if statement?

If someone could take a look at it and let me know If im going in the right direction, I would really appreciate your help.

//////////// Drive Stepper with Pot at 1600 steps per rev using the EasyDriver
//////////// Stepper full rotation is 200steps / 1600 with easy driver

/// Pot defines delay between steps while running at 1600 steps per revolution
// 0-512 > forward with 0 being the shortest delay  and 510 being the longest
// < 513-1023 reverse with 512 having the longest delay and 1023 shortest delay between steps
// shortest delay 1000ms
// longest delay 100ms


///////// What I need to code
//////// var for delay based on POT reading 100ms-1000ms
//////// int stepDelay = pot value converted to micro seconds
//  int sensorReading = analogRead(A0);
//  int knobReading = map(sensorReading, 0, 1023, 0, 100);
///////// convert POT value to microseconds
// the conversion math???

#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

#define DIR_PIN 2
#define STEP_PIN 3

void setup() {
  lcd.begin(16, 2);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
}

void loop(){ 
  // read the sensor value:
  int sensorReading = analogRead(A0);
  int knobReading = map(sensorReading, 0, 1023, 10, 1000);
  int stepDelay = knobReading * 10;
      lcd.setCursor(0, 0);

      if (knobReading < 500) {
      // display pot value
          lcd.setCursor(0, 0);
          lcd.print("Fwd:" );
          lcd.setCursor(5, 0);
          lcd.print(knobReading);
      // display delay
          lcd.setCursor(0, 1);
          lcd.print("Delay:" );
          lcd.print(stepDelay);
       // drive the stepper FORWARD using stepDelay
          digitalWrite(STEP_PIN, HIGH);
          delayMicroseconds(stepDelay); 

          digitalWrite(STEP_PIN, LOW);
          delayMicroseconds(stepDelay);
          
            }else{
    if (knobReading > 500) {
      // display pot value
          lcd.setCursor(0, 0);
          lcd.print("Rev:" );
          lcd.setCursor(5, 0);
          lcd.print(knobReading);
      // display delay
          lcd.setCursor(0, 1);
          lcd.print("Delay:" );
          lcd.print(stepDelay);
          // drive the stepper REVERSE using stepDelay
          digitalWrite(STEP_PIN, HIGH);
          delayMicroseconds(stepDelay);
          
          digitalWrite(STEP_PIN, LOW);
          delayMicroseconds(stepDelay);
    }
  }
}

What I would like is to control the delay between steps using a Potentiomenter. WIth the Sketch below I am able to control the delay with the POT nicely, although I am having some trouble with the math - at its slowest I would like a 500th of second between steps and at its fastest almost no delay.

You are trying to map the potentiometer reading prematurely, in my opinion. You should first determine if the value is above or below the midpoint. If it is above, you want to map with the from range being 512 to 1023, and the to range being the largest delay to the smallest delay:
int delayInMicros = map(sensorReading, 512, 1023, 2000, 1);

if the potentiometer reading is below the midpoint, you want to map in the other direction:
int delayInMicros = map(sensorReading, 511, 0, 2000, 1);

What this will do is allow you to set the direction, based on above or below the midpoint, and get a delay that gets smaller as potentiometer gets farther from the midpoint. So, the stepper moves the fastest in one direction at one extreme, and moves slower and slower as the potentiometer gets closer to the center. As it passes center, the motor begins to pick up speed, going in the other direction.

Thanks Paul, I didnt think of coding it like that.

I did however figure out the direction isue - see below :slight_smile: You will see that when the pot passed the mid point, in th eif statement the dir pin is set to either low or high.
Now I would just like have a wider range of delay time. From very very slow to very fast, any ideas on this?

#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

#define DIR_PIN 2
#define STEP_PIN 3

void setup() {
  lcd.begin(16, 2);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
}

void loop(){ 
  // read the sensor value:
  int sensorReading = analogRead(A0);
  int knobReading = map(sensorReading, 0, 1023, 10, 1000);
  //int stepDelay = knobReading * 10;
  int stepDelay = (knobReading * 1.76);
  //(val * 1.76) + 600
      lcd.setCursor(0, 0);

      if (knobReading < 500) {
      // display pot value
          lcd.setCursor(0, 0);
          lcd.print("Fwd:" );
          lcd.setCursor(5, 0);
          lcd.print(knobReading);
      // display delay
          lcd.setCursor(0, 1);
          lcd.print("Delay:" );
          lcd.print(stepDelay);
       // drive the stepper FORWARD using stepDelay
          digitalWrite(DIR_PIN, HIGH);
          digitalWrite(STEP_PIN, HIGH);
          delayMicroseconds(stepDelay); 

          digitalWrite(STEP_PIN, LOW);
          delayMicroseconds(stepDelay);
          
            }else{
    if (knobReading > 500) {
      // display pot value
          lcd.setCursor(0, 0);
          lcd.print("Rev:" );
          lcd.setCursor(5, 0);
          lcd.print(knobReading);
      // display delay
          lcd.setCursor(0, 1);
          lcd.print("Delay:" );
          lcd.print(stepDelay);
          // drive the stepper REVERSE using stepDelay
          digitalWrite(DIR_PIN, LOW);
          digitalWrite(STEP_PIN, HIGH);
          delayMicroseconds(stepDelay);
          
          digitalWrite(STEP_PIN, LOW);
          delayMicroseconds(stepDelay);
    }
  }
}

Now I would just like have a wider range of delay time. From very very slow to very fast, any ideas on this?

The upper limit you mentioned before, 2000 microseconds between steps, is one the end of the range. No delay is the other end. Since you can't drive a stepper with no delay between steps, the practical limit will be a lot higher than 1 microsecond. You use those limits in the map command to get the speed that you want, recognizing that the upper limit to the speed is NOT the code. It is the nature of the stepper motor.

I understand that there HAS to be a delay, I get it :)

What i would like is a minimul delay between steps at its fastest and 2000ms (2sec) between steps at its slowest, does that make sense?

So if i map the pot from 10 - 2000 and us that value as the delay this should give me 10ms - 2000ms correct?

FYI: The project runs a camera dolly along a track, and id like the speed of tracking to range from 3 seconds per 1600 steps to .1 of a second per 1600 steps. Resulting in super slow tracking to handheld tracking speeds

So if i map the pot from 10 - 2000 and us that value as the delay this should give me 10ms - 2000ms correct?

If the to range is 10 to 2000, and you use the output value in a call to delay(), then yes you will get 10 millisecond to 2 second delays.

1600 steps in 3 seconds is roughly 500 steps per second. The delay at that speed is no where near 2 seconds. It is 2 milliseconds or 2000 microseconds. You need to use the delayMicroseconds() function for that.

1600 steps in 0.1 seconds is 16000 steps per second, and may not be an achievable goal. That would be 80 revolutions, if there are 200 steps per revolution.

Hmmm ive omiited direction based on pot location, just to test range of speed using the below.
Note: that my stepDelay ranges from 10-200 Microseconds now - based on the knobReading.

It is working but I am getting nowhere near the speeds I was getting when I had defined the number of steps per rotation as 1600, which I was the easyDriver sets my 200step motor to. Where could I make that statement so that the stepper is always running at 1600 steps per rotation? and then define my own delay?

void loop(){ 
  // read the sensor value:
  int sensorReading = analogRead(A0);
  //int knobReading = map(sensorReading, 0, 1023, 10, 2000);
  
 int knobReading = map(sensorReading, 0, 1023, 10, 100);
 // knobReading = constrain(knobReading, 10, 200);
  //int stepDelay = knobReading * 10;
  
  int stepDelay = knobReading;
  //(val * 1.76) + 600
  lcd.setCursor(0, 0);

     // if (knobReading < 55) {
      // display pot value
          lcd.setCursor(0, 0);
          lcd.print("KR Fwd:" );
          lcd.setCursor(7, 0);
          lcd.print(knobReading);
      // display delay
          lcd.setCursor(0, 1);
          lcd.print("Delay:" );
          lcd.print(stepDelay);
       // drive the stepper FORWARD using stepDelay
          digitalWrite(DIR_PIN, HIGH);
          digitalWrite(STEP_PIN, HIGH);
          delay(stepDelay); 

          digitalWrite(STEP_PIN, LOW);
          delayMicroseconds(stepDelay);
          digitalWrite(DIR_PIN, HIGH);
          digitalWrite(STEP_PIN, HIGH);
          delay(stepDelay); 

          digitalWrite(STEP_PIN, LOW);
          delayMicroseconds(stepDelay);

I believe that both of those delays should be delayMicroseconds. No?

Ohhhhh right you are :) thank you ! This would explain alot...

So now, it is running at a perfect min delay 2000 Microseconds, but at the other end it is still not going fast enough... It also sounds like it is not running at the 1600 spr that the easy driver sets it to... I could be wrong, it just does not sounds as smooth as it did when I had 1600 steps defined

thoughts? should be declating the steps as a constant, and the delay as a floating varibale somehow?

You have a lot of stuff going on between steps - reading the potentiometer, printing (a lot of stuff) to the lcd, etc. Perhaps you should consider only reading the potentiometer and writing to the LCD every second or so.

80 Rev/Sec = 4800RPM, A bit much for a stepper motor. It is pushing it on a brushless servo motor.

PaulS:
You have a lot of stuff going on between steps - reading the potentiometer, printing (a lot of stuff) to the lcd, etc. Perhaps you should consider only reading the potentiometer and writing to the LCD every second or so.

paul you were correct on this too - when I comment out all the LCD code it runs much faster and smoother, is there a way I can still use the LCD without taxing the arduino so much? I just need to display the direction and speed on start up and then maybe have it go to sleep - thoughts?

Below I am using the POT to define the delay and a switch for direction, where and how could insert the LCD to display and then sleep?

#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

//////////////////////////////////////////////////////////////////
//©2011 bildr
//Released under the MIT License - Please reuse change and share
//Using the easy stepper with your arduino
//use rotate and/or rotateDeg to controll stepper motor
//speed is any number from .01 -> 1 with 1 being fastest - 
//Slower Speed == Stronger movement
/////////////////////////////////////////////////////////////////

#define DIR_PIN 2
#define STEP_PIN 3
#define switchPin 4

void setup() {

  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode (switchPin, INPUT);
} 

void loop(){ 
     if (digitalRead(switchPin)){ //if the switch is HIGH, rotate clockwise
         rotateDeg(-1, 1); // forward    
}
      else { // if the swtich is LOW, rotate counter clockiwise
          rotateDeg(1, 1); // reverse 
      }
}

void rotate(int steps, float speed){
  //rotate a specific number of microsteps (8 microsteps per step) - (negitive for reverse movement)
  //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  int dir = (steps > 0)? HIGH:LOW;
  steps = abs(steps);

  digitalWrite(DIR_PIN,dir); 
  int sensorReading = analogRead(A0);  
  int knobReading = map(sensorReading, 0, 1023, 0, 100);
  knobReading = constrain(knobReading, 0, 100);

  float usDelay = (knobReading/speed) * 70  ;

  for(int i=0; i < steps; i++){
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(usDelay); 

  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(usDelay);

  }
} 

void rotateDeg(float deg, float speed){
  //rotate a specific number of degrees (negitive for reverse movement)
  //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  int dir = (deg > 0)? HIGH:LOW;
  digitalWrite(DIR_PIN,dir); 
  int sensorReading = analogRead(A0);  
  int knobReading = map(sensorReading, 0, 1023, 0, 100);
  knobReading = constrain(knobReading, 0, 100);

  int steps = abs(deg)*(1/0.225);
  float usDelay = (knobReading/speed) * 70  ;

  for(int i=0; i < steps; i++){
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(usDelay); 

  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(usDelay);
  }
}
  int sensorReading = analogRead(A0);

sensorReading is OK as a name. If will contain the value read from a sensor.

  int knobReading = map(sensorReading, 0, 1023, 0, 100);

knobReading is a lousy name. You’ve mapped the sensor reading into something else, but that something else is not a knob reading. Knobs are on doors and drawers, so you can open them. Give this variable a more meaningful name.

  knobReading = constrain(knobReading, 0, 100);

The input to the map function can not fall outside the range 0 to 1023 (the from range), so the output can not fall outside the range 0 to 100 (the to range). Therefore, the constrain function call is unnecessary, and slows things down.

  float usDelay = (knobReading/speed) * 70  ;

  for(int i=0; i < steps; i++){
  digitalWrite(STEP_PIN, HIGH);
  delayMicroseconds(usDelay); 

  digitalWrite(STEP_PIN, LOW);
  delayMicroseconds(usDelay);

  }

Three things, here. Proper indenting (and the { on a new line) would make the extent of the for statement more obvious. The extra blank line at the end is unnecessary, and confusing.

But, more important is that delayMicroseconds expects an unsigned long as input, not a float. You could hardly expect to delay 4.7 microseconds.

s there a way I can still use the LCD without taxing the arduino so much?

Writing to the LCD is not taxing the Arduino. It is the delay caused by the actual hardware taking time to display the data that causes the lcd.print() function to block, thus slowing things down. There are a couple of things you could do. One would be to change the type of display to a serial LCD, where the blocking slows another processor down, not the one driving the stepper motor. The other is to look at the blink without delay example, to see how to use millis() and a previous wrote-to-the-lcd time, to determine if it is time to write to the LCD again. Each time you write, there will be a delay, but by not writing so often, perhaps the delays can be tolerated.

If the direction and speed only need to be written once, does this imply that they only need to be read once, too? Perhaps the thing to do is to keep track of the data that was written last time, and only write when there is a change.