Understanding millis with multiple LEDs

First, I am very grateful to have all of you as a resource.

I am new to Arduino and programming.

I have a project planned and have a few months to complete it (birthday present).

At this point I have probably spent ~40 hrs researching and admiring other projects.

I need to make 8-10 leds flash at different rates. I need to control the timing of each.

I have looked and looked for examples from others but have not found what I need.

So, I need an led to be on for 1000, off for 10000, on 500, off for 4000, etc...

Each will need a separate schedule.

How do I approach this using millis?

I appreciate any INPUT :slight_smile:

You need to use the technique in the Blink Without Delay example sketch. I wrote an extended example in the first post in this Thread. It may give you something to start from.

...R

And for this specific problem, I wrote this sketch:

// Blink without "delay()" - multi!

const int led1Pin =  13;    // LED pin number
const int led2Pin =  10;
const int led3Pin =  11;

int led1State = LOW;        // initialise the LED
int led2State = LOW;
int led3State = LOW;

unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

void setup() {
  pinMode(led1Pin, OUTPUT);      
  pinMode(led2Pin, OUTPUT);      
  pinMode(led3Pin, OUTPUT);      
}

void loop() {
  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count1, 500UL )) {
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW; 
    } 
    digitalWrite(led1Pin, led1State);
  } 

  if (timeout(&count2, 300UL )) {
    if (led2State == LOW) {
      led2State = HIGH;
    }
    else {
      led2State = LOW; 
    } 
    digitalWrite(led2Pin, led2State);
  } 

  if (timeout(&count3, 77UL )) {
    if (led3State == LOW) {
      led3State = HIGH;
    }
    else {
      led3State = LOW; 
    } 
    digitalWrite(led3Pin, led3State);
  } 
}

First, thank you both very much for helping me.

I spent about 3 hours last night and 2 hours so far today trying to figure this out.

I said it in an earlier post but I'll explain again so you don't have to search for it.

Working on just one led now but will eventually be doing multiple.

I would like to turn an led on for 500, off for 1000, then on for 2000 of for 500, then on for.....

With the code Robin2 gave me I am able to control the on and off times for the leds but I have not figured out how to make a string of different on and off times for the same led.

This is what I came up with from the SeveralThingsAtTheSameTimeRev1.ino example

// Adapted from SeveralThingsAtTheSameTimeRev1.ino

// ----CONSTANTS (won't change)

const int redPin =  13;      // the pin numbers for the LEDs
const int yellowPin = 12;
const int greenPin = 11;
const int bluePin = 10;

const int off500Interval = 500; // number of millisecs between blinks
const int off2500Interval = 2500;
const int off4500Interval = 4500;
const int off3000Interval = 3000;


const int on500 = 500; // number of millisecs that Led's are on - all three leds use this
const int on1000 = 1000;
const int on2000 = 2000;
//------- VARIABLES (will change)



byte redState = LOW;             // used to record whether the LEDs are on or off
byte yellowState = LOW;           //   LOW = off
byte greenState = LOW;
byte blueState = LOW;

unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousRedMillis = 0;   // will store last time the LED was updated
unsigned long previousYellowMillis = 0;
unsigned long previousGreenMillis = 0;
unsigned long previousBlueMillis = 0;
//========

