Last Updated: 25/11/2023
Arduino stepper motor control without delay()
Components/How To >> Arduino stepper motor control without delay()
Stepper motor without delay() or delayMicros()
Stepper motors are great for precision movement of items and in the model railway world turntables and traversers can make great use of the.
I have done previous tutorials on state machines and have even covered stepper motors with and without delay before but on a recent project I revisted this subject and felt that the resulting code was simpler as well as working very smoothly.
For those who don't understand why avoiding delay() or delayMicroseconds() is important, the reasin is that when either of these functions is called the Arduino is basically frozen. It cannot receive inputs from buttons or sensors in the normal way.
By writing non blocking codes using millis() of micros() we can keep perfect timing while doing other tasks at the same time. This is important in the model railway world as if I created a turntable, the Arduino could only control the turntable because while that turntable is running it cannot receive any other data. Using a state machine non blocking code the same Arduino can move the turntable while receiving inputs and driving servos or lights at the same time providing everything is written with non blocking code.
Although steppers will run at all sorts of speeds, they do perform best within certain ranges and this varies from stepper to stepper. To help with this example 5 takes the stepper through a range of speeds so you can see and hear what works best for your motor.
Example 1: Standard Stepper Control Code
Click to Download code: Nema17_Stepper_Test_v1.ino
The basic stepper motor example that is used. The code uses delayMicroseconds() to control the steps of the motor. This means the Aruino cannot process anything else
/*Example sketch to control a stepper motor with A4988 stepper motor driver and Arduino without a library. More info: https://www.makerguides.com */
// Define stepper motor connections and steps per revolution:
#define dirPin 9
#define stepPin 8
#define stepsPerRevolution 500
void setup() {
// Declare pins as output:
Serial.begin(9600);
Serial.println("a4988 stepper test");
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
}
void loop() {
// Set the spinning direction clockwise:
Serial.println("clock");
digitalWrite(dirPin, HIGH);
// Spin the stepper motor 1 revolution slowly:
for (int i = 0; i < stepsPerRevolution; i++) {
// These four lines result in 1 step:
digitalWrite(stepPin, HIGH);
delayMicroseconds(500);
digitalWrite(stepPin, LOW);
delayMicroseconds(500);
}
delay(1000);
// Set the spinning direction counterclockwise:
digitalWrite(dirPin, LOW);
Serial.println("anticlock");
// Spin the stepper motor 1 revolution quickly:
for (int i = 0; i < stepsPerRevolution; i++) {
// These four lines result in 1 step:
digitalWrite(stepPin, HIGH);
delayMicroseconds(500);
digitalWrite(stepPin, LOW);
delayMicroseconds(500);
}
delay(1000);
}
Example 2: Timing using micros();
Click to Download code: stepperMicrosV1.ino
This sketch demonstrates the basic principles of non blocking code using micros() and millis() for timing.
This allows the stepper to move while the direction is changed at timed intervals rather than a set number of steps.
/* StepperMicrosV1
21/11/23
Controlling stepper motors using micros() nistead of delay()
*/
const int StepPin = 8;// Step pin
const int DirPin = 9;// Direction pin
int stepDelay = 500;//step timing...adjust to make smooth/increase torque/speed
unsigned long stepMicros;//looks after timing of steps
boolean nextStep; //keeps track of stepPin
unsigned long currentMicros;//the current time in microseconds
unsigned long currentMillis;//the current time in milliseconds
unsigned long directionChangeTimer;// stores value so system knows when to change direction.
byte currentDirection; //keeps track of direction
int directionTime = 2000; //2 seconds between direction changes
void setup() {
Serial.begin(115200);
Serial.println("StepperMicrosV1");
pinMode(StepPin, OUTPUT);
pinMode(DirPin, OUTPUT);
}
void loop() {
currentMicros = micros();//get the current micos at the beginning of the loop, it can then be used in multipe places as needed.
currentMillis = millis();//get current millis() to use anywhere in system
if (currentMicros - stepMicros >= stepDelay) {
nextStep = !nextStep;//swap next step state
digitalWrite(StepPin, nextStep);
stepMicros = currentMicros;
}
if(currentMillis - directionChangeTimer >= directionTime){//is it time to change direction
if(currentDirection > 0){
currentDirection = 0;
}else{
currentDirection = 1;
}
digitalWrite(DirPin, currentDirection); // set direction
directionChangeTimer = currentMillis;
}
}
Example 3: Step counting while using micros()
Click to Download code: stepperMicrosStepCountV1.ino
This sketch demonstrates the basic principles of non blocking code using micros() and millis() for timing.
This allows the stepper to move while the direction is changed at timed intervals rather than a set number of steps.
/* stepperMicrosStepCountV1
21/11/23
Controlling stepper motors using micros() nistead of delay()
This sketch changes the number of steps and direction of the stepper at timed intervals
*/
const int StepPin = 8;// Step pin
const int DirPin = 9;// Direction pin
int stepDelay = 500;//step timing...adjust to make smooth/increase torque/speed
unsigned long stepMicros;//looks after timing of steps
boolean nextStep; //keeps track of stepPin
unsigned long currentMicros;//the current time in microseconds
unsigned long currentMillis;//the current time in milliseconds
unsigned long directionChangeTimer;// stores value so system knows when to change direction.
byte currentDirection; //keeps track of direction
int directionTime = 5000; //2 seconds between direction changes
int stepCount;//keeps track of steps
int stepTarget = 1000;//the number of steps to take.
void setup() {
Serial.begin(115200);
Serial.println("stepperMicrosStepCountV1");
pinMode(StepPin, OUTPUT);
pinMode(DirPin, OUTPUT);
}
void loop() {
currentMicros = micros();//get the current micos at the beginning of the loop, it can then be used in multipe places as needed.
currentMillis = millis();//get current millis() to use anywhere in system
if (stepCount != stepTarget) {//check we haven't reached the target
if (currentMicros - stepMicros >= stepDelay) {
nextStep = !nextStep;//swap next step state
digitalWrite(StepPin, nextStep);
stepCount++;//increment the step counter
stepMicros = currentMicros;
}
}
if (currentMillis - directionChangeTimer >= directionTime) { //is it time to change direction
Serial.println("dir change");
if (currentDirection > 0) {
currentDirection = 0;
} else {
currentDirection = 1;
stepTarget = stepTarget + 1000;//set new target
if(stepTarget > 10000){//start again from beginning
stepTarget = 1000;
}
Serial.println("steptarget: " + String(stepTarget));
}
stepCount = 0;//rest the step counter
digitalWrite(DirPin, currentDirection); // set direction
directionChangeTimer = currentMillis;
}
}
Example 4: Step counting while using micros() in functions
Click to Download code: stepperMicrosStepCountV2.ino
This is the same sketch as above but with the void loop() cleaned up and all the timing put into functions. .
/* stepperMicrosStepCountV2
21/11/23
Same as V1 but code moved into functions to make void loop() cleaner
Controlling stepper motors using micros() nistead of delay()
This sketch changes the number of steps and direction of the stepper at timed intervals
*/
const int StepPin = 8;// Step pin
const int DirPin = 9;// Direction pin
int stepDelay = 500;//step timing...adjust to make smooth/increase torque/speed
unsigned long stepMicros;//looks after timing of steps
boolean nextStep; //keeps track of stepPin
unsigned long currentMicros;//the current time in microseconds
unsigned long currentMillis;//the current time in milliseconds
unsigned long directionChangeTimer;// stores value so system knows when to change direction.
byte currentDirection; //keeps track of direction
int directionTime = 5000; //2 seconds between direction changes
int stepCount;//keeps track of steps
int stepTarget = 1000;//the number of steps to take.
void stepControl() {
if (stepCount != stepTarget) {//check we haven't reached the target
if (currentMicros - stepMicros >= stepDelay) {
nextStep = !nextStep;//swap next step state
digitalWrite(StepPin, nextStep);
stepCount++;//increment the step counter
stepMicros = currentMicros;
}
}
}
void setStepsDir() {
if (currentMillis - directionChangeTimer >= directionTime) { //is it time to change direction
Serial.println("dir change");
if (currentDirection > 0) {
currentDirection = 0;
} else {
currentDirection = 1;
stepTarget = stepTarget + 1000;//set new target
if (stepTarget > 10000) { //start again from beginning
stepTarget = 1000;
}
Serial.println("steptarget: " + String(stepTarget));
}
stepCount = 0;//rest the step counter
digitalWrite(DirPin, currentDirection); // set direction
directionChangeTimer = currentMillis;
}
}
void setup() {
Serial.begin(115200);
Serial.println("stepperMicrosStepCountV2");
pinMode(StepPin, OUTPUT);
pinMode(DirPin, OUTPUT);
}
void loop() {
currentMicros = micros();//get the current micos at the beginning of the loop, it can then be used in multipe places as needed.
currentMillis = millis();//get current millis() to use anywhere in system
stepControl();//Moves stepper if needed
setStepsDir();//chnages direction, number of steps at times intervals
}
Example 5: Stepper tuning...varying the stepper speed using micros()
Click to Download code: stepperMicrosTuneV1.ino
Although stepper motors should run at any speed there are some speeds that are much smoother than others. This sketch takes the stepper through a range of speeds displaying the stepDelay in the Serial Monitor. It also shows how the speed/timing of the stepper can be changed allowing acceeleration/deceleration.
/* stepperMicrosTuneV1
21/11/23
This sketch not only changes direction but also stepper speed
to find the smoothest speed.
Controlling stepper motors using micros() instead of delay()
Motor will spin to 2 seconds before changing direction.
*/
const int StepPin = 8;// Step pin
const int DirPin = 9;// Direction pin
int stepDelay = 10;//step timing...adjust to make smooth/increase torque/speed
unsigned long stepMicros;//looks after timing of steps
boolean nextStep; //keeps track of stepPin
unsigned long currentMicros;//the current time in microseconds
unsigned long currentMillis;//the current time in milliseconds
unsigned long directionChangeTimer;// stores value so system knows when to change direction.
byte currentDirection; //keeps track of direction
int directionTime = 2000; //2 seconds between direction changes
void setup() {
Serial.begin(115200);
Serial.println("StepperMicrosV1");
pinMode(StepPin, OUTPUT);
pinMode(DirPin, OUTPUT);
}
void loop() {
currentMicros = micros();//get the current micos at the beginning of the loop, it can then be used in multipe places as needed.
currentMillis = millis();//get current millis() to use anywhere in system
if (currentMicros - stepMicros >= stepDelay) {
nextStep = !nextStep;//swap next step state
digitalWrite(StepPin, nextStep);
stepMicros = currentMicros;
}
if(currentMillis - directionChangeTimer >= directionTime){//is it time to change direction
if(currentDirection > 0){
currentDirection = 0;
}else{
currentDirection = 1;
stepDelay = stepDelay + 30;
if(stepDelay > 800){
stepDelay = 10;
}
Serial.println(stepDelay);
}
digitalWrite(DirPin, currentDirection); // set direction
directionChangeTimer = currentMillis;
}
}
Additional Resource Links
Arduino Tutorial: Avoiding the Overflow Issue When Using millis() and micros()
Comments
This site has been designed to be child friendly,please make sure that any comments are child friendly in language and style. To add a comment or ask a question please email the address in this image: and use Arduino stepper motor control without delay() as a reference.