Timer Array using Millis

Hi, I'm trying to program a sequence where LEDs come on and off separately (sometimes they are on, well appear to be on, at the same time) for different lengths of time. I have found a sketch using Millis but I would like to have more than one time value for each LED, can I do this using an array? I've tried redefining const unsigned long greenLEDinterval = 500 as const unsigned greenLEDinterval[3] = {500,1000,2000} but it's not working... I would also like to be able to adjust the time that the LED is off, could this also be programmed using an array in the unsigned long greenLEDtimer value?
Thanks

Sorry can't upload sketch as I'm new but basically it's this! https://forum.arduino.cc/t/how-to-make-2-leds-blink-at-the-same-time/366237/8

study blink without delay so that you understand how this works.
then it's just a matter of duplicating this for multiple LEDs. and you could of course use an array, but you have to use it the right way
https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay

1 Like

You may try something like this:

struct LEDType {
     unsigned long LastMillis = 0;
     unsigned long interval   = 500;
     boolean       isOn = false;
     boolean       lastState = false;
     int           Pin;
  };

const int NoOfLEDs = 5;

LEDType LED[NoOfLEDs];

boolean StateHasChanged = false;

void BlinkLedNo(int No){
  if (millis() - LED[No].LastMillis > LED[No].interval){
    LED[No].LastMillis = millis();
    LED[No].isOn = !LED[No].isOn;
    digitalWrite(LED[No].Pin,LED[No].isOn);
    StateHasChanged = true;
  }
}

void BlinkLEDs(){
  for (int i = 0; i < NoOfLEDs; i++){BlinkLedNo(i);}
}  

void PrintSerial(){
  for (int i = 0; i < NoOfLEDs; i++){
   if (LED[i].isOn) Serial.print("1");
              else  Serial.print("0");
    }
  Serial.println();
  StateHasChanged = false;
}

void setup() {
  Serial.begin(115200);
  // Just a quick initialization
  for (int i = 0; i <NoOfLEDs;i++ ) { 
    LED[i].interval = 300*(i+1);
    LED[i].Pin = 9+i; // Do this only in this case as 9 + 4 = 13 !!!
    pinMode(LED[i].Pin,OUTPUT);
    }
  
}

void loop() {
  BlinkLEDs();
  if (StateHasChanged) PrintSerial();
}

There is some "overhead" in thies sketch e.g. to print the LED states to Serial; you can leave this away. I also initialize the Leds by a for-loop() which should be solved differently in a real application. There is another additional concept too, which I will post a little bit later :wink:

1 Like

Which is using a state machine that you may control by Serial (or any other event driven capability):

enum {
  IDLE,
  ALLON,
  ALLOFF,
  ZEROONE,
  ZEROTWOFOUR,
  ONETHREE,
  PROGRAMMED,
  TOGGLE1,
  TOGGLE2
} BlinkType;

int BlinkState = IDLE;

struct LEDType {
  unsigned long LastMillis = 0;
  unsigned long interval   = 500;
  boolean       isOn = false;
  boolean       letBlink = true;
  int           Pin;
};

const int NoOfLEDs = 5;

LEDType LED[NoOfLEDs];

void SetLED(int No, int State) {
  LED[No].isOn = State;
  digitalWrite(LED[No].Pin, LED[No].isOn);
}


void BlinkLedNo(int No) {
  if (LED[No].letBlink) {
    if (millis() - LED[No].LastMillis > LED[No].interval) {
      LED[No].LastMillis = millis();
      LED[No].isOn = !LED[No].isOn;
      SetLED(No, LED[No].isOn);
    }
  } else {
    if (LED[No].isOn) SetLED(No, LOW);
  }
}

void BlinkLEDs() {
  for (int i = 0; i < NoOfLEDs; i++) {
    BlinkLedNo(i);
  }
}

void AllLEDs(int Value) {
  for (int i = 0; i < NoOfLEDs; i++) {
    SetLED(i, Value);
  }
}

void StateMachine() {
  switch (BlinkState) {
    case IDLE  :
      break;
    case ALLOFF :
      AllLEDs(LOW);
      BlinkState = IDLE;
      break;
    case ALLON :
      AllLEDs(HIGH);
      BlinkState = IDLE;
      break;
    case ZEROONE :
      AllLEDs(LOW);
      SetLED(0, HIGH);
      SetLED(1, HIGH);
      BlinkState = IDLE;
      break;
    case ZEROTWOFOUR :
      AllLEDs(LOW);
      SetLED(0, HIGH);
      SetLED(2, HIGH);
      SetLED(4, HIGH);
      BlinkState = IDLE;
      break;
    case ONETHREE :
      AllLEDs(LOW);
      SetLED(1, HIGH);
      SetLED(3, HIGH);
      BlinkState = IDLE;
      break;
    case TOGGLE1 :

      break;
    case PROGRAMMED :
      BlinkLEDs();
      break;
    default : break;
  }
}

