Multiple I2C sensors with the same address

I am using 2 MAG3110 magnetometers for a project with the Arduino Due board. I can’t figure out a way to utilize the MAG3110 library with more than 1 sensor, so I am using the Wire library instead. I am aware that SDA/SCL and SDA1/SCL1 are Wire and Wire1, respectively. I cannot, however, figure out how to call each one to print the x, y, and z ouputs. It seems to always refer to the defaut SDA/SCL. This is the code I’m using, I’m sure it’s entirely wrong so I’d appreciate any help! I basically just edited a code I found to include both Wire and Wire1.

#include <Wire.h>

#define MAG1_ADDR  0x0E //7-bit address for the MAG3110, doesn't change
#define MAG2_ADDR  0x0E

void setup()
{
 Wire.begin();        // join i2c bus (address optional for master)
 Wire1.begin();
 Serial.begin(9600);  // start serial for output
 config();            // turn the MAG3110 on
}

void loop()
{
 print_values();
 delay(2000);
}

void config(void)
{
 Wire.beginTransmission(MAG1_ADDR); // transmit to device 0x0E
 Wire1.beginTransmission(MAG2_ADDR);
 Wire.write(0x11);             // cntrl register2
 Wire1.write(0x11);
 Wire.write(0x80);             // write 0x80, enable auto resets
 Wire1.write(0x80);
 Wire.endTransmission();       // stop transmitting
 Wire1.endTransmission();
 
 delay(15);
 
 Wire.beginTransmission(MAG1_ADDR); // transmit to device 0x0E
 Wire1.beginTransmission(MAG2_ADDR);
 Wire.write(0x10);             // cntrl register1
 Wire1.write(0x10);
 Wire.write(1);                // write 0x01, active mode
 Wire1.write(1); 
 Wire.endTransmission();       // stop transmitting
 Wire1.endTransmission();
}

 float x = Wire.read();
void print_values(void)
{
 Serial.print("x=");
 Serial.print(read_x());
 Serial.print(",");  
 Serial.print("y=");    
 Serial.print(read_y());
 Serial.print(",");       
 Serial.print("z=");    
 Serial.println(read_z());
 Serial.print("x1=");
 Serial.print(read_x());
 Serial.print(",");  
 Serial.print("y1=");    
 Serial.print(read_y());
 Serial.print(",");       
 Serial.print("z1=");    
 Serial.println(read_z());
}

int mag_read_register(int reg)
{
 int reg_val;
 
 Wire.beginTransmission(MAG1_ADDR); // transmit to device 0x0E
 Wire1.beginTransmission(MAG2_ADDR);
 Wire.write(reg);              // x MSB reg
 Wire1.write(reg);
 Wire.endTransmission();       // stop transmitting
 Wire1.endTransmission();
 delayMicroseconds(2); //needs at least 1.3us free time between start and stop
 
 Wire.requestFrom(MAG1_ADDR, 1); // request 1 byte
 Wire1.requestFrom(MAG2_ADDR, 1);
 while(Wire.available())    // slave may write less than requested
 { 
   reg_val = Wire.read(); // read the byte
 }
 
 return reg_val;

  while(Wire1.available())    // slave may write less than requested
 { 
   reg_val = Wire1.read(); // read the byte
 }
 
 return reg_val;
}

int mag_read_value(int msb_reg, int lsb_reg)
{
 int val_low, val_high;  //define the MSB and LSB
 
 val_high = mag_read_register(msb_reg);
 
 delayMicroseconds(2); //needs at least 1.3us free time between start and stop
 
 val_low = mag_read_register(lsb_reg);
 
 int out = (val_low|(val_high << 8)); //concatenate the MSB and LSB
 return out;
}

int read_x(void)
{
 return mag_read_value(0x01, 0x02);
}

int read_y(void)
{
 return mag_read_value(0x03, 0x04);
}

int read_z(void)
{
 return mag_read_value(0x05, 0x06);
}

Edit your post and put code tags around you code!
Provide a link to that library. I guess it’s easy to extend it to provide the I2C port. In your code you read the value but as you just doubled all the calls you cannot return the value the same way. So it’s best to use a class to access the chip and not functions as you do now.

