The merits of delay() vs millis()

I don't understand. Where in this thread has anyone argued for "not debugging and learning better". So what point are you making?

Once again, I am puzzled and don't understand your point. I'll make an attempt... Nowhere have I said anything like "millis ain't so great". In any case, the glacial speed of writing to that LCD was due to a poor library, and nothing whatever to do with the discussion on millis() vs delay().

Your paragraph about using the potty and cleaning your butt... I have no idea what you are banging on about. Are you trying to be rude, suggesting that I haven't learned those skills yet? If so, your efforts are wasted on me. :rofl:

It depends on what you're trying to do.
If you want a responsive app, try it with those fine delays.

Of course what takes some work with non-blocking code... can't be achieved with blocking code at all so why compare an RTS to chess?

That says it all

This topic of some 143 posts could be summarised into just 2. The original post and phrase quoted above but as usual the subject has dragged on but comes to no conclusion any more than the many topics on strings vs Strings

1 Like

Which I did (professionally) for a decade. delay() vs millis() wasn't a decision because there was neither! OK, there effectively was a millis,

You are either being disingenuous or stupid or both and I can't tell which.
You substitute BS rhetorical strawmen for answers.

Delay is not evil. Just please if you teach it, don't boogey-man what they will need later to get out of the remedial group crying for help.

I want fast response. I want to catch intervals before they're late. I want to keep my edge sharp. I want my sketches to run better at 1MHz than delay code at 16MHz.
I want tight, small code. If it can fit in a smaller, cheaper MCU then so much the better.

1 Like

Goodness me, such anger! Take a breath, my friend; we're only talking about Arduino programming!

As you are a more advanced programmer than I am, would you be willing to post a sample of your code, showing how you optimise for maximum responsiveness in a multi-tasking application? I, and many others, would learn a lot from it, I'm sure. (This isn't sarcasm or baiting - I am genuinely committed to learning new and better ways.)

You are right, but as the OP I didn't expect it to come to a conclusion. My sole aim was to challenge some of the experts amongst us who demonise delay(), insisting it is always bad, or even "evil".

My thesis was that delay() is sometimes just fine, depending on the circumstances. The important thing being to recognise its shortcomings when deciding whether to use it.

I've certainly succeeded in making my point in multiple ways! :grinning:

So no, a resolution is unlikely, but with luck perhaps one or two of our experts might be just a teeny bit less dogmatic and "religious" about the subject, and perhaps one or two newbies will be better equipped to spot dogmatism in the advice they are given. With luck!

Software is engineering, and there is NEVER a role for dogmatism in engineering.

Your initial assertion was

I think you were given plenty of food for thoughts why this is not a general truth. You would need to make a longer exception list in your "unless" part...

but really, who cares...

I have several examples built around SIMPLE LESSONS but if you'd like to see a code tool for matching keywords that seems past most users I can dig that up, it can keep up with 250000 baud serial.

// New name addUndelay
// add-a-sketch_un-delay 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/18 by GFS. Compiled on Arduino IDE 1.6.9.
// This sketch shows a general method to get rid of delays in code.
// You could upgrade code with delays to work with add-a-sketch.

#include <avr/io.h>
#include "Arduino.h"

const byte ledPin = 13;
unsigned long delayStart, delayWait;

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Un-Delay Example, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch shows how to get rid of delays in code.\n" ));

  pinMode( ledPin, OUTPUT );
};


/* The section of the original sketch with delays:
 * 
 * digitalWrite( ledPin, HIGH );   --  0
 * delay( 500 );
 * digitalWrite( ledPin, LOW );    --  1
 * delay( 250 );
 * digitalWrite( ledPin, HIGH );   --  2
 * delay( 250 );
 * digitalWrite( ledPin, LOW );    --  3
 * delay( 250 );
 * digitalWrite( ledPin, HIGH );   --  4
 * delay( 1000 );
 * digitalWrite( ledPin, LOW );    --  5
 * delay( 1000 );
 */

byte blinkStep; // state tracking for BlinkPattern() below

