How to use millis instead of delay in the iteration?

JihoonLee

Member
Hi All,
I made an auto-repeat motor control system with three pots to control speed, acceleration and delay time onT3.2.
I was able to stop the auto-repeat by using the Interrupt button, but realized that there is no way to stop it if the delaytime is long.
So, I tried to studying millis for apply it to my code.. but failed finally, Because I'm a just newbie of coding.
Would you advice to me something for replacing delay1, 2, 3 in my code with millis? Or could you recommend a millis-related library?
Sorry. I know it's tricky to reading the code using a library.

Thank you so much.

Code:
#include "TeensyStep.h"
#include <EasyButton.h>

#define BTN0 17 // Button
#define BTNC 13
#define DIP1 22 // Dip Switch
#define DIP2 23

EasyButton button0(BTN0);
EasyButton buttonC(BTNC);

long aPoint = 2560;
long bPoint = 3840;
long cPoint = 7680;

int spdpot = 0;
int accpot = 0;
int dlypot = 0;

constexpr int spd = A10; // Pot
constexpr int acc = A14;
constexpr int dly = A11;

Stepper motor(3, 2); // Motor
StepControl controller;

volatile bool auto_repeat = false;
bool skipDelay = false;

void setup()
{
  pinMode(DIP1, INPUT_PULLDOWN);
  pinMode(DIP2, INPUT_PULLDOWN);

  dlypot = analogRead(dly); //-----------------------------------DLAY MAP
  dlypot = map(dlypot, 0, 1023, 0, 66000);

  button0.begin();
  buttonC.begin();

  button0.onPressed(onButton0Pressed);
  buttonC.onPressed(onButtonCPressed);

  if (buttonC.supportsInterrupt())
  {
    buttonC.enableInterrupt(buttonISR);
  }
}

void loop()
{

  button0.read();
  buttonC.read();

  dlypot = analogRead(dly); //-----------------------------------DLAY MAP
  dlypot = map(dlypot, 0, 1023, 0, 66000);
}

void onButton0Pressed() //==============button0 Pressed
{
  auto_repeat = false;
  skipDelay = false;

  int DIP1v = digitalRead(DIP1);
  int DIP2v = digitalRead(DIP2);

  int var = 0;
  int trn = 0;

  if ((DIP1v == HIGH) && (DIP2v == LOW)) {
    trn = 1;
  }

  else  {
    trn = 100;
  }

  while (var < trn)
  {
    var++;

    dlypot = analogRead(dly);
    dlypot = map(dlypot, 0, 1023, 0, 66000);

    /////// START AUTO REPEAT!

    spdpot = analogRead(spd);
    spdpot = map(spdpot, 0, 1023, 0, 18000);
    accpot = analogRead(acc);
    accpot = map(accpot, 0, 1023, 4000, 25000);

    motor
    .setMaxSpeed(spdpot)
    .setAcceleration(accpot);
    motor.setTargetAbs(aPoint);
    controller.move(motor); ////// move to a point
    delay(50);

    if (auto_repeat == true)
    {
      break;
    }

    if (skipDelay == true)
    {
      delay(dlypot); //-------------------------------------------------delay1***
    }

    motor
    .setMaxSpeed(spdpot)
    .setAcceleration(accpot);

    motor.setTargetAbs(bPoint);

    controller.move(motor); ///////// move to b point
    delay(50);

    if (auto_repeat == true)
    {
      break;
    }

    if (bPoint != 0)
    {
      delay(dlypot); //---------------------------------------------------delay2***
    }

    motor
    .setMaxSpeed(spdpot)
    .setAcceleration(accpot);

    motor.setTargetAbs(cPoint);

    controller.move(motor); ///////// move to c point
    delay(50);

    if (auto_repeat == true)
    {
      break;
    }

    if (cPoint != 0)
    {
      delay(dlypot); //---------------------------------------------------delay3***
    }
    skipDelay = true;
  }
}

void onButtonCPressed() //=================buttonC Pressed
{
}

void buttonISR() //========================buttonC ISR
{
  buttonC.read();
  auto_repeat = true;
}
 
Look at this installed example for tips to see if provides the needed process:

{local install}\examples\02.Digital\BlinkWithoutDelay\BlinkWithoutDelay.ino:
Code:
// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
[B]unsigned long previousMillis = 0;        // will store last time LED was updated[/B]

// constants won't change:
[B]const long interval = 1000;           // interval at which to blink (milliseconds)[/B]

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
[B]  unsigned long currentMillis = millis();[/B]

  [B][U]if (currentMillis - previousMillis >= interval) {[/U][/B]
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}
 
I've already looked up a lot of millis samples code includes this sample code you sent me over the past few days, and I think understand about the basic how to works of millis, but I can't come up with an idea to apply it to my code.
 