Thanks, I edited it!
The library for the sensor can be found here: GitHub - sparkfun/SparkFun_MAG3110_Breakout_Board_Arduino_Library: Arduino Library for the SparkFun Triple Axis Magnetometer Breakout - MAG3110

It’s quite easy to change that libary. Insert that into the private part of the class definiton in the header file

TwoWire &wire;

and change the constructor to

 MAG3110(TwoWire &useWire = Wire);

Move the “#include <Wire.h>” from the source to the header file.

Then the constructor in the source file reads this way

MAG3110::MAG3110(TwoWire &useWire) : wire(useWire) {
	
	//Just some random initial values
	x_offset = 0;
	y_offset = 0;

	x_scale = 0.0f;
	y_scale = 0.0f;
}

Then replace every occurrence of “Wire” by “wire”.

I didn’t try this myself but I think it should work.

Thank you for the help, I’m trying to follow your instructions but I’m still having problems. I’m attaching the source and header files as well as the code I’m trying to run.
The code will not compile after I edited the source and header files and I am getting this error: “‘MAG3110’ does not name a type”.
Also, I am still unsure how to print the x, y, and z on the SDA1/SCL1 sensor. The code only prints to the default SDA/SCL and not both.

#include <SparkFun_MAG3110.h>


MAG3110 mag = MAG3110(TwoWire &useWire); //Instantiate MAG3110

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

  mag.initialize(); //Initializes the mag sensor
  mag.start();      //Puts the sensor in active mode
}

void loop() {

  int x, y, z;
  //Only read data when it's ready
  if(mag.dataReady()) {
    //Read the data
    mag.readMag(&x, &y, &z);

    Serial.print("X: ");
    Serial.print(x);
    Serial.print(", Y: ");
    Serial.print(y);
    Serial.print(", Z: ");
    Serial.println(z);

    Serial.println("--------");
  }
}

SparkFun_MAG3110.cpp (7.13 KB)

SparkFun_MAG3110.h (4.69 KB)

You made several small errors during the insertion of the code.

In the header file you made a copy error in line 35/36:

#define MAG3110_DR_STATUS		#include <Wire.h>
	0x00

In the source file, the first line of the constructor has a lowercase ‘w’ instead of the uppercase variant:

