I2C Loosing Data - 2 Arduino Mega's

Hello guys,

i am trying to establish a I2C connection between 2 Arduino Megas.

I created a code for the master that sends an array of 15 bytes.

The slave should receive it and print it out on the serial.

Master:

#include <Wire.h>

int ang = 1;
int speed_vl = 2;
int speed_vr = 3;
int speed_hl = 4;
int speed_hr = 5;
int current_vl = 6;
int current_vr = 7;
int current_hl = 8;
int current_hr = 9;
int emp_1 = 8;
int emp_2 = 7;
int emp_3 = 6;
int emp_4 = 5;
int emp_5 = 4;
int emp_6 = 3;

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

//int x = 3094;
//int x1 = (x >> 8) & 0xFF;
//int x2 = x & 0xFF;
//int y = 10;
//int z = 20;
//byte msg[] = {x1, x2, y, z};

byte msg_send[] = {ang, speed_vl, speed_vr, speed_hl, speed_hr, current_vl, current_vr, current_hl, current_hr, emp_1, emp_2, emp_3, emp_4, emp_5, emp_6};

void loop() {
  Wire.beginTransmission(8);
  Wire.write(msg_send,15);
  Wire.endTransmission();
  delay(500);
}

Slave:

#include <Wire.h>

int i = 0;
int x[] = {};

void setup() {
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);
}

void loop() {
  delay(100);
}

void receiveEvent(int howMany) {
  while (1 <= Wire.available()) {
    int c = Wire.read();
    Serial.print(c);
    x[i]=c;
    i++;
  }
  Serial.println();
}

This is working fine, but it oft loses data. "123456789876543" is the right message.

Serial Log:

123456789876543
123456789876543
12
12123000000000000
123456789876543
123456789876543
123456789876543
123456789876543
12
12123000000000000
123456789876543
123456789876543

Serial Log w/o delay() on both Master/Slave:

123456789123000000000000
123456789123000000000000
123456789123000000000000
123456789123000000000000
123456789123000000000000
123456789123000000000000
123456789123000000000000
123456789123000000000000
123456789123000000000000

Why does it loses data when running with both delay()’s? And why does it only print it right until the 9th Byte and fills it with 0’s when commenting out both delay()’s?
I want them to communicate as fast as possible without losing data. What is the best way for doing it?

Thank you in advance.

Greets.

EDIT: Connections: SCL to SCL, SDA to SDA, 5V to 5V, GND to GND, and one arduino connected to the PC with a usb cable. No external Pull Ups.

Please, observe the modifications that I have made in your Slave Codes for correct presentation of the received data. Any questions will be welcomed! My setup is: MEGA + UNO. (It should work between MEGA + MEGA.)

screenI2C.png

Slave Codes

#include <Wire.h>

//int i = 0;
byte x[15];//int x[] = {}; //receiving byte sized data; then why int?

void setup() {
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);
}

void loop()
{
  //  delay(100); //let the loop run as fast as possible
}

void receiveEvent(int howMany)
{
  for (int i = 0; i < howMany; i++)  //howMany is always equal to the number of bytes received
    //while (1 <= Wire.available())
  {
    byte c = Wire.read();//int c = Wire.read(); I2C is a byte oriented bus
    x[i] = c;
    Serial.print(c);
    //x[i] = c;
    //i++;
  }
  Serial.println();
}

screenI2C.png

void receiveEvent(int howMany)
{
  for (int i = 0; i < howMany; i++)  //howMany is always equal to the number of bytes received
    //while (1 <= Wire.available())
  {
    byte c = Wire.read();//int c = Wire.read(); I2C is a byte oriented bus
    x[i] = c;
    Serial.print(c);
    //x[i] = c;
    //i++;
  }
  Serial.println();
}

receiveEvent() is called in interrupt context. Never call any method of the Serial object inside interrupt context. In interrupt context interrupts are disabled and the Serial object depends on interrupts to do it's work.

Read the bytes into an array and set a flag that you then check in the loop. Print out the values there. And don't forget to declare "volatile" all variables used in interrupt context and non-interrupt context!

pylon:

void receiveEvent(int howMany)

{
  for (int i = 0; i < howMany; i++)  //howMany is always equal to the number of bytes received
    //while (1 <= Wire.available())
  {
    byte c = Wire.read();//int c = Wire.read(); I2C is a byte oriented bus
    x[i] = c;
    Serial.print(c);
    //x[i] = c;
    //i++;
  }
  Serial.println();
}




receiveEvent() is called in interrupt context. Never call any method of the Serial object inside interrupt context. In interrupt context interrupts are disabled and the Serial object depends on interrupts to do it's work.

Serial.println(c); has been working alright in the Slave Codes of the OP under void receiveEvent() routine which you have termed as 'interrupt context'. How to explain this event when, according to your opinion, 'In interrupt context interrupts are disabled [...]'. So the question comes -- is void receiveEvent() an interrupt context or an event handler?

My 2 cents

https://forum.arduino.cc/index.php?topic=568622.0

Update: looking at what @GolamMostafa did, and that does seem to fix it... cool.

This is interesting to explore the reasons for not working of the void recieveEevnt() routine when there are good number (3+) of Serial.print() methods are executed.

ron_sutherland:
I2C Slave and Serial - Networking, Protocols, and Devices - Arduino Forum

Serial.println(c); has been working alright in the Slave Codes of the OP under void receiveEvent() routine which you have termed as 'interrupt context'. How to explain this event when, according to your opinion, 'In interrupt context interrupts are disabled [...]'. So the question comes -- is void receiveEvent() an interrupt context or an event handler?

Fortunately for you the Serial object writes characters to a buffer. As long as you can be sure that this buffer doesn't fill up in your receiveEvent() routine you're fine. If it fills up you're dead or at least your Arduino is dead, it will wait forever for the serial interrupt to empty at least a few bytes in the buffer. As that interrupt is never called (remember interrupts are blocked in interrupt context) that will never happen: dead lock.

Time to do some fishing...

When we give Wire the receive callback it attaches it to the twi_onSlaveReceive function.

So the question is if that function is run from an ISR?

Yes, it is. So that means interrupts have been turned off while the callback function (receiveEvent) is running. I would say it is better to buffer the data in the callback and then transfer that to the UART when interrupts are enabled to avoid the risk of deadlock.

Update: A deadlock at this point would be an I2C catastrophe since it would hold the I2C clock line low forever.