Timing functions are essential in Arduino programming, whether you’re blinking an LED, controlling a motor, or designing an interactive system. Properly managing time delays is critical for tasks such as pausing execution, synchronizing operations, and ensuring efficient control of your project’s behavior.
Among the various timing functions available in Arduino, the delay() function is the simplest and most widely used. It allows you to pause the program execution for a specified number of milliseconds, making it a go-to tool for many beginners and experienced developers alike. Its syntax is straightforward and easy to implement:
void loop() {
delay(1000); // Pause for 1 second
}
Here, delay(1000); halts the program for 1000 milliseconds (1 second) before continuing execution. While this might seem useful, it stops the microcontroller from performing other tasks during this period.
In this comprehensive guide, we’ll explore the Arduino delay() function in detail, covering its usage, limitations, and alternatives. By the end of this article, you’ll have a deeper understanding of how to use delay() effectively and when to consider other timing methods for your DIY projects. Let’s dive in!
How delay() Works Internally
Arduino boards, which are based on AVR microcontrollers, utilize an internal clock to count time. The delay() function leverages this clock to pause the execution of your program for a specified duration. Internally, delay() works by running an empty loop in the background, continuously checking the elapsed time until the desired delay period is reached.
The delay() function uses milliseconds (ms) as its time unit. For example:
- To create a one-second delay, you would use delay(1000);.
- For a half-second delay, use delay(500);.
- For a very short pause, such as 10 milliseconds, use delay(10);.
Practical Applications of Delay ()
- Blinking an LED
A common beginner project is blinking an LED using delay():
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH); // Turn LED on
delay(1000); // Wait 1 second
digitalWrite(13, LOW); // Turn LED off
delay(1000); // Wait 1 second
}
- Pausing Sensor Readings
In projects using sensors, delay() can be used to control reading intervals:
int sensorvalue;
void loop() {
sensorValue = analogRead(A0);
delay(500); // Read sensor value every 500ms
}
- Delaying Communication Between Devices
When sending data over serial communication, a delay prevents data overflow:
void loop() {
Serial.println("Hello");
delay(2000); // Send data every 2 seconds
}
- Button Debounce
Mechanical switches can register multiple presses unintentionally, delay() helps debounce buttons:
void loop() {
if (digitalRead(2) == HIGH) {
delay(50); // Short delay to debounce
}
}
Limitations of delay()
Blocking Nature of delay()
The delay() function in Arduino is designed to pause the execution of your program for a specified duration. During this pause, the microcontroller essentially “freezes” and does nothing else—it simply waits for the delay period to end. While this behavior is straightforward and easy to use, it has a significant drawback: it blocks all other operations.
This blocking nature makes delay() unsuitable for multitasking or real-time applications, where the system needs to handle multiple tasks simultaneously or respond to events as they occur.
Imagine you have an Arduino project with the following requirements:
- Blink an LED every second.
- Read temperature sensor data every half second.
- Detect a button press at any time.
If you use delay() to manage these tasks, only one task can execute at a time. The system becomes unresponsive to real-time events, such as button presses, during the delay period. Here’s an example to illustrate this issue:
void loop() {
// Check for button press
if (digitalRead(2) == HIGH) {
Serial.println("Button Pressed!");
}
// Blink an LED
digitalWrite(13, HIGH);
delay(3000); // Wait for 3 seconds (blocks button press detection)
digitalWrite(13, LOW);
delay(3000); // Wait another 3 seconds
}
What Happens in This Code?
- When the LED is turned on, the program pauses for 3 seconds using delay(3000).
- During this pause, the Arduino cannot detect button presses or perform any other tasks.
- If the button is pressed during the delay period, the program won’t register it, because it’s stuck waiting.
- After the delay ends, the LED turns off, and another 3-second delay begins, further delaying any response to user input.
Inefficient for Real-Time Applications
If you need to control multiple devices or respond to real-time inputs, delay() is not a good choice because it forces the program to remain idle. A real-time application is a system that must:
- Run Multiple Tasks Simultaneously: For example, reading sensors, controlling motors, and updating a display at the same time.
- Respond Quickly to Inputs or Events: Such as detecting a button press or stopping a motor when an obstacle is detected.
- Maintain Precise Timing: For tasks like measuring pulse width or managing servo motors.
If delay() is used, the system freezes during the delay period, making it unresponsive to real-time inputs.
To illustrate the limitations of delay() in real-time applications, let’s consider an example of an Arduino-based temperature monitoring system with the following requirements:
- Read a temperature sensor every second.
- Send data to an LCD screen every 2 seconds.
- Respond immediately to a button press to activate a fan.
Here’s an example of incorrect code using delay():
void loop() {
int temp = analogRead(A0); // Read temperature sensor
Serial.println(temp);
delay(1000); // Pause for 1 second (blocks everything)
lcd.print(temp); // Update LCD screen
delay(2000); // Pause for 2 seconds (blocks button response)
if (digitalRead(2) == HIGH) { // Check button press
Serial.println("Fan Activated!");
}
}
Problems with This Approach:
- If the button is pressed during the delay(1000) or delay(2000) periods, the program won’t detect it.
- The program cannot read the temperature sensor, update the LCD, and check for button presses simultaneously.
- If the temperature reaches a critical level, the system cannot react immediately because it is stuck in a delay.
Lead to Unresponsive Systems
In projects that require quick reactions—such as robotics, security systems, or interactive devices—the use of delay() can result in sluggish and unresponsive behavior. Let’s explore two real-world examples to illustrate this issue.
- Robotics (Obstacle Avoidance Robot)
An obstacle avoidance robot relies on ultrasonic sensors to detect obstacles and react immediately by stopping or changing direction.
If delay() is used after each movement command, the robot’s sensors cannot detect obstacles during the waiting period. This delay can lead to dangerous situations, such as collisions or missed obstacles.
- Security System (Motion Detector Alarm)
A motion detector security system must trigger an alarm instantly when movement is detected.
If delay() is used to wait before checking the motion sensor again, the system might miss fast-moving intruders or fail to trigger the alarm in time.
Alternatives to delay() for Non-Blocking Code
Using millis() Instead of delay()
The millis() function is a powerful alternative to delay() that keeps track of elapsed time without blocking the program. It returns the number of milliseconds since the Arduino board started running, allowing you to create non-blocking delays.
Example: Blinking an LED Without Blocking
unsigned long previousMillis = 0;
const long interval = 1000; // 1-second interval
void loop() {
unsigned long currentMillis = millis(); // Get the current time
// Check if the interval has passed
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // Save the last time the LED was toggled
digitalWrite(13, !digitalRead(13)); // Toggle the LED
Serial.println("One second passed");
}
// Other tasks can run here without being blocked
}
How It Works:
- millis() tracks the elapsed time in milliseconds.
- By comparing the current time (currentMillis) with the previous time (previousMillis), you can determine if the desired interval has passed.
- This approach allows the program to perform other tasks while waiting for the interval to elapse.
Implementing a State Machine Approach
For tasks requiring microsecond-level precision, the micros() function is a better choice. It works similarly to millis() but counts time in microseconds (µs) instead of milliseconds.
Example: Microsecond-Level Timing
unsigned long startTime = micros(); // Record the start time
void loop() {
if (micros() - startTime > 1000000) { // 1-second delay (1,000,000 microseconds)
Serial.println("One second passed");
startTime = micros(); // Reset the start time
}
// Other tasks can run here without being blocked
}
When to Use micros():
- For high-precision timing, such as controlling servo motors or measuring pulse widths.
- When millisecond-level precision is insufficient.
Implementing a State Machine Approach
A state machine is a programming technique that allows your program to handle multiple tasks simultaneously by dividing the code into distinct states. This approach is particularly useful for multitasking Arduino projects.
Example: Toggling an LED and Reading a Sensor
enum State { LED_ON, LED_OFF };
State currentState = LED_OFF;
unsigned long previousMillis = 0;
const long interval = 1000;
void loop() {
unsigned long currentMillis = millis();
switch (currentState) {
case LED_OFF:
if (currentMillis - previousMillis >= interval) {
digitalWrite(13, HIGH); // Turn LED on
currentState = LED_ON;
previousMillis = currentMillis;
}
break;
case LED_ON:
if (currentMillis - previousMillis >= interval) {
digitalWrite(13, LOW); // Turn LED off
currentState = LED_OFF;
previousMillis = currentMillis;
}
break;
}
// Read sensor data simultaneously
int sensorValue = analogRead(A0);
Serial.println(sensorValue);
}
Advantages of State Machines:
- Enable multitasking by dividing the program into logical states.
- Improve code organization and readability.
- Allow simultaneous execution of multiple tasks.
Advanced Timing Techniques
Running Multiple Delays Simultaneously
By using millis(), you can handle multiple timing events independently, enabling your program to perform several tasks at the same time.
Example: Toggling an LED and Reading a Sensor
unsigned long ledTimer = 0;
unsigned long sensorTimer = 0;
void loop() {
// Toggle LED every 1 second
if (millis() - ledTimer >= 1000) {
ledTimer = millis();
digitalWrite(13, !digitalRead(13)); // Toggle LED
}
// Read sensor every 500 milliseconds
if (millis() - sensorTimer >= 500) {
sensorTimer = millis();
int sensorValue = analogRead(A0);
Serial.println(sensorValue);
}
}
How It Works:
- Each task has its own timer (ledTimer and sensorTimer).
- The program checks if the required interval has passed for each task independently.
- This allows multiple tasks to run simultaneously without blocking each other.
Using Hardware Timers for Precise Delays
For advanced projects requiring extremely precise timing, hardware timers are the best option. Hardware timers run independently of the main program loop and provide higher accuracy than millis() or micros().
When to Use Hardware Timers:
- For tasks requiring precise timing, such as generating PWM signals or controlling stepper motors.
- When software-based timing methods (millis() or micros()) are insufficient.
Conclusion
The delay() function is a simple tool for creating time delays, but its blocking nature makes it unsuitable for multitasking or real-time applications. By using alternatives like millis(), micros(), state machines, and hardware timers, you can create non-blocking code that handles multiple tasks efficiently and responds to events in real time.
You Might Be Interested

