Preferred way to handle multiple timing loops?

Hi all,

I've just got back into Arduino after 15+ years not touching them...

So, looking at some basic coding to flash some LEDs, i came across this problem....

For example, i'm trying to flash 1 LED (LED1) every 1 second, and flash another LED (LED2) every 200mSecs.

In my code here, clearly LED1 turns on/off before LED2 even gets a chance to switch on.

Is there a better way to nest these loops so the timing isn't delayed by waiting for one instruction to finish before the next instruction begins?

For example:
I'd be waiting 1.2 seconds before LED1 cycles on/off, instead of 1 second
I'd also be waiting 1.2 seconds before LED2 cycles on/off, instead of 200mSec.

int LED1=11;
int LED2=12;

void setup() {

pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT); 

}

void loop() {

digitalWrite(LED1,HIGH);
delay(1000);

digitalWrite(LED1,LOW);
delay(1000);

digitalWrite(LED2,HIGH);
delay(200);

digitalWrite(LED2,LOW);
delay(200);

}

Even if i interlace the commands to switch ON LED1 and LED2 immediately, then the 2 different delays upsets overall timing.
I'd like to see LED1 and LED2 switch on together and then continue flashing at their own rates.

This issue is highlighted further when i replace the delay times with 'random', which then speeds/slows the cycle of when LED2 with switch on/off after LED1 has completed its cycle after a 'random' amount of time (upto 1 second).

It seems like i need a command (or array?) that tells both (or more) pins to begin all at once, like a byte-wide operation?

Thanks in advance.

You need to use millis(). Lots of examples.

this will in deed get you started

See the Blink Without Delay Example.

The "trick" for multiple timers is to use multiple Interval and previousMillis variables. For example, IntervalRed and previousMillisRed, etc. Of course there is only one currentMillis.

Also see Demonstration Code For Several Things At The Same Time.

E.g., something like:

#define NUM_LEDS    2

const uint8_t LED1=11;
const uint8_t LED2=12;

//tie the pins and LED timing together with a structure
typedef struct structLED
{
    uint8_t     pin;        //LED pin
    uint32_t    tBlink;     //times the LED toggles
    uint32_t    tDelay;     //determines the period of the toggles
    
}sLED_t;

sLED_t LED[2] = 
{
    { LED1, 0ul, 1000ul },  //LED1 toggles every 1000mS
    { LED2, 0ul, 200ul }    //LED2 toggles every 200mS
    
};

uint32_t
    tNow;

void setup() 
{
    //set the pins to outputs
    for( uint8_t i=0; i<NUM_LEDS; i++ )
        pinMode( LED[i].pin, OUTPUT );
        
}//setup

void loop() 
{
    static uint8_t
        idx = 0;

    //each pass, get the millis() count
    tNow = millis();
    //if this LED's toggle period has expired...
    if( (tNow - LED[idx].tBlink) >= LED[idx].tDelay )
    {
        //save the time for the next toggle
        LED[idx].tBlink = tNow;
        //and toggle this pin with a read-XOR-write
        digitalWrite( LED[idx].pin, digitalRead( LED[idx].pin ) ^ HIGH );
        
    }//if

    //do one LED per pass; bump the index and when we're done all the LEDs
    //cycle back to zero
    idx++;
    if( idx == NUM_LEDS )
        idx = 0;

}//loop

