Bird
Raised Fist0
Arduinoprogramming~10 mins

Non-blocking code architecture in Arduino - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Non-blocking code architecture
Start loop()
↓
Check time elapsed?
No→Do other tasks
↓
Repeat loop()
↓
Perform action
↓
Update last action time
↓
Do other tasks
↓
Repeat loop()
The loop checks if enough time has passed to do an action without stopping other tasks, then performs the action and continues running other code.
Execution Sample
Arduino
unsigned long previousMillis = 0;
const long interval = 1000;

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    // action code here
  }
  // other code here
}
This code runs an action every 1000 milliseconds without stopping other code from running.
Execution Table
StepcurrentMillispreviousMillisCondition (currentMillis - previousMillis >= interval)ActionOutput
1000 - 0 >= 1000 ? FalseSkip actionNo action
25000500 - 0 >= 1000 ? FalseSkip actionNo action
3100001000 - 0 >= 1000 ? TrueUpdate previousMillis=1000; perform actionAction performed
4150010001500 - 1000 >= 1000 ? FalseSkip actionNo action
5200010002000 - 1000 >= 1000 ? TrueUpdate previousMillis=2000; perform actionAction performed
6250020002500 - 2000 >= 1000 ? FalseSkip actionNo action
ExitN/AN/ALoop repeats indefinitelyLoop continuesNo stop
💡 The loop runs forever, checking time and performing actions only when interval passes.
Variable Tracker
VariableStartAfter Step 1After Step 2After Step 3After Step 4After Step 5After Step 6Final
previousMillis00010001000200020002000
currentMillis0050010001500200025002500
Key Moments - 3 Insights
Why doesn't the code use delay() to wait for the interval?
Using delay() would stop all code and block other tasks. The execution_table shows the loop keeps running and checking time without stopping.
How does the code know when to perform the action?
It compares currentMillis and previousMillis to see if the interval passed, as shown in the Condition column of the execution_table.
What happens to previousMillis after the action runs?
It updates to currentMillis to reset the timer, so the next action waits for the full interval again (see Step 3 and Step 5).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at Step 3. What is the value of previousMillis after the action?
A500
B0
C1000
D1500
💡 Hint
Check the 'Action' and 'previousMillis' columns at Step 3 in the execution_table.
At which step does the condition become true for the first time?
AStep 3
BStep 2
CStep 1
DStep 4
💡 Hint
Look at the 'Condition' column in the execution_table to find when it first becomes true.
If interval was changed to 500, how would Step 2's condition change?
A500 - 0 >= 500 would be False
B500 - 0 >= 500 would be True
CCondition would not change
DCondition would be invalid
💡 Hint
Compare the condition formula with the new interval value in the execution_table.
Concept Snapshot
Non-blocking code uses millis() to check elapsed time.
Avoid delay() to keep code responsive.
Use variables to track last action time.
Perform actions only when interval passes.
Loop keeps running other tasks smoothly.
Full Transcript
This example shows how to write Arduino code that does not stop running other tasks while waiting. Instead of delay(), it uses millis() to check if enough time passed since the last action. If yes, it performs the action and updates the timer. Otherwise, it continues running other code. The execution table traces each step, showing how variables change and when actions happen. This method keeps the program responsive and able to do many things at once.

Practice

(1/5)
1. What is the main advantage of using non-blocking code in Arduino programming?
easy
A. It uses less memory than blocking code.
B. It makes the code run slower but more stable.
C. It allows the Arduino to perform multiple tasks without waiting.
D. It automatically fixes hardware errors.

Solution

  1. Step 1: Understand blocking vs non-blocking

    Blocking code stops the program until a task finishes, while non-blocking code lets the program keep running other tasks.
  2. Step 2: Identify the benefit of non-blocking

    Non-blocking code allows multitasking and keeps the Arduino responsive.
  3. Final Answer:

    It allows the Arduino to perform multiple tasks without waiting. -> Option C
  4. Quick Check:

    Non-blocking = multitasking [OK]
Hint: Non-blocking means no waiting, so tasks run together [OK]
Common Mistakes:
  • Thinking non-blocking slows code down
  • Confusing memory use with blocking behavior
  • Believing non-blocking fixes hardware issues
