Raspberry Pi - Arduino communication with I2C

Hello!

I had a problem setting up an I2C communication between a Raspberry Pi model B as master and an Arduino Mega clone as slave. I managed to solve the problem on my own, but I think it could be useful to document what I observed here. As a bonus, if some of you have a better understanding of what is going on than I do, please don't hesitate to add your comments.

The problem I had was related to the number of bytes that I could send in a single message. As I understand the Wire library, the message length limit should be 32 (as defined by the line #define BUFFER_LENGTH 32 in Wire.h). As it turns out, I could only send a few characters, otherwise the Arduino sketch would freeze. I fixed the problem by adding a delay on the Raspberry Pi program after transmission.

Raspberry Pi code:

#include <iostream>
#include <string>
#include <sstream>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define MICROCONTROLER_ADDRESS 0x04
// The I2C bus: This is for V2 pi's
static const char* devName = "/dev/i2c-1";

using namespace std;

int main()
{
	int delayAfterTransmissionInMicroseconds = 50000;
	cout << "I2C connection..." << endl;
	int file;
	if ((file = open(devName, O_RDWR)) < 0)
	{
		cout << "I2C: Failed to access " << devName;
		return -1;
	}
	
	cout << "I2C: acquiring buss to " << MICROCONTROLER_ADDRESS << endl;
	if (ioctl(file, I2C_SLAVE, MICROCONTROLER_ADDRESS) < 0)
	{
		cout << "I2C: Failed to acquire bus access/talk to slave" << endl;
		return -1;
	}
	// Transmit messages of increasing length
	for (int msgLength = 1; msgLength <= 32; msgLength++)
	{
		stringstream msg;
		char c = 'A';
		for (int charNdx = 0; charNdx < msgLength; charNdx++)
		{
			msg << c;
			c++;
		}
		cout << "msg = " << msg.str() << endl;
		ssize_t numberOfBytesTransmitted = write(file, msg.str().c_str(), msg.str().length());
		cout << "numberOfBytesTransmitted = " << numberOfBytesTransmitted << endl;
		if (numberOfBytesTransmitted == (ssize_t) msg.str().length())
			cout << "Transmission succeeded!" << endl;
		else
			cout << "Transmission failed!" << endl;
		usleep(delayAfterTransmissionInMicroseconds);
	}
	
	cout << "Done!" << endl;
}

Arduino Mega code:

#include <Wire.h>
#include <StandardCplusplus.h>
#include <sstream>

#define SLAVE_ADDRESS 0x04

using namespace std;

void setup()
{
  Serial.begin(9600);
  Serial.println("Running RPiArduinoI2CString");
  
  // Initialize I2C as slave
  Wire.begin(SLAVE_ADDRESS);
  
  // Define callbacks for I2C communication
  Wire.onReceive(ReceiveString);
  Wire.onRequest(SendRequestedString);
}

void loop()
{
  delay(100);
}

void ReceiveString(int byteCount)
{
  Serial.print("ReceiveString(");
  Serial.print(byteCount);
  Serial.println(")");
  stringstream msg;
  while(Wire.available()) {
   char c = Wire.read();
   msg << c;
  }
  //Serial.println("Complete message: ");
  Serial.println(msg.str().c_str());
}

void SendRequestedString()
{
}

I tested some values for the delay parameter delayAfterTransmissionInMicroseconds and here is the maximum number of bytes that I could transmit:

delayAfterTransmissionInMicroseconds Maximum number of bytes
10000 2
20000 7
30000 16
40000 25
50000 32

In other words, in order to be able to transmit the full 32 bytes allowed by the Wire library, the Raspberry Pi must be put in sleep mode for at least 50000 µs (i.e. 50 ms).

void loop()
{
  delay(100);
}

Why? There is just no excuse for the delay() there. Why do you care whether loop() does nothing 1000000 times in one second, vs. doing nothing 10000 times in one second?

  while(Wire.available()) {
   char c = Wire.read();
   msg << c;
  }

You KNOW the maximum number of bytes that can be sent. Doing dynamic memory allocation makes NO sense. A static array and a for loop would be orders of magnitude faster.

  Serial.println(msg.str().c_str());

That's just plain silly. A double method call to get the data?

char msg[33];

int msgLen = Wire.available();
for(byte i=0; i<msgLen; i++)
{
   msg[i] = Wire.read();
   msg[i+1] = '\0'; // NULL terminate the array
}

Serial.println(msg);

Hello PaulS,

Thank you for your comments.
I'm not sure how you interpreted my original post, but it was not about speed, at all. It's not a concern for my application. My problem was that I could not transmit 32 characters. I observed that adding a short delay could fix this problem.