Max speed NEMA17 & A4988 - AccelStepper library makes motor skip steps

Hello,

I bought a Usongshine "US-17HS4023" stepper motor (for cheaper from china) to experiment on. I figure the US stand for Usongshine, making it the same as the other 17HS4023 steppers.
I had a 12V 10A power supply laying around that I cooked together with a A4988 driver and an Arduino Mega. I adjusted the max current draw to 1A on the driver board.

I want to do an experiment where I increase the speed until the stepper skips steps. Then I calculate to see if it matches the documentation. Depending on what source to believe it should either be around 200RPM (and actually higher when no load) or ">1400PPS" (Aliexpress link). 200RPM would mean 1.5ms per step (1.8° per step). 1400PPS would mean 714µs/ pulse.

Problem:
I was trying to reach the maxum speed by "Bitbanging" and get stuck at around 800µs/ pulse (50% duty cycle of course: 400µs on, 400µs off) meaning it's pretty close to the documented 1400PPS.

If my math still maths: 800µs/step = 0.8ms/step, (1minute =) 60 000ms/ 0.8ms/step = 75 000steps, 75 000steps/ ((360°/1.8°)steps/rotation) = is around 375RPM (=6RPS, which I highly doubt is actually the case: it does around 3rotations/sec when I film it, as seen in the video, making it around 200RPM).
When I try to go higher, the stepper just (literally) beeps without noticeable motion.

I thought adjusting the program to deal with inertia by using acceleration could bump it up a little bit, but there is where I ran into a problem: I found the "AccelStepper" library recommended online, but when using the library the motor tends to vibrate on every speed. And more peculiar: stepping over 95 "MaxSpeed" (=steps/ second), the stepper skips steps.

95steps/second = 631ms/step 10.5ms/step which is significantly (12.5x) higher than the achieved 800µs when "bitbanging".

  1. Can anyone confirm that it is plausible that 800µs is the limit of driving "17HS4023" NEMA17 with 12V and limiting the driver current to 1A?
  2. Does anyone have an idea how to get the same results with acceleration (using the AccelStepper library)?

Video of bitbanging, 95max speed and 100max speed. (including schematic)

"Bitbanging" code used:

// defines pins numbers
const int stepPin = 52; 
const int dirPin = 53; 
 
void setup() {
  // Sets the two pins as Outputs
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
}
void loop() {
  digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
  // Makes 200 pulses for making one full cycle rotation
  for(int x = 0; x < 200; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(400); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(400); 
  }
  delay(1000); // One second delay
  
  digitalWrite(dirPin,LOW); //Changes the rotations direction
  // Makes 400 pulses for making two full cycle rotation
  for(int x = 0; x < 400; x++) {
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(400);
    digitalWrite(stepPin,LOW);
    delayMicroseconds(400);
  }
  delay(1000);
}

AccelStepper code used:

#include <AccelStepper.h>

// Define motor interface type (DRIVER means using a step/dir driver)
#define motorInterfaceType 1

// Define pin connections
const int dirPin = 53;
const int stepPin = 52;

// Create an instance of the AccelStepper class
AccelStepper stepper(motorInterfaceType, stepPin, dirPin);

void setup() {
  // Set the maximum speed and acceleration
  stepper.setMaxSpeed(95.0);      // Steps per second
  stepper.setAcceleration(30.0);    // Steps per second^2
  
  // Set the target position (e.g., 400 steps = 2 full revolutions on a 1.8° motor)
  stepper.moveTo(800);
}

void loop() {
  // Check if the motor has reached its target position
    // If at the end of travel go to the other end
  if (!stepper.run()) {   // run() returns true as long as the final position has not been reached and speed is not 0.
    stepper.moveTo(-stepper.currentPosition());
  }
}

Edit: specified that the video includes the schematic.
Edit: added units to the RPM calculation as per reply.

Post a wiring diagram.

Why not just use the code that runs the motor smoothly?

What are the units for these?

No. It's just using a library.

You can make your own "acceleration" code by slowly increasing the frequency of the transitions. Perhaps the motor's datasheet shows the calculation for adjusting the A4988 for best use.

Could problems be caused by your wiring? Is your power supply sufficient?

The inverse steps/sec is sec/step. The inverse of 95 steps/sec = 1sec/95step = 10.5ms/step

Thank you for responding, but I get the feeling that you have skipped over a lot of information that I have provided.

It is in the first 10seconds of the video. I think it has no added benefit to copy it here: the video does have the added benefit of visual inspection of my wiring, hence why I added it. This is also a link to documentation with the same schematic, if it is needed.

As stated: I want to have acceleration to rule out inertia as a cause of failure.

I used s to ms, but mistakenly took 1 minute instead of 1s. The 0.8 is the result of the conversion from 800µs to ms. So it would be "60 000ms / 0.8ms". I edited the orignal post to not confuse further readers and changed "60 000" to "1 000ms".

I don't understand what library is used, I never intended to use a library with the following "bitbang" code:

// defines pins numbers
const int stepPin = 52;
const int dirPin = 53;

