Using millis() lets you run many timed tasks without stopping your program. It helps your Arduino do several things at once smoothly.
Multiple timed events with millis() in Arduino
Start learning this pattern below
Jump into concepts and practice - no test required
unsigned long previousMillis1 = 0; unsigned long previousMillis2 = 0; const long interval1 = 1000; // 1 second const long interval2 = 500; // 0.5 second void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis1 >= interval1) { previousMillis1 = currentMillis; // code for first timed event } if (currentMillis - previousMillis2 >= interval2) { previousMillis2 = currentMillis; // code for second timed event } }
Use unsigned long to store time values from millis().
Always subtract previous time from current time to handle timer overflow safely.
unsigned long previousMillis = 0; const long interval = 1000; void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; // toggle LED } }
unsigned long previousMillis1 = 0; unsigned long previousMillis2 = 0; const long interval1 = 1000; const long interval2 = 2000; void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis1 >= interval1) { previousMillis1 = currentMillis; // toggle LED1 } if (currentMillis - previousMillis2 >= interval2) { previousMillis2 = currentMillis; // toggle LED2 } }
// Edge case: when millis() overflows (after ~50 days), subtraction still works correctly unsigned long previousMillis = 4294967290; const long interval = 20; void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; // safe even if millis() overflowed } }
This program blinks two LEDs at different speeds using millis(). It prints the LED states to the Serial Monitor so you can see the timing.
#define LED1 13 #define LED2 12 unsigned long previousMillisLED1 = 0; unsigned long previousMillisLED2 = 0; const long intervalLED1 = 1000; // 1 second const long intervalLED2 = 500; // 0.5 second bool led1State = LOW; bool led2State = LOW; void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); Serial.begin(9600); Serial.println("Starting multiple timed events demo"); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillisLED1 >= intervalLED1) { previousMillisLED1 = currentMillis; led1State = !led1State; digitalWrite(LED1, led1State); Serial.print("LED1 toggled to: "); Serial.println(led1State ? "ON" : "OFF"); } if (currentMillis - previousMillisLED2 >= intervalLED2) { previousMillisLED2 = currentMillis; led2State = !led2State; digitalWrite(LED2, led2State); Serial.print("LED2 toggled to: "); Serial.println(led2State ? "ON" : "OFF"); } }
Time complexity: O(1) per timed event check.
Space complexity: O(1) for each timer variable.
Common mistake: Using delay() instead of millis() blocks the program and stops other events.
Use this method when you want your Arduino to do many things at once without waiting.
Use millis() to track time without stopping your program.
Store previous times in unsigned long variables and compare with current millis().
Subtract times to handle timer overflow safely and run multiple timed events independently.
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