2. Which of the following is the correct way to check elapsed time without blocking in Arduino?
easy
A. delay(1000);
B. if (millis() - previousMillis >= interval) { /* do task */ }
C. while(millis() < 1000) { /* wait */ }
D. delayMicroseconds(1000);

Solution

  1. Step 1: Identify blocking vs non-blocking code

    delay() and while loops that wait block the program, stopping other tasks.
  2. Step 2: Recognize millis() usage

    Using millis() to check elapsed time allows the program to continue running other code.
  3. Final Answer:

    if (millis() - previousMillis >= interval) { /* do task */ } -> Option B
  4. Quick Check:

    millis() check = non-blocking [OK]
Hint: Use millis() difference, not delay(), to avoid blocking [OK]
Common Mistakes:
  • Using delay() which blocks the program
  • Using while loops that wait for time
  • Confusing delayMicroseconds with non-blocking
3. What will be the output of this Arduino code snippet?
unsigned long previousMillis = 0;
const long interval = 1000;
void loop() {
  if (millis() - previousMillis >= interval) {
    previousMillis = millis();
    Serial.println("Tick");
  }
}
medium
A. Prints "Tick" every second repeatedly.
B. Prints "Tick" continuously without delay.
C. Prints "Tick" once and stops.
D. Does not print anything due to syntax error.

Solution

  1. Step 1: Analyze the millis() timing check

    The code checks if 1000 milliseconds (1 second) have passed since last print.
  2. Step 2: Understand the loop behavior

    When 1 second passes, it updates previousMillis and prints "Tick" again, repeating forever.
  3. Final Answer:

    Prints "Tick" every second repeatedly. -> Option A
  4. Quick Check:

    millis() timer triggers print every 1 second [OK]
Hint: millis() timer resets each print for repeated ticks [OK]
Common Mistakes:
  • Thinking it prints only once
  • Assuming continuous printing without delay
  • Believing there is a syntax error
4. Identify the problem in this non-blocking Arduino code and how to fix it:
unsigned long previousMillis = 0;
const long interval = 1000;
void loop() {
  if (millis() > previousMillis + interval) {
    Serial.println("Hello");
  }
}
medium
A. It never updates previousMillis, causing continuous prints.
B. The comparison operator should be '<' instead of '>'.
C. The interval variable must be unsigned long, not long.
D. Serial.println() cannot be used inside loop.

Solution

  1. Step 1: Check the timing logic

    The code checks if millis() is greater than previousMillis + interval but never updates previousMillis.
  2. Step 2: Understand the effect

    Without updating previousMillis, the condition stays true, so "Hello" prints continuously without delay.
  3. Final Answer:

    It never updates previousMillis, causing continuous prints. -> Option A
  4. Quick Check:

    Missing previousMillis update = continuous print [OK]
Hint: Always update previousMillis after action to avoid repeats [OK]
Common Mistakes:
  • Forgetting to update previousMillis
  • Changing comparison operator incorrectly
  • Thinking Serial.println() is invalid in loop
5. You want to blink an LED every 500ms and read a sensor every 200ms without blocking. Which code structure best achieves this?
hard
A. Use while loops to wait 500ms and 200ms before each task.
B. Use delay(500) for LED blink and delay(200) for sensor reading in sequence.
C. Use a single millis() timer for both tasks with combined interval 700ms.
D. Use two millis() timers, one for LED and one for sensor, each updating separately.

Solution

  1. Step 1: Understand multitasking with millis()

    Each task needs its own timer to run independently without blocking.
  2. Step 2: Evaluate options

    Use two millis() timers, one for LED and one for sensor, each updating separately. This uses two separate millis() timers, allowing LED and sensor to run at different intervals smoothly.
  3. Final Answer:

    Use two millis() timers, one for LED and one for sensor, each updating separately. -> Option D
  4. Quick Check:

    Separate timers = true multitasking [OK]
Hint: Use separate millis() timers for each task to avoid blocking [OK]
Common Mistakes:
  • Using delay() which blocks all tasks
  • Combining intervals causing missed timings
  • Using while loops that block execution
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard