Multiple MCP23017 interrupt coding

Hi everyone, my first post here. I’m midway through a chess board project which uses four MCP23017s to detect piece movement. I can get interrupt reporting to work one at a time on the four MCP23017s over an I2C daisychain, so the wiring is confirmed good.

I just can’t find the code to determine which of the four MCP23017s triggered the interrupt. I’ve seen some examples online referring to a Centipede shield (stack of four such devices), but that’s not what I’m using. You’ll see I am using the Adafruit_MCP23017 library because I’m a noob. Relevant code extract below - any help appreciated. Thanks in advance.

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp1;
Adafruit_MCP23017 mcp2;
Adafruit_MCP23017 mcp3;
Adafruit_MCP23017 mcp4;
#define addr1 0 //actually 0x20
#define addr2 1 //actually 0x21
#define addr3 2 //actually 0x22
#define addr4 3 //actually 0x23

// Interrupt code follows
byte ledPin=13;  //assign the internal LED to test DELETE LATER

// Interrupts from the MCP will be handled by this PIN
byte arduinoIntPin=3;

// ... and this interrupt vector
byte arduinoInterrupt=1;

volatile boolean awakenByInterrupt = false;

//Interrupt code ends

void setup(){
  // designate MCP23017 addresses
  mcp1.begin(addr1);
  mcp2.begin(addr2);
  mcp3.begin(addr3);
  mcp4.begin(addr4);

  //Interrupt setup
  Serial.begin(9600);
  pinMode(arduinoIntPin,INPUT_PULLUP);

  mcp1.setupInterrupts(true,false,LOW);
  mcp2.setupInterrupts(true,false,LOW);
  mcp3.setupInterrupts(true,false,LOW);
  mcp4.setupInterrupts(true,false,LOW);
  
  //initiate the MCP23017 reed sensors
  for (byte reed=0; reed<16; reed++){
    mcp1.pinMode(reed, INPUT);
    mcp1.pullUp(reed, HIGH);  // turn on a 100K pullup internally
    mcp1.setupInterruptPin(reed,FALLING);  //enable interrupt reporting
    mcp2.pinMode(reed, INPUT);
    mcp2.pullUp(reed, HIGH);  // turn on a 100K pullup internally
    mcp2.setupInterruptPin(reed,FALLING);  //enable interrupt reporting
    mcp3.pinMode(reed, INPUT);
    mcp3.pullUp(reed, HIGH);  // turn on a 100K pullup internally
    mcp3.setupInterruptPin(reed,FALLING);  //enable interrupt reporting
    mcp4.pinMode(reed, INPUT);
    mcp4.pullUp(reed, HIGH);  // turn on a 100K pullup internally
    mcp4.setupInterruptPin(reed,FALLING);  //enable interrupt reporting
    }
  // We will setup a pin for flashing from the int routine
  pinMode(ledPin, OUTPUT);  // use the p13 LED as debugging
}

void intCallBack(){
  awakenByInterrupt=true;
}

void handleInterrupt(){
  // Get more information from the MCP from the INT
  uint8_t flashes=0;
  uint8_t pin=mcp1.getLastInterruptPin();
  uint8_t val=mcp1.getLastInterruptPinValue();
  flashes=pin+1;
  if(val!=LOW) flashes=3;
    // simulate some output associated to this
  for(int i=0;i<flashes;i++){  
    delay(100);
    digitalWrite(ledPin,HIGH);
    delay(100);
    digitalWrite(ledPin,LOW);
  }
  
  while( ! (mcp1.digitalRead(0) && mcp1.digitalRead(1) && mcp1.digitalRead(2) && mcp1.digitalRead(3)
        && mcp1.digitalRead(4) && mcp1.digitalRead(5) && mcp1.digitalRead(6) && mcp1.digitalRead(7)
        && mcp1.digitalRead(8) && mcp1.digitalRead(9) && mcp1.digitalRead(10) && mcp1.digitalRead(11)
        && mcp1.digitalRead(12) && mcp1.digitalRead(13) && mcp1.digitalRead(14) && mcp1.digitalRead(15)
       
        && mcp2.digitalRead(0) && mcp2.digitalRead(1) && mcp2.digitalRead(2) && mcp2.digitalRead(3)
        && mcp2.digitalRead(4) && mcp2.digitalRead(5) && mcp2.digitalRead(6) && mcp2.digitalRead(7)
        && mcp2.digitalRead(8) && mcp2.digitalRead(9) && mcp2.digitalRead(10) && mcp2.digitalRead(11)
        && mcp2.digitalRead(12) && mcp2.digitalRead(13) && mcp2.digitalRead(14) && mcp2.digitalRead(15)
          
        && mcp3.digitalRead(0) && mcp3.digitalRead(1) && mcp3.digitalRead(2) && mcp3.digitalRead(3)
        && mcp3.digitalRead(4) && mcp3.digitalRead(5) && mcp3.digitalRead(6) && mcp3.digitalRead(7)
        && mcp3.digitalRead(8) && mcp3.digitalRead(9) && mcp3.digitalRead(10) && mcp3.digitalRead(11)
        && mcp3.digitalRead(12) && mcp3.digitalRead(13) && mcp3.digitalRead(14) && mcp3.digitalRead(15)

        && mcp4.digitalRead(0) && mcp4.digitalRead(1) && mcp4.digitalRead(2) && mcp4.digitalRead(3)
        && mcp4.digitalRead(4) && mcp4.digitalRead(5) && mcp4.digitalRead(6) && mcp4.digitalRead(7)
        && mcp4.digitalRead(8) && mcp4.digitalRead(9) && mcp4.digitalRead(10) && mcp4.digitalRead(11)
        && mcp4.digitalRead(12) && mcp4.digitalRead(13) && mcp4.digitalRead(14) && mcp4.digitalRead(15)
        ));
  
  // and clean queued INT signal
  cleanInterrupts();
}

// handy for interrupts triggered by buttons
// normally signal a few due to bouncing issues
void cleanInterrupts(){
  EIFR=0x01;
  awakenByInterrupt=false;
}

void loop(){
  // enable interrupts before going to sleep/wait
  // And we setup a callback for the arduino INT handler.
  attachInterrupt(arduinoInterrupt,intCallBack,FALLING);
  // Simulate a deep sleep
  while(!awakenByInterrupt);
  // Or sleep the arduino, this lib is great, if you have it.
  //LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
  
  // disable interrupts while handling them.
  detachInterrupt(arduinoInterrupt);
  
  if(awakenByInterrupt) handleInterrupt();
}

Store the values of all pins in the Arduino and when an interrupt occurs, compare to current value to the saved. You can even call getLastInterruptPin() on every chip to get an additional hint where to look for the change.

Hi Pylon, thanks for that. Am I right in thinking getLastInterruptPin() on a chip that did NOT interrupt will return 255?

Edit: pretty stuck here. Am I trying to do the impossible? I would like to know which pin on which of four MCP has fired an interrupt on any state change, falling or rising. I'm sure Pylon's answer would work, but is that the most elegant way?

Cheers guys, and thanks again Pylon for taking the time to reply.

Hi Pylon, thanks for that. Am I right in thinking getLastInterruptPin() on a chip that did NOT interrupt will return 255?

No, I would expect it to return the last pin itself generated an interrupt for. So you have to store the values of all chips each time you get an interrupt. Or use a separate pin for each interrupt signal from the pins.