Help with driving a stepper motor, motor will lock up and oscillate at higher speeds

Am using an arduino nano to power a stepper motor.

Am running in full step mode with a drv8825 controller.

Is this pushing the limits for this arduino to handle the pulses? At higher speeds it looses stability and will just lock up and oscillate/buzz loudly.

Im reading about mid band resonance potentially causing issues, but most of this is way over my head sorry.

Do you see any issues with the code?

I found setting the driver to full step mode performed better and lowered the number of cycles for the arduino.

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <AccelStepper.h>




const int dirPin = 8;

const int stepPin = 9;

const int enablePin = 10;

const int clkPin = 2; 

const int dtPin = 3;  

const int swPin = 4; 




const int motorStepsPerRev = 200; // Set Driver to full Step

const float totalRatio = 15.0; 




int drumRPM = 40;      

int runMinutes = 30;   

bool isRunning = false;

bool isStopping = false;

unsigned long startTime = 0;

int menuMode = 0; 




int lastClkState;

unsigned long lastButtonPress = 0;

float targetStepsPerSec = 0;




LiquidCrystal_I2C lcd(0x27, 16, 2); 

AccelStepper stepper(1, stepPin, dirPin);




void setup() {

  pinMode(enablePin, OUTPUT);

  digitalWrite(enablePin, HIGH); 

  pinMode(clkPin, INPUT);

  pinMode(dtPin, INPUT);

  pinMode(swPin, INPUT_PULLUP);

  

  lcd.init();

  lcd.backlight();

  lastClkState = digitalRead(clkPin);

  updateDisplay();

}




void loop() {

  if (!isRunning) {

    handleMenu();

  } else {

    handleMotorLogic();

  }

}




void handleMenu() {

  int currentClkState = digitalRead(clkPin);

  if (currentClkState != lastClkState && currentClkState == LOW) {

    if (digitalRead(dtPin) != currentClkState) {

      if (menuMode == 0) drumRPM = min(drumRPM + 1, 90); 

      if (menuMode == 1) runMinutes = min(runMinutes + 5, 120);

    } else {

      if (menuMode == 0) drumRPM = max(drumRPM - 1, 40); 

      if (menuMode == 1) runMinutes = max(runMinutes - 5, 5);

    }

    updateDisplay();

  }

  lastClkState = currentClkState;




  if (digitalRead(swPin) == LOW && (millis() - lastButtonPress > 400)) {

    lastButtonPress = millis();

    menuMode++;

    if (menuMode > 2) menuMode = 0;

    updateDisplay();

    

    if (menuMode == 0 && !isRunning) { // Cycled through GO back to 0

       // Do nothing, just cycle menu

    } else if (menuMode == 2) {

       // START triggered from GO!

       delay(200);

       startSequence();

    }

  }

}




void startSequence() {




  targetStepsPerSec = (float(drumRPM) * totalRatio * motorStepsPerRev) / 60.0;




  stepper.setMaxSpeed(targetStepsPerSec);

  stepper.setAcceleration(2000);   // add acceleration back

  stepper.move(1000000000);        // large move target




  lcd.clear();

  lcd.setCursor(0,0); lcd.print("RUNNING...");

  lcd.setCursor(0,1); lcd.print("DRUM RPM: "); lcd.print(drumRPM);




  digitalWrite(enablePin, LOW);




  isRunning = true;

  startTime = millis();

  lastButtonPress = millis();

}




void handleMotorLogic() {




  // Stop button

  if (digitalRead(swPin) == LOW && (millis() - lastButtonPress > 800)) {

    lastButtonPress = millis();

    finishStop();

    return;

  }




  // Timer stop

  if ((millis() - startTime) / 60000 >= (unsigned long)runMinutes) {

    finishStop();

    return;

  }




stepper.run();  // constant speed mode

}




void initiateStop() {

  isStopping = true;

  stepper.setSpeed(stepper.speed() * 0.5); 

}




void finishStop() {




  stepper.setSpeed(0);

  digitalWrite(enablePin, HIGH); 




  isRunning = false;

  isStopping = false;

  menuMode = 0;




  lcd.clear();

  lcd.setCursor(2, 0); lcd.print("CYCLE DONE!");

  delay(1000);




  updateDisplay();

}




void updateDisplay() {

  lcd.clear();

  lcd.setCursor(0, 0); lcd.print(menuMode == 0 ? ">Drum RPM: " : " Drum RPM: "); lcd.print(drumRPM);

  lcd.setCursor(0, 1); lcd.print(menuMode == 1 ? ">Runtime:  " : " Runtime:  "); lcd.print(runMinutes); lcd.print("m");

  if (menuMode == 2) { lcd.setCursor(11, 1); lcd.print(">GO!"); }

}

The power for the stepper motor should NOT from the Nano, rather through the Stepper Motor Controller from an external power source. Show a wiring diagram.