void setup() {

  
      // set the Led pins as output:
  pinMode(redPin, OUTPUT);
  pinMode(yellowPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  
}

//=======

void loop() {

      // Notice that none of the action happens in loop() apart from reading millis()
      //   it just calls the functions that have the action code

  currentMillis = millis();   // capture the latest value of millis()
                              //   this is equivalent to noting the time from a clock
                              //   use the same time for all LED flashes to keep them synchronized
  

  updateRedState();
  updateYellowState();
  updateGreenState();
  updateBlueState();
  switchLeds();


}

//========

void updateRedState() {

  if (redState == LOW) {
          // if the Led is off, we must wait for the interval to expire before turning it on
    if (currentMillis - previousRedMillis >= off500Interval) {
          // time is up, so change the state to HIGH
       redState = HIGH;
          // and save the time when we made the change
       previousRedMillis += off500Interval;
          // NOTE: The previous line could alternatively be
          //              previousRedMillis = currentMillis
          //        which is the style used in the BlinkWithoutDelay example sketch
          //        Adding on the interval is a better way to ensure that succesive periods are identical

    }
  }
  else {  // i.e. if redState is HIGH
  
          // if the Led is on, we must wait for the duration to expire before turning it off
    if (currentMillis - previousRedMillis >= on500) {
          // time is up, so change the state to LOW
       redState = LOW;
          // and save the time when we made the change
       previousRedMillis += on500;
    } 
  }
}

//---------------------------

//-----------------------------------------

//=======

void updateYellowState() {

  if (yellowState == LOW) {
    if (currentMillis - previousYellowMillis >= off2500Interval) {
       yellowState = HIGH;
       previousYellowMillis += off2500Interval;
    }
  }
  else {
    if (currentMillis - previousYellowMillis >= on1000) {
       yellowState = LOW;
       previousYellowMillis += on1000;
    } 
  }    
}

//=======

void updateGreenState() {

  if (greenState == LOW) {
    if (currentMillis - previousGreenMillis >= off4500Interval) {
       greenState = HIGH;
       previousGreenMillis += off4500Interval;
    }
  }
  else {
    if (currentMillis - previousGreenMillis >= on500) {
       greenState = LOW;
       previousGreenMillis += on500;
    }
  }    
}

//==========

void updateBlueState() {

  if (blueState == LOW) {
    if (currentMillis - previousBlueMillis >= off3000Interval) {
       blueState = HIGH;
       previousBlueMillis += off3000Interval;
    }
  }
  else {
    if (currentMillis - previousBlueMillis >= on1000) {
       blueState = LOW;
       previousBlueMillis += on1000;
    } 
  }    
}

//========

void switchLeds() {
      // this is the code that actually switches the LEDs on and off

  digitalWrite(redPin, redState);
  digitalWrite(yellowPin, yellowState);
  digitalWrite(greenPin, greenState);
  digitalWrite(bluePin, blueState);
  }

This is an example of my train of thought. I understand why it does not work, I just do not understand how to make it work.

void updateBlueState() {

  if (blueState == LOW) {
    if (currentMillis - previousBlueMillis >= off3000Interval) {
       blueState = HIGH;
       previousBlueMillis += off3000Interval;
    }
  }
  else {
    if (currentMillis - previousBlueMillis >= on1000) {
       blueState = LOW;
       previousBlueMillis += on1000;
    } 
  }    

  if (blueState == LOW) {
    if (currentMillis - previousBlueMillis >= off500Interval) {
       blueState = HIGH;
       previousBlueMillis += off500Interval;
    }
  }
  else {
    if (currentMillis - previousBlueMillis >= on2000) {
       blueState = LOW;
       previousBlueMillis += on2000;
    } 
  }    
}

For the Blink without "delay()" - multi! code Paul__B gave me, I understand what is being programmed. The leds are on and off for the same amount of time yet I can not figure out how to make the on and off times different, let alone make a string of different led on and offs.

// Blink without "delay()" - multi!

const int redPin =  13;    // LED pin number
const int bluePin =  10;
const int greenPin =  11;

int redState = LOW;        // initialise the LED
int blueState = LOW;
int greenState = LOW;
int reddState = HIGH;

unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

void setup() {
  pinMode(redPin, OUTPUT);      
  pinMode(bluePin, OUTPUT);      
  pinMode(greenPin, OUTPUT);      
}

void loop() {
  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count1, 500UL )) {
    if (redState == LOW) {
      redState = HIGH;
    }
    else {
      redState = LOW; 
    }   
    digitalWrite(redPin, redState);
  } 

  if (timeout(&count2, 300UL )) {
    if (blueState == LOW) {
      blueState = HIGH;
    }
    else {
      blueState = LOW; 
    } 
    digitalWrite(bluePin, blueState);
  } 

  if (timeout(&count3, 77UL )) {
    if (greenState == LOW) {
      greenState = HIGH;
    }
    else {
      greenState = LOW; 
    } 
    digitalWrite(greenPin, greenState);
  } 
  }

This was another lame attempt by me... (same train of thought)

