Master to slave, slave to I2C peripheral

Here's my setup:

I have a master arduino (using a Pro Mini 5V) and it addresses a slave. What I want the slave to do is read four pressure sensors and get IMU data from an MPU9250. I want to be able to have the master talk to the slave and the slave talk to the IMU all on the same I2C bus. The problem comes after the master talks to the slave. The slave then wants to talk to the IMU, but the clock signal (SCL) disappears because it is generated by the master not the slave. I tried to use a switch that would disconnect the master SDA line from the slave SDA line and connect the slave SDA line to the IMU SDA line.

Here's the Master code:

#include <Wire.h>

byte Buf[14];
byte Mag[6];
byte pressure[8];

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

void loop() 
{
  //Serial.print("hey");
  Wire.requestFrom(8, 8);
  
  if(!Wire.available())
    {
      Serial.print("wire not available");
    }
  while(Wire.available())
  {
        
    for(int i=0; i<sizeof(Buf); i++)
    {
      Buf[i] = Wire.read();
    }
    
    for(int i=0; i<sizeof(Mag); i++)
    {
      Mag[i] = Wire.read();
    }
    
    for(int i=0; i<sizeof(pressure); i++)
    {
      pressure[i] = Wire.read();
    }
    
  }

  // ____________________________________
  // :::  accelerometer and gyroscope ::: 
 
 
  // Create 16 bits values from 8 bits data
 
  // Accelerometer
  int16_t ax=Buf[0]<<8 | Buf[1];
  int16_t ay=Buf[2]<<8 | Buf[3];
  int16_t az=Buf[4]<<8 | Buf[5];
 
  // Gyroscope
  int16_t gx=Buf[8]<<8 | Buf[9];
  int16_t gy=Buf[10]<<8 | Buf[11];
  int16_t gz=Buf[12]<<8 | Buf[13];


  // _____________________
  // :::  Magnetometer ::: 
  
  // Create 16 bits values from 8 bits data
 
  // Magnetometer
  int16_t mx=Mag[1]<<8 | Mag[0];
  int16_t my=Mag[3]<<8 | Mag[2];
  int16_t mz=Mag[5]<<8 | Mag[4];


  // _________________
  // :::  Pressure ::: 
  
  // Create 16 bits values from 8 bits data

  int16_t p1 = pressure[0]<<8 | pressure[1];
  int16_t p2 = pressure[2]<<8 | pressure[3];
  int16_t p3 = pressure[4]<<8 | pressure[5];
  int16_t p4 = pressure[6]<<8 | pressure[7];


  Serial.print("Accel = ");
  Serial.print(ax);
  Serial.print(",");
  Serial.print(ay);
  Serial.print(",");
  Serial.println(az);

  Serial.print("Gyro = ");
  Serial.print(gx);
  Serial.print(",");
  Serial.print(gy);
  Serial.print(",");
  Serial.println(gz);

  Serial.print("mag = ");
  Serial.print(mx);
  Serial.print(",");
  Serial.print(my);
  Serial.print(",");
  Serial.println(mz);

  Serial.print("pressure = ");
  Serial.print(p1);
  Serial.print(",");
  Serial.print(p2);
  Serial.print(",");
  Serial.print(p3);
  Serial.print(",");
  Serial.println(p4);

  
  
}

And here's the Slave code:

#include <Wire.h>

#define    MPU9250_ADDRESS            0x69
#define    MAG_ADDRESS                0x0C
 
#define    GYRO_FULL_SCALE_250_DPS    0x00  
#define    GYRO_FULL_SCALE_500_DPS    0x08
#define    GYRO_FULL_SCALE_1000_DPS   0x10
#define    GYRO_FULL_SCALE_2000_DPS   0x18
 
#define    ACC_FULL_SCALE_2_G        0x00  
#define    ACC_FULL_SCALE_4_G        0x08
#define    ACC_FULL_SCALE_8_G        0x10
#define    ACC_FULL_SCALE_16_G       0x18

byte pressure[8];
int analog[4] = {A0, A1, A2, A3};
bool configure = true;
// 
//// This function read Nbytes bytes from I2C device at address Address. 
//// Put read bytes starting at register Register in the Data array. 
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
  // Set register address
  Wire.beginTransmission(Address);
  Wire.write(Register);
  Wire.endTransmission();
 
  // Read Nbytes
  Wire.requestFrom(Address, Nbytes); 
  uint8_t index=0;
  while (Wire.available())
    Data[index++]=Wire.read();
}
 
 
// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
  
  // Set register address
  Wire.beginTransmission(Address);
  Wire.write(Register);
  Wire.write(Data);
  Wire.endTransmission();

}

void read_ADC(byte *input)
{
  int j = 0;
  int ADC;
  for(int i=0; i<4; i++)
  {
    ADC = analogRead(analog[i]);
    input[j]=highByte(ADC);
    input[j+1]=lowByte(ADC);
    j=j+2;
  }
}

void setup() 
{
  pinMode(13, OUTPUT);
  pinMode(0, OUTPUT);
  pinMode(8, OUTPUT);
  //configure ADC pins
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);
  pinMode(A4, OUTPUT);
  pinMode(A5, OUTPUT);

  Wire.begin(8);

  Wire.onRequest(run_event);

}

void loop() 
{

}

void run_event()
{
  //turn pin 2 high to switch the data line to the MPU9250
  digitalWrite(0, HIGH);
  
//  digitalWrite(8, HIGH);
  if(configure == true)
  {
    digitalWrite(8, HIGH);
    // Configure gyroscope range
    I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_2000_DPS);

    // Configure accelerometers range
    I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_16_G);
    
    // Set by pass mode for the magnetometers
    I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);
 
    // Request first magnetometer single measurement
    I2CwriteByte(MAG_ADDRESS,0x0A,0x01);
    configure = false;
    digitalWrite(8, LOW);
  }

   // ____________________________________
  // :::  accelerometer and gyroscope ::: 
 
  // Read accelerometer and gyroscope
  byte Buf[14];
  I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);
 
 
  // Create 16 bits values from 8 bits data
 
  // Accelerometer
  int16_t ax=Buf[0]<<8 | Buf[1];
  int16_t ay=Buf[2]<<8 | Buf[3];
  int16_t az=Buf[4]<<8 | Buf[5];
 
  // Gyroscope
  int16_t gx=Buf[8]<<8 | Buf[9];
  int16_t gy=Buf[10]<<8 | Buf[11];
  int16_t gz=Buf[12]<<8 | Buf[13];

  // _____________________
  // :::  Magnetometer ::: 
 
 
  // Read register Status 1 and wait for the DRDY: Data Ready
  uint8_t ST1;
  do
  {
    I2Cread(MAG_ADDRESS,0x02,1,&ST1);
  }
  while (!(ST1&0x01));
 
  // Read magnetometer data  
  byte Mag[6];  
  I2Cread(MAG_ADDRESS,0x03,6,Mag);
 
  // Request next magnetometer single measurement
  I2CwriteByte(MAG_ADDRESS,0x0A,0x01);
 
 
 
  // Create 16 bits values from 8 bits data
 
//  // Magnetometer
  int16_t mx=Mag[1]<<8 | Mag[0];
  int16_t my=Mag[3]<<8 | Mag[2];
  int16_t mz=Mag[5]<<8 | Mag[4];

  read_ADC(pressure);
  
  //turn pin 13 low to switch data line back to master arduino

  digitalWrite(0, LOW);

    
  for(int i=0; i<sizeof(Buf); i++)
  {
    Wire.write(Buf[i]);
  }
  
  for(int i=0; i<sizeof(Mag); i++)
  {
    Wire.write(Mag[i]);
  }
  
  for(int i=0; i<sizeof(pressure); i++)
  {
    Wire.write(pressure[i]);  
  }
}

The digitalWrite(0, HIGH) is for the switch on the SDA line. When the switch is activated, it switches the SDA line on the slave from the master to the IMU. The clock does not keep running though, which was what I hoped for. I think there is something I am misunderstanding about how the clock works.

Any help would be greatly appreciated.

If they're all on the same I2C bus then eliminate the slave and let the master collect the data directly.

nmd89:
Here's my setup:

I have a master arduino (using a Pro Mini 5V) and it addresses a slave. What I want the slave to do is read four pressure sensors and get IMU data from an MPU9250. I want to be able to have the master talk to the slave and the slave talk to the IMU all on the same I2C bus. The problem comes after the master talks to the slave. The slave then wants to talk to the IMU, but the clock signal (SCL) disappears because it is generated by the master not the slave. I tried to use a switch that would disconnect the master SDA line from the slave SDA line and connect the slave SDA line to the IMU SDA line.

Here's the Master code:

byte Buf[14];

byte Mag[6];
byte pressure[8];

