Bidirectional I2C communication with the I2C_anything library

I have 2 sketches (master/slave) to sent 2 integers from one Arduino to another. Now I would like to sent 2 integers from the master to the slave and sent 2 integers from the slave to the master. I have been using the I2C_anything library: Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino

Could someone explain me how to do this?

// I2C communication between 2 Arduinos
// Analog joystick connected to analog pins A0 and A1
// Values from joystick are sent to the slave

#include <Wire.h>
#include <I2C_Anything.h>

const byte SLAVE_ADDRESS = 42;

void setup() 
{
  Wire.begin ();
}  // end of setup

void loop() 
{
  int X = analogRead(A0);

  int Y = analogRead(A1);

  Wire.beginTransmission (SLAVE_ADDRESS);
  I2C_writeAnything (X);
  I2C_writeAnything (Y);
  Wire.endTransmission ();

}

Other sketch:

// I2C communication between 2 Arduinos
// Slave receives the integers X and Y from the master
// Values X and Y are printed to the serial monitor

#include <Wire.h>
#include <I2C_Anything.h>

const byte MY_ADDRESS = 42;

void setup() 
{
  Wire.begin (MY_ADDRESS);
  Serial.begin (115200);
  Wire.onReceive (receiveEvent);
}  // end of setup

volatile boolean haveData = false;
volatile int X;
volatile int Y;

void loop() 
{
  if (haveData)
  {
    Serial.print ("Received X = ");
    Serial.println (X);  
    Serial.print ("Received Y = ");
    Serial.println (Y);  
    haveData = false;  
  }  // end if haveData

}  // end of loop

// called by interrupt service routine when incoming data arrives
void receiveEvent (int howMany)
{
  if (howMany >= (sizeof X) + (sizeof Y))
  {
    I2C_readAnything (X);   
    I2C_readAnything (Y);   
    haveData = true;     
  }  
}

I'm fairly sure the master does:

http://arduino.cc/en/Reference/WireRequestFrom

and in the slave that triggers:

http://arduino.cc/en/Reference/WireOnRequest

In the request handler you just use the same I2C_writeAnything() to send the data. You don't need Wire.beginTransmission() or Wire.endTransmission() in the request handler.

Unfortunately in a request handler you can only do a single write. This is a documented restriction, at least on my web page. ;)

You would need to modify the I2C_Anything code to build into a temporary buffer, and then do a single write of that. Otherwise a fairly simple solution is to read/write a struct (put as many variables as you want into the struct and then send/receive that in a single read/write).

This modified code shows what you were doing above, but in a single read/write with a struct:

// I2C communication between 2 Arduinos
// Analog joystick connected to analog pins A0 and A1
// Values from joystick are sent to the slave

#include <Wire.h>

const byte SLAVE_ADDRESS = 42;

void setup() 
{
  Wire.begin ();
}  // end of setup

void loop() 
{
  struct 
    {
    int X;
    int Y;
    } XY;
  
  XY.X = analogRead(A0);
  XY.Y = analogRead(A1);

  Wire.beginTransmission (SLAVE_ADDRESS);
  Wire.write ((byte *) &XY, sizeof XY);
  Wire.endTransmission ();
}

And:

// I2C communication between 2 Arduinos
// Slave receives the integers X and Y from the master
// Values X and Y are printed to the serial monitor

#include <Wire.h>

const byte MY_ADDRESS = 42;

void setup() 
{
  Wire.begin (MY_ADDRESS);
  Serial.begin (115200);
  Wire.onReceive (receiveEvent);
}  // end of setup

volatile struct 
   {
   int X;
   int Y;
   } XY;
    
volatile boolean haveData = false;

void loop() 
{
  if (haveData)
  {
    Serial.print ("Received X = ");
    Serial.println (XY.X);  
    Serial.print ("Received Y = ");
    Serial.println (XY.Y);  
    haveData = false;  
  }  // end if haveData

}  // end of loop

// called by interrupt service routine when incoming data arrives
void receiveEvent (int howMany)
{
  if (howMany < sizeof XY)
    return;
    
  // read into structure
  byte * p = (byte *) &XY;
  for (byte i = 0; i < sizeof XY; i++)
    *p++ = Wire.read ();
    
  haveData = true;     
}

Thanks for the help.
This is what I have come up with, took another good look at Nick’s page about I2C.

// I2C communication between 2 Arduinos: master sketch
// Analog joystick connected to analog pins A0 and A1
// Values from joystick are sent to the slave
// 2 integers are received from the slave

#include <Wire.h>

const byte MY_ADDRESS = 25;
const byte SLAVE_ADDRESS = 42;

volatile struct
{
int A;
int B;
}
AB;

volatile boolean haveData = false;

void setup()
{
Serial.begin(9600);
Wire.begin (MY_ADDRESS);
Wire.onReceive (receiveEvent);
} // end of setup