void loop() {
  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count1, 500UL )) {
    if (redState == LOW) {
      redState = HIGH;
    }
    else {
      redState = LOW; 
    } 
   if (timeout(&count1, 3000UL )) {
    if (reddState == LOW) {
      reddState = HIGH;
    }
    else {
      reddState = LOW; 
    } 
    
    
    digitalWrite(redPin, redState);
  }

Anyways, if all else fails ask for help, right?

I appreciate all the help I have received and will receive! Thank you.

Different times, eh?

Hmmm.

// Blink without "delay()" - multi!

const int led1Pin =  13;    // LED pin number
const int led2Pin =  10;
const int led3Pin =  11;
int led1State = 0;          // initialise the LED
long int led1time = 10UL;    // and an initial time
int led2State = LOW;
int led3State = LOW;
unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

void setup() {
  pinMode(led1Pin, OUTPUT);      
  pinMode(led2Pin, OUTPUT);      
  pinMode(led3Pin, OUTPUT);      
  led1State =   0;        // initialise the LED
  led1time = 1UL;         // and an initial time
}

void loop() {
  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count1, led1time )) {
    switch (led1State) {
    case 0: 
      {                              // begin the sequence
        led1State = 1;               // next step
        led1time = 500UL;            // for half a second
        digitalWrite(led1Pin, HIGH); // switch it on
        break; 
      }

    case 1: 
      {
        led1State = 2;               // next step
        led1time = 1000UL;           // for however
        digitalWrite(led1Pin, LOW);  // switch it off
        break; 
      }

    case 2: 
      {
        led1State = 3;
        led1time = 2000UL;
        digitalWrite(led1Pin, HIGH); // switch it on
        break; 
      }

    case 3: 
      {
        led1State = 0;
        led1time = 500UL;
        digitalWrite(led1Pin, LOW);  // switch it off
        break; 
      }

    default: 
      led1State = 0;    // reset
    }
  } 

  if (timeout(&count2, 300UL )) {
    if (led2State == LOW) {
      led2State = HIGH;
    }
    else {
      led2State = LOW; 
    } 
    digitalWrite(led2Pin, led2State);
  } 

  if (timeout(&count3, 77UL )) {
    if (led3State == LOW) {
      led3State = HIGH;
    }
    else {
      led3State = LOW; 
    } 
    digitalWrite(led3Pin, led3State);
  } 
}

I hope I can remember this. I just pressed reply a moment ago and it all disappeared.

If you want to have several different intervals for an LED (or anything else) put the intervals in an array and use another variable to keep track of where you are in the array. Something like this. I have marked the new lines. I hope there are no typos. I haven't updated the ELSE part but it needs the same treatment

I think you could move the line "if (currentMillis ...." before "if (redState == LOW) " so you only need it once. Maybe my example needs tidying up. Then you could put the "redIndex ++" bit after the redState Low stuff but just inside the if (currentMillis closing }.

int redInterval = {500, 1000, 2000, 500};   // new
byte redIndex = 0;  // new

void updateRedState() {

  if (redState == LOW) {
          // if the Led is off, we must wait for the interval to expire before turning it on
    if (currentMillis - previousRedMillis >= redInterval[redIndex]) {       // new
          // time is up, so change the state to HIGH
       redState = HIGH;
          // and save the time when we made the change
       previousRedMillis += redInterval;     // new
       
       redInterval ++;            // new
       if (redInterval > 3) {     // new
          redInterval = 0;        // new
       }                          // new

    }
  }
  else {  // i.e. if redState is HIGH
  
          // if the Led is on, we must wait for the duration to expire before turning it off
    if (currentMillis - previousRedMillis >= on500) {
          // time is up, so change the state to LOW
       redState = LOW;
          // and save the time when we made the change
       previousRedMillis += on500;
    } 
  }
}

...R

Robin2:
I hope I can remember this. I just pressed reply a moment ago and it all disappeared.

Do you mean to say that you hit "Post" (or "Preview") and the forum server farted and gave you the "Broken" page with "ARDUINO?" poorly depicted in teletype graphics?

I am perfectly used to that - it is actually quite simple to overcome but extremely annoying and time-wasting. Do not go back or forward or anywhere else. The only thing you need to do, is to click "Reload" on Firefox, which then pops up a reference to "post"ed information so you click "Resend" and you have lost nothing.

You may have to do this a number of times, but the information is not lost.

Yep, total garbage forum software, but that's how it is.

