I2C Arduino-Raspberry Pi 2, time between two write()

Hello everyone,
I managed to connect an arduino Uno with a raspberry pi 2 by I2C.
The RPI 2 is the master and the Arduino Uno is the slave.

Initialisation of the RPI 2

// The I2C bus: This is for V2 pi's. For V1 Model B you need i2c-0
	char *devName = "/dev/i2c-1";

	printf("I2C: Connecting\n");
	 
	if ((file_i2c = open(devName, O_RDWR)) < 0) {
		fprintf(stderr, "I2C: Failed to access %s\n", devName);
		exit(1);
	}

	printf("I2C: acquiring buss to 0x%x\n", ADDRESS);
	 
	if (ioctl(file_i2c, I2C_SLAVE, ADDRESS) < 0) {
		fprintf(stderr, "I2C: Failed to acquire bus access/talk to slave 0x%x\n", ADDRESS);
		exit(1);
	}

Sending a char[3] variable code:

if(write(file_i2c, midi, 3) != 3)
{
	printf("I2C : Error to send data %d %d %d \n", midi[0], midi[1], midi[2]);
	exit(1);
}
usleep(12000);

I don’t really see what I can do to handle the error.

The Arduino Uno code

#include <Wire.h>
 
#define SLAVE_ADDRESS 0x04
int number = 0;

byte commandByte = 0;
byte noteByte = 0;
byte velocityByte = 0;

byte noteOn = 0x90, noteOff = 0x80;
 
void setup() {
 pinMode(13, OUTPUT);
 digitalWrite(13,LOW);
 
 // initialize i2c as slave
 Wire.begin(SLAVE_ADDRESS);
 
 // define callbacks for i2c communication
 Wire.onReceive(receiveData);
 Wire.onRequest(sendData);
 
 Serial.begin(9600);
}
 
void loop() {
  delay(100);
}

 
// callback for received data
void receiveData(int howMany){
  if(Wire.available()>2) {
    commandByte = Wire.read();//read first byte
    noteByte = Wire.read();//read next byte
    velocityByte = Wire.read();//read final byte
    Serial.print("New MIDI Note: "); Serial.print(commandByte); Serial.print(" "); Serial.print(noteByte); Serial.print(" "); Serial.println(velocityByte);
  }
  
}
 
// callback for sending data
void sendData(){
 number++;
 Wire.write(number);
}

My question is:
I have to wait 12ms (usleep(12000)) after sending the 3 bytes to the arduino otherwise the Arduino freezes and I get an “Error to send data …”. My sending function is in a loop of course.
I’d like to know if I have to wait 12ms because of I2C or because of the Uno?
If I replace the Uno with a Mega 2560, could i wait a little bit less?

void loop() {
  delay(100);
}

Get rid of the stupid delay(). Who cares how many times loop() uselessly iterates?

I have to wait 12ms (usleep(12000)) after sending the 3 bytes to the arduino otherwise the Arduino freezes and I get an "Error to send data ...". My sending function is in a loop of course. I'd like to know if I have to wait 12ms because of I2C or because of the Uno? If I replace the Uno with a Mega 2560, could i wait a little bit less?

You have to wait because you are doing interrupt-driven stuff in an an ISR where interrupts are disabled.

Stop doing Serial.print() in an ISR.

Pick up the pace, man. 9600 baud went out with the last ice age!

Minimize the useless data being sent.

No, a Mega won't do the useless stuff you are doing, or the stuff you are doing incorrectly, any faster.

Thanks for the fast reply.

void loop(){
   delay(100);
}

Get rid of the stupid delay(). Who cares how many times loop() uselessly iterates?

Right.

Stop doing Serial.print() in an ISR.

Yep, it was stupid, in my opinion a serial.print() was so negligible :D

Pick up the pace, man. 9600 baud went out with the last ice age!

Ok but this has nothing to do with the I2C, no? 9600 bauds is enough to do a serial.print() :o But ok, i'll try with a bigger rate from now.

Minimize the useless data being sent.

No, a Mega won't do the useless stuff you are doing, or the stuff you are doing incorrectly, any faster.

Alright, I'll keep my UNO !

Ok but this has nothing to do with the I2C, no?

The time you need to wait before you send data again is a function of what you are doing with the data. Sending it slowly takes longer than sending it quickly.

9600 bauds is enough to do a serial.print()

So is 300 baud. But, I don't recommend that.

The reason that you get the error sending the data is because when you send it without the delay, the Arduino is entering the interrupt handler, more than once, and putting data in the outgoing serial buffer. When the buffer gets full, print() blocks waiting for space to become available. Space becomes available only when timer interrupts happen, to shift the data out. But, timer interrupts don't happen while your ISR is running (onReceive and onRequest are ISRs). So, the Arduino appears to have locked up, and the PI can no longer communicate with it.

Get the Serial.print() statements out of the ISR, so the deadlock condition doesn't happen. Then, you'll see that the PI can send data to the Arduino far faster than the Arduino can send the data out the serial port. Especially at 9600 baud.

I followed your instructions:

the sending function on the RPI 2:

void send_midi(char* midi){
	int bufferSend = write(file_i2c, midi, 3);
	if(bufferSend != 3){
		printf("I2C : Error to send data %d %d %d \n", midi[0], midi[1], midi[2]);
	}
	printf("send_midi(%d %d %d) \n", midi[0], midi[1], midi[2]);
	usleep(10000);
}

the Arduino code:

include <Wire.h>

#define SLAVE_ADDRESS 0x04

int number = 0;

double temp;

