Adjusting speed of stepper motor with potentiometer

Hi, I'm new to arduino and have been trying out how to control the stepper motor speed using a potentiometer. I found a video in youtube that is pretty nifty.

How To Make Stepper Motor Speed Controller using A4988 Stepper Motor Driver | Arduino Stepper Motor.

The setup is supposed to control a stepper motor's speed. It has 3 buttons, clockwise turning, stop/free mode, anticlockwise turning. It also has a potentiometer that controls the motor's speed using map() and delayMicroseconds.

I followed his code but instead I used an i2c 16x02 LCD. My problem is, the stepper motor does not change its speed whenever I turn the potentiometer, but it does turn off if the potentiometer is at the minimum. I used a 1.2 A nema 17 motor and it turns just fine. I suspect that it might be how the stepper motor is controlled using map() and delayMicroseconds or the wiring i have since I used an i2c lcd display instead of the regular one. I also want to adjust it so that I can go to higher speeds like 600rpm above. I tried reading but could not, for the life of me, get it to work. What might be causing the issue?

Here is the code I have modified slightly for i2c use from the video:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define potentiometer  A0  //10k Variable Resistor
#define bt_F A1 // Clockwise Button
#define bt_S A2 // Stop Button
#define bt_B A3 // Anticlockwise Button

#define dirPin  8  //8Pin  of Arduino--Direction of stepper motor driver 
#define stepPin 9  //9Pin  of Arduino--Step of stepper motor driver 
#define enPin   10 //10Pin of Arduino--Enabled of stepper motor driver  

int read_ADC;
int Speed_LCD, Speed_Delay;
int Mode=1, flag=0;

void setup() { // put your setup code here, to run once
lcd.init();
lcd.backlight();
lcd.clear();
pinMode(potentiometer, INPUT); // declare potentiometer as input 

pinMode(bt_F, INPUT_PULLUP); // declare bt_F as input
pinMode(bt_S, INPUT_PULLUP); // declare bt_S as input
pinMode(bt_B, INPUT_PULLUP); // declare bt_B as input

pinMode(dirPin,  OUTPUT); // declare as output for Direction of stepper motor driver 
pinMode(stepPin, OUTPUT); // declare as output for Step of stepper motor driver 
pinMode(enPin,   OUTPUT); // declare as output for Enabled of stepper motor driver 
  
lcd.begin(16,2);  
lcd.setCursor(0,0);
lcd.print("Start Up");
lcd.setCursor(0,1);
lcd.print("Page");
delay(2000); // Waiting for a while
lcd.clear();
}

void loop() { 

read_ADC = analogRead(potentiometer); // read analogue to digital value 0 to 1023 
Speed_Delay = map(read_ADC, 0, 1023, 5000, 10); //value map for Microstep resolution Delay
Speed_LCD = map(read_ADC, 0, 1023, 0, 100); //value map to Display on the LCD

lcd.setCursor(0,0);
lcd.print("   Speed: ");
lcd.print(Speed_LCD); 
lcd.print("%  ");

if(digitalRead (bt_F) == 0){Mode = 2; digitalWrite(enPin, LOW);} //For Clockwise

if(digitalRead (bt_S) == 0){ //For Stop
if(flag==0){flag=1;
 if(Mode>1)Mode=1; 
      else{Mode=!Mode; 
      if(Mode==0)digitalWrite(enPin, HIGH);
            else digitalWrite(enPin, LOW);
      }
delay(100);
 }
}else{flag=0;} 

if(digitalRead (bt_B) == 0){Mode = 3; digitalWrite(enPin, LOW);} //For Anticlockwise

lcd.setCursor(0,1);

     if(Mode==0)lcd.print("      Free      ");
else if(Mode==1)lcd.print("      Stop      ");
else if(Mode==2)lcd.print("    Clockwise   ");
else if(Mode==3)lcd.print("  Anticlockwise ");

if(Speed_LCD>0 && Mode>1){ 
   
     if(Mode==2)digitalWrite(dirPin, LOW);// Stepper motor rotates CW (Clockwise)
           else digitalWrite(dirPin, HIGH);// Stepper motor rotates CCW (Anticlockwise)

digitalWrite(stepPin, HIGH);
delayMicroseconds(Speed_Delay);
digitalWrite(stepPin, LOW);
delayMicroseconds(Speed_Delay);
}  

}

