Millis Made Easier

I have been struggling to find a nice sketch to understand millis() better. I ended up writing a small bit of code that might help others bridge the gap between "Blink Without Delay" and "Doing Several Things at the Same Time". Please have a look at my very basic code, that only requires an Arduino hooked to a computer. The code runs through the Serial Monitor and could be easily modified to use LED's.

I call it EasyMillis:

/* Easy Millis()

   Prints symbols at different intervals to serial display, without using
   the delay() function. This means that other code can run at the same
   time without being interrupted by other timers.

   Only an Arduino is needed and this sketch is to be used while attached
   to the computer using the serial monitor.

  Created 2016
  by Greg Stievenart
  modified from Blink Without Delay
  by Scott Fitzgerald

  This example code is in the public domain.

*/

unsigned long Accumulator1 = 0;    // Used varible "Accumulator" for past Millis().
unsigned long Accumulator2 = 0;
unsigned long Accumulator3 = 0;
int intervalOne = 1000;            // Interval at which to display (milliseconds)
int intervalTwo = 2000;
int intervalThree = 3000;

void setup() {

  Serial.begin(9600);              // Make sure to open the Serial Monitor to view output.
}

void loop() {

  // Used three accumulators to read one millis timer. Please note that the timers will produce more
  // than one result during one period of time.

  unsigned long currentMillis = millis();

  if (currentMillis - Accumulator1 >= intervalOne) {

    Accumulator1 = currentMillis;
    Serial.println("----");
  }

  if (currentMillis - Accumulator2 >= intervalTwo) {

    Accumulator2 = currentMillis;
    Serial.println("++++");
  }

  if (currentMillis - Accumulator3 >= intervalThree) {

    Accumulator3 = currentMillis;
    Serial.println("====");
  }
}

Sample of output:

EasyMillis.ino (1.41 KB)

Thanks for sharing.

That is pretty much the same as in Several Things at a Time but without the confusion of the extra code to do things when the interval expires.

Over the long term the timing will be more accurate if you use

Accumulator1 += intervalOne;

rather than

Accumulator1 = currentMillis;

And I must confess that the variable name Accumulator1 does not convey to me the role that that variable plays. I would use previousMinusPrintMillis

...R

Robin2:
Thanks for sharing.

That is pretty much the same as in Several Things at a Time but without the confusion of the extra code to do things when the interval expires.

I agree, that's why I felt to boil it down. I have read more about people confused by millis(), as compared to good examples on millis(). There is too big of a learning gap and I am only trying to help those that "just don't get it".

Thanks for the simplified example. I use this technique frequently and call it "time bookmarking". Meaning, you "bookmark" using millis() to capture the time of the event, then use future calls to millis() to see how much time has elapsed since the event. That's essentially what you're doing here.

Thanks Greg.

