Millis - arrays - time lines Help please

Hi all,
i'm trying to make up to 8 OUTPUTS work (turn on and off) at preset times for different set lengths of time outputs over a 60 second time loop, some outputs may need to come on/off more than once during that 60sec timeline. So i need to be able to set an ON and OFF time or either ON and HOWLONG TO STAY ON setup as the outputs aren't regular (like a LED flashing every single 5 seconds etc)

With the tips of some forum users it's been suggested to try the following code (its a 1 channel output for now till things work then i'll replicate to have 8 outputs working)

The code below when i run it will turn output 1 on for 1 second then off, then nothing else happens.
I must warn im a newbie and its a steep learning curve for me.

Hopefully someone can assist with getting this sample code to work so i can try to expand on that from the basics.

here is what i've got so far:

#define SIZE1 4
int ledPin1 = 10; // the number of the LED pin
int ledState1 = LOW; // ledState used to set the LED
unsigned long previousMillis1 = 0; // will store last time LED was updated
unsigned long onOffTimes1[SIZE1] { 1000, 2000, 3000, 4000 };
int index1 = 0;

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

}

void loop()
{

unsigned long currentMillis = millis();

if(currentMillis - onOffTimes1[index1] >= previousMillis1)
{
ledState1 = !ledState1; // Toggle it
digitalWrite(ledPin1, ledState1); // Update the actual LED
previousMillis1 += onOffTimes1[index1]; // Remember the time
index1++; // Next on/off time
if (index1 >= SIZE1) index1 = 0;

}

}

You could use FireTimer.h to easily initialize and keep track of your timers. The lib comes with an example, handle's timer overflow, and is installable via the Arduino IDE's Libraries Manager.

Here's how you could implement it:

#include "FireTimer.h"


FireTimer Timer_1;
FireTimer Timer_2;
FireTimer Timer_3;
FireTimer Timer_4;


void setup()
{
  Timer_1.begin(1000);
  Timer_2.begin(2000);
  Timer_3.begin(3000);
  Timer_4.begin(4000);
}


void loop()
{
  if (Timer_1.fire())
  {
    // do something
  }

  if (Timer_2.fire())
  {
    // do something
  }

  if (Timer_3.fire())
  {
    // do something
  }

  if (Timer_4.fire())
  {
    // do something
  }
}

The rest of the details should be straightforward to add.

Is it a one time thing or does the sequence repeat every sixty seconds?

Wildbill the outputs have to trigger some relays eventually to operate air rams to move some parts on a display animal model. so the 8 outputs will work the tail, jaw, head , open the mouth, etc etc and we need to be able to adjust when/where the 8 outputs will go on and off to make the model move lifelike. once its done its thing there would be a delay and we'd start all the process again to make the animal move again.

Make an array of bytes of length 60. Each bit position of a byte, 0-7, corresponds to one actuator. Increment a counter from 0-59 on one-second ticks. Use the counter as the index into the array. Same principle as a music box. Transfer the bit status to the outputs with

digitalWrite(actuatorName, bitRead(arrayPosn[n],bitNumber));

Thanks Power_Broker, i havent used any libraries before but i've read up just now how to include them in my sketch and i'm trying to get this basic one working on one LED output but i get errors like
Expected ; before } token which to be honest i'm not up with debugging yet.

this is the code i tried to implement using the library you suggest and just to use a led to come on at a certain time and off at certain time. Am in heading in the right direction with this or completely lost the plot ?

#include "FireTimer.h"

int led = 13; // the pin the LED is connected to

FireTimer Timer_1;
FireTimer Timer_2;
FireTimer Timer_3;
FireTimer Timer_4;

void setup()
{

Timer_1.begin(1000);
Timer_2.begin(3000);
Timer_3.begin(5000);
Timer_4.begin(10000);

pinMode(led, OUTPUT) // Declare the LED as an output

}
void loop()
{
if (Timer_1.fire())
{
digitalWrite(led, HIGH) // Turn the LED on
}

if (Timer_2.fire())
{
digitalWrite(led, LOW) // Turn the LED off
}

if (Timer_3.fire())
{
digitalWrite(led, HIGH) // Turn the LED on
}

if (Timer_4.fire())
{
digitalWrite(led, LOW) // Turn the LED off
}
}

Try this

const byte ledPins[] = {3,5,6,9};
const byte NUMBER_OF_LEDS = sizeof(ledPins);
unsigned long startTimes[NUMBER_OF_LEDS] = {};
byte indexes[NUMBER_OF_LEDS] = {0};
const byte MAX_NUMBER_OF_PERIODS = 10;
unsigned long periods[][MAX_NUMBER_OF_PERIODS] =    //periods for each LED.  zero indicates end of sequence
{
  {1000, 2000, 1500, 2500, 0},
  {500, 200, 1000, 2000, 3000, 4000, 0},
  {400, 1000, 1500, 2000, 0},
  {1100, 2200, 0}
};

void setup()
{
  Serial.begin(115200);
  for (int led = 0; led < NUMBER_OF_LEDS; led++)
  {
    pinMode(ledPins[led], OUTPUT);
  }
}

void loop()
{
  unsigned long currentTime = millis();
  for (int led = 0; led < NUMBER_OF_LEDS; led++)  //iterate through the LEDs
  {
    if (currentTime - startTimes[led] >= periods[led][indexes[led]])  //? time to change ?
    {
      digitalWrite(ledPins[led], !digitalRead(ledPins[led]));  //flip led state
      startTimes[led] = currentTime;  //save start time of state
      indexes[led]++;                    //increment index
    }
    if (periods[led][indexes[led]] == 0)  //if next period is zero (end of sequence)
    {
      indexes[led] = 0;        //reset period index for this LED
    }
  }  //end for loop
}