and here is my schematic diagram:

for reference, here is the original diagram and reference code:

#include <LiquidCrystal.h>
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

#define potentiometer  A0  //10k Variable Resistor
#define bt_F A1 // Clockwise Button
#define bt_S A2 // Stop Button
#define bt_B A3 // Anticlockwise Button

#define dirPin  8  //8Pin  of Arduino--Direction of stepper motor driver 
#define stepPin 9  //9Pin  of Arduino--Step of stepper motor driver 
#define enPin   10 //10Pin of Arduino--Enabled of stepper motor driver  

int read_ADC;
int Speed_LCD, Speed_Delay;
int Mode=1, flag=0;

void setup() { // put your setup code here, to run once
  
pinMode(potentiometer, INPUT); // declare potentiometer as input 

pinMode(bt_F, INPUT_PULLUP); // declare bt_F as input
pinMode(bt_S, INPUT_PULLUP); // declare bt_S as input
pinMode(bt_B, INPUT_PULLUP); // declare bt_B as input

pinMode(dirPin,  OUTPUT); // declare as output for Direction of stepper motor driver 
pinMode(stepPin, OUTPUT); // declare as output for Step of stepper motor driver 
pinMode(enPin,   OUTPUT); // declare as output for Enabled of stepper motor driver 
  
lcd.begin(16,2);  
lcd.setCursor(0,0);
lcd.print(" WELCOME To  My ");
lcd.setCursor(0,1);
lcd.print("YouTube  Channel");
delay(2000); // Waiting for a while
lcd.clear();
}

void loop() { 

read_ADC = analogRead(potentiometer); // read analogue to digital value 0 to 1023 
Speed_Delay = map(read_ADC, 0, 1023, 5000, 10); //value map for Microstep resolution Delay
Speed_LCD = map(read_ADC, 0, 1023, 0, 100); //value map to Display on the LCD

lcd.setCursor(0,0);
lcd.print("   Speed: ");
lcd.print(Speed_LCD); 
lcd.print("%  ");

if(digitalRead (bt_F) == 0){Mode = 2; digitalWrite(enPin, LOW);} //For Clockwise

if(digitalRead (bt_S) == 0){ //For Stop
if(flag==0){flag=1;
 if(Mode>1)Mode=1; 
      else{Mode=!Mode; 
      if(Mode==0)digitalWrite(enPin, HIGH);
            else digitalWrite(enPin, LOW);
      }
delay(100);
 }
}else{flag=0;} 

if(digitalRead (bt_B) == 0){Mode = 3; digitalWrite(enPin, LOW);} //For Anticlockwise

lcd.setCursor(0,1);

     if(Mode==0)lcd.print("      Free      ");
else if(Mode==1)lcd.print("      Stop      ");
else if(Mode==2)lcd.print("    Clockwise   ");
else if(Mode==3)lcd.print("  Anticlockwise ");

if(Speed_LCD>0 && Mode>1){ 
   
     if(Mode==2)digitalWrite(dirPin, LOW);// Stepper motor rotates CW (Clockwise)
           else digitalWrite(dirPin, HIGH);// Stepper motor rotates CCW (Anticlockwise)

digitalWrite(stepPin, HIGH);
delayMicroseconds(Speed_Delay);
digitalWrite(stepPin, LOW);
delayMicroseconds(Speed_Delay);
}  

}

I found a demo vid for the same setup:

demo vid

