Search code examples
arduinointerrupti2c

How to fix 'I2C communication between 2 microcontrollers intterupted by an intterupt'


I'm making a dimmer with my Arduino Nano, This get's values between 0-128 from an ESP8266 controller via I2C. This works fine on the nano until the interrupt (zero cross detection) interrupts this.

I've tried dimming with a potentiometer and this worked perfectly, I've tried doing this without the intterupt (in serial monitor), this worked correctly. I've also tried replacing interrupts and noInterrupts by sei() and cli() with no results. I do sometimes get it working for a moment, but then it looks like this.

63
64
65
66
67
-1
69
-1
72
-1
74

Afterwards it stops working.

Below is my code for the ESP8266 device (This is temporary test code). This only sends Value's to the nano.

#include <Wire.h>

void setup() {
  Wire.begin();
}

void loop() {
  for (int dimValue = 0; dimValue <= 128; dimValue++)
  {
    delay(50);
    Wire.beginTransmission(8);
    Wire.write(dimValue);
    Wire.endTransmission();
  }
}

Below The code for the Nano responsible for dimming and receiving commands via I2C.

#include <Wire.h>

int AC_LOAD = 8;    // Output to Opto Triac pin
int dimming = 128;  // Dimming level (0-128)  0 = ON, 128 = OFF
int zeroCross = 3; // zerocross pin

void setup()
{
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
  attachInterrupt(digitalPinToInterrupt(3), zero_crosss_int, RISING);
  Serial.begin(115200);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
}

void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
{
  int dimtime = (75 * dimming);  // For 60Hz =>65
  delayMicroseconds(dimtime);    // Wait till firing the TRIAC
  digitalWrite(AC_LOAD, HIGH);   // Fire the TRIAC
  delayMicroseconds(10);         // triac On propogation delay
  // (for 60Hz use 8.33) Some Triacs need a longer period
  digitalWrite(AC_LOAD, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
}

void loop()
{
}

void receiveEvent() {
  noInterrupts();
  int x = Wire.read();
  dimming = x;
  Serial.println(x);
  interrupts();
}

The results I should get from the nano should look like this

63
64
65
66
67
68
69
70
71
72
73
74

Solution

  • The problem was that the interrupt handler was busy for too long wich interrupted the I2C receive handling wich overflowed it. Now it's fully working by putting the code from the interrupt in the loop().

    #include <Wire.h>
    
    int AC_LOAD = 9;    // Output to Opto Triac pin
    int dimming = 128;  // Dimming level (0-128)  0 = ON, 128 = OFF
    int zeroCross = 3; // zerocross pin
    boolean triacFire = false;
    
    void setup()
    {
      pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
      attachInterrupt(digitalPinToInterrupt(zeroCross), zero_crosss_int, RISING);
      Serial.begin(115200);
      Wire.begin(8);                // join i2c bus with address #8
      Wire.onReceive(receiveEvent); // register event
    }
    
    void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
    {
      triacFire = true;
    }
    
    void loop()
    {
      if (triacFire == true)
      {
        int dimtime = (75 * dimming);  // For 60Hz =>65
        delayMicroseconds(dimtime);    // Wait till firing the TRIAC
        digitalWrite(AC_LOAD, HIGH);   // Fire the TRIAC
        delayMicroseconds(10);         // triac On propogation delay
        // (for 60Hz use 8.33) Some Triacs need a longer period
        digitalWrite(AC_LOAD, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
        triacFire = false;
      }
    }
    
    void receiveEvent() {
      noInterrupts();
      byte x = Wire.read();    // receive byte as an byte
      dimming = x;
      Serial.println(x);
      interrupts();
    }
    

    Thank you, everyone who helped me with this problem!