Thanks @Paul__B.

I've seen the "broken" thing before and I will try your remedy next time.

On this occasion when I pressed send (I think) it just shifted me to a blank reply page. Of course I might have accidentally hit some other key.

...R

Robin2:
On this occasion when I pressed send (I think) it just shifted me to a blank reply page. Of course I might have accidentally hit some other key.

The "Broken" phenomenon appears (to me) to be connected to a ridiculously delayed reply - which for a "Post" (as against a "Preview" where it shows a "Fetching preview..." message above the submission window) is blank in the meantime.

Thank you guys so much.

I was able to get Paul__B's code to do what I wanted.

I am still working on figuring out how to implement Robin2's version.

Thank you for all of your help!!

1rulon1:
I was able to get Paul__B's code to do what I wanted.

It is designed for simplicity, and extensibility.

So - did you figure out how to on the one hand, make the sequence more complex and on the other, implement the sequences for the other LEDs - as many as you want? I do hope so.

I presumably could develop it to use arrays - I was thinking of that but unless someone is desperate, I will leave it for the present. The more important matter is to test the next step for debouncing of (any reasonable number of) buttons.

Paul__B

It is simple and thats why I was able to figure it out :slight_smile:

I am still trying to figure out Robin2's array, I'd like to see that in action.

But I couldn't sleep last night and figured out my first RGB led button color change with debounce.

That was a doozy for a beginner like me.

Thanks for the help!

Robin2,

I have been trying to figure out the array.

I think I am pretty close.

I am getting an error message "scalar object 'redInterval' requires one element in intializer'.

Where am I going wrong?

Thank you.

const int redPin =  13;      
const int yellowPin = 12;
const int greenPin = 11;
const int bluePin = 10;

const int on500 = 500;
const int on1000 = 1000;
const int on2000 = 2000;

int redInterval = {500, 1000, 2000, 500};
int yellowInterval = {100, 1000, 200, 5000};
int greenInterval = {1500, 700, 800, 2000};
int blueInterval = {400, 1000, 200, 900};

byte redState = LOW;             
byte yellowState = LOW;           
byte greenState = LOW;
byte blueState = LOW;

byte redIndex = 0;
byte yellowIndex = 0;
byte greenIndex = 0;
byte blueIndex = 0;

unsigned long currentMillis = 0;    
unsigned long previousRedMillis = 0;   
unsigned long previousYellowMillis = 0;
unsigned long previousGreenMillis = 0;
unsigned long previousBlueMillis = 0;


