Arduino programming guide series


Can you use delay() inside Interrupt Service Routine?

An interrupt service routine (ISR) looks like a regular function. It can hold any code you want, and it will work as it would in any other function. 

But, the ISR is not a regular function, and you should treat it as special.

The ISR is a function that is registered to be called when an interrupt event occurs. For example, this interrupt can be a change in the state of an interrupt-capable pin, or the expiration of a timer (here's an example sketch of a timer interrupt). 

A question I often receive is about the contents of the ISR: 

Although this is possible, you should not.

Both delay() and Serial.print() functions are "blocking". This means that they stop anything else from happening in the Arduino.

The delay() function will make the Arduino stop until your specified interval has expired. The Serial.print() function will also make the Arduino stop until the entire message has been printed to the serial monitor at the slow communication speeds of the serial interface.

An interrupt service routine should be as light as possible so that it can service an interrupt quickly. The objective is to allow the Arduino to continue doing what it was doing before the interrupt.

If you use a delay(5) inside the ISR, you will be blocking the processor for at least 5ms, which for a computer is a lot of time. 

If you need to check the passage of time inside the ISR, it is much better to use an "if" statement, like I discussed in a separate article.

The Arduino will be able to process the "if" statement within a few nanoseconds, and quickly return control of the program to the code outside of the ISR.

Here is an example sketch showing how to write an ISR that uses the "if" statement to assess the passage of time. This example is from Arduino Step by Step Getting Serious, where I show how to use a rotary encoder.

I have highlighted the relevant block in bold.

You can see this code on Github.

//Original sketch:
//Modified by Peter Dalmaris, July 2015

const int PinCLK=2; // Used for generating interrupts using CLK signal
const int PinDT=3; // Used for reading DT signal
const int PinSW=8; // Used for the push button switch

volatile long     virtualPosition  =0;  // must be volatile to work with the isr

void isr0 ()  {
  static unsigned long lastInterruptTime = 0;
  unsigned long interruptTime = millis();
  // If interrupts come faster than 5ms, assume it's a bounce and ignore
  if (interruptTime - lastInterruptTime > 5) {
      if (!digitalRead(PinDT))

  lastInterruptTime = interruptTime;
  attachInterrupt (0,isr0,RISING);
  } // ISR0

void setup ()  {
  attachInterrupt (0,isr0,RISING);   // interrupt 0 is always connected to pin 2 on Arduino UNO
  Serial.begin (9600);

void loop ()  {
  int lastCount = 0;
  while (true) {
      if (!(digitalRead(PinSW))) {  // check if pushbutton is pressed
        virtualPosition = 0; // if YES, then reset counter to ZERO
        while (!digitalRead(PinSW)) {}  // wait til switch is released
        delay(10); // debounce
        Serial.println("Reset"); // Using the word RESET instead of COUNT here to find out a buggy encoder
      if (virtualPosition != lastCount) {
        lastCount = virtualPosition;
   } // while

"Arduino programming" series

Tech Explorations Arduino intermediate level

Done with the basics? Looking for more advanced topics?

Arduino Step by Step Getting Serious is our comprehensive Arduino course for people ready to go to the next level.

Learn about Wifi, BLE and radio, motors (servo, DC and stepper motors with various controllers), LCD, OLED and TFT screens with buttons and touch interfaces, control large loads like relays and lights, and much much MUCH more.