Stepper motor - timer displaying seconds to next move

I would like to display a countdown timer that shows the time in seconds until a stepper motor is due to make its next move.

I have two bits of code that function independently. I'm wondering if they can be merged together to achieve the above objective. I'm not sure if this is the right approach. I am hoping some can steer me in the right direction?

The two codes are posted below.

Code 1: Stepper motor
Code 2: Countdown timer

int smDirectionPin = 2; //Direction pin
int smStepPin = 3; //Stepper pin
int smEnablePin = 7; //Motor enable pin
 
void setup(){
  /*Sets all pin to output; the microcontroller will send them(the pins) bits, it will not expect to receive any bits from thiese pins.*/
  pinMode(smDirectionPin, OUTPUT);
  pinMode(smStepPin, OUTPUT);
  pinMode(smEnablePin, OUTPUT);
 
  digitalWrite(smEnablePin, HIGH); //Disbales the motor, so it can rest untill it is called uppond
 
  Serial.begin(9600);
}
 
void loop(){
  /*Here we are calling the rotate function to turn the stepper motor*/
  rotate(800, 0.1); //The motor rotates 800 steps clockwise with a speed of 0.1 (slow)
  delay(30000);
  rotate(1600, 0.5); //The motor rotates 1600 steps clockwise with a speed of 0.5 (medium)
  delay(40000);
  rotate(-1600, 1); //The motor rotates 1600 steps counter clockwise with a speed of 1 (fast)
  delay(20000);
}
 
/*The rotate function turns the stepper motor. Tt accepts two arguments: 'steps' and 'speed'*/
void rotate(int steps, float speed){
  digitalWrite(smEnablePin, LOW); //Enabling the motor, so it will move when asked to
 
  /*This section looks at the 'steps' argument and stores 'HIGH' in the 'direction' variable if */
  /*'steps' contains a positive number and 'LOW' if it contains a negative.*/
  int direction;
 
  if (steps > 0){
    direction = HIGH;
  }else{
    direction = LOW;
  }
 
  speed = 1/speed * 70; //Calculating speed
  steps = abs(steps); //Stores the absolute value of the content in 'steps' back into the 'steps' variable
 
  digitalWrite(smDirectionPin, direction); //Writes the direction (from our if statement above), to the EasyDriver DIR pin
 
  /*Steppin'*/
  for (int i = 0; i < steps; i++){
    digitalWrite(smStepPin, HIGH);
    delayMicroseconds(speed);
    digitalWrite(smStepPin, LOW);
    delayMicroseconds(speed);
  }
 
  digitalWrite(smEnablePin, HIGH); //Disbales the motor, so it can rest untill the next time it is called uppond
}

TIMER CODE BELOW:


unsigned long previousMillis = 0;     // will store last time LED was updated
const long interval = 1000;           // interval at which to blink (milliseconds)
int timer = 40;                        // Set the timer to 5 seconds

void setup() {

  Serial.begin(9600);
}

void loop() {

  unsigned long currentMillis = millis();

  // for (int i = timer; i >= 0; i--) {    // Set i to timer; test if timer is greater equal to 0; decrement timer
  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    //Serial.println(i);           // print i to the serial monitor
    Serial.println(timer);
    timer--;//decrease timer count
    if (timer == -1)
    {
      timer = 40;
    }
  }

}
1 Like

Yes, you can merge those 2 codes.

My suggestions would be to learn how to do this

without using delay. By not using the delay function, use millis() instead, your code can continue running and do the count down display thing.

Thanks Idahowalker. I have changed the delay function to millis() as you suggested.
However, my attempt to merge the codes results in the timer acting like a loop counter rather than counting the seconds until the stepper's next move.
Is an interrupt the right way to go to merge the two codes?

int smDirectionPin = 2; //Direction pin
int smStepPin = 3; //Stepper pin

const unsigned long period1 = 5000;  //the value is a number of milliseconds
const unsigned long period2 =10000;  //the value is a number of milliseconds
const unsigned long period3 = 15000;  //the value is a number of milliseconds

unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;
unsigned long previousMillis = 0;     // will store last time update
const long interval = 1000;           // interval at which to blink (milliseconds)
int timer = 10;                        // Set the timer to 10 seconds


void setup() {
  /*Sets all pin to output; the microcontroller will send them(the pins) bits, it will not expect to receive any bits from thiese pins.*/
  pinMode(smDirectionPin, OUTPUT);
  pinMode(smStepPin, OUTPUT);

  Serial.begin(9600);
  startMillis = millis();  //initial start time

}