Is this schematic from WOKWI?
If so, to save us the trouble of assembling it again in the simulator, please provide the link to your project on WOKWI.

Did you set up this project and measure the speed?
Or are you only seeing the speed in Wokwi?

PS: In the simulator you can even use the Arduino as a source for the step motor, but if you set up the project you have to use a separate source to power the motor.

DIRs are backwards. You have 0 = CW, 1 = CCW.

DIR	Direction input: 0=counterclockwise, 1=clockwise

Speed of motor never changes - 1% and 100% rotate at same RPM

diagram.json for wokwi.com

{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": -4.8, "left": -0.5, "attrs": {} },
    {
      "type": "wokwi-pushbutton",
      "id": "btn1",
      "top": 92.6,
      "left": 163.2,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn2",
      "top": 140.6,
      "left": 163.2,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn3",
      "top": 188.6,
      "left": 163.2,
      "attrs": { "color": "green" }
    },
    { "type": "wokwi-potentiometer", "id": "pot1", "top": -68.5, "left": 172.6, "attrs": {} },
    { "type": "wokwi-a4988", "id": "drv1", "top": -129.6, "left": 14.4, "attrs": {} },
    {
      "type": "wokwi-stepper-motor",
      "id": "stepper1",
      "top": -211.21,
      "left": 89.82,
      "attrs": { "size": "8", "arrow": "white" }
    },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -172.04, "left": 67.2, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -48, "left": 76.2, "attrs": {} },
    {
      "type": "wokwi-lcd1602",
      "id": "lcd1",
      "top": 25.6,
      "left": 255.2,
      "attrs": { "pins": "i2c" }
    },
    {
      "type": "wokwi-text",
      "id": "legendservo1",
      "top": 96,
      "left": 115.2,
      "attrs": { "text": "CW" }
    },
    {
      "type": "wokwi-text",
      "id": "legendservo2",
      "top": 192,
      "left": 96,
      "attrs": { "text": "CCW" }
    },
    {
      "type": "wokwi-text",
      "id": "legendservo3",
      "top": 144,
      "left": 96,
      "attrs": { "text": "STOP" }
    }
  ],
  "connections": [
    [ "nano:GND.1", "btn1:1.l", "black", [ "v0" ] ],
    [ "nano:GND.1", "btn2:1.l", "black", [ "v0" ] ],
    [ "nano:GND.1", "btn3:1.l", "black", [ "v0" ] ],
    [ "nano:A1", "btn1:2.l", "green", [ "v0" ] ],
    [ "nano:A2", "btn2:2.l", "green", [ "v0" ] ],
    [ "nano:A3", "btn3:2.l", "green", [ "v0" ] ],
    [ "pot1:GND", "nano:GND.3", "black", [ "v0" ] ],
    [ "pot1:SIG", "nano:A0", "green", [ "v67.2", "h-163.6" ] ],
    [ "pot1:VCC", "nano:5V.2", "red", [ "v0" ] ],
    [ "nano:9", "drv1:STEP", "green", [ "v-19.2", "h-48", "v-19.2" ] ],
    [ "nano:8", "drv1:DIR", "green", [ "v-28.8", "h-48", "v-9.6" ] ],
    [ "drv1:SLEEP", "drv1:RESET", "green", [ "h-19.2", "v-9.6" ] ],
    [ "nano:10", "drv1:ENABLE", "green", [ "v-9.6", "h-48", "v-96" ] ],
    [ "gnd1:GND", "drv1:GND.1", "black", [ "v0" ] ],
    [ "gnd1:GND", "drv1:GND.2", "black", [ "v0" ] ],
    [ "vcc1:VCC", "drv1:VMOT", "red", [ "v0" ] ],
    [ "vcc1:VCC", "drv1:VDD", "red", [ "v0" ] ],
    [ "drv1:2B", "stepper1:A+", "green", [ "h0" ] ],
    [ "drv1:2A", "stepper1:A-", "green", [ "h0" ] ],
    [ "drv1:1A", "stepper1:B-", "green", [ "h0" ] ],
    [ "drv1:1B", "stepper1:B+", "green", [ "h0" ] ],
    [ "lcd1:GND", "nano:GND.3", "black", [ "h-57.6", "v-38.4" ] ],
    [ "lcd1:VCC", "nano:5V.2", "red", [ "h-38.4", "v9.7" ] ],
    [ "lcd1:SDA", "nano:A4", "green", [ "h0" ] ],
    [ "lcd1:SCL", "nano:A5", "green", [ "h0" ] ]
  ],
  "dependencies": {}
}