void loop()
{
if (haveData)
{
Serial.print ("Received A = ");
Serial.println (AB.A);
Serial.print ("Received B = ");
Serial.println (AB.B);
haveData = false;
} // end if haveData

struct
{
int X;
int Y;
}
XY;

XY.X = analogRead(A0);
XY.Y = analogRead(A1);

Wire.beginTransmission (SLAVE_ADDRESS);
Wire.write ((byte *) &XY, sizeof XY);
Wire.endTransmission ();
delay(50);
}

void receiveEvent (int howMany)
{
if (howMany < sizeof AB)
return;

// read into structure
byte * p = (byte *) &AB;
for (byte i = 0; i < sizeof AB; i++)
*p++ = Wire.read ();

haveData = true;
}

// I2C communication between 2 Arduinos: slave sketch
// Slave receives the integers X and Y from the master
// Slave sents the integers A and B to the master
// Values X and Y are printed to the serial monitor

#include <Wire.h>

const byte MY_ADDRESS = 42;
const byte OTHER_ADDRESS = 25;

void setup()
{
Wire.begin (MY_ADDRESS);
Serial.begin (9600);
Wire.onReceive (receiveEvent);
} // end of setup

volatile struct
{
int X;
int Y;
}
XY;

volatile boolean haveData = false;

void loop()
{
if (haveData)
{
Serial.print ("Received X = ");
Serial.println (XY.X);
Serial.print ("Received Y = ");
Serial.println (XY.Y);
haveData = false;
} // end if haveData

struct
{
int A;
int B;
}
AB;

AB.A = analogRead(A0);
AB.B = analogRead(A1);
Wire.beginTransmission (OTHER_ADDRESS);
Wire.write ((byte *) &AB, sizeof AB);
Wire.endTransmission ();
delay(50);

} // end of loop

// called by interrupt service routine when incoming data arrives
void receiveEvent (int howMany)
{
if (howMany < sizeof XY)
return;

// read into structure
byte * p = (byte *) &XY;
for (byte i = 0; i < sizeof XY; i++)
*p++ = Wire.read ();

haveData = true;
}

That works, I trust. If you are going to use the “2 ints in a struct” idea in multiple places you probably want a typedef, like this:

// I2C communication between 2 Arduinos: slave sketch
// Slave receives the integers X and Y from the master
// Slave sents the integers A and B to the master
// Values X and Y are printed to the serial monitor

#include <Wire.h>

const byte MY_ADDRESS = 42;
const byte OTHER_ADDRESS = 25;

typedef struct 
{
  int X;
  int Y;
} twoInts;

void setup()
{
  Wire.begin (MY_ADDRESS);
  Serial.begin (9600);
  Wire.onReceive (receiveEvent);
}  // end of setup

volatile twoInts XY;

volatile boolean haveData = false;

void loop()
{
  if (haveData)
  {
    Serial.print ("Received X = ");
    Serial.println (XY.X); 
    Serial.print ("Received Y = ");
    Serial.println (XY.Y); 
    haveData = false; 
  }  // end if haveData

  twoInts AB;

  AB.X = analogRead(A0);
  AB.Y = analogRead(A1);
  Wire.beginTransmission (OTHER_ADDRESS);
  Wire.write ((byte *) &AB, sizeof AB);
  Wire.endTransmission ();
  delay(50);

}  // end of loop

// called by interrupt service routine when incoming data arrives
void receiveEvent (int howMany)
{
  if (howMany < sizeof XY)
    return;

  // read into structure
  byte * p = (byte *) &XY;
  for (byte i = 0; i < sizeof XY; i++)
    *p++ = Wire.read ();

  haveData = true;     
}

Oh, still have lots to learn. First time I have used a struct and a typedef. Thanks for the help. I have updated my code and integrated it in the sketch for a robot. The code works but I'm afraid that the I2C bus hangs after a couple of minutes. Will have to figure out if it is a hardware or a software problem :~

Hi guys, and thanks for this thread. It has helped a lot. I got this to work with int float and double but can anyone explain how come this does not work with strings? Or am I doing something wrong?

kwesipk: Hi guys, and thanks for this thread. It has helped a lot. I got this to work with int float and double but can anyone explain how come this does not work with strings? Or am I doing something wrong?

A String is an Object not a simple variable.

You cannot easily sent an Object (functions, methods, data, class) over the I2C bus.

Any single I2C transmission is limited to 32 bytes. The Ram image of an Object is a jump table of addresses pointing to the Objects methods and functions, followed by all of the Objects data. Unless you have the exactly same code loaded in each Arduino, the Jump table would contain different offsets to the String methods. The compiler would optimize away unused methods, and the memory locations where the methods exist would/could be different.

If You want to pass the data of the String, build a set of function that access the raw cstring data and handle the exchange.

Since Strings are random length you are going to have to communicate this length plus the actual data, and since the I2C Wire.h library can only send 32 bytes at a time you might need to send multiple blocks.

Chuck.