void loop() {
      
  currentMillis = millis();  //get time in milliseconds since the program started

//  Stepper - first move:

    if (currentMillis - startMillis == period1)  //test whether the period has elapsed
    {      
      Countdown();
       /*Here we are calling the rotate function to turn the stepper motor*/
      rotate(1600, 0.5); //The motor rotates 1600 steps counter clockwise with a speed of 0.5 (medium)
    }

//  Stepper - second move:

    currentMillis = millis();  //get time in milliseconds since the program started
    if (currentMillis - startMillis == period2)  //test whether the period has elapsed
    {      
      Countdown();
      /*Here we are calling the rotate function to turn the stepper motor*/
      rotate(-1600, 1); //The motor rotates 400 steps clockwise with a speed of 0.001 (slow)
    }
    
//    Stepper - third move:
      currentMillis = millis();  //get time in milliseconds since the program started
    if (currentMillis - startMillis == period3)  //test whether the period has elapsed
    {      
      Countdown();
      /*Here we are calling the rotate function to turn the stepper motor*/
      rotate(-3200, 1); //The motor rotates 800 steps clockwise with a speed of 0.1 (fast)
      startMillis = currentMillis;  //IMPORTANT to save the start time.
    }
}

void Countdown() {

  unsigned long currentMillis = millis();

  // for (int i = timer; i >= 0; i--) {    // Set i to timer; test if timer is greater equal to 0; decrement timer
  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    //Serial.println(i);           // print i to the serial monitor
    Serial.println(timer);
    timer--;//decrease timer count
    if (timer == -1)
    {
      timer = 10;
    }
  }

}

/*The rotate function turns the stepper motor. Tt accepts two arguments: 'steps' and 'speed'*/
void rotate(int steps, float speed) {

  /*This section looks at the 'steps' argument and stores 'HIGH' in the 'direction' variable if */
  /*'steps' contains a positive number and 'LOW' if it contains a negative.*/
  int direction;

  if (steps > 0) {
    direction = HIGH;
  } else {
    direction = LOW;
  }

  speed = 1 / speed * 70; //Calculating speed
  steps = abs(steps); //Stores the absolute value of the content in 'steps' back into the 'steps' variable

  digitalWrite(smDirectionPin, direction); //Writes the direction (from our if statement above), to the EasyDriver DIR pin

  /*Steppin'*/
  for (int i = 0; i < steps; i++) {
    digitalWrite(smStepPin, HIGH);
    delayMicroseconds(speed);
    digitalWrite(smStepPin, LOW);
    delayMicroseconds(speed);
  }

}

Your code does not properly use millis() for timing.

Can you spot why this does NOT work?

How will you get elapsed time if, on every loop, you update the time before it has had a chance to elapse?

If you are unable to get millis() working, using an interrupt will be more complicated.

Review Demonstration code for several things at the same time

1 Like

Not really. Should the 'currentMillis = millis() be in the setup rather than the loop?

I hope to run the stepper using Millis() and no blocking delays.
In the code below I am trying to move the stepper 100 steps using millis() inside a For Loop, however the stepper just keeps turning and doesn't stop at 100.
The For Loop works fine with a delay() but not millis(). Should I be taking a different approach?


byte directionPin = 2;
byte stepPin = 3;
int numberOfSteps = 100;
  
unsigned long curMillis;
unsigned long prevStepMillis = 0;
unsigned long millisBetweenSteps = 1; // milliseconds

void setup() { 

     pinMode(directionPin, OUTPUT);
     pinMode(stepPin, OUTPUT);
    }


void loop() { 
    curMillis = millis();
    moveStepper();
    }


void moveStepper() {

    for(int n = 0; n < numberOfSteps; n++) {

        if (curMillis - prevStepMillis >= millisBetweenSteps) {
            prevStepMillis = curMillis;
            
            digitalWrite(directionPin, HIGH);
            digitalWrite(stepPin, HIGH);
            digitalWrite(stepPin, LOW);
    }
    }
    }

Don't forget that loop is called over and over forever. So as soon as moveStepper is done, it gets called again.

Wildbill, you are quite right. Thanks for pointing that out.
In the code below I used a for loop inside a millis hoping to create a 5 second pause between stepper moves, but no luck. The stepper just clicks every 5 secs, but doesn't move.
Maybe I could use the loop() function with a counter to keep count of each stepper move?
Any suggestions would be appreciated.


byte directionPin = 2;
byte stepPin = 3;
int numberOfSteps = 200;
  
unsigned long curMillis;
unsigned long curMicros;
unsigned long millisPrevMove = 0;
unsigned long microsPrevPulse = 100;
unsigned long millisInterval = 5000; // interval between stepper moves (milliseconds)
unsigned long microsPulseWidth = 100; // interval between High-Low pulses (microseconds)

void setup() 
{ 
   pinMode(directionPin, OUTPUT);
   pinMode(stepPin, OUTPUT);
   digitalWrite(directionPin, HIGH);
}
     
void loop() 
{ 
   unsigned long curMillis = millis();
   unsigned long curMicros = micros();

      if (curMillis - millisPrevMove >= millisInterval) 
      {  
          for(int j = 0; j <= numberOfSteps; j++)
          {  
            if (curMicros - microsPrevPulse >= microsPulseWidth)
            {   digitalWrite(stepPin, HIGH);
                digitalWrite(stepPin, LOW);  
                microsPulseWidth = curMicros;                  
            } 
          }        
          millisPrevMove = curMillis;  
      }
}

You are setting the wrong variable here:

        microsPulseWidth = curMicros;

Thanks. Would a 'constant long' be suitable? I am still learning about which variables are appropriate.

