TL;DR : How to prevent step-skipping with a NEMA gearbox motor at low speed.
I am in the process of creating a system in which I want an object to turn on a plate and to show a letter depending on the angle of the rotation. The object is quite heavy hence the rotation should be quite slow, this is why I took a Planetary Gearbox NEMA Stepper.
My system consists in:
- An Arduino Uno
- A NEMA Stepper Motor
- A A4988 Driver
- Some wires
- 2 Iron plates
- A ball bearing element in between the Iron plates
- Screws to fit everything together.
This is the code I ended up with:
// Define stepper motor connections and steps per revolution:
#define dirPin 6
#define stepPin 7
#define stepsPerRevolution 5373
int degree_init = 1;
int mouvement=0;
// create a mapping between letters and degrees
int letterToDegree[33] ;
int degree;
// function to map letters to degrees
int mapLetterToDegree(char letter) {
if (letter=='.'){
return letterToDegree[27];
}
else if (letter==':'){
return letterToDegree[28];
}
else if (letter=="\'"){
return letterToDegree[29];
}
else if (letter==' '){
return letterToDegree[30];
}
else {
int letterIndex = letter - 'A'; // get the index of the letter in the alphabet
return letterToDegree[letterIndex]; // return the degree associated with that letter
}
}
void setup() {
// Declare pins as output:
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
Serial.begin(9600);
for (int i = 0; i < 33; i++){
letterToDegree[i]= degree_init;
degree_init=degree_init+10;
}
}
void loop() {
int p= NULL;
// Set the spinning direction clockwise:
digitalWrite(dirPin, HIGH);
String input= "NOW THE NOW THE AAAAA";
// loop through the letters
for (int i = 0; i < input.length(); i++) {
// get the degree for each letter
degree = mapLetterToDegree(input[i]);
Serial.print("Letter: ");
Serial.print(input[i]);
Serial.print(" Degrees: ");
Serial.println(degree);
// wait for the movement to complete
delay(1000);
int p_new= map(degree,0,360,0,5373);
Serial.print(" position: ");
Serial.println(p_new);
if (p!=NULL){
mouvement=p_new-p;
p=p_new;
Serial.print(" mouvement: ");
Serial.println(mouvement);
}
else{
p = p_new;
}
if (mouvement<0){
digitalWrite(dirPin, LOW);
mouvement=abs(mouvement);
Serial.println("change de direction");
}
Serial.print(" stepper: ");
Serial.println(mouvement);
// Spin the stepper motor 1 revolution slowly:
for (int i = 0; i < mouvement; i++) {
// These four lines result in 1 step:
digitalWrite(stepPin, HIGH);
delay(20);
digitalWrite(stepPin, LOW);
delay(20);
}
delay(1000);
digitalWrite(dirPin, HIGH);
}
}
I tried following this tutorial and it works in broad terms but sometimes the motor skips steps, especially when changing direction.
I can't seem to understand how torque works within this system and how to minimise the stress I put on the motor. My hypothesis is that since the system is at equilibrium and the friction is minimised due to the ball bearing, there should be no need for a lot of power from the motor, and that this motor should suffice.
My hypothesis of why steps are skipped:
- Too high a speed for the motor
- Not enough torque power supplied
- Not good enough electricity supply to the motor.
I am not sure how to go about and test these hypothesis and if those are correct. I am also having a lot of trouble understanding the relationship between the time delay in the code and the movement of the motor.
Especially this bit:
for (int i = 0; i < mouvement; i++) {
// These four lines result in 1 step:
digitalWrite(stepPin, HIGH);
delay(20);
digitalWrite(stepPin, LOW);
delay(20);
}
Is it ok to change them as I did? do they need to be symetrical (same delay for high and low)?
I am also looking for an explanation of how to apply acceleration and decceleration algorithms for stepper motor, even though I am not sure it makes sense at this speed.
My questions are:
- How to calculate the best speed for such a system and how to implement it?
- Is there need for acceleration/ decceleration?
- Does microstepping makes sense when using a gearbox?
- Are there some obvious flaws in my system/ code that I am missing out?
AccelStepper
library, which will do that for you.if (p != NULL) {
block ...else
is not needed ...p = p_new;
is always executed ... putp = p_new;
after theif
block