Your sketch is very hard to read and understand because of that many empty lines and that few comments.
But as far is I see, you are using Accelstepper wrong- especially when stopping. You cannot stop by simply not calling .run() and setting speed to 0 by setSpeed(). For Accelstepper the move is not finished, and when calling .run() again it will again drive the stepper with the full speed of setMaxSpeed() without any acceleration - which will not work.

That sounds like the motor is starving for power and or the lines are picking up EMI. A annotated schematic as requested by @xfpd will help us help you. Also post links to technical links on the hardware devices.

how fast? how many pulses/sec?

sorry, poor choice of words, i meant to say drive the stepper, the DRV8825 is powered by my lab power supply right now, running at 24V. There is no way the 5V supply for my arduino could power this thing thats for sure!

Anyway, im trying to work out if its a logic issue, limitations of the driver and/or arduino.
Dont have a wiring diagram as such, its just on my breadboard at the moment, but essentially the DIR, STEP and EN pins of the driver go directly into the arduino, along with the i2C LCD and the rotary encoder, i have a 100UF cap across the power rail for the driver too.
M0,M1 and M2 are all grounded to allow full step mode.

Wiring to arduino as follows:

DRV8825 pins:

EN —> D10

STP—> D9

DIR—> D8

I2C for LCD:

SDA —>A4

SCL —>A5

KY040 rotary encoder

CLK —> D2

DT —> D3

SW —> D4

I also played around with this library last night, didnt give me any better results.

Right now im considering I push the voltage a bit higher on the stepper motor, it seems i can safely go up to 36V?
Model no of motor is 23KM-K036-P5V
Cant find the exact datasheet, but here is this for reference, im guessing its rated around 1.5A, but i could be wrong.

its geared down 15:1 and at 70rpm(where issues begin) it should be pulsing around 3500 pulses a second if im not mistaken.

I had issues with it ramping down causing the same lockup and oscillating. I found it easier to just have the unit come to an instant stop and shut off the driver through the enable pin.

It did cross my mind, right now on my bench, its all on a breadboard with jumper cables and crocodile leads to the motor, could the length be an issue here? They are about 700mm long.

are you giving it time to accelerate? to complete a step before the next step

yes, it slowly ramps up, have tried slower ramp up times, but still presents the same issue, ideally I would like it to run stable up to 80RPM, but i can get away at 70RPM, but seems to be the maximum it handles, i let it run for a bit and it locked up and oscillated for me on one occasion while testing.
I should also note it presents the same issue whether a load is applied or not.

what is the max speed of the motor?

I have absolutely no idea sorry, that datasheet i linked earlier does suggest that its capable of 10,000 pulses going by some of the graphs shown, but torque drops significantly at that point.

My exact model is not quoted in that datasheet, but shows similar models in that series.

Edit:

Have tweaked voltages and has made some improvement, currently running at 90rpm, not sure if its actually achieving this speed or not, as i cant tell much difference between 80 and 90rpm, but it is working.

I tried 30V and the motor oscillates on start up, however seems that the sweet spot is somewhere between 25 and 30V, i backed it down to around 26-27V and it appears stable, but I need more tests.

I also noticed an issue when if i restart the motor at the same rpm i last had it running on, that the motor will oscillate, i have to adjust it, even 1rpm difference than before will allow me to restart the motor with no issues.

But that's definitely not how AccelStepper works.

You are tinkering with the symptoms without eliminating the actual cause: the incorrect use of AccelStepper.

[Edit] Be aware, that AccelStepper can create a max of ~4000 Steps per second on a classic Nano. See the AccelStepper documentation:

Performance
The fastest motor speed that can be reliably supported is about 4000 steps per second at a clock frequency of 16 MHz on Arduino such as Uno etc

Would I be better off using a different library such as ContinuousStepper?

Looks like 80rpm is the maximum i can get with this unit then, so thats ok.

Should voltage matter, in other words should this run normal at 24v?

I find it interesting that depending that higher voltages will make it stable, but if i go too high i get the same issues again.

Anyway, ive replaced the setspeed(0) function with the stop() function
the motor stops fine, but oscillates upon restart no matter what speed.

I also see that with the load removed, that this oscillates at speeds over 50rpm or so, meanwhile it starts up fine with the load applied.

Is there anything wrong you see here with the initial startup sequence?

That is what produces the torque with your controller board. Higher voltage means the current pulses can be sent faster. Use the maximum your controller can handle.

OK, so nothing in code that would cause this?

will 36V be ok on my drv8825?
As i said earlier, it seems i was having more issues with a higher voltage, but didnt go above 36v.

Here are some changes ive made, i think ive got the stop sequence fixed now, however it locks up while ramping down, it sounds a little rough for a second or two while it ramps up, but it appears to run normal.

I can make it come to an abrupt stop if i remove the run() function, but as far as i can tell, thats not the correct implementation.

Other things im exploring:

No specified pulse width

Is current set too low? Is 1-5-1.6A too low for this motor? (i still dont know the correct specs)

Vref on the driver is set close to 0.8 at the moment

Here is what im now using:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

const int dirPin = 8, stepPin = 9, enablePin = 10, clkPin = 2, dtPin = 3, swPin = 4;
const int motorStepsPerRev = 200; 
const float totalRatio = 15.0; 