Hello
I did it in a similar way as Blackfin. By using C++ the compiler can do all the jobs of operations calculation for the used structured array. The "range-based for loop " is a powerful C++ instruction to code a slim and smart multiple blink sketch. Try it out and add some addtional LEDs.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  https://forum.arduino.cc/t/preferred-way-to-handle-multiple-timing-loops/935676
*/
#define ProjectName "Preferred way to handle multiple timing loops"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr byte ButtonPin {A0};      // portPin o---|button|---GND
constexpr byte LedRedPin {3};       // portPin o---|220|---|LED|---GND
constexpr byte LedGreenPin {5};     // portPin o---|220|---|LED|---GND
constexpr byte LedBluePin {6};      // portPin o---|220|---|LED|---GND
constexpr byte LedWhitePin {7};     // portPin o---|220|---|LED|---GND
constexpr  unsigned long RedBlinkTime {1000};
constexpr  unsigned long GreenBlinkTime {500};
constexpr  unsigned long BlueBlinkTime {250};
constexpr  unsigned long WhiteBlinkTime {125};
// CONSTANT DEFINITION
constexpr  unsigned long OutPutTestTime {1000};
#define OutPutTest

// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct TIMER {              // has the following members
  unsigned long duration;   // memory for interval time
  unsigned long stamp;      // memory for actual time
};
struct LEDBLINK {
  byte pin;                 // portPin o---|220|---|LED|---GND
  TIMER flash;
} blinkLeds [] {
  {LedRedPin, RedBlinkTime, 0},
  {LedGreenPin, GreenBlinkTime, 0},
  {LedBluePin, BlueBlinkTime, 0},
  {LedWhitePin, WhiteBlinkTime, 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
  for (auto blinkLed : blinkLeds) pinMode(blinkLed.pin, OUTPUT);
#ifdef OutPutTest
  // check outputs
  for (auto blinkLed : blinkLeds) digitalWrite(blinkLed.pin, HIGH), delay(OutPutTestTime);
  for (auto blinkLed : blinkLeds) digitalWrite(blinkLed.pin, LOW), delay(OutPutTestTime);
#endif
}
void loop () {
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  for (auto &blinkLed : blinkLeds) {
    if (currentTime - blinkLed.flash.stamp >= blinkLed.flash.duration) {
      blinkLed.flash.stamp = currentTime;
      digitalWrite(blinkLed.pin, digitalRead(blinkLed.pin)^HIGH);
    }
  }
}

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

Meh. I don't understand why the Arduino Community as a whole is "to do almost anything, you should search for an existing library. EXCEPT non-blocking delays, in which case you must learn the paths of millis() in hard-to-read inline code, and unsigned arithmetic avoiding overflows, and ..."

I wrote some trivial code that allows:

  static unsigned long atime, btime, ctime;
  static int ledstate = false;
  if (delay_without_delaying(ledtime, 500)) {
    ledstate = !ledstate;
    digitalWrite(13, ledstate);
  }
  if (delay_without_delaying(atime, 100)) {
    Serial.print("A");
  }
  if (delay_without_delaying(btime, 200)) {
    Serial.print("B");
  }
  if (delay_without_delaying(ctime, 30)) {
    Serial.print("C");
  }

Maybe I should make it OOP, or add additional functionality, but ... it's already pretty easy...
Full code here
(for accuracy, you need to make sure other things in your loop don't block, either.)

Hello
You are right.
The delay() function schould be renamed to sleep() :nerd_face:

perhaps a little simpler than blackfin's version

struct LedTmr {
    const byte          pin;
    const unsigned long Period;
          unsigned long msec;
} ledTmrs [] = {
    { 10, 518 },
    { 11, 423 },
    { 12, 300 },
    { 13, 125 },
};
#define N   (sizeof(ledTmrs)/sizeof(LedTmr))

void loop() {
    LedTmr *p = ledTmrs;

    unsigned long msec = millis ();

    for (unsigned n = 0; n < N; n++, p++)  {
        if ( (msec - p->msec) > p->Period)  {
            p->msec = msec;
            digitalWrite (p->pin, ! digitalRead (p->pin));
        }
    }
}


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

    for (unsigned n = 0; n < N; n++)
        pinMode (ledTmrs [n].pin, OUTPUT);
}

Thanks everyone for your input.

I certainly have a lot of reading to do :wink:

Thanks again.

15+ ? You used a serial RS-232 cable to the Arduino board and a alpha version of the Arduino IDE ? Then it is still 15 years and not 15+.