Strange behavior while communicating between two arduinos via I2C

Hello,

I am creating a project where I need communication between two arduinos in mode Master writer, slave reader.

I have one Arduino Uno and Arduino nano (as a reader). Connection is simple, Uno is powered by Nano (because nano is connected via usb to read serial output) And pins 4 and 5 are connected on both devices.

I have a test code

For master

#include <Wire.h>

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

void loop() {
  Wire.beginTransmission(8);
  char test[40] = "tady bude nejaka jakakoli zprava";
  Wire.write(1);
//  Wire.send("test");       
  //Wire.write(x);              // jakakoli promenna
  Wire.endTransmission();    // transmission end
  
  delay(2000);
}

And this is reader (slave) code.

#include <Wire.h>
// receiver dat, ridici arudino

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

void loop() {
  delay(100);

}

void receiveEvent() {
  char retezec[30];
  while (Wire.available()) { // loop through all but the last
    Serial.println(".");
    Serial.print(Wire.read());
  }
}

But I receive only this output

1.
182.
214.
112.
74.

Again and again, still repeating.

Please do you have any suggestion how to send char array into slave?

Thanks for help, I don't know what I am missing and doing wrong :frowning:

And pins 4 and 5 are connected on both devices.

For I2C you have to connect pins SDA and SCL (or A4 and A5). Pins 4 and 5 are not used for I2C.

void receiveEvent() {
  char retezec[30];
  while (Wire.available()) { // loop through all but the last
    Serial.println(".");
    Serial.print(Wire.read());
  }
}

You must not use the Serial object in interrupt context as in interrupt context interrupts are disabled and the Serial object depends on interrupts to do it's work correctly.

Hello,

sorry I mean A4 and A5, my mistake, it is properly connected.

I see, I will try different way, thanks.

I was following this

First time absolutelly same code but with char array sending. It was not working.

Later I will try (I am at work).

M.

@OP

1. Make UNO and NANO connections as per following diagram (Fig-1).
i2c-msX.png

2. Upload the following codes in Master UNO. (slight revision of your sketch.)

#include <Wire.h>
char test[40] = "tady bude nejaka jakakoli zprava";
void setup()
{
  Wire.begin();
}

void loop()
{
  Wire.beginTransmission(8);
  //  char test[40] = "tady bude nejaka jakakoli zprava";
  Wire.write(test, sizeof(test));//Wire.write(1);
  //  Wire.send("test");
  //Wire.write(x);              // jakakoli promenna
  Wire.endTransmission();    // transmission end

  delay(2000);
}

3. Upload the following codes in Slave NANO. (Major revision of your sketch.)

//#include <EEPROM.h>

#include <Wire.h>
// receiver dat, ridici arudino

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

void loop()
{
  //delay(100);

}

void receiveEvent(int howmany)
{
  // char retezec[30];
  // while (Wire.available()) { // loop through all but the last
  for (int i = 0; i < howmany; i++)
  {
    //Serial.println(".");
    Serial.print((char)Wire.read());
  }
  Serial.println();
}

4. This is the output on Serial Monitor of NANO.
sm-13.png

5. If you have any question regarding the revision of your sketches, do not hesitate to ask.

sm-13.png

i2c-msX.png

pylon:
You must not use the Serial object in interrupt context as in interrupt context interrupts are disabled and the Serial object depends on interrupts to do it's work correctly.

The receiver/slave has already finished all the activities of the interrupt process by receiving all the characters and saving them in the local FIFO buffer. Now, the slave can safely enter into receiveEvent() handler, read the characters from the FIFO buffer and show them on the Serial Monitor. Please, see the slave codes of Post#3.

The receiver/slave has already finished all the activities of the interrupt process by receiving all the characters and saving them in the local FIFO buffer. Now, the slave can safely enter into receiveEvent() handler, read the characters from the FIFO buffer and show them on the Serial Monitor. Please, see the slave codes of Post#3.

I know that it works in some cases because the Arduino HardwareSerial class uses a buffer for the data to be sent by the serial interface but it's definitely not safe!
If the buffer gets full (for any reason) the Arduino will enter an endless loop waiting for the serial interrupt to empty the buffer but that interrupt will never be called (as interrupts are disabled). receiveEvent() is called in interrupt context, that's why any call to the Serial object is a must not.

And it doesn't help that the Tutorial includes the same wrong code, that should be fixed soon.