void BlinkPattern()
{
  // This one-shot timer replaces every delay() removed in one spot.  
  // start of one-shot timer
  if ( delayWait > 0 ) // one-shot timer only runs when set
  {
    if ( millis() - delayStart < delayWait )
    {
      return; // instead of blocking, the undelayed function returns
    }
    else
    {
      delayWait = 0; // time's up! turn off the timer and run the blinkStep case
    }
  }
  // end of one-shot timer

  // here each case has a timed wait but cases could change Step on pin or serial events.
  switch( blinkStep )  // runs the case numbered in blinkStep
  {
    case 0 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 0 doing something unspecified here at " ));
    Serial.println( delayStart = millis()); // able to set a var to a value I pass to function
    delayWait = 500; // for the next half second, this function will return on entry.
    blinkStep = 1;   // when the switch-case runs again it will be case 1 that runs
    break; // exit switch-case

    case 1 :
    digitalWrite( ledPin, LOW );
    Serial.println( F( "Case 1 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 2;
    break;

    case 2 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 2 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 3;
    break;

    case 3 :
    digitalWrite( ledPin, LOW );
    Serial.println( F( "Case 3 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 4;
    break;

    case 4 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 4 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 1000;
    blinkStep = 5;
    break;

    case 5 :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "Case 5 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 1000;
    blinkStep = 0;
    break;
  }
}


void loop()  // runs over and over, see how often
{            
  BlinkPattern();
}

// addOnOffBlinker 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/2018 by GFS. Compiled on Arduino IDE 1.6.9.
// This sketch shows a led blink task turned on-off by another task.
// This led blinker has a built-in on-off switch, if wait > 0 it is ON.
// BlinkSwitcher is a demo that uses OnOffBlinker. 

// blinker vars
byte ledState, ledPin = 13;
word startBlink, waitBlink; // 16 bit millis is good to 65.535 seconds

// blink switching vars
byte blinkCounter, lastLedState; // these are to change the blinking
word startOffTime;
const word waitOffTime = 3000; // millis


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Blink Led, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch shows a led blink task turned on-off by another task." ));

  pinMode( ledPin, OUTPUT );

  waitBlink = 250;
}

void OnOffBlinker() // only blinks if there's a wait time, can be switched on/off
{
  if ( waitBlink > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink >= waitBlink ) // difference in time by subtracting start from end
    {
      ledState = !ledState;  // ! is NOT: not_0/true becomes 0/false else 0 becomes 1.
      digitalWrite( ledPin, ledState ); // the led changes state.
      startBlink += waitBlink; // next blink starts when it should, where diff > wait.
    }
  }
  else if ( ledState > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( ledPin, ledState = LOW ); //  make sure the led is OFF
  } // yes, you can set a variable during calculation in C, the write here is LOW.
}

void BlinkSwitcher()
{
  // this task pauses the blinker after 5 blinks then turns blinking back on, repeat.
  if ( waitBlink > 0 ) // while the led is blinking
  {
    if ( ledState != lastLedState ) // blink has just transitioned
    {
      if ( ledState == 0 ) // count a blink only if turned off, blink is finished
      {
        blinkCounter++;
        if ( blinkCounter == 5 ) // 5 blinks then none for 3 seconds
        {
          blinkCounter = 0; // the if has the ++ first, count stats at 1.
          waitBlink = 0; // turn the blinking off, start off timing
          startOffTime = millis(); // set start to off time
        }
      }
      lastLedState = ledState;
    }
  }
  else // this only runs when blinking is off
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startOffTime >= waitOffTime )
    {
      startBlink = millis();
      waitBlink = 250;
    }
  }
}

void loop()
{
  // this is the interruptable blink task
  OnOffBlinker(); // sets 'now' only when blinking
  // this task pauses the blinker after 5 blinks then turns blinking back on, repeat.
  BlinkSwitcher();
}

// New name -- addButtonArray 
// add-a-sketch_buttons 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 6/2018 by GFS. Compiled on Arduino IDE 1.6.9.
/*  Button Debounce Example

  --- for this example connect a button between pin 7 and GND
  --- or stick a jumper in pin 7 and while holding the board steady
  --- tap the free end onto the grounded USB port box to press.

  Yes I'm using a 16 bit micros timer to time fractions of millis as micros.
  The button reader only reads 1 button per call so as to not block void loop().
  Each button has a history byte that holds the last 8 reads with 256 possible
  states but only 4 of them being significant.
  0 is the button held down
  255 is the button left up
  127 is the buton transitioning from up to down, button just released.
  128 is the button transititioning from down to up, button just pressed.
  everything else is to be ignored as bounce.

  For multiple buttons on 1:1 pins the between-reads time is reduced and each
  pin is read in turn. This demo makes pin 0 be ON-RESET/OFF and pin 1 adjusts
  blink time.
*/