if only simulator , change these lines and test again:

Speed_Delay = map(read_ADC, 0, 1023, 5000, 10); //value map for Microstep resolution Delay

to:

Speed_Delay = map(read_ADC, 0, 1023, 200, 10); //value map for Microstep resolution Delay

and

digitalWrite(stepPin, HIGH);
delayMicroseconds(Speed_Delay);
digitalWrite(stepPin, LOW);
delayMicroseconds(Speed_Delay);

to

digitalWrite(stepPin, HIGH);
delay(Speed_Delay);
digitalWrite(stepPin, LOW);
delay(Speed_Delay);

Hi, I apologize for the late response. Yes it is in WOKWI, here's the link: https://wokwi.com/projects/416227317085342721

I did have my setup the same as the one in WOKWI, for some reason, the potentiometer does not change the speed of the motor but will turn it off when I turn it to zero.

And yes, I have a source to power both the a4988 driver and the arduino.

DIRs are backwards. You have 0 = CW, 1 = CCW.

I don't understand what you mean here. Can you please enlighten me? My code has CW = 2 and CCW = 3, that's for the push buttons.

Speed of motor never changes - 1% and 100% rotate at same RPM

also, how do I make it so that the motor changes speed? I'm kinda lost in the code.

Hi, this seems to have worked but the speed of rotation is a little too slow for me. What if I want to increase the maximum RPM of the motor, how could I do it?

The motor driver appears to be one A4988, having a DIRECTION pin. Pulling the DIRECTION pin LOW makes the driver command the motor counter clockwise. Pulling the DIRECTION pin HIGH makes the driver command the motor clockwise.

The motor driver appears to be one A4988, having a DIRECTION pin. Pulling the DIRECTION pin LOW makes the driver command the motor counter clockwise. Pulling the DIRECTION pin HIGH makes the driver command the motor clockwise.

Oh, I see. The setup turns in the right direction as the buttons indicate so I did not notice it. Thanks.

Your wiring is backwards. Sorry... I was thinking "DC" motor... I know you are using stepper.

Hi, this seems to have worked but the speed of rotation is a little too slow for me. What if I want to increase the maximum RPM of the motor, how could I do it?

I have found a code from somewhere (link below for wokwi where I tried the code) which seems to be able to speed up a motor. I want to apply this to the setup and I want to be able to change the directions and stop it with a button. I noticed this one is pretty similar to the structure of the current code I have but I don't know how to integrate it to the setup.

https://wokwi.com/projects/416120810001786881

ops something was wrong wit my answer.

Just copy the code to a new file in the IDE

You understand that if you put a value of 10 in "SpeedDelay", that will result in a step rate of 50000 steps per second or (with a 200 step per rev motor), 15000 RPM? NOT likely.

I would set "speedDelay" to 3000 (100 RPM) and do something like:

digitalWrite(stepPin, HIGH);
delayMicroseconds(20);
digitalWrite(stepPin, LOW);
delayMicroseconds(Speed_Delay - 20);

And work your way up from there.

My post was meant for @neutzche ,sorry. :grimacing:

What if I want to increase the maximum RPM of the motor, how could I do it?

