Using Millis for 8 timed scheduled outputs

Hi All, very much a newbie with this stuff so sorry if some of your tech help goes over my head. i am trying to achieve a timeline of 8 outputs triggering at various intervals and various times in a timeline, sometimes an output may need to be triggered several times on the same timeline.

this idea/project is to try to control an array of air rams and solenoids in a timed sequence, then delay then repeat the sequence, etc etc.

as i mentioned complete newbie and i've just started by playing around with 4 outputs to LED's using millis, but not sure how i can tell a certain output to come on at a certain time of the time line (i.e not triggered from time line Zero). i've attached my first effort and also a drawing to give you a visual on what i'm trying to achieve just in case my explanation is a little rambling.

Cheers

philf4flashing.ino (3.64 KB)

 // These variables store the flash pattern
// and the current state of the LED

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
long OnTime1 = 1000;           // milliseconds of on-time
long OffTime1 = 9000;          // milliseconds of off-time

int ledPin2 =  11;      // the number of the LED pin
int ledState2 = LOW;             // ledState used to set the LED
unsigned long previousMillis2 = 0;        // will store last time LED was updated
long OnTime2 = 2000;           // milliseconds of on-time
long OffTime2 = 8000;          // milliseconds of off-time

int ledPin3 =  12;      // the number of the LED pin
int ledState3 = LOW;             // ledState used to set the LED
unsigned long previousMillis3 = 0;        // will store last time LED was updated
long OnTime3 = 3000;           // milliseconds of on-time
long OffTime3 = 7000;          // milliseconds of off-time

int ledPin4 =  13;      // the number of the LED pin
int ledState4 = LOW;             // ledState used to set the LED
unsigned long previousMillis4 = 0;        // will store last time LED was updated
long OnTime4 = 4000;           // milliseconds of on-time
long OffTime4 = 6000;          // milliseconds of off-time

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

void loop()
{
  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();
 
  if((ledState1 == HIGH) && (currentMillis - previousMillis1 >= OnTime1))
  {
    ledState1 = LOW;  // Turn it off
    previousMillis1 = currentMillis;  // Remember the time
    digitalWrite(ledPin1, ledState1);  // Update the actual LED
  }
  else if ((ledState1 == LOW) && (currentMillis - previousMillis1 >= OffTime1))
  {
    ledState1 = HIGH;  // turn it on
    previousMillis1 = currentMillis;   // Remember the time
    digitalWrite(ledPin1, ledState1);    // Update the actual LED
  }
  
  if((ledState2 == HIGH) && (currentMillis - previousMillis2 >= OnTime2))
  {
    ledState2 = LOW;  // Turn it off
    previousMillis2 = currentMillis;  // Remember the time
    digitalWrite(ledPin2, ledState2);  // Update the actual LED
  }
  else if ((ledState2 == LOW) && (currentMillis - previousMillis2 >= OffTime2))
  {
    ledState2 = HIGH;  // turn it on
    previousMillis2 = currentMillis;   // Remember the time
    digitalWrite(ledPin2, ledState2);   // Update the actual LED
  }
  if((ledState3 == HIGH) && (currentMillis - previousMillis3 >= OnTime3))
  {
    ledState3 = LOW;  // Turn it off
    previousMillis3 = currentMillis;  // Remember the time
    digitalWrite(ledPin3, ledState3);  // Update the actual LED
  }
  else if ((ledState3 == LOW) && (currentMillis - previousMillis3 >= OffTime3))
  {
    ledState3 = HIGH;  // turn it on
    previousMillis3 = currentMillis;   // Remember the time
    digitalWrite(ledPin3, ledState3);   // Update the actual LED
  }
  if((ledState4 == HIGH) && (currentMillis - previousMillis4 >= OnTime4))
  {
    ledState4 = LOW;  // Turn it off
    previousMillis4 = currentMillis;  // Remember the time
    digitalWrite(ledPin4, ledState4);  // Update the actual LED
  }
  else if ((ledState4 == LOW) && (currentMillis - previousMillis4 >= OffTime4))
  {
    ledState4 = HIGH;  // turn it on
    previousMillis4 = currentMillis;   // Remember the time
    digitalWrite(ledPin4, ledState4);   // Update the actual LED
  }


  
}

