2

I've spent three days on this, and either I'm blind to something, or something just ain't right.

Doorbell. Front door switch and back door switch. Two interrupt service routines, one attached to each pin. Rudimentary debouncing in the ISR seems to work.

But for some reason, both interrupt routines are firing off when either of the switches is pressed. I've tried RISING, FALLING, LOW, CHANGE, and it all works the same. I tried INPUT_PULLUP and INPUT_PULLDOWN and changed the polarity on the switches. No change. I've tried different pins, digital, analog. Nothing seems to keep both ISRs from firing.

Pressing front door button, it should print: front door high

Releasing front door button front door low

Pressing back door button back door high

Releasing back door button back door low

But for some reason, both interrupt service routines are called when pushing either button. Here's what I actually get:

Pressing front door button: back door low front door high

Releasing front door button front door low

Pressing back door button front door WTF? back door high

Releasing back door button front door WTF? back door low

Here's the code. I hope someone can see something I can't:

#include <Arduino.h>

#define FRONT_DOOR_SWITCH A2
#define BACK_DOOR_SWITCH A3

volatile int frontDoorStatus = 0;
volatile int backDoorStatus = 0;

void frontDoorIsr()
{
    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // If interrupts come faster than 200ms, assume it's a bounce and ignore
    if (interrupt_time - last_interrupt_time > 200) {
        if (digitalRead(FRONT_DOOR_SWITCH) == HIGH) {
            frontDoorStatus = 1;
        }
        else if (digitalRead(FRONT_DOOR_SWITCH) == LOW) {
            frontDoorStatus = 2;
        }
        else {
            frontDoorStatus = -1;
        }
    }
    last_interrupt_time = interrupt_time;
}

void backDoorIsr()
{
    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // If interrupts come faster than 200ms, assume it's a bounce and ignore
    if (interrupt_time - last_interrupt_time > 200) {
        if (digitalRead(BACK_DOOR_SWITCH) == HIGH) {
            backDoorStatus = 1;
        }
        else if (digitalRead(BACK_DOOR_SWITCH) == LOW) {
            backDoorStatus = 2;
        }
        else {
            backDoorStatus = -1;
        }
    }
    last_interrupt_time = interrupt_time;
}

void setup() {
    pinMode(FRONT_DOOR_SWITCH, INPUT_PULLDOWN);
    pinMode(BACK_DOOR_SWITCH, INPUT_PULLDOWN);
        
    attachInterrupt(digitalPinToInterrupt(FRONT_DOOR_SWITCH), frontDoorIsr, CHANGE);
    attachInterrupt(digitalPinToInterrupt(BACK_DOOR_SWITCH), backDoorIsr, CHANGE);
}

void loop()
{
    switch (frontDoorStatus) {
        case 0:
            break;
        case -1:
            Serial.println("front door WTF?");
            frontDoorStatus = 0;
            break;
        case 1:
            Serial.println("front door high");
            frontDoorStatus = 0;
            break;
        case 2:
            Serial.println("front door low");
            frontDoorStatus = 0;
            break;
    }

    switch (backDoorStatus) {
        case 0:
            break;
        case -1:
            Serial.println("back door WTF?");
            backDoorStatus = 0;
            break;
        case 1:
            Serial.println("back door high");
            backDoorStatus = 0;
            break;
        case 2:
            Serial.println("back door low");
            backDoorStatus = 0;
            break;
    }
}

Board is Adafruit Feather Express M4 (SAMD51)

6
  • So far the only way I can think to explain the interrupts being called the way you describe depends on there being more code than you're showing. Is this, in fact, the entire sketch being tested?
    – timemage
    Commented Mar 7, 2021 at 17:56
  • Yes, that's what I first thought. I reduced it down to see if something else could have been causing it, but what I posted runs and fires off both ISRs. Commented Mar 8, 2021 at 16:21
  • Unfortunately I have no SAMD51 devices at all. And I'm not sure I want to put my wild guess as an "answer." without any confidence. As a diagnostic test, what I've have you try is using your same code with #define FRONT_DOOR_SWITCH 21 and #define BACK_DOOR_SWITCH 22 and hooking your signals to those pins labeled on the board as SCL and SDA. If this improves things, I'll give you an answer as to why I think it may have.
    – timemage
    Commented Mar 8, 2021 at 16:31
  • Sorry to say, no improvement with SCL and SDA. Both ISRs are still being called. What's your theory? Commented Mar 8, 2021 at 18:45
  • Not sure what I was thinking rises to "theory" level. And what you did sort of disproves it. Pins don't have dedicated interrupts. They share the same interrupt with a handful of other pins. In particular your Arduino pin A3 shares an interrupt with one of the onboard SPI chip lines. SDA and SCL seem to not be sharing with anything else, at least nothing else broken out on the board, so no other pin can be triggering their external interrupt routine. So, like I said, not that.
    – timemage
    Commented Mar 8, 2021 at 19:41

1 Answer 1

1

Turns out that, in the Cortex Mx processors, interrupts don't work the same way as older 8- and 16-bit Arduino-style chips work.

As far as I can tell, there's only one ISR that's connected to the pins. In that routine, you've gotta check the status of the pins you are monitoring and set a global variable accordingly.

I'm not sure this is the only way to do it (seems kludgy), but it does work and it looks like it can scale to any number of pins. I'm using only two.

Here's my demonstration code if anyone is having the same problem. I put the debouncing code in the ISR. I know that's probably not best practice, but it doesn't take a lot of cycles and it's a lot easier than putting it in the loop.

#include <Arduino.h>

#define FRONT_DOOR_SWITCH A2
#define BACK_DOOR_SWITCH A3

volatile bool frontDoor = LOW;
volatile bool backDoor = LOW;

void isr()
{
    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // If interrupts come faster than 200ms, assume it's a bounce and ignore
    if (interrupt_time - last_interrupt_time > 50)
    {
        int fds = digitalRead(FRONT_DOOR_SWITCH);
        int bds = digitalRead(BACK_DOOR_SWITCH);
        if (fds == HIGH) frontDoor = HIGH;
        if (fds == LOW) frontDoor = LOW;
        if (bds == HIGH) backDoor = HIGH;
        if (bds == LOW) backDoor = LOW;
        last_interrupt_time = interrupt_time;
    }
}

void setup() 
{
    Serial.begin(115200);
    while (!Serial);
    Serial.println("in setup...");
    pinMode(FRONT_DOOR_SWITCH, INPUT_PULLDOWN);
    pinMode(BACK_DOOR_SWITCH, INPUT_PULLDOWN);
        
    attachInterrupt(digitalPinToInterrupt(FRONT_DOOR_SWITCH), isr, CHANGE);
    attachInterrupt(digitalPinToInterrupt(BACK_DOOR_SWITCH), isr, CHANGE);
}

bool lastFrontDoor = LOW;
bool lastBackDoor = LOW;

void loop()
{
    if ((frontDoor == HIGH) && (lastFrontDoor == LOW))
    {
        Serial.print("fd+");
        lastFrontDoor = HIGH;
    }
    if ((frontDoor == LOW) && (lastFrontDoor == HIGH))
    {
        Serial.println("fd-");
        lastFrontDoor = LOW;
    }
    if ((backDoor == HIGH) && (lastBackDoor == LOW))
    {
        Serial.print("bd+");
        lastBackDoor = HIGH;
    }
    if ((backDoor == LOW) && (lastBackDoor == HIGH))
    {
        Serial.println("bd-");
        lastBackDoor = LOW;
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.