int drumRPM = 40, runMinutes = 30, menuMode = 0; 
bool isRunning = false;
unsigned long lastButtonPress = 0; 

LiquidCrystal_I2C lcd(0x27, 16, 2); 
AccelStepper stepper(1, stepPin, dirPin);

void setup() { 
  pinMode(enablePin, OUTPUT); 
  digitalWrite(enablePin, HIGH); 
  pinMode(clkPin, INPUT); 
  pinMode(dtPin, INPUT); 
  pinMode(swPin, INPUT_PULLUP);

  lcd.init(); lcd.backlight(); 
  updateDisplay(); 
  
  stepper.setAcceleration(500); // Gentle ramp to break inertia
}

void loop() { 
  if (!isRunning) { 
    handleMenu(); 
  } else { 
    executeRun(); // Dedicated loop for running
  } 
}

void handleMenu() { 
  static int lastClk;
  int currentClk = digitalRead(clkPin);
  if (currentClk != lastClk && currentClk == LOW) {
    bool up = digitalRead(dtPin) != currentClk;
    if (menuMode == 0) drumRPM = constrain(drumRPM + (up ? 5 : -5), 10, 80); 
    if (menuMode == 1) runMinutes = constrain(runMinutes + (up ? 5 : -5), 5, 120); 
    updateDisplay(); 
  } 
  lastClk = currentClk;

  if (digitalRead(swPin) == LOW && (millis() - lastButtonPress > 400)) { 
    lastButtonPress = millis(); 
    if (++menuMode > 2) menuMode = 0; 
    updateDisplay();
    if (menuMode == 2) {
      delay(400);
      isRunning = true;
    }
  } 
}

void executeRun() {
  float targetStepsPerSec = (float(drumRPM) * totalRatio * motorStepsPerRev) / 60.0;
  
  digitalWrite(enablePin, LOW);
  delay(100); 

  stepper.setMaxSpeed(targetStepsPerSec); 
  stepper.setCurrentPosition(0);
  stepper.moveTo(2000000000); 
  
  unsigned long startMillis = millis();
  unsigned long runTimeMillis = (unsigned long)runMinutes * 60000;
  
  lcd.clear();
  lcd.print("RUNNING...");

  // Dedicated high-priority loop to prevent pulse jitter
  while (isRunning) {
    stepper.run();

    // Check for Stop Button or Timer
    if ((digitalRead(swPin) == LOW && (millis() - lastButtonPress > 1000)) || 
        (millis() - startMillis >= runTimeMillis)) {
      
      lastButtonPress = millis();
      lcd.clear(); lcd.print("STOPPING...");
      
      // Correct Standard Stop Sequence
      stepper.stop(); 
      while (stepper.isRunning()) {
        stepper.run(); // Ramp down to zero speed, if i remove this it stops abruptly
      }
      
      isRunning = false; 
        }
  }

  // Cleanup
  digitalWrite(enablePin, HIGH);
  menuMode = 0;
  lcd.clear(); lcd.print("CYCLE DONE!");
  delay(1500);
  updateDisplay();
}

void updateDisplay() { 
  lcd.clear(); 
  lcd.setCursor(0, 0); 
  lcd.print(menuMode == 0 ? ">RPM: " : " RPM: "); lcd.print(drumRPM); 
  lcd.setCursor(0, 1); 
  lcd.print(menuMode == 1 ? ">Min: " : " Min: "); lcd.print(runMinutes); 
  if(menuMode == 2) { lcd.setCursor(12, 1); lcd.print("GO!"); } 
}

80 RPM from a 15 : 1 gearbox would require the motor to run at 1200 rpm or 4000 steps per second, and require acceleration in the range of 200 ~ 400 steps per second squared. I've not been able to find a datasheet for the model number you posted (23KM-K036-P5V). Maybe that motor requires more current than DRV8825 can supply?

im not 100% sure, but I was afraid of destroying the driver if i cranked it up, so far it has not been getting hot anyhow, but if it gets warm during operation i can attach a heatsink and fan if necessary.

My PSU is drawing about 600mA with it running at this stage.

I have played around with more simple code to test the basic functions, and have tried different pulse widths with no real noticeable effect, from 5ms to 100ms i think this library defaults to 20ms if none is specified.

This unit is a tumbler, so the 15:1 gear ratio is with the drum sitting on the roller, taking the drum diameter into account.

The roller itself is geared down 4:1. What is interesting is how the unit sometimes actually performs worse with no load applied, it can oscillate with the drum removed.

Now i remember reading how if the motor locks up while starting to swap one of the pairs of wires around to the motor, i tried that and all that happens is the motor runs in the opposite direction, is this typical?

edit: have tried cranking up driver current to full, but doesn’t change any behavior.

However, i thought i would experiment with increasing acceleration.

Using stepper.setAcceleration(50000) here and it starts and stops fine even under load, do i even need to worry about ramping up and down slow? This seems to be where im experiencing the issues.

If your lab power supply has current limiting, turn it off!