MAG3110::MAG3110(TwoWire &useWire) : wire(usewire)  {

instead of

MAG3110::MAG3110(TwoWire &useWire) : wire(useWire)  {

In the rest of the source file ‘Wire’ is not replaced by ‘wire’.

In your sketch the instantiation is not done but is just a copy of the header line. Correct is p.e.:

MAG3110 mag = MAG3110(Wire1); //Instantiate MAG3110

Then it compiles at least.

I would try this:

MAG3110 mag = MAG3110(Wire);
MAG3110 mag1 = MAG3110(Wire1);

Thanks for all of your help, the code compiles and seems to work! However, I still am only printing out one sensor output. How do I differentiate the Serial.print for each sensor?

Your mag_read_register function returns after it has read the first sensor. There's no way it can read the second sensor.
You'll have to modify the function so that it returns one or both values in global variables instead of as the value of the function.

Pete

Can you elaborate a little more on that? I’ve been struggling with this for about a week and can’t seem to find a whole lot of information on the Due I2C subject. :confused:

I had to give up on trying to edit the librariy and just went back to the original code that uses only Wire.h. I managed to get it to compile and display the info for both sensors. It seems to be working fine for the SDA1/SCL1 sensor (shockingly), but for the SDA/SCL sensor it only gives me one initial reading and remains constant. I tested the sensor with the code for singular sensor operation and it works, so I know it must be something in my dual code, but I have no idea what. Any suggestions?

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Wire1.begin();
  Serial.begin(9600);  // start serial for output
  config();            // turn the MAG3110 on
}

void loop()
{
  print_values();
  delay(5000);
}

void config(void)
{
   Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
  Wire1.beginTransmission(MAG_ADDR);
  Wire.write(0x11);             // cntrl register2
  Wire1.write(0x11);
  Wire.write(0x80);             // write 0x80, enable auto resets
  Wire1.write(0x80);
  Wire.endTransmission();       // stop transmitting
  Wire1.endTransmission();
  
  delay(15);
  
  Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
  Wire1.beginTransmission(MAG_ADDR);  
  Wire.write(0x10);             // cntrl register1
  Wire.write(0x10);
  Wire.write(1);                // write 0x01, active mode
  Wire.write(1);
  Wire.endTransmission();       // stop transmitting
  Wire.endTransmission();
}

void print_values(void)
{
  Serial.print("x=");
  Serial.print(read_x());
  Serial.print(",");  
  Serial.print("y=");    
  Serial.print(read_y());
  Serial.print(",");       
  Serial.print("z=");    
  Serial.println(read_z());
  Serial.print("x1=");
  Serial.print(read_x1());
  Serial.print(",");  
  Serial.print("y1=");    
  Serial.print(read_y1());
  Serial.print(",");       
  Serial.print("z1=");    
  Serial.println(read_z1());
  delay(1000);
}

int mag_read_register(int reg)
{
  int reg_val;
  
  Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
  Wire.write(reg);              // x MSB reg
  Wire.endTransmission();       // stop transmitting
 
  delayMicroseconds(2); //needs at least 1.3us free time between start and stop
  
  Wire.requestFrom(MAG_ADDR, 1); // request 1 byte
  while(Wire.available())    // slave may write less than requested
  { 
    reg_val = Wire.read(); // read the byte
  }
  
  return reg_val;
}

int mag_read_value(int msb_reg, int lsb_reg)
{
  int val_low, val_high;  //define the MSB and LSB
  
  val_high = mag_read_register(msb_reg);
  
  delayMicroseconds(2); //needs at least 1.3us free time between start and stop
  
  val_low = mag_read_register(lsb_reg);
  
  int out = (val_low|(val_high << 8)); //concatenate the MSB and LSB
  return out;
}

int read_x(void)
{
  return mag_read_value(0x01, 0x02);
}

int read_y(void)
{
  return mag_read_value(0x03, 0x04);
}

int read_z(void)
{
  return mag_read_value(0x05, 0x06);
}
int mag_read_register1(int reg1)
{
  int reg_val1;
  
  Wire1.beginTransmission(MAG_ADDR); // transmit to device 0x0E
  Wire1.write(reg1);              // x MSB reg
  Wire1.endTransmission();       // stop transmitting
 
  delayMicroseconds(2); //needs at least 1.3us free time between start and stop
  
  Wire1.requestFrom(MAG_ADDR, 1); // request 1 byte
  while(Wire1.available())    // slave may write less than requested
  { 
    reg_val1 = Wire1.read(); // read the byte
  }
  
  return reg_val1;
}

int mag_read_value1(int msb_reg1, int lsb_reg1)
{
  int val_low1, val_high1;  //define the MSB and LSB
  
  val_high1 = mag_read_register1(msb_reg1);
  
  delayMicroseconds(2); //needs at least 1.3us free time between start and stop
  
  val_low1 = mag_read_register1(lsb_reg1);
  
  int out1 = (val_low1|(val_high1 << 8)); //concatenate the MSB and LSB
  return out1;
}

int read_x1(void)
{
  return mag_read_value1(0x01, 0x02);
}

int read_y1(void)
{
  return mag_read_value1(0x03, 0x04);
}

int read_z1(void)
{
  return mag_read_value1(0x05, 0x06);
}
  Wire.beginTransmission(MAG_ADDR); // transmit to device 0x0E
  Wire1.beginTransmission(MAG_ADDR); 
  Wire.write(0x10);             // cntrl register1
  Wire.write(0x10);
  Wire.write(1);                // write 0x01, active mode
  Wire.write(1);
  Wire.endTransmission();       // stop transmitting
  Wire.endTransmission();

You really must avoid interspersing Wire. and Wire1 commands together because you end up with errors like the above.
Do an entire transaction with Wire and then do one with Wire1.

Pete