pylon:
I know that it works in some cases because the Arduino HardwareSerial class uses a buffer for the data to be sent by the serial interface but it's definitely not safe!
If the buffer gets full (for any reason) the Arduino will enter an endless loop waiting for the serial interrupt to empty the buffer but that interrupt will never be called (as interrupts are disabled). receiveEvent() is called in interrupt context, that's why any call to the Serial object is a must not.

And it doesn't help that the Tutorial includes the same wrong code, that should be fixed soon.

The context is the I2C Interface and then secondarily the UART Interface. In I2C Protocol, data transmit/receive happens 1-byte at a time on interrupt basis. When the data byte arrives at the TWIDR Register of the Slave, the Slave is interrupted; the Slave goes to the ISR, reads the data byte from its TWIDR and saves it into the Arduino's FIFO Buffer.

The OP sends 32-byte data bounded by beginTransmissio() and endTransmission(). At the end of receieving/storing all the data bytes in the buffer, the Slave goes to the receiveEvent() procedures and executes the codes that are there.

I don't see any rationality why are you saying -- [...] but it's definitely not safe!. Is there any data loss? How? The user is just executing Wire.read() and Serial.print() instructions (in the receiveEvent() procedure) which reads data from I2C's FIFO Buffer and then write the data into the UART Port/UART Transmit Buffer.

I don't see any rationality why are you saying -- [...] but it's definitely not safe!. Is there any data loss? How? The user is just executing Wire.read() and Serial.print() instructions (in the receiveEvent() procedure) which reads data from I2C's FIFO Buffer and then write the data into the UART Port/UART Transmit Buffer.

Not only that data may be lost, you risk a dead lock.
I repeat: receiveEvent is called in interrupt context and in interrupt context all interrupts are disabled (I hope you agree). If you call Serial.write() (or any method calling this as p.e. print(), println(), etc.) inside interrupt context, the data provided is written to a circular buffer that the HardwareSerial class manages. The problem arises if that buffer gets full. In that case the write() method just enters and endless loop until the buffer gets some free space again. In standard context this happens because the hardware is sending out the current character in the register and then triggers an interrupt to get the next character from that buffer. As we are in interrupt context (you remember?) this interrupt will never happens because all interrupts are disabled, so the buffer is never discharged and the write method waits forever.
I seriously hope you agree now that writing anything to the Serial object inside an interrupt handler is not safe and should be avoided.

GolamMostafa:
@OP

1. Make UNO and NANO connections as per following diagram (Fig-1).
i2c-msX.png

2. Upload the following codes in Master UNO. (slight revision of your sketch.)

#include <Wire.h>

char test[40] = "tady bude nejaka jakakoli zprava";
void setup()
{
  Wire.begin();
}

void loop()
{
  Wire.beginTransmission(8);
  //  char test[40] = "tady bude nejaka jakakoli zprava";
  Wire.write(test, sizeof(test));//Wire.write(1);
  //  Wire.send("test");
  //Wire.write(x);              // jakakoli promenna
  Wire.endTransmission();    // transmission end

delay(2000);
}




**3.** Upload the following codes in Slave NANO. (Major revision of your sketch.)


//#include <EEPROM.h>

#include <Wire.h>
// receiver dat, ridici arudino

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

void loop()
{
  //delay(100);

}

void receiveEvent(int howmany)
{
  // char retezec[30];
  // while (Wire.available()) { // loop through all but the last
  for (int i = 0; i < howmany; i++)
  {
    //Serial.println(".");
    Serial.print((char)Wire.read());
  }
  Serial.println();
}




**4.** This is the output on Serial Monitor of NANO.
![sm-13.png|520x403](upload://c682YCEBs6lGMQ9fwW6O8m3Lzmi.png)

**5.** If you have any question regarding the revision of your sketches, do not hesitate to ask.

Thank You so much! It is working now and I am a little bit smarter! Thanks so much!

Have a nice day

Martin

pylon:
I repeat: receiveEvent is called in interrupt context and in interrupt context all interrupts are disabled (I hope you agree).

I think that the issue of the discussion is receiveEvent() of I2C Bus and not the serialEvent() of UART Port.

I think that the issue of the discussion is receiveEvent() of I2C Bus and not the serialEvent() of UART Port.

I never wrote about serialEvent, don't bring new stuff into the discussion without need. What of the description in answer #7 didn't you understand? In your code you're calling Serial.print() inside the receiveEvent() function, don't you?