An Interrupt that is triggered by Another Interrupt

I am writing code for Arduino with the use of two passive infrared motion sensors to detect if someone has entered a room or if someone has left the room. One sensor will be on the inner portion of the door and another sensor will be outside of the door.

I want to create an interrupt mechanism such that if the sensor on the outside is triggered first and the sensor on the inside is triggered next, a message is printed on the serial monitor that someone has entered the room and vice versa.

I am trying to implement the first case (someone entering the room) by having an interrupt inside of another interrupt. But it doesn't work. Because if the sensor inside of the room is triggered first and the sensor outside of the room is triggered next, the program is still printed on the serial monitor that someone has entered the room. But I don't want this to be the case. I only want it to print out that some has entered the room if the first sensor triggers first and then the second sensor triggers.

The code that I write is provided below. Does anyone know how I can resolve this issue?

const byte INSIDE = 2; 
const byte OUTSIDE =3;
volatile byte state = LOW;

byte x;
byte y;

void InsideTrig() {
  attachInterrupt(digitalPinToInterrupt(OUTSIDE), OutsideTrig, HIGH);
} 

void OutsideTrig() {
  state= !state;
}

void setup() {
  digitalWrite(INSIDE, HIGH);
  digitalWrite(OUTSIDE, HIGH); 
  Serial.begin(9600);
}

void loop() {
  attachInterrupt(digitalPinToInterrupt(INSIDE), InsideTrig, HIGH);
  if (state == 1) {
    Serial.println("Someone has entered the room.");
  }
  delay(1000);
}

Is there any particular reason you chose to use interrupts, rather than polling?
Wake-from-sleep?

you need to know that the outside sensor has been triggered before noting that the inside has been triggered
simplest to use a state machine alongs the lines of

// test digital interrupts
// press switch 1 to move to sw1State (1)
// then press switch 2 to move to sw2State (2)

#define sw1Pin    2
#define sw2Pin    3

enum {none, sw1Pressed, sw2Pressed} state;

void setup() {                
    Serial.begin(115200); 
    Serial.println("DMU shield button test\n");
    attachInterrupt(digitalPinToInterrupt(sw1Pin), sw1Trig, HIGH);
    attachInterrupt(digitalPinToInterrupt(sw2Pin), sw2Trig, HIGH);
}

void sw1Trig() {
  Serial.println("Sw1 pressed ");
  state=sw1Pressed;
}

void sw2Trig() {
  Serial.println("Sw2 pressed ");
  if(state==sw1Pressed)state=sw2Pressed;
}

void loop() 
{
  delay(1000);
  Serial.println(state);
}

switch 1 has to be pressed before switch 2 otherwise switch 2 presses are ignored

NEVER print from within an interrupt, as suggested in reply #2. It will cause your program to hang.

Instead, set a global flag, declared volatile, and exit. Print in loop().

Printing depends on interrupts, which are turned off during interrupt routine execution.

AWOL:
Is there any particular reason you chose to use interrupts, rather than polling?
Wake-from-sleep?

Hey Awol, thanks for the reply. I am new to Arduino so I don't know what polling is.

My main problem with this project is that I need a way to know the sequence of when the sensors are triggered. If the inside sensor is triggered before the outside sensor, I want to print out that a person has left the room. If the outside sensor is triggered before the inside sensor, I want to print out that a person has entered the room.

That is why I was thinking of using an interrupt routine that would wait for one of the sensors to be triggered and then wait for another sensor to be triggered.

If this could be done without interrupts, I am open to any suggestions.

Thanks again for the feedback.

jremington:
NEVER print from within an interrupt, as suggested in reply #2. It will cause your program to hang.

Instead, set a global flag, declared volatile, and exit. Print in loop().

Printing depends on interrupts, which are turned off during interrupt routine execution.

Thank you for the suggestion jremington! I will definitely take this into account.

horace:
you need to know that the outside sensor has been triggered before noting that the inside has been triggered
simplest to use a state machine alongs the lines of

// test digital interrupts

// press switch 1 to move to sw1State (1)
// then press switch 2 to move to sw2State (2)

#define sw1Pin    2
#define sw2Pin    3

enum {none, sw1Pressed, sw2Pressed} state;

void setup() {               
    Serial.begin(115200);
    Serial.println("DMU shield button test\n");
    attachInterrupt(digitalPinToInterrupt(sw1Pin), sw1Trig, HIGH);
    attachInterrupt(digitalPinToInterrupt(sw2Pin), sw2Trig, HIGH);
}

void sw1Trig() {
  Serial.println("Sw1 pressed ");
  state=sw1Pressed;
}

void sw2Trig() {
  Serial.println("Sw2 pressed ");
  if(state==sw1Pressed)state=sw2Pressed;
}

void loop()
{
  delay(1000);
  Serial.println(state);
}



switch 1 has to be pressed before switch 2 otherwise switch 2 presses are ignored

Hey Horace,

Thank you so much for the feedback. It works well. However, I also want to determine when a person has entered the room, that is the outside sensor is triggered before the inside sensor, I want to print , "A person has entered the room" in the serial monitor.

I modified your code to suit my application and was able to find a way to both print out when a person entered the room and when they left the room. However, I had to declared an extra volatile variable that I called "x". Moreover, due to the sensitivity of the outside sensor, I had to put a delay of 30 seconds within the code once it initially went high, to send it back down to low.

I have provide my updated code below. Is there a more efficient way to improve the code so that it detects both instances of a person entering and leaving a room. I should mention that I want it continuous check if someone has entered the room and if someone has left the room. In other words, I just don't want it to detect this once, but to always be able to detect it.

// test digital interrupts
// If inside PIR sensor is trigger move to insideTriggered (1)
// then if outside PIR sensor is triggered move to then move to outsideTriggered (2)

#define INSIDE    2
#define OUTSIDE   3

volatile byte x= LOW;

enum {none, insideTriggered, outsideTriggered} state;

void setup() {                
    Serial.begin(9600); 
    Serial.println("DMU shield button test\n");
    attachInterrupt(digitalPinToInterrupt(INSIDE), insideTrig, HIGH);
    attachInterrupt(digitalPinToInterrupt(OUTSIDE), outsideTrig, HIGH);
}

void insideTrig() {
  Serial.println("Inside triggered ");
  if (state == outsideTriggered){ x=HIGH; }
  state=insideTriggered;
}

void outsideTrig() {
  Serial.println("Outside triggered ");
  if(state==insideTriggered)state=outsideTriggered;
}

void loop() 
{
  delay(1000);
  Serial.println(state);

  if (state == outsideTriggered){ 

    Serial.println("Someone has left the room");
  }

  if (x == HIGH){

    Serial.println("Someone has entered the room.");

    delay(30000);

    x=LOW;
  }
} [\code]

you probably require a timeout when an event is triggered, e.g. the inside event is triggered and the person turns around and does not go out - after a time you need to clear the event
I failed to removed the prints in the interrupt service routines after debugging the code - clearly the code in interrupt service routines should be as short as possible so other interrupts are not held up

probably need more states, e.g.

#define sw1Pin    2
#define sw2Pin    3

volatile enum {none, sw1Pressed, sw2Pressed, inside, outside} state;

void setup() {                
    Serial.begin(115200); 
    Serial.println("DMU shield button test\n");
    attachInterrupt(digitalPinToInterrupt(sw1Pin), sw1Trig, RISING);
    attachInterrupt(digitalPinToInterrupt(sw2Pin), sw2Trig, RISING);
}

void sw1Trig() {
  //Serial.println("Sw1 pressed ");
  if(state==sw2Pressed)state=outside;
  else   state=sw1Pressed;
}

void sw2Trig() {
  //Serial.println("Sw2 pressed ");
  if(state==sw1Pressed)state=inside;
  else state = sw2Pressed;
}

void loop() 
{
  static int oldstate=0;
  if (state != oldstate)
  {
    oldstate=state;
    if(state == inside)Serial.println("inside");
    if(state == outside)Serial.println("outside");
  }
}

Pat1287:
My main problem with this project is that I need a way to know the sequence of when the sensors are triggered. If the inside sensor is triggered before the outside sensor,

I think at this stage the real problem is that you have not given sufficient thought to how the system should work. When you know that, writing the code will relatively easy.

For example it is easy to record the value of millis() to mark the time when each sensor is triggered. Subtracting one time from the other will tell you which one happened first.

But you also need to think about how to distinguish between different pairs of readings. Suppose person A goes through the door and triggers both sensors. And immediately after that person B goes half-way through the door but then turns back (to speak to person A) so that person B only triggers one sensor. In a short space of time there are 3 sensor values. How do you ensure that the first two readings are used rather than the middle reading and the last reading? This is not a coding problem. It is a system design problem.

...R

Thanks for the feedback Robin2.

Yes, my design does not take into account a lot of intricacies, such as the one you mentioned, but also whether two people enter the room at virtually the same time. But it is not meant to be that complex. Horace mentioned a the timeout mechanism, but I am relatively new to Arduino so I don't know how timeout works.