I've already looked up a lot of millis samples code includes this sample code you sent me over the past few days, and I think understand about the basic how to works of millis, but I can't come up with an idea to apply it to my code.

Indeed, that example solves for a simple running loop and the p#1 code has the delay()'s in button response code. And they cascade with actions in between 'wait' times. That speaks to use of a State Machine.

That would require that button response code {and needed 'context' variable for the state at hand} to be perhaps moved to loop() for ongoing attention after the first response needed to the first delay(). That would set the state machine in motion to run freely in loop() to enter the next step when the time expires. Take the next step, update the state of the state machine with a new timer wait value and repeat, running freely through loop() until that time expires and that state can enter to move the process along. The rest of loop running could monitor for conditions that would act to stop or alter the steps in that state machine.

An alternative hack/approach might be to leave all that code in the button response function(), but change the delay() to a while( 'delay time') type loop where that loop monitors for a need to exit - but if that code is in response to another button that might involve a recursive call to the same code? And that could get ugly.

Moving the work done in onButton0Pressed() into loop() with a state machine might lead to a clean solution, where the onButton0Pressed() code flags the button set and indicates the first state. Somebody wrote on FORUM or linked to a good description of a state machine if a reference is needed.

It takes some drawing on paper of the independent actions needed, and the conditions required to move from one to the next - in this case waiting for time to pass. With no delay()'s or complex operations keeping the T_3.2 busy it will cycle through loop() 10's of thousands of times per second so millisecond accuracy is to be expected.

This is NOT the FORUM note I saw long ago - but is the first that came up and evolves the idea perhaps usably:

adafruit.com/multi-tasking-the-arduino-part-1/all-together-now
 
I would take a look at using elapsedMillis instead of millis: https://www.pjrc.com/teensy/td_timing_elaspedMillis.html. So maybe do something like

