Problem with Master-Slave, I2C and MCP23017

Hello,
I am having an issue with 2 arduinos communicating together via I2C and using the MCP23017 on the slave one.

Setup:
MASTER: MEGA 2560
SLAVE: Arduino UNO

So first off, it kinda works except for 1 thing.
The whole point of this is that the slave will be controlling 24 relays. Hence why I will be using a MCP23017.

  1. I am able to control the slave with I2C, using the wire.etc I am able to light LEDs and action some relays, using the normal digitalwrite command.

  2. for testing purposes, I only connected a LED to the MCP23017. If I put my mcp code in the void loop, no issue (see code below) and I get a flashing LED. But as soon as I add the mcp code within the wire.read event, it just hangs.

So my MCP seems to work but the problem happens when I send an action from my master and that action requires to use my MCP code.

Sorry if it doesnt make sense but im trying to make sense of it at same time and I am still a noob in the arduino world. I am learning alot but there still some issues I cant find the answer.

Here is my code, hopefully its just a coding error or maybe I cannot do it that way.

This code is sent when I press a button and turn on a LED while button is pushed down on the master side circuit.
MASTER send action to SLAVE

Wire.beginTransmission(11);
Wire.write('1');
Wire.endTransmission();

Like I said, the code in the loop works if I enable it. I get a flashing led. The problem is in my receiveEvent function. The relay turns on and thats it.. nothing else happens
When I press the button from Master, the master LED stays on and the relay on slave side turn on but pressing button again doesnt do anything and LED and Relays are staying on. My guess is it stops right at the mcp1 code. Yes MCP is connected on SDA and SCL as well as the master. (is that the problem?)

*** If I remove the mcp code from the receiveEvent, it works as expected but my goal here is using the MCP to be able to use 24 pins with a UNO
SLAVE CODE

//SLAVE1
#include <Wire.h>
#include <Adafruit_MCP23017.h>
const int relayPin1 = 8;    
const int relayPin2 = 9; 

#define relayPin3 2
#define ledPin3 0
Adafruit_MCP23017 mcp1; // chip 1

#define addr1 0 // 0 = A2 low, A1 low, A0 low

void setup() {
  // put your setup code here, to run once:
  Wire.begin(11);
  Wire.onReceive(receiveEvent);
  mcp1.begin(addr1);
  mcp1.pinMode(relayPin3, OUTPUT);
  mcp1.pinMode(ledPin3, OUTPUT);
  pinMode(relayPin1,OUTPUT);
  pinMode(relayPin2,OUTPUT);
  digitalWrite(relayPin1, HIGH);
  digitalWrite(relayPin2, HIGH);
  mcp1.digitalWrite(relayPin3, HIGH);
  mcp1.digitalWrite(ledPin3, LOW);
}

void loop() {
      //mcp1.digitalWrite(ledPin3, HIGH);
      //delay(500);
      //mcp1.digitalWrite(ledPin3, LOW);
      //delay(500);
}

void receiveEvent(int howMany)
{
  while(Wire.available())
  {
    char c = Wire.read();
    if(c == '1'){
      digitalWrite(relayPin1, LOW);
      mcp1.digitalWrite(ledPin3, HIGH);
      delay(500);
      digitalWrite(relayPin1, HIGH);
      mcp1.digitalWrite(ledPin3, LOW);
      delay(1);
      }
    } 
}

My relays needs to be on MCP (if possible). Only using LED for testing right now. There wont be any regular digitalwrite
I appreciate all the help I can get
Thanks

What mechanism does the Adafruit_MCP23017 library use to communicate between the board and the MCP23017? If that mechanism uses interrupts, you will NOT be able to use that mechanism from inside an interrupt handler, because interrupts are disabled when the ISR is running.

And, yes, receiveEvent() IS an ISR.

If you posted a link to the library, and to the mysterious device it is communicating with, perhaps we could offer more help.

Here is the library

And this is the MCP23017, which is an I2C 16 port expander

If it is in fact an interrupt problem, is there a way around this to be able to use it?

is there a way around this to be able to use it?

Yes. The ISR (receiveEvent()) should set a global variable with the value that it gets.

Then, loop(), instead of doing nothing, does something (the stuff that you can't do in the ISR).

Ok so update. It now works. Being a noob I didnt know the ISR term (lol)
So did some research on running interrupt within an ISR and found a solution.

the following code works so far, by enabling and disabling interrupts within my "receiveEvent" function.

I also learned that you cannot use "delay" in interrupts so I had to use "delayMicroseconds".

Asking me questions made me find the solution! Thanks guys :slight_smile:

void receiveEvent(int howMany)
{
  while(Wire.available())
  {
    char c = Wire.read();
    if(c == '1'){
      static boolean IsRunning = false;
      if(IsRunning) return; //we have been interrupted while already processing an interrupt
      IsRunning = true;
      interrupts(); //re-enable interrupts so that interrupt-based functions can be used inside this function

      mcp1.digitalWrite(relayPin3, LOW);
      delayMicroseconds(1000);
      mcp1.digitalWrite(relayPin3, HIGH);
      delayMicroseconds(1);

      noInterrupts(); //turn off interrupts
      IsRunning = false;
      return;
      }
    } 
}

That is the WRONG approach.

The receiveEvent() method is called when the master has something for the slave to do. The slave can do that task RIGHT THIS VERY NANOSECOND, as you have butchered the code to do, or it can do it real soon.

Setting a flag in the ISR that loop() will discover is set very, very soon after it is set, and doing the work then is almost certainly going to be seen enough.

Then there is no need to worry about being interrupted while you are already interrupted.

This discussion describes how to work around the MCP interrupt issue.

thanks I will look at the post.
Yes it works now, I never said it was the best way. Following your comment I may not use it however nobody suggested anything yet so its the only option I have now.

But I will look at the other post.