Instead of just looking at the led state to decide which time to use for the next interval, you need an array of the on/off times for each of the waveforms, and a variable which increments for each edge (so you know the index next value to use from the array).

When you detect an edge with the millis() logic, you need to toggle the output and move to the next element in the array. When you reach the end of the array, reset the index back to zero.

Also, if you want to keep the waveforms “in step” with respect to each other and not drift, you need to do this to set the previous time...

previousMillis1 = previousMillis1 + onOffTime1;

Just using currentMillis is not good enough. It doesn’t consider what happens if the current time has ticked slightly past the required on/off time. When that happens with the supplied code you introduce a minuscule random error with each edge, and over time these errors add up, but differently for each waveform giving drift.

This is very important but regrettably then reference guide for using millis() doesn’t mention or explain this.

 #define SIZE1 4
unsigned long onOffTimes1[SIZE1] { 100, 200, 300, 400 };
int index1 = 0;

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;
}

Repeat for each of your waveforms.

Edit: Oh, and if you want a different start offset for each waveform....

 #define SIZE1 4
unsigned long interval1 = 50; // start offset
unsigned long onOffTimes1[SIZE1] { 100, 200, 300, 400 };
int index1 = 0;

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

thanks pcbbc for the quick reply. Sorry most has gone over my head because of my inexperience, but i'll try getting my head around to make that code you suggest work

getting errors with the 'if' statement so i'll have to google and read up further so i get grasp your comments more clearly.

phil91:
thanks pcbbc for the quick reply. Sorry most has gone over my head because of my inexperience, but i'll try getting my head around to make that code you suggest work

getting errors with the 'if' statement so i'll have to google and read up further so i get grasp your comments more clearly.

Which one? What’s the error?
The supplied code is just a snippet supplied as an example to get you started and wasn’t tested. There may be minor errors or typos despite me checking it several times.

Yes no doubt it will be my attempt to implement your code into mine without really understanding it all fully yet

the error was unqualified ID before IF statement, i'm sure its no issue with what you provided just my attempts to make it all work together :wink:

As you’ve already seen/started, explore two outputs, and figure it out, then move to an array of the outputs & timer values, which will shrink your code significantly, and reduce the chance of cut & paste typos.
You may consider a struct to contain the details of each output, which will also,let you organise your code better, and add readability in the process.

thanks for the tips and help so far guys

Hi Guys i did manage to find this code somewhere on the forum which has helped me be able to set TIME ON and TIME OFF periods of 4 outputs to LEDS.
what i'd ideally like to do and help with would be rather than say having output 1 FLASH (EVERY 5 seconds) or whatever the time period is set to,be able to make the code run so when the on period is reached in the timeline (say 5 sec point) then that output will go on for the designated PERIOD time (1sec), it dont really want that output to flash EVERY 5 seconds for 1 second, would like it to flash for 1 second at the 5 second point of my loop/(timeline). Doing this over all the output channels at various points along the timeline.

So summary trying to make LED's come on and off at certain points for certain lengths of time (sometime the same output may need to be ON a few times during the timeline), then after the time line peroid (example say 40 seconds) has passed the whole scenario starts again.

//blink 4 LEDs, each with different periods on/off

const byte ledPins[] = {10, 11, 12, 13};
const byte NUMBER_OF_LEDS = sizeof(ledPins) / sizeof(ledPins[0]);
unsigned long startTimes[NUMBER_OF_LEDS];
unsigned long periods[][NUMBER_OF_LEDS] = { {5000, 15000, 20000, 1000}, {1000, 2000, 5000, 2000} }; //on/off periods

void setup()
{
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++)
{
byte ledState = digitalRead(ledPins[led]);
if (currentTime - startTimes[led] >= periods[ledState][led])
{
digitalWrite(ledPins[led], !ledState);
startTimes[led] = currentTime;
}
}
}