Stepper Motor w/ LCD Display and Speed Controller. Too slow, Choppy. Arduino UNO

So currently, I am developing a pulley system that will be moving only a string at a max speed of 30 rev/sec. This system must have the ability to be tested at different variable speeds (using potentiometer right now). The current speed I have been able to produce is nowhere near my goal. The torque should be low given I will only be pulling a string, so my loss through speed shouldn’t be an issue. I have tested without any load on the stepper with no difference in speed. Along with this, I have seen the stepper make lots of noise with a choppy rotation.

I have attempted to use microstepping to help with this choppiness with some success, but now I wish to speed up the rotation. Is there something I’m missing???

I have attempted this with a

  • 12v, 2A power supply

- Nema 17 Stepper Motor with these Specs:

Motor type: Bipolar Stepper
Phase Resistance: 1.5Ohm
Phase Inductance: 2.8mH
Detent Torque: 2.2N.cm
Rotor Inertia: 54g.cm2
Holding Torque: 40N.cm
Rated current 1.7A
Step angle: 1.8 deg
Step angle accuracy: + - 5%(full step, not load)
Resistance accuracy: + - 10%
Inductance accuracy: + - 20%
Temperature rise: 80deg Max(rated current, 2 phase on)
Ambient temperature: —20deg ~+50deg
Insulation resistance: 100MΩ Min, 500VDC
Insultion Strength: 500VAC for one minute

- TB6600 Stepper Motor Driver with these Specs:

9-42V.
5 subdivision mode options (1/1, 1/2, 1/4, 1/8, 1/16,1/32) for microstepping.
Output current: IOUT = 4.0A (peak, in 100ms).
rated output: IOUT = 3.5 A
Resistance (a +) = 0.4
Alarm output pin current:Ialert = 1mA
Monitoring output pin (MO): Imo= 1mA

Here is what code I have currently being using:

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

#define POT_PIN_SIG  A0 //Speed Controller
#define ON_OFF_SIG  A1 // On-Off Potentiometer (dont have switch on hand)

// 200 steps * 1.8 degrees/step = 1 rev
//Max speed = 30 rev/s = 6000 steps/s

// defines pins numbers
float cir_pulley = 8.16 ; /circumference for speed calculation
const int stepPin = 5; //steps output for motor
const int dirPin = 2; //direction of motor
const int enPin = 8; //enables motor

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

void setup() {
  
// Sets the two pins as Outputs
pinMode(stepPin,OUTPUT); 
pinMode(dirPin,OUTPUT);

pinMode(enPin,OUTPUT);

lcd.begin(16,2); // define lcd matrix
  lcd.setCursor(0, 0); // define below message's start point
  lcd.print("String Linear");
  lcd.setCursor(0, 2);
  lcd.print("Speed: ");
  lcd.setCursor(12,2);
  lcd.print("cm/s");
}

void loop() {
 // read the sensor value:
 int On_Or_Off = analogRead(ON_OFF_SIG);
if (On_Or_Off > 500) {
    
  int sensorReading = analogRead(POT_PIN_SIG);
 // map it to a range from -90 to 90:
  int motorSpeed = map(sensorReading, 0, 1023, -90, 90);
   
  // set the motor speed:
    // define the speed using the potentiometer to update the delay
    // which in turn will change speed    

    //microstepping of 1/4 tested here

    if (motorSpeed > 3){
      digitalWrite(enPin,LOW); //motor on
      digitalWrite(dirPin,HIGH);//chnge direction to CW
      digitalWrite(stepPin,HIGH); 
      delayMicroseconds(7500/(4*motorSpeed));
      digitalWrite(stepPin,LOW); 
      delayMicroseconds(7500/(4*motorSpeed));
    }    
    else if(motorSpeed < -3) {
    
      digitalWrite(enPin,LOW); //motor on
      digitalWrite(dirPin,LOW);//change direction to CCW
      digitalWrite(stepPin,HIGH); 
      delayMicroseconds(7500/(4*abs(motorSpeed)));    
      digitalWrite(stepPin,LOW); 
      delayMicroseconds(7500/(4*abs(motorSpeed)));    
    }
    else if(-3 <= motorSpeed <= 3 ) {
      
      digitalWrite(enPin,HIGH); //motor off
      delayMicroseconds(500);    
    }
 lcd.setCursor(6, 2);
  // print motor speed to lcd
 lcd.print(float abs(motorSpeed)/3*cir_pulley) ;
}}

