Arduino Uno Locking up

Hello everyone,
I have built a system for my fire station that opens the bay doors automatically when we show up and will close them 15 minutes later after we've left. However, the Arduino seems to be locking up, or at least not responding to input after a time period. Any ideas what might be causing it? I've attached the .ino and a wiring diagram. I'm using a 2amp 12v power supply that runs the Arduino, 1x 6-Channel relay board, 1x 1-channel relay board, 1 motion sensor (30ma at 12v draw max).

Project Basics:

  1. There are 4 doors
  2. There is a relay attached to each door button
  3. There are 4 door sensors (one for each door)
  4. There are 2 lighting relays, one to cut power (for blinking) and one to bypass the switch (to turn the lights on if the switch is off)
  5. When the pager tones are received, a relay is triggered to start the siren, I used the other side of the relay to trigger the project
  6. Once triggered, it waits for motion on the sensor near the man door to open the bay doors and turn on the lights
  7. 15 minutes later it will flash the lights and close the doors
  //Import Libraries
  #include <ezButton.h>

  // Configuration Variables
  int TimeDoorsOpen = 900; // This is the time in seconds that the doors will remain open

  // Map constant names to IO pins
  const byte Furnace_Rly = 13;
  const byte LightOn_Rly = 12;
  const byte LightOff_Rly = 11;
  const byte Door1_Rly = 10;
  const byte Door2_Rly = 9;
  const byte Door3_Rly = 8;
  const byte Door4_Rly = 7;

  // Define EZButton
  ezButton Door1_Sen(6);
  ezButton Door2_Sen(5);
  ezButton Door3_Sen(4);
  ezButton Door4_Sen(3);
  ezButton Tone_Sen(2);
  ezButton Motion_Sen(1);

  // Create System Variables
  volatile int SecCount = 9000; // Used for counting seconds from Timer 1 overflow
  bool OpenDoors = false; // Opening the doors
  bool CloseDoors = false; //Boolean value for Closing the doors
  bool FlashLight = false; // Boolean value for flashing the lights
  bool SystemActivated = false; // Boolean value for the system being activated
  int Door1_State; // State of the door sensor
  int Door2_State; // State of the door sensor
  int Door3_State; // State of the door sensor
  int Door4_State; // State of the door sensor
  int Furnace_Tmr; // Furnace Timer

void setup(){
  // Setup EZButton
  Door1_Sen.setDebounceTime(250); // set debounce time to 250 milliseconds
  Door2_Sen.setDebounceTime(250); // set debounce time to 250 milliseconds
  Door3_Sen.setDebounceTime(250); // set debounce time to 250 milliseconds
  Door4_Sen.setDebounceTime(250); // set debounce time to 250 milliseconds
  Tone_Sen.setDebounceTime(250); // set debounce time to 250 milliseconds
  Motion_Sen.setDebounceTime(250); // set debounce time to 250 milliseconds
  
  // Setup IO pins
  pinMode(Furnace_Rly, OUTPUT); // Relay to turn the furnace off
  pinMode(LightOn_Rly, OUTPUT); // Override Light On Control Relay
  pinMode(LightOff_Rly, OUTPUT); // Ovveride Light Off Control Relay
  pinMode(Door1_Rly, OUTPUT); // Bay 1 Door Relay
  pinMode(Door2_Rly, OUTPUT); // Bay 2 Door Relay
  pinMode(Door3_Rly, OUTPUT); // Bay 3 Door Relay
  pinMode(Door4_Rly, OUTPUT); // Bay 4 Door Relay
  
  // Define timer parameters
  TCCR1A = 0; // Setup the Timer Register A
  TCCR1B = 0; // Setup the Timer Register B
  TCNT1 = 3036; // Offsetting the timer to be 1 second (initial)
  TCCR1B |= (1<<CS12); // Setup the Timer Prescaler
  TIMSK1 |= (1<<TOIE1); // Setup Overflow interrupt
}

ISR(TIMER1_OVF_vect){
  if (SecCount == 9000){ // Initialization 
    SecCount = TimeDoorsOpen - 17; // Trigger the sequence
    digitalWrite(LightOn_Rly, HIGH); // Turn Lights On
  }
  SecCount = SecCount + 1; //Incriment the counter by 1 second
  if (SecCount > 7200){SecCount = 3600;} // Reset timer if it reaches 2 hours to prevent overflow of the integer
  if (SecCount == TimeDoorsOpen - 17){FlashLight = true;} // Preload the relay
  if (SecCount == TimeDoorsOpen - 15){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 13){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 11){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 9){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 7){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 5){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 3){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen - 1){FlashLight = true;} // Light Flash
  if (SecCount == TimeDoorsOpen){CloseDoors = true;} // Close the doors
  if (SecCount == TimeDoorsOpen + 20 ){digitalWrite(LightOn_Rly, LOW);} // Turn off the lights
  TCNT1 = 3036; // Adjust the quarts timer closer to 1 second (repeated)
}

void loop(){
  // EZButton
  Door1_Sen.loop();
  Door2_Sen.loop();
  Door3_Sen.loop();
  Door4_Sen.loop();
  Tone_Sen.loop();
  Motion_Sen.loop();
  
  // System Activation by Tone, don't reactivate within 15 seconds
  if (Tone_Sen.isReleased()){
    if (SecCount > 15){
      SystemActivated = true;
      SecCount = 0;
      digitalWrite(LightOn_Rly, HIGH); // Turn Lights On
    }
  }

  // Trigger Doors open when motion is sensed within the response time
  if (Motion_Sen.isReleased()){
    if (SystemActivated == true){
      if(SecCount < TimeDoorsOpen - 1){
        SystemActivated = false;
        OpenDoors = true;
        SecCount = 0;
      }
    }
  }

  // Read Door Sensors
  Door1_State = Door1_Sen.getState();
  Door2_State = Door2_Sen.getState();
  Door3_State = Door3_Sen.getState();
  Door4_State = Door4_Sen.getState();

 
  if (OpenDoors == true) { // This section will run the process for opening the doors and turning on the lights
    OpenDoors = false; // Set the flag back to not trigger on next loop

    if (Door1_State == LOW) { // Sense if Door 1 is Open
      digitalWrite(Door1_Rly, HIGH); // Open Door 1 if not open
    }
    if (Door2_State == LOW) { // Sense if Door 2 is Open
      digitalWrite(Door2_Rly, HIGH); // Open Door 2 if not open
    }
    if (Door3_State == LOW) { // Sense if Door 3 is Open
      digitalWrite(Door3_Rly, HIGH); // Open Door 3 if not open
    }
    if (Door4_State == LOW) { // Sense if Door 4 is Open
      digitalWrite(Door4_Rly, HIGH); // Open Door 4 if not open
    }
    delay(750); // Wait for 750 millisecond(s)
    digitalWrite(Door1_Rly, LOW); // Close door switch after a delay
    digitalWrite(Door2_Rly, LOW); // Close door switch after a delay
    digitalWrite(Door3_Rly, LOW); // Close door switch after a delay
    digitalWrite(Door4_Rly, LOW); // Close door switch after a delay
  }

  if(FlashLight == true){ // This section will flash the lights by overriding them off
    FlashLight = false; // Set the flag back to not trigger on next loop
    digitalWrite(LightOff_Rly, HIGH); // Switch the lights off override on
    delay(900); // Wait 250 miliseconds
    digitalWrite(LightOff_Rly, LOW); // Switch the lights off override off
  }

  if(CloseDoors == true){ // This section will close the doors and turn the lights off
    CloseDoors = false; // Set the flag back to not trigger on next loop
    if (Door1_State == HIGH) { // Sense if Door 1 is Closed
      digitalWrite(Door1_Rly, HIGH); // Close Door 1 if not Closed
    }
    if (Door2_State == HIGH) { // Sense if Door 2 is Closed
      digitalWrite(Door2_Rly, HIGH); // Close Door 2 if not Closed
    }
    if (Door3_State == HIGH) { // Sense if Door 3 is Closed
      digitalWrite(Door3_Rly, HIGH); // Close Door 3 if not Closed
    }
    if (Door4_State == HIGH) { // Sense if Door 4 is Closed
      digitalWrite(Door4_Rly, HIGH); // Close Door 4 if not Closed
    }
    delay(250); // Wait for 250 milliseconds
    digitalWrite(Door1_Rly, LOW); // Close door switch after a delay
    digitalWrite(Door2_Rly, LOW); // Close door switch after a delay
    digitalWrite(Door3_Rly, LOW); // Close door switch after a delay
    digitalWrite(Door4_Rly, LOW); // Close door switch after a delay
  }

   // Shutdown Furnace if doors are open
  if((Door1_State == HIGH) || (Door2_State == HIGH) || (Door3_State == HIGH) || (Door4_State == HIGH)) {
    digitalWrite(Furnace_Rly, HIGH);
  }
  else{
    digitalWrite(Furnace_Rly, LOW);
  }
  delay(10); // Delay to assist with processing
}

Station_Automation_Programming.ino (7.17 KB)

lmfd1550:
I'm using a 2amp 12v power supply that runs the Arduino...

Is the regulator getting hot?

Using an interrupt handler for this application is a bad choice. After just one quick pass through your code I was able to spot a race condition. I suspect it is not alone.

Your application looks like a simple classic state machine. Recoding it as a state machine (no interrupt handler) will make your life so much better. You'll be able to debug it if something doesn't work. You'll be able to make changes without introducing weird side effects. {Insert long list of reasons to avoid interrupts.}

Your description is a good starting point for a state machine. It is, however, incomplete. For example, what should happen if motion is never detected at the man door?

The interrupts are needed for the timer and to prevent a blocking state that could occur if the system is currently activated and another activation occurs. If nobody enters the man door, the lights come on but the bay doors don't open and the system resets itself after 15 mins.

I am new to this, so forgive some of my ignorant questions, but what do you mean by race condition? And what is that you saw that indicates that?

Google "race condition" and "state machine", that's going to get you started...

You really need to nail down the behaviour as a state transition diagram and check its all
doing what you want - no need for interrupts unless there's some really fast hardware to
service. (really fast means microseconds!).

State machines make all the behaviour explicit and removes all the business logic into the
diagram, not the code, which is great for reliability and correctness. Various states will need
a timeout, but this is just another transition in the diagram.

The interrupts are needed for the timer and to prevent a blocking state that could occur if the system is currently activated and another activation occurs.

I very much doubt that. Your requirements seem quite simple and could be coded as a state machine using millis() for non blocking timing

UKHeliBob:
I very much doubt that. Your requirements seem quite simple and could be coded as a state machine using millis() for non blocking timing

Your confidence in that makes me doubt my understanding of how blocking works. If I need the code to wait for 15 minutes, and during that time I need it to still reset and activate if the relay is tripped during that 15-minute wait, how would I do that without an interrupt?

Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

Not sure, I'll have to check, is that common?

lmfd1550:
If nobody enters the man door, the lights come on but the bay doors don't open and the system resets itself after 15 mins.

Excellent. A new transition for the "waiting for motion" state.

Repeat that process until every state has a transition for every event and your design becomes provably correct. Once you've worked the bugs out of your code your application becomes provably correct. Guaranteed correct. It's a good feeling.

I am new to this, so forgive some of my ignorant questions, but what do you mean by race condition?

Technically, it is now called a "data race". Which is also a good phrase for Google Search.

And what is that you saw that indicates that?

Accessing unprotected shared data. Which is difficult to get correct. And a solid reason by itself to avoid interrupts.

In other words, by including an interrupt handler, you've gone from solving one problem (automating some equipment) to solving two problems. And the second is a much more difficult problem.

lmfd1550:
Not sure, I'll have to check, is that common?

From your description it's dropping 7 volts at whatever current the Arduino is drawing. Others on this forum have reported "thermo shutdown" in such situations.

With 12V powering the Arduino, you'll probably have about 100mA available. You have 8? relays powered from Arduino's VCC (5V) each (guessing) requiring about 80mA. You're going to need a separate supply for those relay boards.

Are they SSR modules? Any more details?

UKHeliBob:
Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

Welp, Starting over again I guess... Are the ezbutton libraries considered interrupts?

dlloyd:
With 12V powering the Arduino, you'll probably have about 100mA available. You have 8? relays powered from Arduino's VCC (5V) each (guessing) requiring about 80mA. You're going to need a separate supply for those relay boards.

Are they SSR modules? Any more details?

My mistake in the diagram, they are actually on VIN and not VCC.
This is the 6x https://www.amazon.com/gp/product/B07HWH1QVJ/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1
This is the 1x (of the set) https://www.amazon.com/gp/product/B086GN7KCX/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

Are the ezbutton libraries considered interrupts?

No. ezButton is a library based on digitalRead() of a pin or multiple pins with a push button switch. The library handles debounce and transitions.

Its probable that switching one of the loads is causing the problem (the relay module isn't wired to enable its opto isolation and there probably isn't any arc suppression).

Does the latch-up problem go away if you disconnect the loads (or relay modules)?

Here's simple heart-beat code for the builtin LED that'll let you know if the code freezes.

Put this in setup:
pinMode(LED_BUILTIN, OUTPUT);and this in the loop:

digitalWrite(LED_BUILTIN, !(millis() & 0x1E0));

dlloyd:
Its probable that switching one of the loads is causing the problem (the relay module isn't wired to enable its opto isolation and there probably isn't any arc suppression).

Does the latch-up problem go away if you disconnect the loads (or relay modules)?

Here's simple heart-beat code for the builtin LED that'll let you know if the code freezes.

Put this in setup:

pinMode(LED_BUILTIN, OUTPUT);

and this in the loop:

digitalWrite(LED_BUILTIN, !(millis() & 0x1E0));

Can you explain what this part means/does?

!(millis() & 0x1E0))

Think of the millis timer as just a 32-bit counter. If looking at bit 10, it will toggle 0→1→0→1 at about 1Hz squarewave. Looking at a small range of lower bits (bits 6-9 = 0x1e0 = 0b111100000), if any bits within this range becomes true, then the LED is on. This blinks the LED at about 2Hz, not squarewave but more like a fast heartbeat.
Could also use:
digitalWrite(LED_BUILTIN, !(millis() & 480));or

digitalWrite(LED_BUILTIN, !(millis() & 0b111100000));

UKHeliBob:
Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

Well, that took a long time to rewrite, but I think it’s done, attached is my .ino if anyone is interested.

Station_Automation_Programming_V2.ino (21.8 KB)

Good news that you got it working

I see multiple variables in your code with numbers in their names. This is very often an indication that the code could be shortened by the use of arrays, but I have not looked in detail