In this comprehensive tutorial, we will provide a detailed exploration of various Arduino boards, delve into essential concepts, and highlight their distinctive features. Our aim is to assist you in

This article introduces six beginner-friendly projects for Arduino UNO, a popular microcontroller board. These projects cover a range of applications, from marquee lights and stepper motor control to ultrasonic ranging,

This project demonstrates the construction of a simple thermometer using an Arduino and a DS18B20 digital temperature sensor. The temperature data is displayed in both Celsius and Fahrenheit on a

Unlock your computer with a touch using Arduino! Transform your keyboard by adding a fingerprint module for seamless login. Discover our step-by-step guide to upgrade your setup with biometric security

Explore Arduino speakers, from piezo buzzers to dynamic speakers and DFPlayer Mini modules. Learn how to choose, amplify, and control audio output for your projects. Whether adding a simple beep

This Arduino project employs a vibration sensor, ball switch, and LED to detect tilt or collision events. The LED lights up when vibrations occur and turns off afterward. By connecting

This comprehensive guide elucidates the process of enabling multi-core functionality on the ESP32 microcontroller, leveraging its dual-core architecture for enhanced parallel processing capabilities. Through detailed steps and code examples, developers

In this article, we’ll start with the basics of ultrasonic sensors, walk you through using Arduino for distance measurement, and explore practical applications of this technology in projects like smart