As many LEDs as you like each with its own on/off timing. Simply add more entries to the arrays

Sorry, borked the timer compare in my previous code. Not sure what I was thinking when I wrote it. :roll:
My apologies for all the confusion.

The following is tested and working on my ProMini...

#define SIZE1 4

int ledPin1 =  LED_BUILTIN;               // the number of the LED pin
int ledState1 = LOW;                      // ledState used to set the LED
unsigned long previousMillis1 = 0;        // will store last time LED was updated
unsigned long onOffTimes1[SIZE1] { 1000, 2000, 3000, 4000 };
int index1 = 0;

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

void loop()
{
 
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis1 >= onOffTimes1[index1])
  {
      ledState1 = !ledState1;  // Toggle it
      digitalWrite(ledPin1, ledState1);  // Update the actual LED
      previousMillis1 += onOffTimes1[index1];  // Remember the time
      index1++; // Next on/off time
      if (index1 >= SIZE1) index1 = 0;    
  }
}

UKHeliBob's code will also work for you, with a small modification.

This line has the potential to introduce relative drift of the timers if the loop does not execute at least once exactly on every millisecond:

startTimes[led] = currentTime;  //save start time of state

That's because any "delay" in detecting the event is accumulated in startTimes[led]. Say you were 1ms late in detecting the correct time to toggle the led. Now that 1ms error will be added into startTimes[led] in perpetuity. What you actually need (in order to keep everything in step) is for the next period to be 1ms shorter in order to compensate for the unexpected 1ms longer of the last period.

Corrected:

startTimes[led] += periods[led][indexes[led]];

Some of the forum tutorials on this subject, and nearly every bit of timing code I see posted here are all also borked in this regard. Not a problem when you only have one timer. Big problem when you have multiple timers, AND you must ensure all those timers run in step with each other. As would be the case with an animatronic model, otherwise it's going to look like it is dad dancing after a while.

Edit: Actually, on checking, I see SeveralThingsAtTheSameTimeRev1 actually doesn't suffer the bug.

Very helpful thankyou all. trying UKHeliBobs code with the changes suggested by PCBBC and as i'm not in the workshop i've been testing tonight with tinkercad simulator. So i dumbed it down for myself and just using 1 array and 1 output for more

so the data i put for the ON times and HOWLONG times in the 1st array are (1000,1000, 5000, 5000, 0) which i 'assumed' that LED would come on at 1sec for 1sec, then on at 5 seconds for 5 seconds. what i found from running the code with the simulator is that the led came on for @1 sec point for 1 sec but instead of coming on again at 5sec point it came on at 1+1+5 (7 sec point) and when that when off was off at 12 sec (1+1+5+5). it seem to add all the times in the array before hand rather than those secondary set of values being the 'actual' on and off times. Any thoughts ?

const byte ledPins = {3,5,6,9};
const byte NUMBER_OF_LEDS = sizeof(ledPins);
unsigned long startTimes[NUMBER_OF_LEDS] = {};
byte indexes[NUMBER_OF_LEDS] = {0};
const byte MAX_NUMBER_OF_PERIODS = 10;
unsigned long periods[MAX_NUMBER_OF_PERIODS] = //periods for each LED. zero indicates end of sequence
{
{1000, 1000, 5000, 5000, 0},

The behaviour that you are seeing is exactly as intended

The on and off periods are in pairs in the array and once program reads a value of zero the pattern repeats. Exactly what pattern are you trying to achieve ?

The pairs in the array. (1000,1000,5000,5000) for example I thought the 5000 would be the point in the timeline where it activates but it's actually 5000ms after the previous triggered. Is that correct ?

(1000,1000 ,5000,5000, 0)

On for 1 second, off for one second, on for 5 seconds, off for 5 seconds, repeat for ever

(1000,500, 2000,100, 100,1000, 0)

On for 1 second, off for 1/2 second, on for 2 seconds, off for 1/10 second, on for 1/10 second, off for 1 second, repeat for ever

Note that depending on how your LED is wired on and off may be reversed

Ok Thanks UKHeliBob for that i understand that now, just wondering if you have any thoughts on as when i have an example array like below:

{1000, 2000, 1500, 2500, 0},
{500, 500, 1000, 2000, 3000, 4000, 0},
{1000,500, 2000,100, 100,1000,0},
{1100, 2200, 0}

where there are different "amounts" of on/off times (for example the last line in the array has only 1 on and 1 off time > 1100,2200) so it keeps looping on/off while the other arrays are still doing there thing, so it may run 4-5 times before the other arrays have even done one run through.

so each array needs to run their course and when they all finish, short delay and we go again

i'm trying to control some animation (air rams) moving some model animals so again in this example the bottom set of times in the array maybe for say wagging its tail, i only want that to happen once in the timeline while other outputs (arrays above) maybe doing their thing to move other parts. Sorry i may have confused you more with my attempt at an explanation :slight_smile:

where there are different "amounts" of on/off times (for example the last line in the array has only 1 on and 1 off time > 1100,2200) so it keeps looping on/off while the other arrays are still doing there thing, so it may run 4-5 times before the other arrays have even done one run through.

That is exactly what it does. Each timed output does its own thing so unless the total time in each array is the same then some will repeat before others. The example was not written to meet your requirements, which you had not fully explained, merely to blink LEDs at independent times for independent and possibly varying periods.

You could introduce a new array of booleans, one for each LED, indicating whether the output should change state or not. Set the boolean to true (start state) if the output should change state and false if it should not.

When the program reads the "end of sequence" zero set the boolean for that output to false instead of setting the array index to zero and make the output dependent on it being true. When the whole sequence has finished (all booleans set to zero) wait a while, set each of the booleans to true and the indices to zero to start again.

Having said that, now that your actual requirements are known it might be better to start over instead of bending the program to your needs