Code:
elapsedMillis buttonTest;


    if (skipDelay == true)
    {
      buttonTest = 0;
      //delay(dlypot); //-------------------------------------------------delay1***
     while(buttonTest<dlypot) {
        ///test for button pressed
        /// if pressed do something
       /// return or break or something
    }

Just my thoughts. Exact implementation is up to you.
 
I would take a look at using elapsedMillis instead of millis: https://www.pjrc.com/teensy/td_timing_elaspedMillis.html. So maybe do something like

...
Just my thoughts. Exact implementation is up to you.

Indeed elapsedMillis encapsulates the work of tracking the time nicely. That solution looks like the 'alternative' to state machine mentioned. Not knowing what other code is to be completed - having a button response function look for another button to arrive seemed like it might cause conflict - and all of that would keep it from getting back to loop() for any other task that may be needed.

{Hi Mike!}
 
I totally agree with @mjs513, that the elapsedMillis is the easiest way to go and works great.

Usually the only time I don't use it, is if I need to build the code for different processor boards....

In which case I would more or less unwind the code to more or less what it does like:

Code:
uint32_t start_time;


    if (skipDelay == true)
    {
      start_time = millis();
      //delay(dlypot); //-------------------------------------------------delay1***
     while((millis() - start_time)<dlypot) {
        ///test for button pressed
        /// if pressed do something
       /// return or break or something
    }
 
Indeed, that example solves for a simple running loop and the p#1 code has the delay()'s in button response code. And they cascade with actions in between 'wait' times. That speaks to use of a State Machine.

That would require that button response code {and needed 'context' variable for the state at hand} to be perhaps moved to loop() for ongoing attention after the first response needed to the first delay(). That would set the state machine in motion to run freely in loop() to enter the next step when the time expires. Take the next step, update the state of the state machine with a new timer wait value and repeat, running freely through loop() until that time expires and that state can enter to move the process along. The rest of loop running could monitor for conditions that would act to stop or alter the steps in that state machine.

An alternative hack/approach might be to leave all that code in the button response function(), but change the delay() to a while( 'delay time') type loop where that loop monitors for a need to exit - but if that code is in response to another button that might involve a recursive call to the same code? And that could get ugly.

Moving the work done in onButton0Pressed() into loop() with a state machine might lead to a clean solution, where the onButton0Pressed() code flags the button set and indicates the first state. Somebody wrote on FORUM or linked to a good description of a state machine if a reference is needed.

It takes some drawing on paper of the independent actions needed, and the conditions required to move from one to the next - in this case waiting for time to pass. With no delay()'s or complex operations keeping the T_3.2 busy it will cycle through loop() 10's of thousands of times per second so millisecond accuracy is to be expected.

This is NOT the FORUM note I saw long ago - but is the first that came up and evolves the idea perhaps usably:

adafruit.com/multi-tasking-the-arduino-part-1/all-together-now

Thank you for your detailed explanation of the working process of the state machine with millis. And also about the useful adfruit link.
I actually moved your comment to my coding study notes. And I will study it by reference. Thank you very much.
 
I would take a look at using elapsedMillis instead of millis: https://www.pjrc.com/teensy/td_timing_elaspedMillis.html. So maybe do something like

Code:
elapsedMillis buttonTest;


    if (skipDelay == true)
    {
      buttonTest = 0;
      //delay(dlypot); //-------------------------------------------------delay1***
     while(buttonTest<dlypot) {
        ///test for button pressed
        /// if pressed do something
       /// return or break or something
    }

Just my thoughts. Exact implementation is up to you.

I actually read the documentation about elapsedMillis. But it felt a bit difficult for me so I passed it then.

Now I'm going to try it again refer to your code.
Thanks for the advice and suggesting me a solution.
 
I totally agree with @mjs513, that the elapsedMillis is the easiest way to go and works great.

Usually the only time I don't use it, is if I need to build the code for different processor boards....

In which case I would more or less unwind the code to more or less what it does like:

Code:
uint32_t start_time;


    if (skipDelay == true)
    {
      start_time = millis();
      //delay(dlypot); //-------------------------------------------------delay1***
     while((millis() - start_time)<dlypot) {
        ///test for button pressed
        /// if pressed do something
       /// return or break or something
    }

I will try elapsedMillis first following your opinion.
Thank you very much for the code correction.
 
Indeed elapsedMillis encapsulates the work of tracking the time nicely. That solution looks like the 'alternative' to state machine mentioned. Not knowing what other code is to be completed - having a button response function look for another button to arrive seemed like it might cause conflict - and all of that would keep it from getting back to loop() for any other task that may be needed.

{Hi Mike!}

Sorry Tim just saw the message with the HI MIKE - so Hi Tim. Back on forum or at least trying.
 
every person has a preference so this is what I mostly use:

Code:
static uint32_t t = millis();
if ( millis() - t > 1000 ) {
  // DoSomething();
  t = millis();
}

DoSomething() every 1 second...
benefit here is it's local to the scope so you don't need to add the variable to globals.


For timeout code, you can also do this:
Code:
volatile uint32_t timeout = 0; //(this is now put as global) (volatile if isr context)


if ( timeout && (millis() - timeout > 1000) ) {
  // DoSomething(); // stop the motor on timeout?
  timeout = 0;
}

Notice here that when timeout is 0, the statement is stopped from running. I use this technique to let me know when CAN bus goes idle while the handler sets the counter (timeout).

(Yes I could use the register's bits to identify idle, but it isn't effective over a period of time and not as clean as user readable code :) )
 
Last edited:
I finally made this delay code using millis with a state machine.
In order not to break the existing code layout, I needed a millis machine that works the same as delay();.
Thanks to those who provided wise advice and sample code.

Code:
bool repeat = true;

void setup() {
  pinMode(7, INPUT_PULLUP);
  Serial.begin(115200);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  if (repeat == false) {
    repeat = true;
    loop();
  }
}

void loop()
{
  time_loop();

  if (repeat == true);
  {
    for (int count = 0; count < int(10); count++)
    {
      digitalWrite(2, 1);

      time_delay(5000);

      digitalWrite(3, 1);

      time_delay(5000);

      digitalWrite(4, 1);

      time_delay(5000);

      digitalWrite(2, 0);

      time_delay(5000);

      digitalWrite(3, 0);

      time_delay(5000);

      digitalWrite(4, 0);

      time_delay(5000);
    }
  }

}

void time_delay(unsigned long prvTime) /////////////interval time
{
  long endTime = millis() + prvTime;
  while (millis() < endTime){
    time_loop();
  if (repeat == false) {
    break;
  }
}

void time_loop()
{
  if (digitalRead(7) == LOW)
  {
    repeat = false;
   
  }
}
 
every person has a preference so this is what I mostly use:

Code:
static uint32_t t = millis();
if ( millis() - t > 1000 ) {
  // DoSomething();
  t = millis();
}

DoSomething() every 1 second...
benefit here is it's local to the scope so you don't need to add the variable to globals.


For timeout code, you can also do this:
Code:
volatile uint32_t timeout = 0; //(this is now put as global) (volatile if isr context)


if ( timeout && (millis() - timeout > 1000) ) {
  // DoSomething(); // stop the motor on timeout?
  timeout = 0;
}

Notice here that when timeout is 0, the statement is stopped from running. I use this technique to let me know when CAN bus goes idle while the handler sets the counter (timeout).

(Yes I could use the register's bits to identify idle, but it isn't effective over a period of time and not as clean as user readable code :) )

Your millis code looks very simple and efficient!
I don't need the timeout code in my project yet, but I'll scrap the code sample you suggested.
Thank you for your kindness.
 
Back
Top