10x Speed Up after receiving an I2C Event()?

First off, I have gleaned and learned so much from this forum over the years by lurking and searching. Thank you all!

I have finally ran into something that I don't understand and can't seem to find the answer too, maybe I am searching for the wrong tags or phrases.

Arduino v1.05 IDE, using LedControl.h & Wire.h

I am sending an I2C event from a master 328p UNO R3. The slave Arduino UNO R3 receives the I2C event fine, calls the receiveEvent(int eventCode); which then..

Calls the alarm(); just fine as well. alarm(); is a simple routine to blink a set of LEDs in a pattern of 6 times, 1 second on, .5 second off.

However the execution speed increases dramatically or at least appears so, for any code being called from within receiveEvent.

delay(1000); = has a speed of approximately 100ms, so I have to increase delay(10000); to equal 1000ms of wall clock time.
delay(500); = has a speed of approximately 50ms. so I have to increase delay(5000); to equal 500ms

If I call alarm(); outside of receiveEvent(int eventCode);, it now becomes delay(10000); is equal to 10000ms of wall clock time.

I've tested putting the alarm(); test code in front of the switch Wire.read()) {, and its still ~10x faster

It's like the receiving of the I2C event, removes all AVR background interrupts or something to that effect. Honestly I've never seen Arduino code run this fast.

Any thoughts, or ideas on what I missed?

void receiveEvent(int eventCode) {
//alarm(); - Yep still fast.
     switch (Wire.read()) {
      case 2:
       ledOFF();
       break;       
      case 3:
       alarm();
       delay(5000);
       break;
      case 9:
        //Message();
        //alarm();
       break;
      case 10:
        //DanceMode();
       break;
      case 11:
        //SystemFailure1();
        //delay(5000);
       break;
      case 12:
        alarm();
        SystemFailure2();
        delay(5000);
       break;
       case 13:
       alarm();
       delay(5000);
//        cantina();
       break;
 

      default: 
       // if nothing else matches, do the default
       // so we are going to do nothing... for that matter not even waste time
       break;
      }
}



void alarm(){
 if (LogicsI2CAdapter==1){
   // bunch of code here for...
 }

 
  if (LogicsI2CAdapter==2){
   if (PSISlide==false){
    lc2.clearDisplay(0);
    lc2.clearDisplay(1);
    for (int cnt=0; cnt<6; cnt++){
//    int pcols=B11000000;
      for (int row=0; row<4; row++)
        lc2.setRow(fpsidev,row,B00110000);

      delay(10000);// when called from I2C event... delays are 10x faster so this gets me about a second
      for (int row=0; row<4; row++)
        lc2.setRow(fpsidev,row,B00000000);
      delay(5000);// when called from I2C event... delays are 10x faster so this gets me about a half second
    }
   }
   if (PSISlide==true){
    lc2.clearDisplay(0);
    lc2.clearDisplay(1);
    for (int cnt=0; cnt<6; cnt++){
        lc2.setRow(fpsidev,0,B01010000);
        lc2.setRow(fpsidev,1,B10100000);
        lc2.setRow(fpsidev,2,B01010000);
        lc2.setRow(fpsidev,3,B10100000);
      delay(10000);// when called from I2C event... delays are 10x faster so this gets me about a second
      for (int row=0; row<4; row++)
        lc2.setRow(fpsidev,row,B00000000);
      delay(5000);// when called from I2C event... delays are 10x faster so this gets me about a  half second
    }
   }
 }

 if (LogicsI2CAdapter==3){
//code here
 }
  
  
}

Logics_Adapter_i2c_TestCase.ino (39.4 KB)

or ideas on what I missed?

The rest of the code.

You can attach it, using the Additional Options link.

My (un)educated guess:

Your receiveEvent is being called from with in an ISR - as such delay() and millis() will NOT work right

Majenko I believe your right, looking at ISR info now...

PaulS, a slightly smaller version is now uploaded & attached to the first post.

So I need to re-enabled the interrupts. Which looks like I can do via sei();

Would it be better to do:

void receiveEvent(int eventCode) {
 int i2cEvent=Wire.read();
     switch (i2cEvent) {
      case 2:
       ledOFF();
       break;
....

or re-enable the interrupt via sei(); like:

void receiveEvent(int eventCode) {
     switch (Wire.read()) {
      case 2:
       sei();
       ledOFF();
       break;
....

or possibly:

void receiveEvent(int eventCode) {
     sei();
     switch (Wire.read()) {
      case 2:
       ledOFF();
       break;
....

It would be better by far to do:

volatile boolean receiveHappened = false;
volatile int i2cEvent = 0;

void receiveEvent(int eventCode) {
  receiveHappened = true;
  receivedValue = Wire.read();
}

void loop()
{
  if (receiveHappened) {
    receiveHappened = false;
    switch (i2cEvent) {
      case 2:
        ledOFF();
        break;
       // ...
    }
  }
}

A rule of thumb you should have as little as possible in your ISR and do all the work in the main loop(). An ISR should not run for longer than the minimum period between subsequent ISRs.

To be even more "safe" you could add the incoming events to a queue (a ring buffer is simple to implement - see the source code to HardwareSerial for examples) so you don't miss an event while you're processing one.