// multi_buttons vars
const byte buttons = 2;
byte buttonIndex, lastIndex; // only process 1 button every waitButtonTime micros
byte buttonPin[ buttons ] = { 6, 7 };
byte buttonHistory[ buttons ];
word markButtonTime;        // 16-bit micros timers
const word waitButtonTime = 250; // micros, 20 to 500 / more to fewer buttons.
// type word as micros can time across 65.535 millis before rollover, can be a few late

// added sketch task, on-off blinker vars
byte ledState, ledPin = 13; // use byte for small values, int cost 2 bytes
word startBlink, waitBlink; // 16 bit millis is good to time 65.535 seconds


void multiButtonsTask()
{ // read twice per milli, bits 0 to 6 all same sets the state
  if ( word( micros()) - markButtonTime >= waitButtonTime ) // read occaisoinally
  {
    buttonHistory[ buttonIndex ] <<= 1; // if you don't know <<= look it up in the Arduino Reference
    // keep a browser open to that page when you use the IDE.
    buttonHistory[ buttonIndex ] += digitalRead( buttonPin[ buttonIndex ] ); // read history streams through buttonHistory
    markButtonTime = micros(); // gets the low 16 bits of micros(), time to 60 ms + margin
  }

  // ++buttonIndex pre-increments buttonIndex before comparing to buttons
  if ( ++buttonIndex >= buttons )   buttonIndex = 0;
}

void OnOffBlinker() // only blinks if there's a wait time, can be switched on/off
{
  if ( waitBlink > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink >= waitBlink ) // difference in time by subtracting start from end
    {
      ledState = !ledState;  // ! is NOT: not_0/true becomes 0/false else 0 becomes 1.
      digitalWrite( ledPin, ledState ); // the led changes state.
      startBlink += waitBlink; // next blink starts when it should, where diff > wait.
    }
  }
  else if ( ledState > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( ledPin, ledState = LOW ); //  make sure the led is OFF
  } // yes, you can set a variable during calculation in C, the write here is LOW.
}


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Button Debounce Example, free by GoForSmoke\n" ));
  Serial.println( F( "This code shows use of struct, union, bit fields and 16 bit timers." ));
  Serial.println( F( "It can work with many buttons, uses structs to hold button data." ));
  Serial.println( F( "Button data structs can be arrayed even for matrix arrangements." ));
  Serial.println( F( "\n-- for this example connect a button between pin 7 and GND" ));
  Serial.println( F( "--- or stick a jumper in pin 7 and while holding the board steady" ));
  Serial.println( F( "--- tap the free end onto the grounded USB port box to press." ));

  pinMode( ledPin, OUTPUT );

  for ( byte i = 0; i < buttons; i++ )
  {
    pinMode( buttonPin[ i ], INPUT_PULLUP );
    buttonHistory[ i ] = 255;
  }
};


void loop()
{
  multiButtonsTask();
  /*
    0 is the button held down
    255 is the button left up
    127 is the buton changing from up to down, button just released.
    128 is the button changing from down to up, button just pressed.
    everything else is to be ignored as bounce.
  */

  if ( buttonIndex != lastIndex )
  {
    lastIndex = buttonIndex;

    switch ( buttonHistory[ buttonIndex ] ) // buttonHistory does not change as fast as this runs
    {
      case 128 : // pin is HIGH in bit 7, LOW for 7 reads, up to down detected
        buttonHistory[ buttonIndex ] = 0; // change detected, make it into no change now
        Serial.print( F( "button " ));
        Serial.print( buttonIndex );
        Serial.print( F( "  press detected     " ));
        Serial.println( millis());
        if ( buttonIndex == 0 )
        {
          if ( waitBlink == 0 ) // toggle action tied to button press
          {
            waitBlink = 500; // makes the blinking start
            startBlink = millis(); // gets the low 16 bits
          }
          else // press button 2 changes the blink rate
          {
            waitBlink = 0; // makes the blinking stop
          }
        }
        else
        {
          if (( waitBlink -= 100 ) < 100 ) // setting a var in an expression is okay C
          {
            waitBlink = 1000;
          }
        }
        break;
      case 127 : // pin is LOW in bit 7, HIGH for 7 reads, down to up detected
        buttonHistory[ buttonIndex ] = 255; // change detected, make it into no change now
        Serial.print( F( "button " ));
        Serial.print( buttonIndex );
        Serial.print( F( "  release detected   " ));
        Serial.println( millis());
        break;
    }
  }

  OnOffBlinker();
}

// addLoopCounter 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 29/2018 by GFS. Compiled on Arduino IDE 1.6.9.
// This sketch counts times that loop has run each second and prints it.
// It uses the void LoopCounter() function that does not block other code.

#define microsInOneSecond 1000000UL

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Loop Counter, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch counts times that loop has run each second and prints it." ));
}

void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    Serial.println( count ); // 32-bit binary into decimal text = many micros
    count = 0; // don't forget to reset the counter 
  }
}

void loop()  // runs over and over, see how often with LoopCounter()
{
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.
}

This is a test run from setup() feeding the match routine though it might be reading the letters to match from flash. A word missed shows error.

The match is a tool for other code. The data is good for auto-completes and name/word list generation. To that end; size and speed are concerns.

I have written and used several versions of this in the 80's and 90's on contracts.
This and the one I finished in 2016 are improved over what deadlines limited.

This kind of code really needs to be used on ANSI terminals or emulators.

I can help you use this or similar but I draw the line at the paid work level. I do this to teach, to pass on what I know as I can. There's a gem of an algorithm at the heart of the search part of match().

// serial_word_match_speed_test ver 1.0 Feb 11, 2019 by GoForSmoke on the Arduino.cc forum.

/*
  User loads a RAM array to hold a sorted word list

  1-pass match/no_match keywords and evaluate numbers in text stream/data.
*/

void usage()
{
  Serial.println( F( "\nserial_word_match_RAM" ));
}

byte keys = 0; 

byte *startPoint[ 95 ]; // 1st word starting with each char from 33 to 121


char text;
int current = -1;  // current match word index, may be -1 for none
PGM_P curPtr;  // pointer into PROGMEM to get word list chars
byte charsMatched, charToMatch;  // no key words over 255 chars....
byte matchState; // match_start, matching, number, no_match, list_matched, finished_match

byte maxDigits = 5;
unsigned long ulAccum;


enum matchType { match_start = 0, matching, number, no_match, list_matched, finished_match };

void listWords()
{
  int i;
  byte j;
  long listLen = 0;

  Serial.println( "\n\n\n\n\n\n\n\nword list" );
  for ( i = 0; i < keys; i++ )
  {
    Serial.print( "key " );
    Serial.print( i );
    Serial.print( " - " );
    curPtr = (PGM_P)pgm_read_word( key_list + i );
    j = 0;
    while (( charToMatch = pgm_read_byte( curPtr + j++ )) > 0 )
    {
      listLen++;
      Serial.write( charToMatch );
    }
    Serial.println();
  }

  Serial.print( "\nTotal list characters = " );
  Serial.println( listLen );

  Serial.println( "\na - z starts" );
  for ( j = 0; j < 26; j++ )
  {
    i = pgm_read_word( a_z_starts + j );
    if ( i >= 0 )
    {
      Serial.write( 'a' + j );
      Serial.print( " " );
      Serial.println( i );
    }
  }

  Serial.println( "\n\nchars that match previous word chars" );
  for ( i = 0; i < keys; i++ )
  {
    j = pgm_read_byte( charsMatchingPrevWord + i );
    Serial.print( "key " );
    Serial.print( i );
    Serial.print( " - " );
    Serial.println( j, DEC );
  }
}



/*
   need int indexes to current and alternate words and byte chars matched offset.

   states - match_start, matching, number, no_match, list_matched, finished_match
*/