constant unsigned long would have been good, because then the compiler would have told you about your mistake.

Actually, the compiler did tell me about my mistake so I changed the variable type thinking that had fixed it. There is something I'm missing here. How did you know the variable was wrong?

It's ok, I see my error! Thanks

Corrected the variable but still the same issue unfortunately. The stepper clicks every 5 seconds but does not move.


byte directionPin = 2;
byte stepPin = 3;
int numberOfSteps = 200;
  
unsigned long curMillis;
unsigned long curMicros;
unsigned long millisPrevMove = 0;
unsigned long microsPrevPulse = 0;
const unsigned long millisInterval = 5000; // interval between stepper moves (milliseconds)
const unsigned long microsPulseWidth = 100; // interval between High-Low pulses (microseconds)

void setup() 
{ 
   pinMode(directionPin, OUTPUT);
   pinMode(stepPin, OUTPUT);
   digitalWrite(directionPin, HIGH);
}
     
void loop() 
{ 
   unsigned long curMillis = millis();
   unsigned long curMicros = micros();

      if (curMillis - millisPrevMove >= millisInterval) 
      {  
          for(int j = 0; j <= numberOfSteps; j++)
          {  
            if (curMicros - microsPrevPulse >= microsPulseWidth)
            {   digitalWrite(stepPin, HIGH);
                digitalWrite(stepPin, LOW);  
                microsPrevPulse = curMicros;                  
            } 
          }        
          millisPrevMove = curMillis;  
      }
}

I was suspicious of your old code because it had no delay between setting the step pin high and low. Usually there is a brief delay there, but it was working for you in the earlier code. Might be worth changing as a test.

The other difference I see is that you haven't set the direction pin, although I don't expect that to matter either.

OK, so at the beginning of the loop you create a variable to hold a start time.

If you make the above change can you see how long the difference between when you first recorded the time and what the current time has past?

PUT

as global. Now you can see that time passes. Read how to use millis().

I could not get millis() working inside a for loop so I ended up using a counter++ instead.
For loops are blocking anyway, so best avoided where timing is concerned.

The timer in this sketch uses only millis and no delays, whereas the code for the stepper is still blocking. Blocking occurs between timing events so does not appear to be an issue.
For my purposes the sketch below achieves what I set out to do, ie countdown the remaining time until the next move of a stepper motor.

#include <Wire.h>

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);


int smDirectionPin = 2; //Direction pin
int smStepPin = 3; //Stepper pin
int stepperMoveCount = 192;
int count = 0;
int timer = 936;            // Set the timer 93.6 seconds
unsigned long stepperPrevMillis = 0;
unsigned long timePrevMillis = 0; 
unsigned long curMillis;
unsigned long interval = 93600;         //inverval betweeen stepper motor moves (milliseconds)


void setup() {
  /*Sets all pin to output; the microcontroller will send them(the pins) bits, it will not expect to receive any bits from thiese pins.*/
  pinMode(smDirectionPin, OUTPUT);
  pinMode(smStepPin, OUTPUT);

  // initialize the LCD
  lcd.begin();
  lcd.clear();

  Serial.begin(9600);
}

void loop() {

  curMillis = millis();

  // Stepper instructions:

  if (curMillis - stepperPrevMillis >= interval)
  { stepperPrevMillis = curMillis;
    count++;                  // on every loop,adds 1 to the count int
    if (count <= stepperMoveCount) {
      rotate(6, 0);           // The motor rotates 6 steps clockwise with a speed of 0 (slowest)
    }
    else {
      rotate(-1152, 0.1);     // The motor rotates 1152 steps counter clockwise with a speed of 0.1 (slow)
      count = 0;
    }
  }

  // Timer instructions:

  if ((millis() - timePrevMillis >= 100) && (timer >= 0))
  { timePrevMillis = millis();

    lcd.backlight();
    lcd.print(timer);
    lcd.print(" ");
    lcd.setCursor(0, 0);  // set cursor to 1st character position.
    timer--;

    if (timer <= 0)
      timer = 936 - 1; //  reset timer to 93.6 seconds..  less 0.01sec calibration
  }
}

/*The rotate function turns the stepper motor. Tt accepts two arguments: 'steps' and 'speed'*/
void rotate(int steps, float speed) {

  /*This section looks at the 'steps' argument and stores 'HIGH' in the 'direction' variable if */
  /*'steps' contains a positive number and 'LOW' if it contains a negative.*/
  int direction;

  if (steps > 0) {
    direction = HIGH;
  } else {
    direction = LOW;
  }

  speed = 1 / speed * 70; //Calculating speed
  steps = abs(steps); //Stores the absolute value of the content in 'steps' back into the 'steps' variable

  digitalWrite(smDirectionPin, direction); //Writes the direction (from our if statement above), to the EasyDriver DIR pin

  /*Steppin'*/
  for (int i = 0; i < steps; i++) {
    digitalWrite(smStepPin, HIGH);
    delayMicroseconds(speed);
    digitalWrite(smStepPin, LOW);
    delayMicroseconds(speed);
  }
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.