Using multiple devices of same address on microcontrollers using multiplexers.

An Arduino has only a single I2C pin pair. As you know, for two devices with similar I2C address to work with a single microcontroller, we need 2 pin pairs. Although a few microcontrollers would maybe give you this freedom by giving you 2 I2C buses, but maybe you want 4? 6? What’d you do in that case? Well I have a fix, and it allows me to read upto 8 devices of same address on a single pair of I2C pins. It uses multiplexers to switch devices.

Here’s how it works, we use a pair of cheap multiplexer ICs (e.g. 4051). The pinout is given below. (Link)

4051_pins

As evident from the diagram on the right, we have to select the channel that is to be connected to the output using S0,S1,S2. the pins 6,7,8 are to be shorted to GND. VCC requires 5V power. I was using only 4 channels to connect 4 MPU6050 boards, I shorted the SCL of all 4 boards to the Y0,Y1,Y2,Y3 of one of the IC, and SDA to corresponding pins of another IC. The Output Z of  ICs was connected to SCL and SDA respectively. Upon selection, the output and the pin aren’t entirely shorted, a resistance of about 350 ohms still remains between them, which is not a concern for I2C purposes.

Finally we connect S0 and S1 to the microcontroller digital pins and keep changing the logic to select the corresponding module.

IMG_20160320_020947

Here in this video I demonstrate my final setup, working for 4 MPU6050. I’m pretty sure it’d work for more of them too.

Here’s the code used for getting the data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
 
#include<Wire.h>
const int MPU = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
double compAngleX, compAngleY,/*gzangle,*/compAnglez, timer;
double accXangle , accYangle, acczangle , gyroXrate , gyroYrate, gyroZrate;
double gyroXAngle, gyroYAngle, gyroZAngle;
// float rgyro,w;
int ap = 0.955;
 
void getraw(const int add)
{
  Wire.beginTransmission(add);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  // Serial.println("HERE");
  Wire.requestFrom(add, 14, true); // request a total of 14 registers
  AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
 
 
 
  accXangle = (atan2(AcY, AcZ) * RAD_TO_DEG);
  accYangle = (atan2(AcX, AcZ) * RAD_TO_DEG);
  acczangle = (atan2(AcX, AcY) * RAD_TO_DEG); /* my attempt to calculate yaw but not correct*/
  gyroXrate = GyX / 16.5;
  gyroYrate = GyY / 16.5;
  gyroZrate = GyZ / 16.5;
  timer = millis();
  //angular position
  gyroXAngle += gyroXrate * (millis() - timer) / 1000;
  gyroYAngle += gyroYrate * (millis() - timer) / 1000;
  gyroZAngle += gyroZrate * (millis() - timer) / 1000; /* my attempt to calculate yaw but not correct*/
  //---------------------------\\
  //COMPLIMENTRY FILTER STARTED\\
  //---------------------------\\
 
  compAngleX = ap * (compAngleX + gyroXAngle) + (1 - ap) * accXangle;
  compAngleY = ap * (compAngleY + gyroYAngle) + (1 - ap) * accYangle;
  compAnglez = ap * (compAnglez + gyroZAngle) + (1 - ap) * acczangle; /*yaw but not correct*/
 
  Serial.print("compAngleX: ");
  Serial.println(compAngleX);
  Serial.print("compAngleY: ");
  Serial.println(compAngleY);
  Serial.print("compAnglez: ");
  Serial.println(compAnglez);
 
}
 
void wakeupmpu()
{
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);  // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
}
void setup()
{
 
  Serial.begin(115200);
  pinMode(A0, OUTPUT);
  pinMode(A2, OUTPUT);
 
}
int loopnumber;
void loop()
{
  wakeupmpu();
  delay(20);
  digitalWrite(A0, loopnumber % 2);
  digitalWrite(A2, loopnumber / 2);
  Serial.print("#############################");
  Serial.print(loopnumber);
  Serial.println("#################################");
  getraw(MPU);
  loopnumber++;
  if (loopnumber == 4)
  {
    loopnumber = 0;
  }
  delay(100);
}

