Problem with a I2C LCD in a slave node.

Hello, i’m having a problem with a i2c comunication project.

In my project i have a master acquiring raw data at 250Hz from a MPU9250 and calculating roll and pitch angles. This information than is sended to a slave at 5Hz. The slave collectit and display the angles at a LCD i2c module. However, when the slave is receiving the information its seems that the lcd get corrupted as well the slave doesnt collect the information from the master.

MPU i2c address: 0x68
Slave i2c address: 0x20
LCD i2c address: 0x27

Does anyone knows why this is happening? I’m doing something wrong with the master-slave communication? When i just use the master to acquire and display it at the lcd, everything works fine.

Thanks!!

Slave code:

#include <Wire.h>
#define nodeAdress 0x20
#define dataSize 4
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2);

int data[4];
unsigned int aux0;
unsigned int aux1;
float rollAngle, pitchAngle;
int ok = 1;

void setup() {
  
  Serial.begin(57600);
  Serial.println("1");
  Wire.begin(nodeAdress);
  Wire.setClock(400000);
  Serial.println("1");
  Wire.onReceive(receiveEvent);
  Serial.println("1");
  lcd.begin();
  Serial.println("1");
  lcd.backlight();
  Serial.println("1");
  lcd.clear();
}

void loop() {
  delay(100);
  
  aux0 = (data[1]<<8) | data[0];
  pitchAngle = (aux0/100.0)-180.0;

  aux1 = (data[3]<<8) | data[2];
  rollAngle = (aux1/100.0)-180.0;
  
  Serial.print("Pitch angle: ");
  Serial.print(pitchAngle);
  Serial.print("     -     Roll angle: ");
  Serial.println(rollAngle);
  
  lcdPrint();
}

void lcdPrint() {
  lcd.setCursor(0, 0);
  lcd.print(F("Pitch: "));
  lcd.print(pitchAngle);
  lcd.print(F("o"));
  lcd.setCursor(0, 1);
  lcd.print(F("Roll: "));
  lcd.print(rollAngle);
  lcd.print(F("o"));
}

void receiveEvent(int howMany) {
  if(Wire.available()){
    data[0] = Wire.read();
    data[1] = Wire.read();
    data[2] = Wire.read();
    data[3] = Wire.read();
   }    
}

Master code:

#include <Wire.h>
#define dataSize 4
#define nodeAdress 21

int gyro_x, gyro_y, gyro_z;
long acc_x, acc_y, acc_z, acc_total_vector;
int temperature;
long gyro_x_cal, gyro_y_cal, gyro_z_cal;
long loop_timer;
int lcd_loop_counter;
float angle_pitch, angle_roll;
int angle_pitch_buffer, angle_roll_buffer;
boolean set_gyro_angles;
float angle_roll_acc, angle_pitch_acc;
float angle_pitch_output, angle_roll_output;
byte data[4];
unsigned int aux;
int contador;

void setup() {
  Serial.begin(57600);

  Wire.begin(); 
  Wire.setClock(400000);
  setup_mpu_6050_registers(); 

  for (int cal_int = 0; cal_int < 2000 ; cal_int ++) { 
    read_mpu_6050_data(); 
    gyro_x_cal += gyro_x;      
    gyro_y_cal += gyro_y;    
    gyro_z_cal += gyro_z;  
    delay(3);  
  }
  gyro_x_cal /= 2000;  
  gyro_y_cal /= 2000;  
  gyro_z_cal /= 2000;  

  loop_timer = micros();   

}

void loop() {


  read_mpu_6050_data();                                                

  gyro_x -= gyro_x_cal;                                                
  gyro_y -= gyro_y_cal;                                               
  gyro_z -= gyro_z_cal;                                               

  angle_pitch += gyro_x * 0.0000611;                                   
  angle_roll += gyro_y * 0.0000611;                                   

  
  angle_pitch += angle_roll * sin(gyro_z * 0.000001066);            
  angle_roll -= angle_pitch * sin(gyro_z * 0.000001066);              

  acc_total_vector = sqrt((acc_x * acc_x) + (acc_y * acc_y) + (acc_z * acc_z)); 
  //57.296 = 1 / (3.142 / 180) The Arduino asin function is in radians
  angle_pitch_acc = asin((float)acc_y / acc_total_vector) * 57.296;    
  angle_roll_acc = asin((float)acc_x / acc_total_vector) * -57.296;   

  angle_pitch_acc -= 0.0;
  angle_roll_acc -= 0.0; 

  if (set_gyro_angles) {
    angle_pitch = angle_pitch * 0.9996 + angle_pitch_acc * 0.0004; 
    angle_roll = angle_roll * 0.9996 + angle_roll_acc * 0.0004;  
  }
  else {
    angle_pitch = angle_pitch_acc;
    angle_roll = angle_roll_acc;   
    set_gyro_angles = true;    
  }

  angle_pitch_output = (angle_pitch_output * 0.9 + angle_pitch * 0.1);
  angle_roll_output = (angle_roll_output * 0.9 + angle_roll * 0.1);

  
  aux = (unsigned int)((angle_pitch_output + 180.0) * 100.0);
  data[0] = (int)((aux & 0XFF));
  data[1] = (int)((aux >> 8) & 0XFF);
  aux = (unsigned int)((angle_roll_output + 180.0) * 100.0);
  data[2] = (int)((aux & 0XFF));
  data[3] = (int)((aux >> 8) & 0XFF);

  Serial.print("Pitch angle: ");
  Serial.print(angle_pitch_output);
  Serial.print("     -     Roll angle: ");
  Serial.println(angle_roll_output);

  if(contador < 50){
    contador++;
  } else{
    Wire.beginTransmission(0x20);
    Wire.write(data[0]);
    Wire.write(data[1]);
    Wire.write(data[2]);
    Wire.write(data[3]);
    Wire.endTransmission();
    contador = 1;
  }

  Serial.println(contador);

  Serial.println(micros() - loop_timer);

  while (micros() - loop_timer < 4000)
  loop_timer = micros(); 
}