As for your idea of time stamping when the sensors are triggered, I thought of that, but my research indicated that I needed to download the DateTime library. I tried downloading but was having trouble getting to work for some reason. I presume the millis() function does not require me to download any library?

horace:
you probably require a timeout when an event is triggered, e.g. the inside event is triggered and the person turns around and does not go out - after a time you need to clear the event
I failed to removed the prints in the interrupt service routines after debugging the code - clearly the code in interrupt service routines should be as short as possible so other interrupts are not held up

probably need more states, e.g.

#define sw1Pin    2

#define sw2Pin    3

volatile enum {none, sw1Pressed, sw2Pressed, inside, outside} state;

void setup() {               
    Serial.begin(115200);
    Serial.println("DMU shield button test\n");
    attachInterrupt(digitalPinToInterrupt(sw1Pin), sw1Trig, RISING);
    attachInterrupt(digitalPinToInterrupt(sw2Pin), sw2Trig, RISING);
}

void sw1Trig() {
  //Serial.println("Sw1 pressed ");
  if(state==sw2Pressed)state=outside;
  else  state=sw1Pressed;
}

void sw2Trig() {
  //Serial.println("Sw2 pressed ");
  if(state==sw1Pressed)state=inside;
  else state = sw2Pressed;
}

void loop()
{
  static int oldstate=0;
  if (state != oldstate)
  {
    oldstate=state;
    if(state == inside)Serial.println("inside");
    if(state == outside)Serial.println("outside");
  }
}

Thank for the feedback again Horace. Any idea on how I can implement the timeout. Robin2 mentioned to use the millis() function for Arduino. I was also thinking of using the DateTime() function before but it required me to download a library, which I was having trouble with. I will try to use the millis() function since it seems it does not require me to use a library and see how it goes.

Thanks again!

for an example of millis() have a look at

Pat1287:
As for your idea of time stamping when the sensors are triggered, I thought of that, but my research indicated that I needed to download the DateTime library.

Of course not. millis() and micros() are standard Arduino functions.

Don't make a solution more complex than it has to be.

How long is the interval between triggering the two sensors when a person walks through the door? I strongly suspect there is no need to use interrupts - just polling the sensors will probably be sufficiently accurate.

...R

You do not need the date/time library at this point. Stick to millis(), which is very easy to use.

Make sure that all variables associated with millis() are declared unsigned long.

To implement a timeout is very simple. For example, suppose you want to wait for pin 3 to go high, but for no more than two seconds.

Here is a siily example, untested:

   unsigned long start=millis();  //note start time
   while (digitalRead(3) == 0) {
   if (millis() - start > 2000UL) break; // exit if timeout
   Serial.println("waiting for button press");
   }
   if (digitalRead(3) == 0) Serial.println("timeout");  //expect 1 if no timeout

Robin2:
Of course not. millis() and micros() are standard Arduino functions.

Don't make a solution more complex than it has to be.

How long is the interval between triggering the two sensors when a person walks through the door? I strongly suspect there is no need to use interrupts - just polling the sensors will probably be sufficiently accurate.

...R

The interval should be less than 5 seconds. I don't know what polling is, so I have do more research on that.

The code posted in reply#13 polls digital pin 3.

polling is where you are in a loop checking for changes of state, e.g. switch 1 pressed

void loop() 
{
  if(!digitalRead(sw1Pin))
   {
    if(state==sw2Pressed) 
        { 
        state=outside;
        Serial.println("outside");
        }
    else   
        {
         state=sw1Pressed;
         Serial.println("Sw1 pressed "); 
         }
    while(!digitalRead(sw1Pin));   // wait for switch release
   }
....   // process switch 2 etc

the switch interrupts were edge triggered - in the case of polling you may need to wait for switch release (otherwise it will print "Sw1 pressed " every loop() while it is pressed)
the problem with polling is that you can miss events if you don't check often enough

Pat1287:
The interval should be less than 5 seconds. I don't know what polling is, so I have do more research on that.

Polling is just using digitalRead() everytime loop() repeats - which should be hundreds or thousands of times per second. that would perfectly adequate to detect things that happen every few seconds.

...R

Robin2:
Polling is just using digitalRead() everytime loop() repeats - which should be hundreds or thousands of times per second. that would perfectly adequate to detect things that happen every few seconds.

...R

My concern with that method is whether I could accurately detect which sensor goes off first. But as you mentioned I could use the millis() function to timestamp the events' occurances.

horace:
for an example of millis() have a look at
Using millis() for timing | Multi-tasking the Arduino - Part 1 | Adafruit Learning System

Hey Horace, thanks again for the resource!