Bird
Raised Fist0
Arduinoprogramming~15 mins

Blink without delay pattern in Arduino - Deep Dive

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Overview - Blink without delay pattern
What is it?
The Blink without delay pattern is a way to make an LED blink on and off without stopping the rest of the program. Instead of using a command that pauses the program, it checks the time and changes the LED only when enough time has passed. This lets the Arduino do other things at the same time. It is a smarter way to blink LEDs compared to the simple delay method.
Why it matters
Without this pattern, the Arduino would stop and wait every time it blinks an LED, which means it can't do anything else during that wait. This makes programs slow and unresponsive. Using Blink without delay lets the Arduino multitask, making projects smoother and more useful, like reading sensors or controlling motors while blinking an LED.
Where it fits
Before learning this, you should know how to write basic Arduino code and use the delay() function. After this, you can learn about timers, interrupts, and more advanced multitasking techniques on microcontrollers.
Mental Model
Core Idea
Instead of stopping to wait, check the clock and act only when the right time has passed.
Think of it like...
It's like watching a clock while doing chores: you don't stop everything to wait, but you look at the clock and only switch tasks when the time is right.
┌─────────────────────────────┐
│ Start loop                  │
│                             │
│ Check current time          │
│ ┌─────────────────────────┐ │
│ │ Has interval passed?    │─┤ Yes
│ └────────────┬────────────┘ │
│              │ No           │
│              ▼             │
│ Continue other tasks       │
│              │             │
│              ▼             │
│ Update LED state           │
│              │             │
│              ▼             │
│ Save current time          │
│              │             │
│              ▼             │
│ End loop                   │
└─────────────────────────────┘
Build-Up - 6 Steps
1
FoundationBasic LED blinking with delay
🤔
Concept: Learn how to blink an LED using the simple delay() function.
In Arduino, you can turn an LED on and off with digitalWrite() and pause the program with delay(). For example: void loop() { digitalWrite(LED_BUILTIN, HIGH); // LED on delay(1000); // wait 1 second digitalWrite(LED_BUILTIN, LOW); // LED off delay(1000); // wait 1 second } This makes the LED blink every second but stops the program during delay.
Result
The LED blinks on and off every second, but the Arduino cannot do anything else during the delay.
Understanding this simple method shows why delay() blocks the program and limits multitasking.
2
FoundationUsing millis() to track time
🤔
Concept: Learn how to use the millis() function to get the current time since the Arduino started.
millis() returns the number of milliseconds since the Arduino began running. It keeps counting up without stopping. For example: unsigned long currentTime = millis(); You can use this to check how much time has passed without stopping the program.
Result
You can get the current time in milliseconds anytime without pausing the program.
Knowing millis() lets you measure time intervals without blocking the program.
3
IntermediateComparing time to create intervals
🤔Before reading on: do you think subtracting two millis() values can tell how much time passed? Commit to your answer.
Concept: Learn how to find out if a certain time interval has passed by subtracting previous time from current time.
To check if it's time to change the LED, save the last time you changed it in a variable. Then, each loop, subtract that saved time from the current millis() value. If the difference is bigger than your interval (like 1000 ms), it's time to toggle the LED. Example: unsigned long previousMillis = 0; const long interval = 1000; void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; // toggle LED } }
Result
You can check time intervals without stopping the program and decide when to act.
Understanding time difference calculation is key to non-blocking timing.
4
IntermediateImplementing Blink without delay pattern
🤔Before reading on: do you think this pattern can blink LED and run other code simultaneously? Commit to your answer.
Concept: Combine millis() timing with LED control to blink without blocking the program.
Here is a full example: const int ledPin = LED_BUILTIN; unsigned long previousMillis = 0; const long interval = 1000; bool ledState = LOW; void setup() { pinMode(ledPin, OUTPUT); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; ledState = !ledState; // toggle LED state digitalWrite(ledPin, ledState); } // Other code can run here without delay }
Result
The LED blinks every second while the Arduino can do other tasks at the same time.
This pattern allows multitasking by avoiding blocking delays.
5
AdvancedHandling millis() overflow safely
🤔Before reading on: do you think millis() can overflow and break timing? Commit to your answer.
Concept: Learn how the millis() value resets after about 50 days and how subtraction still works correctly.
millis() is stored as an unsigned long and rolls over to zero after about 49.7 days. But subtracting two unsigned longs still gives the correct time difference because of how unsigned math works in Arduino. Example: if ((currentMillis - previousMillis) >= interval) { previousMillis = currentMillis; // safe even if millis() overflowed }
Result
Your timing code continues to work correctly even after millis() overflows.
Knowing unsigned math behavior prevents bugs in long-running Arduino projects.
6
ExpertUsing Blink without delay in multitasking systems
🤔Before reading on: do you think Blink without delay replaces all multitasking needs? Commit to your answer.
Concept: Understand how this pattern fits in with more complex multitasking like state machines or RTOS on Arduino.
Blink without delay is a simple way to avoid blocking delays, but for complex projects, you might use state machines or real-time operating systems (RTOS). This pattern can be part of those systems to handle timing without blocking. For example, in a state machine, you check millis() to decide when to change states or blink LEDs without stopping other tasks. This pattern is a building block for responsive, multitasking Arduino programs.
Result
You can build complex, responsive systems that handle many tasks smoothly.
Understanding this pattern is foundational for advanced multitasking and real-time control.
Under the Hood
The Arduino keeps a timer running in the background that counts milliseconds since startup. The millis() function reads this timer without stopping the program. By saving the last time an event happened and comparing it to the current millis(), the program can decide when to act next. This avoids using delay(), which stops the processor from doing anything else. The subtraction of unsigned longs handles timer overflow automatically, so timing stays accurate over long periods.
Why designed this way?
The delay() function was simple but blocked the processor, making multitasking impossible. The Blink without delay pattern was designed to let programs keep running other code while waiting for time to pass. Using millis() and subtraction is efficient and uses built-in hardware timers. This design avoids complex interrupts or external libraries, making it easy for beginners and powerful enough for many projects.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ Hardware      │       │ Arduino Code  │       │ LED           │
│ Timer         │──────▶│ millis()      │──────▶│ State Change  │
│ (counts ms)   │       │ (reads time)  │       │ (on/off)      │
└───────────────┘       └───────────────┘       └───────────────┘
        ▲                      │                       │
        │                      │                       │
        └──────────────────────┴───────────────────────┘
                 Loop checks time difference
                 and updates LED without delay
Myth Busters - 4 Common Misconceptions
Quick: Does using Blink without delay mean your program runs instantly without any waiting? Commit yes or no.
Common Belief:Blink without delay makes the program run instantly with no waiting at all.
Tap to reveal reality
Reality:Blink without delay does not remove waiting; it just avoids stopping the whole program. The program still waits by checking time but can do other things meanwhile.
Why it matters:Thinking it removes waiting leads to expecting impossible real-time behavior and ignoring the need for careful timing design.
Quick: Can you use delay() and Blink without delay together without problems? Commit yes or no.
Common Belief:You can mix delay() and Blink without delay freely in the same program.
Tap to reveal reality
Reality:Using delay() blocks the program and defeats the purpose of Blink without delay, causing unresponsive behavior.
Why it matters:Mixing them causes bugs and poor multitasking, confusing beginners about why their program freezes.
Quick: Does millis() overflow cause timing bugs that break Blink without delay? Commit yes or no.
Common Belief:When millis() overflows, Blink without delay stops working correctly.
Tap to reveal reality
Reality:Because of unsigned math, the subtraction still works correctly even after overflow, so timing continues properly.
Why it matters:Not knowing this causes unnecessary complex fixes or fear of long-running Arduino projects.
Quick: Is Blink without delay the best solution for all Arduino timing needs? Commit yes or no.
Common Belief:Blink without delay is the perfect solution for every timing and multitasking problem on Arduino.
Tap to reveal reality
Reality:It is simple and useful but limited; complex projects may need state machines, interrupts, or RTOS for better control.
Why it matters:Overreliance on this pattern can limit project scalability and lead to messy code.
Expert Zone
1
The subtraction of unsigned longs in millis() timing handles overflow naturally, a subtle behavior many overlook.
2
Blink without delay can be combined with state machines to manage multiple timed events cleanly without blocking.
3
Using this pattern improves power efficiency by allowing the processor to sleep between tasks instead of busy waiting.
When NOT to use
Avoid Blink without delay when precise timing or very fast response is needed; use hardware timers or interrupts instead. For complex multitasking, consider using an RTOS or event-driven frameworks.
Production Patterns
In real projects, Blink without delay is often part of a larger event loop or state machine. Professionals use it to keep UI responsive, handle sensor polling, and manage multiple LEDs or outputs without blocking. It is a foundational pattern before moving to more advanced multitasking.
Connections
State Machines
Builds-on
Blink without delay timing is often integrated into state machines to manage transitions without blocking, enabling complex behavior.
Real-Time Operating Systems (RTOS)
Precursor to
Understanding Blink without delay prepares you for RTOS concepts by teaching non-blocking timing and multitasking basics.
Project Management
Analogous pattern
Just like managing multiple tasks by checking deadlines instead of waiting idly, Blink without delay teaches efficient time management in programming.
Common Pitfalls
#1Using delay() inside the loop with Blink without delay code.
Wrong approach:void loop() { digitalWrite(LED_BUILTIN, HIGH); delay(1000); // blocks program digitalWrite(LED_BUILTIN, LOW); delay(1000); // blocks program // Blink without delay code here }
Correct approach:void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; ledState = !ledState; digitalWrite(LED_BUILTIN, ledState); } // Other non-blocking code here }
Root cause:Confusing delay() with non-blocking timing causes the program to freeze, defeating the pattern's purpose.
#2Not updating the previousMillis variable after toggling LED.
Wrong approach:if (millis() - previousMillis >= interval) { // forgot to update previousMillis ledState = !ledState; digitalWrite(LED_BUILTIN, ledState); }
Correct approach:if (millis() - previousMillis >= interval) { previousMillis = millis(); ledState = !ledState; digitalWrite(LED_BUILTIN, ledState); }
Root cause:Forgetting to update the last action time causes the LED to toggle continuously without delay.
#3Using signed integers for time variables causing overflow bugs.
Wrong approach:int previousMillis = 0; int interval = 1000; void loop() { if (millis() - previousMillis >= interval) { previousMillis = millis(); // toggle LED } }
Correct approach:unsigned long previousMillis = 0; const unsigned long interval = 1000; void loop() { if (millis() - previousMillis >= interval) { previousMillis = millis(); // toggle LED } }
Root cause:Using signed types causes incorrect calculations when millis() overflows, breaking timing.
Key Takeaways
Blink without delay lets you blink LEDs without stopping the whole program, enabling multitasking.
Using millis() and subtracting previous time from current time is the core technique for non-blocking timing.
This pattern handles timer overflow safely because of unsigned math behavior in Arduino.
Avoid mixing delay() with this pattern to keep your program responsive and smooth.
Blink without delay is a foundation for more advanced multitasking and real-time control on microcontrollers.

Practice

(1/5)
1. What is the main advantage of using the Blink without delay pattern in Arduino programming?
easy
A. It allows the Arduino to perform other tasks while blinking an LED.
B. It makes the LED blink faster than usual.
C. It uses less power than the delay() function.
D. It requires fewer lines of code than delay().

Solution

  1. Step 1: Understand delay() limitation

    The delay() function pauses the whole program, stopping other tasks.
  2. Step 2: millis() allows multitasking

    Using millis() tracks time without stopping the program, so other code runs simultaneously.
  3. Final Answer:

    It allows the Arduino to perform other tasks while blinking an LED. -> Option A
  4. Quick Check:

    Blink without delay = multitasking [OK]
Hint: Remember millis() tracks time without stopping code [OK]
Common Mistakes:
  • Thinking delay() lets other code run
  • Believing blink speed is faster with millis()
  • Assuming millis() uses less power
2. Which of the following code snippets correctly initializes the variable to store the last time the LED was toggled in the Blink without delay pattern?
easy
A. unsigned long previousMillis = 0;
B. int previousMillis = 0;
C. float previousMillis = 0.0;
D. boolean previousMillis = false;

Solution

  1. Step 1: Identify correct data type for millis()

    millis() returns an unsigned long value representing milliseconds.
  2. Step 2: Match variable type to millis()

    To store millis() values, use unsigned long to avoid overflow and negative values.
  3. Final Answer:

    unsigned long previousMillis = 0; -> Option A
  4. Quick Check:

    Use unsigned long for time tracking [OK]
Hint: Use unsigned long for millis() time variables [OK]
Common Mistakes:
  • Using int which can overflow quickly
  • Using float which is unnecessary and imprecise
  • Using boolean which can't store time
3. What will be the output behavior of the following Arduino code snippet using Blink without delay pattern?
const int ledPin = 13;
unsigned long previousMillis = 0;
const long interval = 1000;
bool ledState = false;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    ledState = !ledState;
    digitalWrite(ledPin, ledState ? HIGH : LOW);
  }
}
medium
A. The LED will blink rapidly without delay.
B. The LED on pin 13 will blink on and off every 1 second without stopping other code.
C. The LED will stay on permanently after 1 second.
D. The program will cause a compile error due to bool type.

Solution

  1. Step 1: Analyze timing logic

    The code checks if 1000 ms passed since last toggle, then flips ledState.
  2. Step 2: Understand LED toggle and output

    ledState toggles true/false every second, controlling LED on/off without delay blocking.
  3. Final Answer:

    The LED on pin 13 will blink on and off every 1 second without stopping other code. -> Option B
  4. Quick Check:

    Millis timing toggles LED every 1 second [OK]
Hint: Look for millis() interval check to predict blink timing [OK]
Common Mistakes:
  • Thinking bool causes compile error
  • Assuming LED stays on permanently
  • Confusing rapid blinking with 1-second interval
4. Identify the error in this Blink without delay code snippet:
const int ledPin = 13;
unsigned long previousMillis = 0;
const long interval = 1000;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    digitalWrite(ledPin, !digitalRead(ledPin));
  }
}
medium
A. The code will cause a runtime error due to digitalWrite logic.
B. Using > instead of >= in the if condition causes timing issues.
C. The variable previousMillis should be int, not unsigned long.
D. digitalRead() cannot be used on output pins.

Solution

  1. Step 1: Check use of digitalRead on output pin

    digitalRead() on an output pin is unreliable and not recommended for toggling.
  2. Step 2: Understand proper toggle method

    Better to track LED state in a variable rather than reading output pin state.
  3. Final Answer:

    digitalRead() cannot be used on output pins. -> Option D
  4. Quick Check:

    Don't use digitalRead on output pins [OK]
Hint: Avoid digitalRead on pins set as OUTPUT [OK]
Common Mistakes:
  • Thinking > vs >= causes major error
  • Believing previousMillis type is wrong
  • Assuming digitalWrite logic causes runtime error
5. You want to blink two LEDs independently using the Blink without delay pattern: LED1 every 500 ms and LED2 every 1000 ms. Which approach correctly manages both LEDs without blocking the program?
hard
A. Toggle LED1 every 500 ms and LED2 every 1000 ms using nested for loops.
B. Use one previousMillis variable and toggle both LEDs at the same time every 500 ms.
C. Use two separate previousMillis variables and check each interval independently in loop().
D. Use delay(500) for LED1 and delay(1000) for LED2 in sequence inside loop().

Solution

  1. Step 1: Understand independent timing needs

    Each LED needs its own timer variable to track its blinking interval separately.
  2. Step 2: Avoid blocking delays and incorrect loops

    Using delay() or nested loops blocks code and prevents independent blinking.
  3. Final Answer:

    Use two separate previousMillis variables and check each interval independently in loop(). -> Option C
  4. Quick Check:

    Separate timers for independent blinking [OK]
Hint: Track each LED's time separately with its own variable [OK]
Common Mistakes:
  • Using one timer for both LEDs
  • Using delay() which blocks code
  • Trying nested loops for timing