Maybe make these 'unsigned int' or 'unsigned long'
Gives you the full range for the type. (don't need negative numbers :wink: ):

int intervalOne = 1000; // Interval at which to display (milliseconds)
int intervalTwo = 2000;
int intervalThree = 3000;

example: unsigned long myInterval = 400000UL;

Also this is usually accepted as more correct to prevent slippage:

Accumulator1 = currentMillis;
Change to:
Accumulator1 = Accumulator1 + intervalOne;
etc.

Did this a way back, might find something interesting here:

//Blink without Delay skeleton 
//4 examples demonstrated
//LarryD

//LED wiring options
//=============================================
//Depending which way your LEDs are wired, uncomment the next line.
//#define PlusEqualsON 

#ifdef PlusEqualsON
//wired so +5V turns LED ON
#define ledON  HIGH
#define ledOFF LOW
//=========================
#else
//wired so +5V turns LED OFF
#define ledON  LOW
#define ledOFF HIGH
//=========================
#endif

//switch wiring options
//=============================================
//Depending which way your switches are wired, uncomment the next line.
#define PushEqualsLOW 

#ifdef PushEqualsLOW 
//pushing the switch makes pin LOW
#define Pushed   LOW
#define Released HIGH
//=========================
#else
//pushing the switch makes pin HIGH
#define Pushed   HIGH
#define Released LOW
//=========================
#endif

//=============================================
unsigned long currentMillis;
unsigned long pin13Millis;
unsigned long pin12Millis;
unsigned long pin11Millis;
unsigned long SwitchMillis;

//if these are not changed in the sketch, they can be const
unsigned long debounceMillis = 100UL;     //100ms
unsigned long ledOnTime      = 5*1000UL;  //5 seconds

byte laststartSwitchState    = HIGH;
byte buttonState             = HIGH;
byte counter                 = 0;

//the following are enable/disable flags
//some of these might not be used in this sketch
boolean flag13 = true;
boolean flag12 = true;
boolean flag11 = true;
boolean flag10 = true;

const byte startSwitch = 2; //pushed = LOW
const byte testSwitch  = 3; //pushed = LOW

//**********************************************************************

void setup()
{
  Serial.begin(9600);
  
  digitalWrite(13,ledOFF);
  pinMode(13, OUTPUT); 

  digitalWrite(12,ledOFF);
  pinMode(12, OUTPUT);

  digitalWrite(11,ledOFF);
  pinMode(11, OUTPUT);
  
  digitalWrite(10,ledOFF);
  pinMode(10, OUTPUT);

  pinMode(startSwitch, INPUT_PULLUP); //pushed = LOW
  pinMode(testSwitch,  INPUT_PULLUP); //pushed = LOW

} //  >>>>>>>>>>>>>> E N D  O F  s e t u p ( ) <<<<<<<<<<<<<<<<<

void loop()
{
  //save the current time
  currentMillis = millis();

  //************************************* E x a m p l e  1
  //toggle pin 13 every 200mS
  //has 200ms or more gone by?
  if (currentMillis - pin13Millis >= 200UL)
  {
    //code here runs every 200ms
    //get ready for next iteration
    pin13Millis = pin13Millis + 200UL;
    //toggle pin 13
    digitalWrite(13,!digitalRead(13));
  }

  //************************************* E x a m p l e  2
  //at power up, pin 12 LED goes ON, after 3 seconds goes OFF and stays OFF
  //could be used as a powerup reset signal
  if (flag12 == true && currentMillis - pin12Millis <= 3000UL)
  {
    //code here runs for 3 seconds after power up, then stops
    digitalWrite(12,ledON);
  }
  else
  {
    digitalWrite(12,ledOFF);
    //disable further pin 12 control
    flag12 = false;
  }

  //************************************* E x a m p l e  3
  //if testSwitch is pushed and released
  //pin 11 LED goes ON for 5 seconds, then goes OFF 
  buttonState = digitalRead(testSwitch);
  
  //are we are allowed to check the switch and is it pressed?
  if(flag11 == true && buttonState == Pushed)
  {    
    //enable timing of LED on pin 11
    flag11 = false; //false --> timing is enabled
    //turn LED ON
    digitalWrite(11,ledON);
    //record the time LED turned ON
    pin11Millis = currentMillis;
  }
    
  //are we allowed and is it time to control pin 11
  if (flag11 == false && currentMillis - pin11Millis >= ledOnTime)
  {
    //if enabled, code here runs after ledOnTime ms goes by
    digitalWrite(11,ledOFF);
    //allow switch press detection again
    flag11 = true; //true --> switch monitoring is enabled
  }

  //************************************* E x a m p l e  4
  //is it time to check the switches?
  //in particular, pushing startSwitch will turn ON/OFF (toggle) an output pin 10
  //is it time to check the switches
  if (currentMillis - SwitchMillis >= debounceMillis)
  {
    //code here runs every debounceMillis ms
    //get ready for the next iteration
    SwitchMillis += debounceMillis; 
    //go and check the switches
    checkSwitches();    
  } 

  //*********************************
  //put other non-blocking stuff here
  //*********************************

} //  >>>>>>>>>>>>>> E N D  O F  l o o p ( ) <<<<<<<<<<<<<<<<<


//======================================================================
//                      F U N C T I O N S
//======================================================================


//****************** c h e c k S w i t c h e s ( ) *********************
//switches are checked every debounceValue milli seconds 
//no minimum switch press time is validated with this code (i.e. No glitch filter)
void checkSwitches()  
{
  //re-usable for all the switches  
  boolean thisState;    

  //************************************* E x a m p l e  Push ON push OFF (toggle)   
  //check if this switch has changed state
  thisState = digitalRead(startSwitch);
  if (thisState != laststartSwitchState)
  {  
    //update the switch state
    laststartSwitchState = thisState;  

    //this switch position has changed so do some stuff

    //"HIGH condition code"
    //switch went from LOW to HIGH
    if(thisState == HIGH)        
    {
      //Do some HIGH switch stuff here
    }

    //"LOW condition code"
    //switch went from HIGH to LOW
    else                          
    {
      //Do some LOW switch stuff here  
      digitalWrite(10, !digitalRead(10));
      //print number of pushes
      counter++;
      Serial.println(counter);
    }

  } //END of startSwitch code

  //*****************************************  
  //similar code for other switches goes here 
  //*****************************************  

} //END of checkSwitches()

//**********************************************************************

//======================================================================
//                      E N D  O F  C O D E
//======================================================================

Thanks for the responses...

LarryD, I personally like your example but, it's just too much for a beginner to swallow.

I agree that a beginner would find it more difficult.

Here is a similar way of doing the same thing.
This uses a 'structure' technique.
You may find this intersting if you have never used a structure in coding.

Note: a third way would be writing a 'class'.
I have one of these also if you are interested.

//Blink without Delay skeleton example using a 'structure' .
//LarryD

//======================================================================
struct timer
{
  //lastMillis = the time this "timer" was (re)started
  //waitMillis = delay time (mS) we are looking for. You can use math 60*60*1000 for 1 hour
  //restart    = do we start "this timer" again and again  
  //enableFlag = is "this timer" enabled/allowed to be accessed
  //**********************
  //For each timer object you need: 
  //Example:
  //   timer myTimer = //give the timer a name "myTimer"
  //   {
  //     0, 200UL, true, true  //lastMillis, waitMillis, restart, enableFlag 
  //   };
  // You have access to: 
  // myTimer.lastMillis, myTimer.waitMillis, myTimer.restart, myTimer.enableFlag, myTimer.CheckTime() 
  //**********************

  unsigned long lastMillis; 
  unsigned long waitMillis; 
  bool          restart; 
  bool          enableFlag;
  bool CheckTime() //Delay time expired function "CheckTime()"
  {
    //is the time up for this task?
    if (enableFlag && millis() - lastMillis >= waitMillis) 
    //Note: if delays of < 2 millis are needed use micros() and adjust waitMillis as needed
    {
      //should this start again? 
      if(restart)
      {
        //get ready for the next iteration
        lastMillis += waitMillis;  
      }
      //time was reached
      return true;
    }
    //time was not reached
    return false;
  } //END of CheckTime()

}; //END of structure timer
//======================================================================


//**********************************************************************
//Let's create 6 timer objects and initialize them in this sketch
//**********************************************************************
timer pin13 = //create timer pin13
{
  0, 200UL, true, true //lastMillis, waitMillis, restart, enableFlag
};
//***************************
timer pin12 = //create timer pin12
{
  0, 3*1000UL, true, true //lastMillis, waitMillis, restart, enableFlag
};
//***************************
timer pin11 = //create timer pin11
{
  0, 10*1000UL, true, true //lastMillis, waitMillis, restart, enableFlag
};
//***************************
timer pin10 = //create timer pin10
{
  0, 6*1000UL, true, true //lastMillis, waitMillis, restart, enableFlag
};
//***************************
timer Toggle10 = //create timer Toggle10
{
  0, 50UL, true, true //lastMillis, waitMillis, restart, enableFlag
};
//***************************
timer checkSwitches = //create timer checkSwitches
{
  0, 50UL, true, true //lastMillis, waitMillis, restart, enableFlag
};
//***************************

byte lastMySwitchState = 1; //for mySwitch on Pin 2
byte counter           = 0; 

const byte Pin13 = 13;
const byte Pin12 = 12;
const byte Pin11 = 11;
const byte Pin10 = 10;
const byte Pin9  =  9;

const byte mySwitch = 2;

//**********************************************************************

void setup()
{
  Serial.begin(9600);

  pinMode(Pin13,OUTPUT);
  pinMode(Pin12,OUTPUT);
  pinMode(Pin11,OUTPUT);
  pinMode(Pin10,OUTPUT);
  pinMode(Pin9, OUTPUT);

  digitalWrite(Pin13,LOW);
  digitalWrite(Pin12,LOW);
  digitalWrite(Pin11,LOW);
  digitalWrite(Pin10,LOW);
  digitalWrite(Pin9, LOW);

  pinMode(mySwitch,INPUT_PULLUP);


} //  >>>>>>>>>>>>>> E N D  O F  s e t u p ( ) <<<<<<<<<<<<<<<<<


void loop()
{
  //Below are examples demonstrating different timing situations 

  //***************************
  //example 1    Toggle Pin13 every 200ms
  if (pin13.CheckTime())
  {
    //Toggle Pin13
    digitalWrite(Pin13,!digitalRead(Pin13));

    //if you only want this section of code to happen once
    //uncomment the next line 
    //pin13.enableFlag = false;
  }

  //***************************
  //example 2    After 3 seconds, Pin12 goes and stays HIGH 
  if (pin12.CheckTime())
  {
    //Make Pin12 HIGH now
    digitalWrite(Pin12,HIGH);
    //disable timing section of code 
    pin12.enableFlag = false;
  }

  //***************************
  //example 3    Pin11 is HIGH for 10 seconds, then goes and stays LOW
  if (pin11.enableFlag && !pin11.CheckTime())
  {
    digitalWrite(Pin11,HIGH);
  }
  //10 seconds is now up now, leave the Pin11 LOW
  else
  {
    digitalWrite(Pin11,LOW);
    //disable timing section of code 
    pin11.enableFlag = false;
  }

  //***************************
  //example 4    For 6 seconds, toggle Pin10
  if (pin10.enableFlag && !pin10.CheckTime())
  {
    //example 5  Toggling Pin10 every 50mS
    if(Toggle10.CheckTime())
    {  
      //toggle Pin10
      digitalWrite(Pin10,!digitalRead(Pin10));    
    }
  }
  //6 seconds is now up, toggling is stopped
  else
  {
    digitalWrite(Pin10,LOW);
    //disable timing section of code 
    pin10.enableFlag = false;
  }

  //***************************
  //example 6    Is it time to check the switches?
  if (checkSwitches.CheckTime())
  {
    //time to read the switches
    Switches();      
  } 

  //**********************************
  //Put other non-blocking stuff here
  //**********************************

} //  >>>>>>>>>>>>>> E N D  O F  l o o p ( ) <<<<<<<<<<<<<<<<<


//======================================================================
//                      F U N C T I O N S
//======================================================================


//**********************************************************************
//switches are checked every checkSwitches.waitMillis milli seconds 
//no minimum switch press time is validated with this code (i.e. No glitch filter)
void Switches()  
{
  boolean thisState; //re-usable for all the switches      

  //*************************** mySwitch Pin 2 code  
  //check if this switch has changed state
  thisState = digitalRead(mySwitch); 
  if (thisState != lastMySwitchState)
  {  
    //update the switch state
    lastMySwitchState = thisState;  

    //This switch position has changed, let's do some stuff

    //"HIGH condition code"
    //switch goes from LOW to HIGH
    if(thisState == HIGH)        
    {
      //example: LED on Pin9 is Push ON, Push OFF
      digitalWrite(Pin9,!digitalRead(Pin9)); 
    }

    //"LOW condition code"
    //switch goes from HIGH to LOW
    else                          
    {
      //example: display the current switch push count 
      Serial.println(++counter);      
    }
  } //END of mySwitch Pin 2 code

  //******************************************  
  //similar code for other switches goes here 
  //******************************************  

} //END of Switches()


//======================================================================
//                        E N D  O F  C O D E
//======================================================================