void read_mpu_6050_data() { 
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x3B);                                                    //Send the requested starting register
  Wire.endTransmission();                                              //End the transmission
  Wire.requestFrom(0x68, 14);                                          //Request 14 bytes from the MPU-6050
  while (Wire.available() < 14);                                       //Wait until all the bytes are received
  acc_x = Wire.read() << 8 | Wire.read();                              //Add the low and high byte to the acc_x variable
  acc_y = Wire.read() << 8 | Wire.read();                              //Add the low and high byte to the acc_y variable
  acc_z = Wire.read() << 8 | Wire.read();                              //Add the low and high byte to the acc_z variable
  temperature = Wire.read() << 8 | Wire.read();                        //Add the low and high byte to the temperature variable
  gyro_x = Wire.read() << 8 | Wire.read();                             //Add the low and high byte to the gyro_x variable
  gyro_y = Wire.read() << 8 | Wire.read();                             //Add the low and high byte to the gyro_y variable
  gyro_z = Wire.read() << 8 | Wire.read();                             //Add the low and high byte to the gyro_z variable

}

void setup_mpu_6050_registers() {
  //Activate the MPU-6050
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x6B);                                                    //Send the requested starting register
  Wire.write(0x00);                                                    //Set the requested starting register
  Wire.endTransmission();                                              //End the transmission
  //Configure the accelerometer (+/-8g)
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x1C);                                                    //Send the requested starting register
  Wire.write(0x10);                                                    //Set the requested starting register
  Wire.endTransmission();                                              //End the transmission
  //Configure the gyro (500dps full scale)
  Wire.beginTransmission(0x68);                                        //Start communicating with the MPU-6050
  Wire.write(0x1B);                                                    //Send the requested starting register
  Wire.write(0x08);                                                    //Set the requested starting register
  Wire.endTransmission();                                              //End the transmission
}

Your "slave" is only a slave for the other Arduino but it's a master for the LCD. This results in a multimaster setup which is not completely supported by the Wire library.

Even if that works there's no synchronization between the reception of data and the display of it. If the data[1] is read and shifted but before data[0] is read new values arrive you will have wrong data displayed.

Why don't you do all that stuff on the same Arduino? Why are two Arduinos in the setup?

pylon:
Your "slave" is only a slave for the other Arduino but it's a master for the LCD. This results in a multimaster setup which is not completely supported by the Wire library.

Even if that works there's no synchronization between the reception of data and the display of it. If the data[1] is read and shifted but before data[0] is read new values arrive you will have wrong data displayed.

Why don't you do all that stuff on the same Arduino? Why are two Arduinos in the setup?

Hi Pylon! Yes, the arduino act like a master/slave. The point of the whole project is to acquire some sensors datas (Pressure, Altitude, Humidity, Temperature and Roll and Pitch angles) onboard in a vehicle and send the values on a CAN bus using the MCP2515. The problem at processing all this in a single Arduino is that i cant calculate the roll and pitch angles at a high speed (like 250Hz). So i need a second arduino to act like a IMU, processing the angles at a high rate, and send it to the principal one.

Is there a way to organize the i2c bus to avoid this syncronization failure?

Thanks Pylon!

Is there a way to organize the i2c bus to avoid this syncronization failure?

Not a reliable one.

If you plan to use a CAN bus for the communication why don't you use it now? The problem arise because you use the I2C bus in a way that is not ideal for it. If you must use the serial interface for debugging at the moment, connect the two Arduinos by SPI. Although I doubt that you gain much speed by transferring the data between two Arduinos. I would expect the Arduino to be fast enough for that purpose.