byte commandByte = 0;
byte noteByte = 0;
byte velocityByte = 0;

byte noteOn = 0x90, noteOff = 0x80;

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13,LOW);

  // initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  Wire.onReceive(receiveData);
  Wire.onRequest(sendData);

  Serial.begin(115200);
}

void loop() {
  choice();
  Serial.print("New MIDI Note: "); 
  Serial.print(commandByte); 
  Serial.print(" "); 
  Serial.print(noteByte); 
  Serial.print(" "); 
  Serial.println(velocityByte);
  number++;
  if(number>200) number=0;
}


// callback for received data
void receiveData(int howMany){
  if(Wire.available()>2) {
    commandByte = Wire.read();//read first byte
    noteByte = Wire.read();//read next byte
    velocityByte = Wire.read();//read final byte
  }
}

// callback for sending data
void sendData(){
  Wire.write(number);
}

void cross(){
  while(1){
    digitalWrite(13,HIGH);
    if(commandByte==noteOff && noteByte==60){
      digitalWrite(13,LOW);
      break;
    } 
  }
}

void choice_move(){
  switch(noteByte){
  case 60:
    cross();
    break;

  default:
    break;
  }
}

As you can see there is a Wire.onRequest(sendData); wich sends a counter incremented in the loop(). Thanks to that, I can know if the Arduino is still working or not ! (later i will send more important data)
There is another function which blinks the LED on the pin 13, if the arduino receives the good data!

Here is the function on the RPI 2 which sends a request to the Arduino each 5 min. (millis() is from the wiringPi library)

void receive_midi(){
	static uint32_t end, diff, start = 0;
	static int first_time = 1;
	char buf[1];
	int message=0;
	
	if(first_time){
	//set start
		start = millis();
		first_time = 0;
	}
	
	end = millis();
	diff = end-start;
	
	// if >5 sec
	if(diff>5000){
		if (read(file_i2c, buf, 1) == 1) {
			message = (int) buf[0];
			printf("Arduino message : %d \n", message);
		}
		//reset start
		start = millis();
		usleep(10000);
	}
}

At the start of the prog on the RPI 2 and on the Arduino, it works pretty well.
The counter is incremented then reset.
The data received by the arduino are printed on the serial monitor.
The led blinks when I want.

But if i decide to send too many data (I press a key to send the 3 bytes) with the send_midi() function of the RPI 2, an error occurs.
For example: I2C : Error to send data 144 63 127.
The arduino received 144 127 8… ( why :sob: ) and then i cant send new data to the arduino.
The counter is still incremented/reset => the arduino is still working.
But the serial.print() shows the same data untill the end 144 127 8… etc I can’t blink the LED anymore.

The Arduino is still working but can’t received any new data after the unexpected error on the RPI 2.

I need to send MIDI data (3 bytes).
Maybe I can send bytes, one by one?

But I don’t know if it is a good idea and if I have to put a usleep(10000) or not between each write(file_i2c, midi*, 1)*
Like that:
* *void send_midi(char* midi){ if( write(file_i2c, midi[0], 1) != 1){ printf("I2C : Error to send data %d %d %d \n", midi[0], midi[1], midi[2]); }else         if( write(file_i2c, midi[1], 1) != 1){ printf("I2C : Error to send data %d %d %d \n", midi[0], midi[1], midi[2]);         }else         if( write(file_i2c, midi[2], 1) != 1){ printf("I2C : Error to send data %d %d %d \n", midi[0], midi[1], midi[2]);         } printf("send_midi(%d %d %d) \n", midi[0], midi[1], midi[2]); usleep(10000); }* *
It’s ugly…

There are some problems with the Arduino code that need to be addressed. Whether they solve the ultimate problem, or not, remains to be seen.

byte commandByte = 0;
byte noteByte = 0;
byte velocityByte = 0;

These variables are written to by the ISR and used by loop() and cross() and other places.

They are not, however, marked volatile, so the compiler assumes that it can read the values once, and that they won't change within a function.

void cross(){
  while(1){
    digitalWrite(13,HIGH);
    if(commandByte==noteOff && noteByte==60){
      digitalWrite(13,LOW);
      break;
    } 
  }
}

If this function gets called, it will end after the while loop iterates once, if those two variables contain, AT THE TIME THE FUNCTION IS CALLED, those two values. If the two variables contain ANY other values, the while loop will never end, even though interrupts happen and the global variables get new values.

If you declare those three variables to be volatile, the contents will be fetched EVERY time the while loop iterates, so if the interrupt happens, and changes the value in those variables, the while loop might actually end.

It doesn't change anything.

Actually, I could end the loop without the volatile byte because the LED blinked. I just sent the note 60 on (the led turns on) and then the note 60 off (the led turns off), it was working and it sill works!

The problem is when a sending error on the RPI 2 occurs. It is like the ISR onReceive() doesn't work anymore... I can't understand why. What should I do on the RPI 2 when a I2C error occurs? And how can I handle/detect that on the Arduino?

PS: the onReceive() can bug in the while(1) or out of it. => the serial monitor can still being updated or not (no new wire.print() ).

PS2: I've found a solution. It's like a fflush()... How to handle the data transmission error on the Arduino side:

void receiveData(int howMany){
  if(Wire.available()>2) {
    commandByte = Wire.read();//read first byte
    noteByte = Wire.read();//read next byte
    velocityByte = Wire.read();//read final byte
  }
else{
    while(Wire.available())
    garbage=Wire.read();
}
}

I still don't know why sometimes I2C fails ... I've replace my wires but there are still random errors..