void setup() {

  
      
  pinMode(redPin, OUTPUT);
  pinMode(yellowPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  
}



void loop() {

      

  currentMillis = millis();   

  updateRedState();
  updateYellowState();
  updateGreenState();
  updateBlueState();
  switchLeds();


}

//========

void updateRedState() {

  if (redState == LOW) {
          
    if (currentMillis - previousRedMillis >= redInterval[redIndex]) {       
          
       redState = HIGH;
          
       previousRedMillis += redInterval;     
       
       redInterval ++;            
       if (redInterval > 3) {     
          redInterval = 0;        
       }                          

    }
  }
  else {  
  
          
    if (currentMillis - previousRedMillis >= on500) {
        
       redState = LOW;
          
       previousRedMillis += on500;
    } 
  }
}



void updateYellowState() {

  if (yellowState == LOW) {
          
    if (currentMillis - previousYellowdMillis >= yellowInterval[yellowIndex]) {       
          
       yellowState = HIGH;
          
       previousYellowMillis += YellowInterval;     
       
       yellowInterval ++;            
       if (yellowInterval > 3) {     
          yellowInterval = 0;        
       }                          

    }
  }
  else {  
  
          
    if (currentMillis - previousYellowMillis >= on500) {
        
       yellowState = LOW;
          
       previousYellowMillis += on500;
    } 
  }
}
void updateGreenState() {

  if (greenState == LOW) {
          
    if (currentMillis - previousGreenMillis >= greenInterval[greenIndex]) {       
          
       greenState = HIGH;
          
       previousGreenMillis += greenInterval;     
       
       greenInterval ++;            
       if (greenInterval > 3) {     
          greenInterval = 0;        
       }                          

    }
  }
  else {  
  
          
    if (currentMillis - previousGreenMillis >= on500) {
        
       greenState = LOW;
          
       previousGreenMillis += on500;
    } 
  }
}


void updateBlueState() {

  if (blueState == LOW) {
          
    if (currentMillis - previousBlueMillis >= blueInterval[blueIndex]) {       
          
       blueState = HIGH;
          
       previousBlueMillis += blueInterval;     
       
       blueInterval ++;            
       if (blueInterval > 3) {     
          blueInterval = 0;        
       }                          

    }
  }
  else {  
  
          
    if (currentMillis - previousBlueMillis >= on500) {
        
       blueState = LOW;
          
       previousBlueMillis += on500;
    } 
  }
}



void switchLeds() {
      

  digitalWrite(redPin, redState);
  digitalWrite(yellowPin, yellowState);
  digitalWrite(greenPin, greenState);
  digitalWrite(bluePin, blueState);
  }

Nevermind, []

Still have some errors to figure out though....

1rulon1:
But I couldn't sleep last night and figured out my first RGB led button color change with debounce.

Hmm, perhaps you might be interested in my ("new") button debounce code.

// Blink without "delay()" - multi!

const int led1Pin =  13;    // LED pin number
const int led2Pin =  10;
const int led3Pin =  11;
const int button1 =  4;
int led1State = LOW;        // initialise the LED
int led2State = LOW;
int led3State = LOW;
char bstate1 = 0;
unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;
unsigned long bcount1 = 0;

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
  switch (*butnstate) {               // Odd states if was pressed, >= 2 if debounce in progress
  case 0: // Button up so far, 
    if (button == HIGH) return false; // Nothing happening!
    else { 
      *butnstate = 2;                 // record that is now pressed
      *marker = millis();             // note when was pressed
      return false;                   // and move on
    }

  case 1: // Button down so far, 
    if (button == LOW) return false; // Nothing happening!
    else { 
      *butnstate = 3;                 // record that is now released
      *marker = millis();             // note when was released
      return false;                   // and move on
    }

  case 2: // Button was up, now down.
    if (button == HIGH) {
      *butnstate = 0;                 // not debounced; reset the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 1;               // jackpot!  reset the state
        return true;                  // because we are done!
      } 
      else 
        return false;                 // not done yet; just move on
    }

  case 3: // Button was down, now up.
    if (button == LOW) {
      *butnstate = 1;                 // not debounced; reset the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 0;               // Debounced; just reset the state
        return false;                 // but it is a non-event
      } 
      else 
        return false;                 // not done yet; just move on
    }
  default:                            // Error; recover anyway
    {  
      *butnstate = 0;
      return false;                   // Definitely false!
    }
  } 
}

void setup() {
  pinMode(led1Pin, OUTPUT);      
  pinMode(led2Pin, OUTPUT);      
  pinMode(led3Pin, OUTPUT);      
  pinMode(button1, INPUT);      
  digitalWrite(button1,HIGH);        // internal pullup all versions
}

void loop() {
  // Toggle LED if button debounced
  if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW; 
    } 
    digitalWrite(led1Pin, led1State);
  } 

  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count2, 300UL )) {
    if (led2State == LOW) {
      led2State = HIGH;
    }
    else {
      led2State = LOW; 
    } 
    digitalWrite(led2Pin, led2State);
  } 

  if (timeout(&count3, 77UL )) {
    if (led3State == LOW) {
      led3State = HIGH;
    }
    else {
      led3State = LOW; 
    } 
    digitalWrite(led3Pin, led3State);
  } 
}

Note the four arguments - the first argument as digitalRead(button_pin) - whichever pin that is, a counter - unsigned long as for the "timeout" as the second argument (using "&"), a state marker defined as a char similar to the individual ledstates, also with an "&", and the unsigned long debounce interval.

OK, so the function looks fairly complex, but you can just ignore that - you just use it and it is designed to give "rock solid" debouncing.

I understand why debouncing may be necessary and how to deal with it.

What intrigues me is that I can't remember a situation where I have needed it. Most of my "switches" are just pieces of wire that I stick into the Arduino pins and touch together when I want switch action. I would expect if anything was going to bounce that would.

...R

@ Paul__B

(been on vacation) Thanks, I am defintely going to try to incorporate that into my project. Thanks for looking after me,