Try this code:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define potentiometer  A0  //10k Variable Resistor
#define bt_F A1 // Clockwise Button
#define bt_S A2 // Stop Button
#define bt_B A3 // Anticlockwise Button

#define dirPin  8  //8Pin  of Arduino--Direction of stepper motor driver 
#define stepPin 9  //9Pin  of Arduino--Step of stepper motor driver 
#define enPin   10 //10Pin of Arduino--Enabled of stepper motor driver  

int read_ADC;
long Speed_LCD;
long Speed_Delay;
int Mode = 1, flag = 0;
//-------------------------------------------------------------------------
long myMap(long x, long in_min, long in_max, long out_min, long out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
//-------------------------------------------------------------------------
void setup() { // put your setup code here, to run once
  //Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  pinMode(potentiometer, INPUT); // declare potentiometer as input

  pinMode(bt_F, INPUT_PULLUP); // declare bt_F as input
  pinMode(bt_S, INPUT_PULLUP); // declare bt_S as input
  pinMode(bt_B, INPUT_PULLUP); // declare bt_B as input

  pinMode(dirPin,  OUTPUT); // declare as output for Direction of stepper motor driver
  pinMode(stepPin, OUTPUT); // declare as output for Step of stepper motor driver
  pinMode(enPin,   OUTPUT); // declare as output for Enabled of stepper motor driver

  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Start Up");
  lcd.setCursor(0, 1);
  lcd.print("Page");
  delay(2000); // Waiting for a while
  lcd.clear();
}
//-------------------------------------------------------------------------
void loop() {

  read_ADC = analogRead(potentiometer); // read analogue to digital value 0 to 1023
  Speed_Delay = myMap(read_ADC, 0, 1023, 50000, 10); //value map for Microstep resolution Delay
  Speed_LCD = myMap(read_ADC, 0, 1023, 0, 100); //value map to Display on the LCD

  lcd.setCursor(0, 0);
  lcd.print("   Speed: ");
  lcd.print(Speed_LCD);
  lcd.print("%  ");

  if (digitalRead (bt_F) == 0) {
    Mode = 2;  //For Clockwise
    digitalWrite(enPin, LOW);
  }

  if (digitalRead (bt_S) == 0) { //For Stop
    if (flag == 0) {
      flag = 1;
      if (Mode > 1)Mode = 1;
      else {
        Mode = !Mode;
        if (Mode == 0)digitalWrite(enPin, HIGH);
        else digitalWrite(enPin, LOW);
      }
      delay(100);
    }
  } else {
    flag = 0;
  }

  if (digitalRead (bt_B) == 0) {
    Mode = 3;  //For Anticlockwise
    digitalWrite(enPin, LOW);
  }

  lcd.setCursor(0, 1);

  if (Mode == 0)lcd.print("      Free      ");
  else if (Mode == 1)lcd.print("      Stop      ");
  else if (Mode == 2)lcd.print("    Clockwise   ");
  else if (Mode == 3)lcd.print("  Anticlockwise ");

  if (Speed_LCD > 0 && Mode > 1) {

    if (Mode == 2)digitalWrite(dirPin, LOW); // Stepper motor rotates CW (Clockwise)
    else digitalWrite(dirPin, HIGH);// Stepper motor rotates CCW (Anticlockwise)
    //Serial.print(read_ADC);
    //Serial.print(" = ");
    //Serial.println(Speed_Delay);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(Speed_Delay);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(Speed_Delay);
  }

}

If you want speed, spend less time doing the UI bits. Most of the time, the pot and the buttons aren't being updated and don't need recalculating and re-drawing. You can improve performance significantly by rate-limiting the UI updates.

Quick 4-line rate-limiting change:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define potentiometer  A0  //10k Variable Resistor
#define bt_F A1 // Clockwise Button
#define bt_S A2 // Stop Button
#define bt_B A3 // Anticlockwise Button

#define dirPin  8  //8Pin  of Arduino--Direction of stepper motor driver 
#define stepPin 9  //9Pin  of Arduino--Step of stepper motor driver 
#define enPin   10 //10Pin of Arduino--Enabled of stepper motor driver  

int read_ADC;
long Speed_LCD;
long Speed_Delay;
int Mode = 1, flag = 0;
//-------------------------------------------------------------------------
long myMap(long x, long in_min, long in_max, long out_min, long out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
//-------------------------------------------------------------------------
void setup() { // put your setup code here, to run once
  //Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  pinMode(potentiometer, INPUT); // declare potentiometer as input

  pinMode(bt_F, INPUT_PULLUP); // declare bt_F as input
  pinMode(bt_S, INPUT_PULLUP); // declare bt_S as input
  pinMode(bt_B, INPUT_PULLUP); // declare bt_B as input

  pinMode(dirPin,  OUTPUT); // declare as output for Direction of stepper motor driver
  pinMode(stepPin, OUTPUT); // declare as output for Step of stepper motor driver
  pinMode(enPin,   OUTPUT); // declare as output for Enabled of stepper motor driver

  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Start Up");
  lcd.setCursor(0, 1);
  lcd.print("Page");
  delay(2000); // Waiting for a while
  lcd.clear();
}
//-------------------------------------------------------------------------
void loop() {

  static uint32_t lastUI = 0;
  if (millis() - lastUI > 100) {
    lastUI = millis();

    read_ADC = analogRead(potentiometer); // read analogue to digital value 0 to 1023
    Speed_Delay = myMap(read_ADC, 0, 1023, 50000, 10); //value map for Microstep resolution Delay
    Speed_LCD = myMap(read_ADC, 0, 1023, 0, 100); //value map to Display on the LCD

    lcd.setCursor(0, 0);
    lcd.print("   Speed: ");
    lcd.print(Speed_LCD);
    lcd.print("%  ");

    if (digitalRead (bt_F) == 0) {
      Mode = 2;  //For Clockwise
      digitalWrite(enPin, LOW);
    }

    if (digitalRead (bt_S) == 0) { //For Stop
      if (flag == 0) {
        flag = 1;
        if (Mode > 1)Mode = 1;
        else {
          Mode = !Mode;
          if (Mode == 0)digitalWrite(enPin, HIGH);
          else digitalWrite(enPin, LOW);
        }
        delay(100);
      }
    } else {
      flag = 0;
    }

    if (digitalRead (bt_B) == 0) {
      Mode = 3;  //For Anticlockwise
      digitalWrite(enPin, LOW);
    }

    lcd.setCursor(0, 1);

    if (Mode == 0)lcd.print("      Free      ");
    else if (Mode == 1)lcd.print("      Stop      ");
    else if (Mode == 2)lcd.print("    Clockwise   ");
    else if (Mode == 3)lcd.print("  Anticlockwise ");
  }
  if (Speed_LCD > 0 && Mode > 1) {

    if (Mode == 2)digitalWrite(dirPin, LOW); // Stepper motor rotates CW (Clockwise)
    else digitalWrite(dirPin, HIGH);// Stepper motor rotates CCW (Anticlockwise)
    //Serial.print(read_ADC);
    //Serial.print(" = ");
    //Serial.println(Speed_Delay);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(Speed_Delay);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(Speed_Delay);
  }
}

600 RPM at 200 steps/rev needs only 600*200/60=2000Hz stepping, or 500us/step.

The recalculation and redrawing take significantly longer than the 2*10us=20us of delay controlled by the pot. With the above code, you might increase the lower end of the mapping to 250us for 600RPM.

(This code drops can drop right into the #6 Wokwi simulation setup. You can put a false in the test to compare rate limited vs not-rate-limited performance)

Hi, thanks for your response. I tried but it would not work, I'm probably missing something conceptually. Will experiment on it later after I get home. Thank you!

Hi, thank you for this, I'll try it once I get home and update you later.