Good job using code tags on your first posting.

else if(-3 <= motorSpeed <= 3 ) {
      
      digitalWrite(enPin,HIGH); //motor off
      delayMicroseconds(500);    
    }

The syntax of this conditional for a range is wrong. If you want the motor off between -3 and +3 then

else if(( motorSpeed >= -3) && (motorSpeed <= 3 ) ){
      
      digitalWrite(enPin,HIGH); //motor off
      delayMicroseconds(500);    
    }

A few things occur to me.

This code

     digitalWrite(stepPin,HIGH); 
      delayMicroseconds(7500/(4*motorSpeed));
      digitalWrite(stepPin,LOW); 
      delayMicroseconds(7500/(4*motorSpeed));

can be simplified to

     digitalWrite(stepPin,HIGH); 
      delayMicroseconds(10);
      digitalWrite(stepPin,LOW); 
      delayMicroseconds(7500/(2*motorSpeed));

The width of the stepper pulse does not affect the speed and most stepper drivers are content with a very brief pulse. What affects the speed is the interval between steps.

Using delayMicroseconds() (or just delay() ) will add to the time taken to refresh the LCD before loop() can repeat so the actual time between steps might be much longer than you expect.

There should be no need to update the LCD after every step - especially for a high step rate. Updating it maybe twice per second will probably be enough for the human eye and brain.

Have a look at how millis() is used to manage timing without blocking in Several things at a time. Note how each function runs very briefly and returns to loop() so the next one can be called. Long running processes are achieved a tiny piece at a time. And there may be dozens of calls to some functions before it is actually time for anything to happen.

And see Using millis() for timing. A beginners guide if you need more explanation.

Also have a look at the second example in this Simple Stepper Code

...R

30 rev/sec… I guess that the stepper uses 400 steps per rev. That brings us 12 000 steps per second <> 83 microseconds between steps. I wonder if Your stepper handles that. Using microsteps only call for more pulses/steps per second.
To achive high stepping rates I think accelstepper would be good. Steppers have a “run in”, and “run out”, parameter telling what the maximum pulse rate for a motor at 0 RPM, as well as stopping, reaching 0 RPM running at max RPM. Acceleration resp. deceleration is needed.

Sorry, "pull in" resp. "pull out"....

That speed is no problem - I'm building a stepper project where I'm doing almost 60,000 steps per second (280 rpm, 64 microsteps). I did have to replace the digitalWrite by a direct port call. Could do faster if I'd run the ATtiny84a at 16 or 20 MHz instead of 8 MHz.

The stepper though, that's a different story. I've managed nearly 700 rpm (a bit lower microstepping rate), at higher speeds it just won't run at all, though I have to admit that was without using acceleration. No load on the stepper. Just over 10 rev/s. So 30 rev/s may be possible with a different stepper, or by accelerating to that speed, I don't know as it was just for the fun of it and that 280 rpm is good enough.

For OPs project: I don't see the use of a stepper here. High speed, low torque: this is more the territory of a proper brushed motor with gear box to slow it down to the required 1800 rpm. 1:5-1:10 gearing probably, depending on the motor. Add an encoder to the motor and control the speed using PWM. Overall much easier on both the hardware and the software side.

Or if it must be a stepper: add a gear box to increase the speed of rotation. 10:1 or 20:1 or so would be a good starting point. Of course you do lose torque that way, but as the requirement is low torque and a stepper has very high torque available that will likely work out perfectly.

@wvmarle.
OP asks for 30 rev/sec. That is 1800 rev/minute. Quite a lot more I think. As You say, an other type of motor looks like a better choice.

Sorry, wasn't clear, I meant pulse speed, not rpm speed. That 12kHz pulses is no problem. The 1800 rpm, is.

Ok. Sending 12 kHz pulse rate is no problem for an Arduino but will be for the stepper to receive.

Just a quick mention, the Min positive duty cycle of this motor driver is 2.2us. Thank you Robin for your insightful resources! They have really improved the stability of the system. No longer having choppy movement. As for the speed concerns, I do not foresee having to use greater than 900 rpm for extended periods of time (1800 rpm would just be nice to achieve but not likely to be needed for testing purposes). Currently tweaking the code as we speak and testing the max speed I can reach.

Thank you all for the quick replies!