void loop()
{
 //Serial.print("hey");
 Wire.requestFrom(8, 8);
 
 if(!Wire.available())
   {
     Serial.print("wire not available");
   }
 while(Wire.available())
 {
       
   for(int i=0; i<sizeof(Buf); i++)
   {
     Buf[i] = Wire.read();
   }
   
   for(int i=0; i<sizeof(Mag); i++)
   {
     Mag[i] = Wire.read();
   }
   
   for(int i=0; i<sizeof(pressure); i++)
   {
     pressure[i] = Wire.read();
   }
   
 }

Looking at just the first part of the master's loop() has me concerned.

You are requesting 8 bytes from slave 8? and then you are reading 28 bytes?

The while() loop is only ever executed once.

I think you need to describe how your program should work, then translate this description to code.

Does the slave package 28 bytes to send to the master at once? Or do you want to do 3 reads?

#define BUFSIZE 14
#define MAGSIZE 6
#define PRESSURESIZE 8

// I dislike having multiple instances of constants,  I usually mis-type  one and it take forever to find it!

byte Buf[BUFSIZE];
byte Mag[MAGSIZE];
byte pressure[PRESSURESIZE];

#define BLOCKSIZE 28

void loop() 
int i;
int count=Wire.requestFrom(8,BLOCKSIZE); 
if(count == BLOCKSIZE) { // got a good block
  for(i=0;i<BUFSIZE; i++){
    Buf[i]=Wire.read();
    }
  for(i=0;i<MAGSIZE; i++){
    Mag[i]=Wire.read();
    }
  for(i=0;i<PRESSURESIZE; i++){
    pressure[i]=Wire.read();
    }
  }
else { // bad block, skip
  Serial.print("badBlock length=");
  Serial.print(count,DEC);
  }
}

Chuck.

@arctic_eddie:

Eventually I am going to put multiple slaves with four pressure sensors and an IMU connected to it. The IMUs can only be addressed through 1 of 2 bytes. I need the slave devices to get info from the four pressure sensors and an IMU to then send to the master. That's why I can't eliminate the slave.

@Chuck:

I'm sorry I didn't update the code after running a simpler test on it. Yes, I want to get 28 bytes from the slave. I should have put "Wire.requestFrom(8, 28). The master should request 28 bytes from the slave. On request, the slave gets information from the IMU and the four pressure sensors. The slave is having trouble getting information from the IMU because the clock from the master cuts out. I would need the slave to behave as a master after getting a request from the original master. Is there a way to do that?

nmd89:
@Chuck:

I'm sorry I didn't update the code after running a simpler test on it. Yes, I want to get 28 bytes from the slave. I should have put "Wire.requestFrom(8, 28). The master should request 28 bytes from the slave. On request, the slave gets information from the IMU and the four pressure sensors. The slave is having trouble getting information from the IMU because the clock from the master cuts out. I would need the slave to behave as a master after getting a request from the original master. Is there a way to do that?

Your description of the problem does not match with my experience.

I have used 2 UNO's + RTCC + EEPROM + MPC23008(LCD20x4) + MCP23008(4x4 Keypad) all on the same I2C bus.

Both Uno were I2C masters, and Slaves.

There was not much effort, just check the return codes on requestFrom() and endTransmission().

Make sure you do not leave a transaction with a Re-Start, i.e. ( endTransmission(false)); Because this is how the I2C bus can be held between Transactions. i.e. reading from an EEPROM, first you have to Write() the address, then Read() the data back.

Wire.beginTransmission(0x51);
Wire.Write((uint8_t) 0);
Wire.Write((uint8_t) 7);
uint8_t err = Wire.endTransmission(false); // hold the bus so that the other master cannot interfere

if (err==0){ // good set address for read
  err=Wire.requestFrom(0x51,20); // when this cmd executes the bus will be release for the other master.
  if(err==20){// good  read of data
    char buf[20];
    uint8_t i=0;
    while(Wire.available()) buf[i++]=Wire.read();
    Serial.write(buf,i);
    }
  }

One priviso, the Wire.h library has a gotcha in the OnRequest() handling. You are only allowed ONE Wire.write() inside the OnRequest() Handler. Each time you issue a Wire.write() while inside the OnRequest() handler, the Wire.h library reinitialized the outgoing I2C buffer, so, only the Last Wire.write() is actually sent to the requesting Master (if the master requested 10 bytes and you were filling the request one byte at a time, only the last byte plus 9 0xFF would be received by the master).
There has been talk about fixing this 'feature' in a future release of the environment. To work around it, build up a buffer and use the Wire.write(buf[],len); function call instead of the single byte Wire.write(byte); call.

Chuck.

p.s. attached is a modified Wire.h library that includes addition error detection, and timeout support, it includes examples of multiple master code for UNO's that bounce data back and forth.
The major difference is that a Wire.write(7); will actually send 2 bytes, a 0 and a 7 because the compiler promotes constants to ints (2bytes) and I added an overloaded Wire.write(uint16) that actually sends both bytes.
Just use the library manager to install from this zip. After it is installed you will get a warning about duplicated Wire.h:

Multiple libraries were found for "Wire.h"
Used: C:\Users\user\Documents\Arduino\libraries\Wire
Not used: C:\Program Files\Arduino\hardware\arduino\avr\libraries\Wire

To remove my library just delete the whole subdirectory, on my system:
C:\Users\user\Documents\Arduino\libraries\Wire

Wire.zip (16 KB)

You could also connect master to slaves via serial then slaves to devices via I2C. Each slave would have an ID and respond only when addressed. The others would read but ignore the message.