A function to replace delay()?

Like most newbies, I’m trying to get my head around millis() in a consistent way. I get the basic gist, but writing all the steps out every time I want to use delay() is tedious. So I wondered if I could write a function that I can just drop in to my sketches.

This is what I came up with. The function is lines 6-12, wrapped in a basic sketch to give it context. What I’m hoping for is some feedback as to whether it’s doing what I think does? It certainly looks to be working like delay() when I run the sketch. I’m curious if I’m just sending the program off to a function for however long and loop() is still stuck waiting for the function to finish?

// Set up the builtin LED
const int ledPin =  LED_BUILTIN; // the number of the LED pin

// Attempt at a delay replacement function
// USAGE: myDelay(<delay in milliseconds>);
void myDelay(int del) {
  unsigned long myPrevMillis = millis();
  unsigned long myCurrentMillis = myPrevMillis;
  while (myCurrentMillis - myPrevMillis <= del) {
  myCurrentMillis = millis();
  }
}

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

void loop() {
  digitalWrite(ledPin,HIGH);
  myDelay(500);
  digitalWrite(ledPin,LOW);
  myDelay(200);
}

should work OK - you could simplify it too

void myDelay(int del) {
  unsigned long myPrevMillis = millis();
  while (millis()- myPrevMillis <= del);
}

what are you trying to achieve? you have replicated the functionality of delay()

horace:
what are you trying to achieve? you have replicated the functionality of delay()

Exactly, I am trying to replicate delay() so that I don't have to write out the whole millis() thing each time as shown in the examples (ie. the pinned thread and the BlinkWithoutDelay sketch).

Thanks for the simplified code. That makes sense and I imagine as I get more experience it will come naturally for me.

Using BWD with the millis() function is supposed to make your code non blocking.

Is your function blocking?

EDIT
Study this example.

The whole point of using millis() is to avoid blocking your code by the use of delay(). Your function blocks just like delay() so it provides no benefit. Blocking code is bad whether it uses delay() or not. You can find non-blocking timing functions/libraries but really just doing "blink without delay" style millis() usage is quite easy once you understand the basic concept.

Just spend some time studying File > Examples 02.Digital > BlinkWithoutDelay and the associated tutorial:

delay() has a yield(). Your function doesn't. That is about as far as you can go with a delay() idea.

You will not be able to write a non-blocking function that replaces delay. You need to think differently.

You can however write a function that checks if a certain duration has lapsed.

/*
  check if a duration has lapsed
  In:
    duration (in ms)
  Returns:
    false if duration has not lapsed, else true
 */
bool wait(unsigned long duration)
{
  static unsigned long startTime;
  static bool isStarted = false;

  // if wait period not started yet
  if(isStarted == false)
  {
    // set the start time of the wait period
    startTime = millis();
    // indicate that it's started
    isStarted = true;
    // indicate to caller that wait period is in progress
    return false;
  }

  // check if wait period has lapsed
  if(millis() - startTime >= duration)
  {
    // lapsed, indicate no longer started so next time we call the function it will initialise the start time again
    isStarted = false;
    // indicate to caller that wait period has lapsed
    return true;
  }

  // indicate to caller that wait period is in progress
  return false;
}

And in loop() you can use something like

void loop()
{
  bool waitFinished = wait(1000);
  if(waitFinished == true)
  {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN);
  }

  // do something else here
}

Now this a 'difficult' way to do a blink-without-delay but it's there to demonstrate the use of the wait function. Note that this code is not tested.

Now, if you have a need for a delay, it usually indicates that your program has a few steps to execute in sequence. In that case you can look at statemachines.

In the below example, a statemachine is used.

First we define the steps (states); in this case there are 4
1)
switch led on
2)
wait for given duration
3)
switch led off
4)
wait for given duration

Each time a step is completed, the code will go to the next step. Using the earlier wait function, the code will not go to the next step till the wait function indicates that the duration has lapsed.

#define ST_LEDON 0    // state where the led will be switched on
#define ST_WAITON 1   // state where we wait till the led has been on for a given duration
#define ST_LEDOFF 2   // state where the led will be switched off
#define ST_WAITOFF 3  // state where we wait till the led has been off for a given duration

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  // remember the current state; initialise with ST_LEDON
  static byte currentState = ST_LEDON;

  switch (currentState)
  {
    case ST_LEDON:
      // switch the led on
      digitalWrite(LED_BUILTIN, HIGH);
      // go to the next step (state)
      currentState = ST_WAITON;
      break;
    case ST_WAITON:
      // check if on duration has lapsed
      if (wait(750) == true)
      {
        // if lapsed, go to next step (state)
        currentState = ST_LEDOFF;
      }
      break;
    case ST_LEDOFF:
      // switch the led off
      digitalWrite(LED_BUILTIN, LOW);
      // go to the next step (state)
      currentState = ST_WAITOFF;
      break;
    case ST_WAITOFF:
      // check if off duration has lapsed
      if (wait(250) == true)
      {
        // if lapsed, go to next step (state)
        currentState = ST_LEDON;
      }
      break;
  }
}

/*
  check if a duration has lapsed
  In:
    duration (in ms)
  Returns:
    false if duration has not lapsed, else true
*/
bool wait(unsigned long duration)
{
  static unsigned long startTime;
  static bool isStarted = false;

  // if wait period not started yet
  if (isStarted == false)
  {
    // set the start time of the wait period
    startTime = millis();
    // indicate that it's started
    isStarted = true;
    // indicate to caller that wait period is in progress
    return false;
  }

  // check if wait period has lapsed
  if (millis() - startTime >= duration)
  {
    // lapsed, indicate no longer started so next time we call the function it will initialise the start time again
    isStarted = false;
    // indicate to caller that wait period has lapsed
    return true;
  }

  // indicate to caller that wait period is in progress
  return false;
}

Code tested

Here is an interesting thread to look at:

Ultimate Blink Without Delay Timer Class Library With Lambda & Callback- for UNO - Exhibition / Gallery - Arduino Forum

Alternative function:

bool wait(unsigned long &prev, const unsigned long dur, const unsigned long cur = millis()){
  if(cur - prev >= dur){
    prev = cur;
    return true;
  }
  return false;
}

Or with a simple class:

class Wait{
  public:
    Wait() : Wait(1000){}
    Wait(const unsigned long dur) : _dur(dur){}
    void setDuration(const unsigned long dur){
      _dur = dur;
    }
    bool done(const unsigned long cur){
      if(cur - _prev >= _dur){
        _prev = cur;
        return true;
      }
      return false;      
    }
    bool done(){
      done(millis());
    }
  private:
    unsigned long _prev;
    unsigned long _dur;
};

horace:
should work OK - you could simplify it too

void myDelay(int del) {

unsigned long myPrevMillis = millis();
  while (millis()- myPrevMillis <= del);
}



what are you trying to achieve? you have replicated the functionality of delay()

This is ridiculous.

Because it uses WHILE it has the exact same blocking effect as delay()

…R

Take a look at https://github.com/WestfW/Duino-hacks/blob/master/delay_without_delay/delay_without_delay.ino