Problem with interruptions in Arduino Uno

I work with interruptions in Arduino UNO. In this project, I want to when the Door is opened the LED blink 10 times, and when the door is closed again, stop blinking the LED and exit the function. But in this code the LED only turn on and off once and it does not flash again. My other problem is that, when the door is opened or closed, sometimes the opened or closed word appears several times in the Series monitor.

const byte LED_Red = 13;
const byte DOOR_SENSOR = 2; // magnetic door sensor pin

volatile int SensorState = LOW; // 0 close - 1 open wwitch

void setup()
{
    Serial.begin(9600);
    pinMode(LED_Red, OUTPUT);
    pinMode(DOOR_SENSOR, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(DOOR_SENSOR), DoAction, CHANGE);
}

void DoAction()
{
    SensorState = digitalRead(DOOR_SENSOR);
    if (SensorState == HIGH) {
        Serial.println("Opened");
        blinkLED(10, 500);
    }
    else {
        Serial.println("Closed");
    }
}

void blinkLED(int repeats, int time)
{
    for (int i = 0; i < repeats; i++) {
        if (SensorState == HIGH) {
            digitalWrite(LED_Red, HIGH);
            delay(time);
            digitalWrite(LED_Red, LOW);
            delay(time);
        }
        else
            return;
    }
}

void loop()
{
}

One reason for your problem is that delay() does not work in an ISR because it depends on an interrupt and they are disabled when in an ISR

Millis is not updated while interrupts are disabled. Serial.print uses interrupts. Interrupts are disabled in an ISR. See any problems? Don't use millis() in an ISR for timing. Don't print from within an ISR.

Interrupts are not the way to deal with this, anyway.

You are using a switch on the door to sense the door position? You may need to deal with switch "bounce". Debouncing can be done with hardware (a 0.1uf cap across the switch or with software). Google will have information about switch bounce and ways to deal with it.

groundFungus:
Millis is not updated while interrupts are disabled. Serial.print uses interrupts. Interrupts are disabled in an ISR. See any problems? Don’t use millis() in an ISR for timing. Don’t print from within an ISR.

Interrupts are not the way to deal with this, anyway.

You are using a switch on the door to sense the door position? You may need to deal with switch “bounce”. Debouncing can be done with hardware (a 0.1uf cap across the switch or with software). Google will have information about switch bounce and ways to deal with it.

I deleted the delay() and for loop as well as the Serial.print from the code. But the problem did not resolve, and Blink still does not work.

Yes ,i use “MC-51” magnetic door sensor in my project.

With this code and without calling the Blink method, I can turn on the LED once when I open the door and turn off the LED door by closing it. My problem is executing the Blink function.
this code is workings well(i remove the blinkLED function):

const byte LED_Red = 13;
const byte DOOR_SENSOR = 2; // magnetic door sensor pin

volatile int SensorState = LOW; // 0 close - 1 open wwitch

void setup()
{
    Serial.begin(9600);
    pinMode(LED_Red, OUTPUT);
    pinMode(DOOR_SENSOR, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(DOOR_SENSOR), DoAction, CHANGE);
}

void DoAction()
{
    SensorState = digitalRead(DOOR_SENSOR);
    if (SensorState == HIGH) {
        Serial.println("Opened");
       digitalWrite(LED_Red, HIGH);
    }
    else {
        Serial.println("Closed");
        digitalWrite(LED_Red, LOW);
    }
}

void loop()
{
}

Do you know another solution to do this?

Don't do serial I/O in interrupt context.

Why do think you even need to use interrupts in this case?

Here is a way to do what (I think) you want to do without using any delays or interrupts. Interrupts are for things that happen quickly (microseconds) and irregularly. A switch controlled by a door opening and closing is does not satisfy the quickly part. That is better done using "polling".

See the beginner's guide to millis() for a tutorial on using millis() for timing.

const byte LED_Red = 13;
const byte DOOR_SENSOR = 2; // magnetic door sensor pin

boolean SensorState = LOW; // 0 close - 1 open switch
boolean blinkState = false;

unsigned long blinkInterval = 200;  // sets blink rate
unsigned long loopInterval = 100;  // sets how often we check the switch

void setup()
{
   Serial.begin(9600);
   pinMode(LED_Red, OUTPUT);
   pinMode(DOOR_SENSOR, INPUT_PULLUP);
}

void loop()
{
   static unsigned long loopTimer = 0;   
   if (millis() - loopTimer >= loopInterval)
   {
      loopTimer = millis();
      SensorState = digitalRead(DOOR_SENSOR);
      
      if (SensorState == HIGH)
      {
         Serial.println("Opened");
         blinkState = true; // enable blinking
      }
      else
      {
         Serial.println("Closed");
         blinkState = false; // disable blinking
         digitalWrite(LED_Red, LOW); // make sure LED is off
      }
   }
   blink();  // called every time through loop.  Will only blink when blinkState is true
}

void blink()
{
   static unsigned long blinkTimer = 0;   
   if (millis() - blinkTimer >= blinkInterval && blinkState == true)
   {
      blinkTimer = millis();
      digitalWrite(LED_Red, !digitalRead(LED_Red)); // toggle LED state
   }
}

Here I added the 10 blinks when the door is opened and the prints only happen once when the door state changes. That part is done using the state change detection method.

const byte LED_Red = 13;
const byte DOOR_SENSOR = 2; // magnetic door sensor pin

boolean SensorState = LOW; // 0 close - 1 open switch
boolean lastSensorState = LOW;
boolean blinkState = false;
byte blinkIndex = 0;
byte numberOfBlinks = 10;

unsigned long blinkInterval = 200;  // sets blink rate
unsigned long loopInterval = 100;  // sets how often we check the switch

void setup()
{
   Serial.begin(9600);
   pinMode(LED_Red, OUTPUT);
   pinMode(DOOR_SENSOR, INPUT_PULLUP);
}

void loop()
{
   static unsigned long loopTimer = 0;
   if (millis() - loopTimer >= loopInterval)
   {
      loopTimer = millis();
      SensorState = digitalRead(DOOR_SENSOR);
      if (SensorState != lastSensorState)
      {
         if (SensorState == HIGH)
         {
            Serial.println("Opened");
            blinkState = true; // enable blinking
         }
         else
         {
            Serial.println("Closed");
            blinkState = false; // disable blinking
            digitalWrite(LED_Red, LOW); // make sure LED is off
            blinkIndex = 0; // reset index
         }
      }
      lastSensorState = SensorState;
   }
   blink();
}

void blink()
{
   static unsigned long blinkTimer = 0;
   if (millis() - blinkTimer >= blinkInterval && blinkState == true)
   {
      blinkTimer = millis();
      digitalWrite(LED_Red, !digitalRead(LED_Red)); // toggle LED state
      blinkIndex ++;
      if (blinkIndex > numberOfBlinks * 2)
      {
         digitalWrite(LED_Red, LOW); // make sure LED is off
         blinkState = false;
         blinkIndex = 0;
      }
   }
}

If you do want the interrupt to trigger blinking, set a flag in the ISR to indicate that.

Then, in loop(), if the flag is set, and it is time, toggle the state of the LED. If the flag is not set, turn the LED off.