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)
1 Answer 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;
}
}
#define FRONT_DOOR_SWITCH 21
and#define BACK_DOOR_SWITCH 22
and hooking your signals to those pins labeled on the board asSCL
andSDA
. If this improves things, I'll give you an answer as to why I think it may have.