2.2 Micro seconds? Allowing a pulse rate of some 450 000 steps per second? No way. That more looks like minimum positive pulse length. What about the delay needed until next pulse? Max RPM ought to be a parameter found in the specs.

Yes Railroader I agree, I misquoted that detail. This motor was a cheap Chinese piece. Any datasheet I came across only detailed the information I have already outlined.

Implementing millis() into the code, I am having trouble understanding whether to use it in controlling the speed of the stepper. I understand the advantage in using it in this context for the LCD.

Would I use micros() in this manner for the stepper? If so, would i be calculating a Steprefresh time every loop? Are there advantages of using micros over the current method? This is where I am currently:

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

#define POT_PIN_SIG  A0 //Speed Controller
#define ON_OFF_SIG  A1 // On-Off Potentiometer (dont have switch on hand)

// 200 steps * 1.8 degrees/step = 1 rev
//Max speed = 20 rev/s 

// defines pins numbers
unsigned long startLCDMillis;
unsigned long currentMillis;
const unsigned long LCDrefresh = 350;  //period during which button input  is valid

float cir_pulley = 8.16 ; // circumference for speed calculation
const int stepPin = 5;
const int dirPin = 2;
const int enPin = 8;
const int microSteps = 4;

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

void setup() {

  // Sets the two pins as Outputs
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  pinMode(enPin, OUTPUT);

  startLCDMillis = millis();  //save the start time

  lcd.begin(16, 2); // define lcd matrix
  lcd.setCursor(0, 0); // define below message's start point
  lcd.print("String Linear");
  lcd.setCursor(0, 2);
  lcd.print("Speed: ");
  lcd.setCursor(12, 2);
  lcd.print("cm/s");
}

void loop() {

  currentMillis = millis();

  // read the sensor value:
  int On_Or_Off = analogRead(ON_OFF_SIG);
  if (On_Or_Off > 500) {

    int sensorReading = analogRead(POT_PIN_SIG);
    // map it to a range from -60 to 60:
    int motorSpeed = map(sensorReading, 0, 1023, -60, 60);
    //Lowered Max speed to 20 rev/sec

    // set the motor speed:
    // define the speed using the potentiometer to update the delay
    // which in turn will change speed

    if (motorSpeed > 3) {
      digitalWrite(enPin, LOW); //motor on
      digitalWrite(dirPin, HIGH); //chnge direction to CW
      digitalWrite(stepPin, HIGH);
      delayMicroseconds(5);
      digitalWrite(stepPin, LOW);
      delayMicroseconds(15000 / (microSteps * abs(motorSpeed)));
    }
  else if (motorSpeed < -3) {

    digitalWrite(enPin, LOW); //motor on
    digitalWrite(dirPin, LOW); //change direction to CCW
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(5);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(15000 / (microSteps * abs(motorSpeed)));
  }
else if (( motorSpeed >= -3) && (motorSpeed <= 3 ) ) {

  digitalWrite(enPin, HIGH); //motor off
  delay(1);
}
// if ( (motorSpeed < -1) or (motorSpeed > 1) )  {
if (currentMillis - startLCDMillis >= LCDrefresh) {
  //true until the period elapses.  Note that this is the reverse of BWOD
  lcd.setCursor(6, 2);
  // print motor rpm to lcd
  float CirSpeed = (1000 / (3000 / (microSteps * abs(motorSpeed)) + 1)) / microSteps;
  //Calculations to return speed given the 5us delay 
  lcd.print(CirSpeed * cir_pulley) ;
  startLCDMillis = currentMillis;
}
}
}

As a professional, a long time ago, I was involved in designing a high tech driver board for steppers. Sorry I don't remember the demands and the final result of it. However, running at 18 000 RPM was not the target. Personally I think You need an other kind of motor.

I run a sketch using Micro-delay after each pulse and it works for me. I can't tell what stuff like AccelStepper offers. A.S. offers a lot I think, especially acceleration that's needed to reach the very high speeds.

Look at some other topics and the encouragdement "How to use Millis". Thats good for "normal", low frequency happenings like a limited times per second. Running steppers at som kHz pulse rate or higher will not work as I understand. Look for answers from helper Robin2. Hi's good at this.

Usinf Micros... Remeber that the loop takes some time. No idea using Micros if the loop needs more time then some 500 Micros.

Is there a delay, time mismatch or what in Forum inputs? Missing answers and questions.