void setup() {
// Sets the two pins as Outputs
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
}
void loop() {
digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
// Makes 200 pulses for making one full cycle rotation
for(int x = 0; x < 200; x++) {
digitalWrite(stepPin,HIGH);
delayMicroseconds(400);
digitalWrite(stepPin,LOW);
delayMicroseconds(400);
}
delay(1000); // One second delay

digitalWrite(dirPin,LOW); //Changes the rotations direction
// Makes 400 pulses for making two full cycle rotation
for(int x = 0; x < 400; x++) {
digitalWrite(stepPin,HIGH);
delayMicroseconds(400);
digitalWrite(stepPin,LOW);
delayMicroseconds(400);
}
delay(1000);
}

Hence why I added the video. The voltage of the motor should be between 8-35V DC, as mentioned I provide 12V DC 10A. The rated current is 0.7 or 1A depending on the source. I limit the driver to 1A.

True, somehow I took 60s instead of 1s, the conclusion remains the same: 10.5ms <> 800µs: so it is 12.5x slower. I will edit the original post.

EDIT: I thought I mistakenly took 60 000ms instead of 1 000ms but when editing I noticed it to be correct since I am calculating RPM (rotations per minute, 1minute = 60 000ms)

We need to see it here in a post please.
YOUR circuit, a picture of you project would also help.

Do you have a DMM? (Digital MultiMeter)

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

Arduino.h - where you will find pinMode(), digitalWrite(), delayMicroseconds(), delay(), et c. Found here: ArduinoCore-avr/cores/arduino/Arduino.h at master · arduino/ArduinoCore-avr · GitHub

Therefore, the code is not, because the sketch is using a library.

Write your own sketch to slowly increase speed. ( "... as stated..."). Decrease time between pulses... or is it decrease duration of transition... I do not know. You can "manually" step a motor by using switches a button to signal the pulse, so the stepper can spin "that" slow... simulated here... https://wokwi.com/projects/327424914940232274

These are the images of the circuit I made. I made "clearer" pictures then what I used in the video (which seems to have had trouble rendering the images). I connected enable to 5V for the purpose of taking the image while the board is powered on.

I tried troubleshooting by connecting MS1-3 & enable to ground (MS1-3 have pull-down resistors, but you never know).

I also switched the A4988 driver for a new one which current limit was lower than 1A (Uref = 0.65V = current limit around 0.8A) and the behavior remains the same.

I have a multimeter.

Maybe I misunderstood "bitbanging": I refer to it as not using a ready function out of a library to manage a pulse train by switching outputs. I am working in Arduino IDE so naturally I use Arduino.h

I wrote the sketch to slowly increase speed but it is actually worse. The motor starts skipping then pulls through and then skips again.
The "vibration" I was refering to earlier might just be noticeable stepping noise, which is barely noticeable on a steady faster speed compared to accelerating (even when reaching peak speed).

The behavior with the provided code is erratic:
It accelerates and at around 5000µs step time it skips steps, until about 4200µs. Then around 1600µs it starts skipping again and does not recover (ultimately being driven at 1000µs).

Then it changes direction and rotates happily the other way, also at 1000µs step time. I even adjusted it as far as 640µs stepping time before skipping occurs in the other direction.

I changed direction in the program but that does not influence anything, it also does not always happen on the same angle of the shaft (I stopped during operation so it started in another angle).

// defines pins numbers
const int stepPin = 48;
const int dirPin = 49;
int dlyDec = 6000;

void setup() {
// Sets the two pins as Outputs
Serial.begin(9600);
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
dlyDec = 3000;
}

void loop() {
digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
// Makes 200 pulses for making one full cycle rotation
for(int x = 0; x < 1000; x++) {
  dlyDec -= 5;
  if (dlyDec <= 500) // Lowest pulse width know to be succesful when accelerating
  {
  dlyDec = 500;
  }
  
digitalWrite(stepPin,HIGH);
Serial.println(dlyDec);
delayMicroseconds(dlyDec);
digitalWrite(stepPin,LOW);
delayMicroseconds(dlyDec);
}
dlyDec = 3000;
delay(1000); // One second delay

digitalWrite(dirPin,LOW); //Changes the rotations direction
// Makes 400 pulses for making two full cycle rotation
for(int x = 0; x < 400; x++) {
digitalWrite(stepPin,HIGH);
delayMicroseconds(500);
digitalWrite(stepPin,LOW);
delayMicroseconds(500);
}
//dlyDec = 5000;
delay(1000);
}

This Your sketch [in post #7] seems to "work." Steps forward, quickly, steadily (no change in speed) for 1000 steps (5 revolutions) and backward, slower, steadily (no change in speed) for 400 steps (2 counter-revolutions). I think the Serial Monitor is showing pulse width... 3000 to 500. I thought this it was the code from the "smooth" motor... but you say it is erratic.

Have you tried switching the leads just on 2A and 2B?

Try a simpler AccelStepper sketch... see if it has the same issue.

#include <AccelStepper.h> // https://github.com/adafruit/AccelStepper/blob/master/AccelStepper.h

#define dirPin 49
#define stepPin 48
#define motorInterfaceType 1 // set to 1 when using a driver

AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin); // Create instance of AccelStepper class:

int maxSpeed = 800;
int acceleration = 30;

void setup() {
  stepper.setMaxSpeed(maxSpeed);
  stepper.setAcceleration(acceleration);
}

void loop() {
  stepper.moveTo(1600);    // Set the target position: (blocking)
  stepper.runToPosition(); // Run to target position with set speed and accel/decel
  delay(1000);

  stepper.moveTo(0);       // Move back to zero (blocking)
  stepper.runToPosition(); // run to target position with set speed and accel/decel
  delay(1000);
}