Handling multiple patterns at the same time is certainly possible with a platform like Arduino, and there are a number of ways you could go about it.
One method I would consider is writing functions which effectively represent each pattern mathematically. You'd just pass it the total time that's elapsed in your program so far, and it will do the appropriate action for that specific moment in time. It will return immediately afterwards (no delays or anything).
To do that, you'll first need to know how long a single cycle of the pattern lasts. You can then use the modulo operator to figure out how far through the current cycle you are. From there, all you need to do is have some if
conditions to determine what to do at any given time.
Here's what it might look like for your "5 seconds on, 5 seconds off" pattern:
function pattern5on5off(unsigned long totalTime)
{
// Calculate how far through the current cycle we are
const unsigned long cycleTime = totalTime % 10000;
// If we're in the first 5 seconds of the cycle then turn the light on.
// Otherwise, turn it off.
if (cycleTime < 5000)
digitalWrite(3, HIGH);
else
digitalWrite(3, LOW);
}
Admittedly, constantly calling digitalWrite()
when you don't technically need to isn't very efficient. It shouldn't cause any harm though, and is fairly easy to optimise if necessary.
To use the above example in a sketch, you'd just need to call it in loop()
, and pass the number you get from millis()
; e.g.:
void loop()
{
const unsigned long totalTime = millis();
pattern5on5off(totalTime);
// call other patterns here...
}
Other patterns will be more complex, but follow the same principle. You'd just to use appropriate if
statements to express your logic.
The vital thing to remember is that the function represents a specific moment in time. It should never pause or delay the program, otherwise it will prevent the other patterns from running.
Edit: Timing on the first cycle
As jfpoilpret noted in the comments, the very first cycle will start at a random point. This is because the first time you call millis()
in loop()
, it won't start at 0 (the device will already have been running for a short time before loop()
gets called). It's easy to resolve though, if necessary.
You would do it by offsetting the totalTime
value by the whatever value you got on the very first time around loop()
. For example:
unsigned long g_startTime = 0;
void loop()
{
unsigned long totalTime = 0;
if (g_startTime == 0) {
// This is the first cycle.
// Store the start time so we can compensate later.
g_startTime = millis();
} else {
// This is not the first cycle.
// Compensate for the start time.
totalTime = millis() - g_startTime;
}
pattern5on5off(totalTime);
// etc..
}