I hope you find this discussion useful! Keep following my blog!

12 thoughts on “Using multiple devices of same address on microcontrollers using multiplexers.

  1. The explanation aren’t clear. Could you please, put more information about it? Please give me your e-mail

    • Let’s say you have 2 sensors, you connect the SDA pin of both to pin no 13 and 14 of 4051. You then connect the SCL pins to pin no 13, 14 of another 4051. For both 4051, Pin 6,7,8 are grounded and pin 16 is connected to 5v. Then the analog out pin (3) is connected to SDA and SCL for the first and second IC respectively. Now you have to select the pins 13 and 14 using the pin nos 9, 10, 11. After that you have to power the sensors, and in the code set pin no 11,10,9 as low,low,low (000) or low,low,high (001) for selecting y0 (pin 13) or y1 (pin 14) respectively. You can set them as 111 for pin y7 and so on (binary numbers simply). In the code, in the loop, we simply have to set these pins once and then take a reading as you would have for a regular I2C sensor. You may see in my code, after setting the pins using loopnumber%2 and loopnumber/2, I called getraw function each time, which takes the raw reading from the sensor each time it is called. I’m not very sure how the SDP610-025PA works, but looking up a simple code for Arduino should do. In short, set the pins and take the reading, set pins, take another reading and so on.

      I seriously do hope this helps!

  2. Hello Siddharth Jha, very nice project you’ve done there! I didn’t understand very well the connections you made. In the video I can’t see the arduino ports A4 & A5 connected. You don’t use the I2C ports of the arduino? And how do you write the adrres to the 4051? should’t you use the digital pins? I also didn’t see it connected there.

    • Hey David,
      Thanks for commenting. For the Arduino Uno and Mega, there is a separate breakout for I2C pins, namely SDA and SCL on the board, the names of which are generally written below the board, depending upon the make… Look for two pins adjacent to pin no 13, AREF and GND, they should be SDA and SCL. There is no such facility on the Nano, and hence you have to use A4 and A5.
      It was indeed foolish of me to use the A0 and A2 pins as the digital pins for the Arduino, as it might confuse the viewers. But actually it doesn’t do any sort of harm using the analog input pins as digital input/output. Rest assured, using any digital pin would do. I chose A0 and A2 just because I like my jumpers short 🙂
      Hope this helps

    • Hello William,

      You can use any input/output pin on the Arduino for the same. In the code example I have on my blog, I have used pins A0 and A2, but you can frankly use any I/O pin.

      • Can you tell me why you only use 2 pins A0 A2 .because there are 3 pins S0,S1,S2.
        I have done this with 2 Mpu6050 but i got errors :all data is -135
        As i can see in your project, SDA(1) connect y0(4051(1)) ,SDA(2) connect y1 (4051(1)) and Output Z (1) connect to A4(SDA)
        SCL(1) connect y0(4051(1)) ,SCl(2) connect y1 (4051(1)) and Output Z (2) connect to A5(SCL)
        S0, S1,S2 connect to A0, A2
        Is that right ?
        i hope you can help me ,because i have tried some methods but they didn’t work.

        • There are 8 input channels (Y0 to Y7) on the multiplexer IC, which can be represented by 000,001,010…..,111 on pins S2, S1 and S0. I only had 4 I2C devices to connect, so I used only S0 and S1 and set S2 to LOW. If S0 and S1 are HIGH and S2 is LOW pin y3 is selected (1+2+0 = 3). If S0 and S2 are LOW and S1 HIGH pin y2 is selected (0+2+0 = 2).

          So as you have only 000 (Y0) and 001 (Y1) connected, you can short S2 and S1 to ground and change S0 in each loop between high and low. I hope you get this:

          Y0 =000 (S2=LOW, S1=LOW, S0=LOW) and Y1 = 001 (S2=LOW, S1=LOW, S0=HIGH).

          Hope this helps

Leave a Reply to David Sandoval Cancel reply

Your email address will not be published. Required fields are marked *