void AllowAllToBlink() {
  for (int i = 0; i < NoOfLEDs; i++ ) {
    LED[i].letBlink = true;
  };
}

void DisableToBlink(int No) {
  LED[No].letBlink = false;
}


void getSerialCommand() {
  if (Serial.available()) {
    char c = Serial.read();
    switch (c) {
      case 'A' :
        BlinkState = ALLON;
        break;
      case 'a' :
        BlinkState = ALLOFF;
        break;
      case 'P' :
        AllowAllToBlink();
        BlinkState = PROGRAMMED;
        break;
      case 'I' :
        BlinkState = IDLE;  // Leaves LED states as they are at time of key pressure
        break;
      case '0' :
        BlinkState = ZEROONE;
        break;
      case '1' :
        BlinkState = ZEROTWOFOUR;
        break;
      case '2' :
        BlinkState = ONETHREE;
        break;
      case 'T' :
        AllowAllToBlink();
        DisableToBlink(0);
        DisableToBlink(2);
        DisableToBlink(4);
        BlinkState = PROGRAMMED;
        break;
      case 't' :
        AllowAllToBlink();
        DisableToBlink(1);
        DisableToBlink(3);
        BlinkState = PROGRAMMED;
        break;
      default : break;
    }
    c = ' ';
  }

}

void setup() {
  Serial.begin(115200);
  // Just a quick initialization
  for (int i = 0; i < NoOfLEDs; i++ ) {
    LED[i].interval = 300 * (i + 1);
    LED[i].Pin = 9 + i; // Do this only in this case as 9 + 4 = 13 !!!
    pinMode(LED[i].Pin, OUTPUT);
  }
  AllLEDs(HIGH);
  delay(500);
  AllLEDs(LOW);
  Serial.println("Use keys A,a, P, I, 0, 1 ,2 , T, t on Serial to switch LED control");
}

void loop() {
  getSerialCommand();
  StateMachine();
}
1 Like

Hello zed789
Try this C++ example sketch to do your own coding studies for arrary´s.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  Many thanks to LarryD
  https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
  https://forum.arduino.cc/t/timer-array-using-millis/956815
*/
#define ProjectName "Timer Array using Millis"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr byte LedPins[] {9, 10, 11};     // portPin o---|220|---|LED|---GND
constexpr int numberOfTimeSequence {4};
// CONSTANT DEFINITION
enum {Red, Green , Blue};
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct LEDONOFF {           // has the following members
  byte pin;                 // port pin
  byte counter;             // counter for timer squence
  unsigned long duration[numberOfTimeSequence]; // durations times per sequence
  unsigned long stamp;      // memory for actual time
};
LEDONOFF LEDonOffs[] {
  {LedPins[Red],0, 1000, 500, 200, 250, 0},
  {LedPins[Green],0, 200, 1000, 100, 500, 0},
  {LedPins[Blue],0, 200, 200, 500, 500, 0},
};
// -------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  //  https://www.learncpp.com/cpp-tutorial/for-each-loops/
  for (auto Output_ : LedPins) pinMode(Output_, OUTPUT);
}
void loop () {
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  for (auto &LEDonOff : LEDonOffs) {
    if (currentTime - LEDonOff.stamp >= LEDonOff.duration[LEDonOff.counter]) {
      LEDonOff.stamp = currentTime;
      ++LEDonOff.counter %= numberOfTimeSequence;
      digitalWrite(LEDonOff.pin,!digitalRead(LEDonOff.pin));
    }
  }
}

Have a nice day and enjoy coding in C++.

1 Like

Do you always want to control the sequences so they run the same?
Then good news, you can store loads of sequence values as binary data in flash (program memory space also stores constant data).

I simplify the job, for 1 led on at a time I can run delays unless I want a start/stop button as well. So don't block/delay to leave that open.

For each led I need data; pin, ontime, offtime.
For the sequence, an array of led numbers to blink and an index to the array will do.
I need 1 start time unsigned long.

My state machine only needs to
case 0: read the next-or-first led number to blink, turn the pin on, start time = millis, set state to 1.
case 1: if (now - start time >= ontime) then turn the pin off, start time = millis, set state to 2.
case 2: if (now - start time >= offtime) then if the sequence is over... start over? if not, set state to 0.

1 Like

Welcome

Here is something that you may find useful LED librairie simplifiée + exemple - Pastebin.com

Thank you for all the options. The pastebin code is coming up with the effect I was looking for more or less, but will have a little play around!

To learn more, write your own small sketch and use it a bit, see what it does and
write a better version knowing what the first try showed you.

Arduino void loop() runs again and again. Functions inside of that main loop start again and again, mostly just to see no change and return but sometimes a step of work is done at 10's of 1000's of steps per second.

When you treat inputs as unpredictable, you code may handle unexpected events.

Millis code lets you sense and set up over-time events. Serial IO deals with communication events and you have pin status change events as well as times.

Play with the idea of event-driven code in a main loop as an approach to automation.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.