Multiple timed events with millis() in Arduino - Time & Space Complexity
Start learning this pattern below
Jump into concepts and practice - no test required
When using multiple timed events with millis(), it's important to know how the program's work grows as more events are added.
We want to understand how checking many timers affects the program's speed.
Analyze the time complexity of the following code snippet.
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
const unsigned long interval1 = 1000;
const unsigned long interval2 = 2000;
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis1 >= interval1) {
previousMillis1 = currentMillis;
// Event 1 action
}
if (currentMillis - previousMillis2 >= interval2) {
previousMillis2 = currentMillis;
// Event 2 action
}
}
This code checks two timed events repeatedly to see if their intervals have passed, then triggers actions.
- Primary operation: Two if-checks inside the loop function.
- How many times: Both checks run every time loop() runs, which is very often.
Each additional timed event adds one more if-check inside the loop.
| Number of Events (n) | Approx. Operations per loop |
|---|---|
| 2 | 2 if-checks |
| 10 | 10 if-checks |
| 100 | 100 if-checks |
Pattern observation: The number of checks grows directly with the number of timed events.
Time Complexity: O(n)
This means the time spent checking timers grows linearly as you add more timed events.
[X] Wrong: "Adding more timed events won't affect performance much because each check is simple."
[OK] Correct: Even simple checks add up; many events mean many checks every loop, which slows the program.
Understanding how multiple timed events affect performance shows you can write efficient Arduino code that scales well as projects grow.
What if we stored timed events in an array and looped through them? How would that change the time complexity?
Practice
millis() for timing multiple events in Arduino instead of delay()?Solution
Step 1: Understand
delay()behaviordelay()pauses the whole program, stopping all other actions.Step 2: Understand
millis()advantagemillis()returns elapsed time without stopping the program, so other tasks can run simultaneously.Final Answer:
It allows the program to run other tasks while waiting. -> Option DQuick Check:
millis()enables multitasking [OK]
delay() stops, millis() doesn't [OK]- Thinking
millis()pauses the program - Confusing
delay()with non-blocking timing - Believing
millis()resets Arduino
millis()?Solution
Step 1: Identify the correct data type for time
Sincemillis()returns an unsigned long value, the variable must be unsigned long.Step 2: Check variable initialization
Initializing to 0 is correct to mark the start time.Final Answer:
unsigned long lastEventTime = 0; -> Option AQuick Check:
Use unsigned long formillis()times [OK]
millis() [OK]- Using int which can overflow quickly
- Using float which is not precise for time
- Using char which is for characters, not numbers
millis() returns 5000 at the moment of checking?unsigned long previousMillis = 3000;
unsigned long interval = 1500;
if (millis() - previousMillis >= interval) {
Serial.println("Event triggered");
} else {
Serial.println("Waiting");
}Solution
Step 1: Calculate elapsed time
Elapsed time = 5000 (current millis) - 3000 (previousMillis) = 2000 ms.Step 2: Compare elapsed time with interval
Interval is 1500 ms. Since 2000 >= 1500, the condition is true, so "Event triggered" should print.Final Answer:
Event triggered -> Option CQuick Check:
Elapsed time 2000 >= 1500 triggers event [OK]
- Mixing up >= and > operators
- Forgetting to subtract previousMillis
- Assuming output without calculation
millis():unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
const long interval1 = 1000;
const long interval2 = 2000;
void loop() {
if (millis() - previousMillis1 >= interval1) {
digitalWrite(LED1, !digitalRead(LED1));
previousMillis1 = millis();
}
if (millis() - previousMillis1 >= interval2) {
digitalWrite(LED2, !digitalRead(LED2));
previousMillis2 = millis();
}
}Solution
Step 1: Check timing variables in conditions
The second if condition incorrectly usespreviousMillis1instead ofpreviousMillis2, causing wrong timing for LED2.Step 2: Understand impact of error
This mistake means LED2 timing depends on LED1 timing, so LED2 won't blink at its own interval.Final Answer:
Second if condition uses previousMillis1 instead of previousMillis2. -> Option BQuick Check:
Each event needs its own previousMillis variable [OK]
- Reusing the same previousMillis variable for multiple events
- Not updating previousMillis after event
- Confusing interval variables
delay(). Which approach correctly manages all three timed events using millis()?Solution
Step 1: Understand independent timing needs
Each LED needs its own timer to blink independently at different intervals.Step 2: Evaluate options
Use three separatepreviousMillisvariables and check each with its own interval insideloop(), allowing independent timing without blocking.Final Answer:
Use three separatepreviousMillisvariables and check each with its own interval insideloop(). -> Option AQuick Check:
Separate timers for each event [OK]
- Using one timer for all events causing sync issues
- Using delay which blocks other events
- Trying to calculate toggles from one millis() value without state