void match() // uses globals, new char is in char text
{
  if ( matchState == finished_match )  matchState = match_start;

  switch ( matchState ) // match_start, matching, number, no_match, list_matched, finished_match
  {
    case match_start :
      if ( text >= 33 && text <= 127 )
      {
        current = int( pgm_read_word( a_z_starts + ( text - 'a' ) ));
        if ( current < 0 )  // no list word starts with the text letter
        {
          matchState = no_match; // no_match
        }
        else
        {
          matchState = matching;
          charsMatched = 1;
        }
      }
      else
      {
        matchState = no_match; // no_match
      }
      break;

    case matching : // matching
    case list_matched :
      if ( text == ' ' || text == '\n' )
      {
        if ( matchState == list_matched )      matchState = finished_match;
        else if ( text == '\n' )    matchState = match_start;
        else                        matchState = no_match;
        break; // exit the case
      }

      curPtr = (PGM_P)pgm_read_word( key_list + current );
      charToMatch = pgm_read_byte( curPtr + charsMatched );

      if ( text == charToMatch ) // matches the list word so far
      {
        charsMatched++;
        charToMatch = pgm_read_byte( curPtr + charsMatched );

        if ( charToMatch == 0 )
        {
          matchState = list_matched; // matched a list word
        }
      }
      else // find an alternate match or no_match
      {
        current++;

        // we have chars matched and how many chars of the current word the next word matches
        // if that number is smaller than already matched chars then no_match
        // if that number is bigger then try the next list word
        // if that number is the same then try to match the next char to text,
        // ---- if they match then new current word and charsMatched++
        // ---- else try the next list word

        while ( current < keys )
        {
          charToMatch = pgm_read_byte( charsMatchingPrevWord + current );

          if ( charToMatch < charsMatched )
          {
            matchState = no_match;
            break; // break from the while loop
          }
          else if ( charToMatch > charsMatched )
          {
            current++;
          }
          else // check this one
          {
            curPtr = (PGM_P)pgm_read_word( key_list + current );
            charToMatch = pgm_read_byte( curPtr + charsMatched );

            if ( text == charToMatch )
            {
              charsMatched++;
              charToMatch = pgm_read_byte( curPtr + charsMatched );

              if ( charToMatch == 0 )
              {
                matchState = list_matched;
              }
              break;  // break from the while loop
            }
            else
            {
              current++;
            }
          }
        }

        if ( current >= keys )
        {
          matchState = no_match; // no_match
        }
      }
      break;

    case no_match :
      if ( text == '\n' ) // reads and discards serial until newline
      {
        current = -1;
        charsMatched = 0;
        matchState = match_start;
        Serial.println( "\n error\n" );
      }
      break;

    case number :
      if ( text >= '0' && text <= '9' )
      {
        if ( charsMatched++ < maxDigits )
        {
          ulAccum *= 10UL;
          ulAccum += byte( text - '0' );
        }
        else  matchState = no_match; // exceeded max length
      }
      else if ( text == ' ' || text == '\n' )
      {
        matchState = finished_match;
      }
      break;
  }
}

void hitAny()
{
  while ( Serial.available() < 1 );
  while ( Serial.available() > 0 )
  {
    Serial.read();
    delay( 250 ); // in case autorepeat is held down
  }
}

void speedTest( int count )
{
  static int i;
  static byte j;

  while ( count-- > 0 )
  {
    for ( i = 0; i < keys; i++ )
    {
      curPtr = (PGM_P)pgm_read_word( key_list + i );
      j = 0;
      while (( text = pgm_read_byte( curPtr + j++ )) > 0 )
      {
        match();
        if ( matchState == no_match )
        {
          Serial.print( "test no_match fail at count " );
          Serial.print( count, DEC );
          Serial.print( "   word " );
          Serial.print( i );
          Serial.print( "   char " );
          Serial.println( j );
          return;
        }
      }
      text = '\n';
      match();
      if ( matchState != finished_match ) 
      {
          Serial.print( "test state " );
          Serial.print( matchState );
          Serial.print( " fail at count " );
          Serial.print( count, DEC );
          Serial.print( "   word " );
          Serial.print( i );
          Serial.print( "   char " );
          Serial.println( j );
        return;
      }
    }
  }

  return;
}
//===========================================================

void setup()
{
  Serial.begin( 115200 );
  Serial.flush();
//  listWords();
//  hitAny();
  usage();

  unsigned long startms = millis();

  speedTest( 1000 );

  if ( matchState == finished_match )
  {
    startms = millis() - startms; // startms now has elapsed millis
    Serial.print( "\n millis = " );
    Serial.println( startms );
  }
}


void loop()
{
}

Do we need a new category for never ending discussions?

1 Like

isn't that what the bar is about ? :wink:
(may be time to move this thread there)

Hell I’ve been drinking all thru this, does that mean I have to stop ? :dizzy_face:

2 Likes

Anyone for or against this?

It’s overdue.

1 Like

long overdue indeed. It's not longer a programming question, it's a religion discussion :wink:

1 Like

Thanks for posting all that, @GoForSmoke. My Internet connection is down so I can only follow this on my phone, which is absolutely hopeless for looking at code, but it should be back tomorrow. Thanks again.

Nooo, noooo.... it's an anti-religion discussion! :grinning:

At least that's what I was shooting for when I started the thread.

Moved, as requested.

Isn't that like 'flammable' and 'inflammable', they mean the same thing!

1 Like