You just found the perfect I2C sensor, available in a handy chainable Qwiic, or STEMMA QT package, and you want to wire up two or three or four of them to your microcontroller when you realize "Uh oh, this chip has a fixed I2C address, and from what I know about I2C, you cannot have two devices with the same address on the same SDA/SCL pins!" Are you out of luck? You would be, if you didn't have this ultra-cool Adafruit PCA9546 4 Channel STEMMA QT / Qwiic I2C Multiplexer!

Finally, a way to get up to 4 same-address I2C devices hooked up to one microcontroller - this multiplexer acts as a gatekeeper, shuttling the commands to the selected I2C port with your command. If you need to have up to 8 multiplexed devices, check out the 8-channel PCA9548 version of this board.

In case you're wondering why this uses the PCA9546 and not the TCA9546A, the PCA9546 is the 'fraternal twin sister' of the TCA9546 but is easier to get during the great chip shortage of 2022-23. It works exactly the same, just can't go down to 1.8V power which is OK because QT boards are 3V or 5V only anyways. You can still use any example code or library for the TCA9546 or TCA9548

Using it is fairly straight-forward: the multiplexer itself is on I2C address 0x70 (but can be adjusted from 0x70 to 0x77 using jumpers on the back) and you simply write a single byte with the desired multiplexed output number to that port, and bam - any future I2C packets will get sent to that port. In theory, you could have 8 of these multiplexers on each of 0x70-0x77 addresses in order to control 4*8 = 32 of the same-I2C-addressed-part.

The Adafruit STEMMA QT / Qwiic PCA9546 Breakout - 4 Channel has five JST SH 1mm connectors: 1 input and 4 outputs. There's one port at the end that connects to your I2C controller (there are also breadboard breakout pins if you need them). Use this breakout to add as many I2C devices to the bus as you need. Complete with mounting holes so the board can be added to any system. A small power LED lets you know that the hub board has connectivity.

There's even an onboard 3.3V 500mA regulator, so if you're using this with a 5V microcontroller like an Arduino 328-compatible, you can level shift all the QT ports to have 3V power and logic level. Simply cut/solder the jumper on the bottom of the PCB to force the power and logic level to be 3V.

Of course, because STEMMA QT is Qwiic compatible, it will work with any and all STEMMA QT or Qwiic boards and parts we have in the Adafruit shop.

Comes with only the assembled PCB, no cables or sensors included (we have tons available though!)

The default I2C address is 0x70.

Power Pins

  • V+ - this is the power pin. Since the multiplexer chip uses 3-5 VDC, to power the board, give it the same power as the logic level of your microcontroller - e.g. for a 5V micro like Arduino, use 5V.
  • - - common ground for power and logic.

I2C Logic Pins - Control

  • SCL - I2C clock pin, connect to your microcontroller I2C clock line. This pin is level shifted so you can use 3-5V logic, and there's a 10K pullup on this pin. This connection is shared with the STEMMA QT port on the opposite side of the board.
  • SDA - I2C data pin, connect to your microcontroller I2C data line. This pin is level shifted so you can use 3-5V logic, and there's a 10K pullup on this pin. This connection is shared with the STEMMA QT port on the opposite side of the board.
  • STEMMA QT - These connectors allow you to connect to dev boards with STEMMA QT connectors or to other things with various associated accessories. There's one port at the end that connects to your microcontroller. The other four connectors in two rows of two are discussed below.

I2C Logic Pins - Multiplexed

  • STEMMA QT Ports 0 to 3 - There are four JST SH 1mm connectors in two rows of two, all with the power, ground, and SDA/SCL pins connected. There are 4 sets of SDA and SCL pins, from SD0/SC0 to SD3/SC3. These are the multiplexed pins. Each one is a completely separate I2C bus set. You can have 4 I2C devices with identical addresses, as long as they are on one I2C bus each. The power input for the connectors is the output from the Vout pin, and is either V+ or 3.3V 500mA.
These ports do not have any pullups installed, so if you are using a chip or breakout without I2C pullups, be sure to add them!

Address Pins

On the back of the board are three address jumpers, labeled A0, A1, and A2, below the board description text on the silkscreen. These jumpers allow you to chain up to 8 of these boards on the same pair of I2C clock and data pins. To do so, you solder the jumpers "closed" by connecting the two pads.

The default I2C address is 0x70. The other address options can be calculated by adding the A0/A1/A2 to the base of 0x70.

A0 sets the lowest bit with a value of 1, A1 sets the next bit with a value of 2 and A2 sets the next bit with a value of 4. The final address is 0x70 + A2 + A1 + A0 which would be 0x77.


So for example if A2 is soldered closed and A0 is soldered closed, the address is 0x70 + 4 + 1 = 0x75.


If only A0 is soldered closed, the address is 0x70 + 1 = 0x71


If only A1 is soldered closed, the address is 0x70 + 2 = 0x72


If only A2 is soldered closed, the address is 0x70 + 4 = 0x74

The table below shows all possible addresses, and whether the pin(s) should be high (closed) or low (open).

Reset Pin

  • RST - Reset pin for resetting the multiplexer chip. Pulled high by default, connect to ground to reset.

Vout Jumpers

These jumpers select between supplying the 4 STEMMA QT ports with either the incoming voltage on V+ (3-5 VDC) or the output from the 3V regulator at 3.3V 500mA (labeled 3V). For example, if you're using this with a 5V microcontroller like an Arduino 328-compatible, you can level shift all the QT ports to have 3V power and logic level by cutting/soldering these jumpers.

  • V+ jumper (default) - This jumper is located on the back of the board, to the right of the address jumpers. It supplies the incoming voltage on V+ (3-5 VDC) to the attached multiplexed devices. Cut the trace to this jumper to disconnect V+ from the multiplexed devices and set the output voltage to 3.3V 500mA.
  • 3V jumper - This jumper is located on the back of the board, to the right of the address jumpers. Solder this pad to the center Vout pad to set the output logic level to 3.3V 500mA from the onboard 3V regulator.

Power LED

  • Power LED - In the upper left corner, above the STEMMA connector, on the front of the board, is the power LED, labeled on. It is the green LED.

It's easy to use the PCA9546 with Python or CircuitPython, and the Adafruit_CircuitPython_TCA9548A module. This module allows you to easily write Python code that allows you to multiplex up to 4 STEMMA boards with the PCA9546 I2C multiplexer. You can use this multiplexer with any CircuitPython microcontroller board or with a computer that has GPIO and Python thanks to Adafruit_Blinka, our CircuitPython-for-Python compatibility library.

If you're curious why you'd need an I2C multiplexer, be sure to check out this guide that goes in depth on working with multiple copies of the same I2C device, which most likely have the same I2C address.

Why the Adafruit_CircuitPython_TCA9548A Module?

The PCA9546 is the four output version of the PCA9548 and the PCA9548 is the 'fraternal twin sister' of the TCA9548. The PCA954x chips are easier to get during the great chip shortage of 2022-23. They work exactly the same, they just can't go down to 1.8V power which is OK because QT boards are 3V or 5V only anyways. You can still use any example code or library for the TCA9546, but a PCA9546A class has been added to the library that only allows for reading up to four attached devices.

CircuitPython Microcontroller Wiring

First, wire up a PCA9546 to your board exactly as shown below. Here's an example of wiring a Feather RP2040 to the PCA9546 with I2C using one of the handy STEMMA QT connectors. Then, plug two TSL2591 STEMMA boards into the PCA9548 via STEMMA plug 0 and STEMMA plug 1:

  • Feather 3V to multiplexer VIN (red wire)
  • Feather GND to multiplexer GND (black wire)
  • Feather SCL to multiplexer SCL (yellow wire)
  • Feather SDA to multiplexer SDA (blue wire)
  • TSL2591 1 to multiplexer STEMMA port 0
  • TSL2591 2 to multiplexer STEMMA port 1

Python Computer Wiring

Since there's dozens of Linux computers/boards you can use, below shows wiring for Raspberry Pi. For other platforms, please visit the guide for CircuitPython on Linux to see whether your platform is supported

Here's the Raspberry Pi wired to the I2C multiplexer using I2C and a STEMMA QT connector:

  • Pi 3V to multiplexer VIN (red wire)
  • Pi GND to multiplexer GND (black wire)
  • Pi SCL to multiplexer SCL (yellow wire)
  • Pi SDA to multiplexer SDA (blue wire)
  • TSL2591 1 to multiplexer STEMMA port 0
  • TSL2591 2 to multiplexer STEMMA port 1

Python Installation of TCA9548A Library

You'll need to install the Adafruit_Blinka library that provides the CircuitPython support in Python. This may also require enabling I2C on your platform and verifying you are running Python 3. Since each platform is a little different, and Linux changes often, please visit the CircuitPython on Linux guide to get your computer ready!

Once that's done, from your command line run the following command:

  • sudo pip3 install adafruit-circuitpython-tca9548a

If your default Python is version 3, you may need to run pip instead. Make sure you aren't trying to use CircuitPython on Python 2.x, it isn't supported!

CircuitPython Usage

To use with CircuitPython, you need to first install the TCA9548A library, and its dependencies, into the lib folder on your CIRCUITPY drive. Then you need to update code.py with the example script.

Thankfully, we can do this in one go. In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, and copy the entire lib folder and the code.py file to your CIRCUITPY drive.

Your CIRCUITPY/lib folder should contain the following folders and file:

  • adafruit_bus_device/
  • adafruit_tca9548a.mpy
  • adafruit_tsl2591.mpy
CIRCUITPY

Python Usage

Once you have the library pip3 installed on your computer, copy or download the following example to your computer, and run the following, replacing code.py with whatever you named the file:

python3 code.py

Simple Test Example Code

# SPDX-FileCopyrightText: 2021 Carter Nelson for Adafruit Industries
# SPDX-License-Identifier: MIT

# This example shows using TCA9548A to perform a simple scan for connected devices
import board
import adafruit_tca9548a

# Create I2C bus as normal
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller

# Create the PCA9546A object and give it the I2C bus
mux = adafruit_tca9548a.PCA9546A(i2c)

for channel in range(4):
    if mux[channel].try_lock():
        print("Channel {}:".format(channel), end="")
        addresses = mux[channel].scan()
        print([hex(address) for address in addresses if address != 0x70])
        mux[channel].unlock()

If running CircuitPython: Once everything is saved to the CIRCUITPY drive, connect to the serial console to see the data printed out!

If running Python: The console output will appear wherever you are running Python.

In this simple test for the PCA9546, an I2C scan is performed for all four of its ports. If any devices are connected, then the I2C address will be printed to the REPL next to the channel number. If no device is connected, then the port will print with empty brackets ([]).

Multi-Sensor Example Code

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

# This example shows using two TSL2491 light sensors attached to PCA9546A channels 0 and 1.
# Use with other I2C sensors would be similar.
import time
import board
import adafruit_tsl2591
import adafruit_tca9548a

# Create I2C bus as normal
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller

# Create the PCA9546A object and give it the I2C bus
mux = adafruit_tca9548a.PCA9546A(i2c)

# For each sensor, create it using the PCA9546A channel instead of the I2C object
tsl1 = adafruit_tsl2591.TSL2591(mux[0])
tsl2 = adafruit_tsl2591.TSL2591(mux[1])

# After initial setup, can just use sensors as normal.
while True:
    print(tsl1.lux, tsl2.lux)
    time.sleep(0.1)

In the multi-sensor example, the PCA9546 is used as an I2C multiplexer with two TSL2591 light sensors. When the connected sensors are instantiated over I2C, the I2C pins declared are the ports from 0 and 1 on the PCA9546.

In the example, the first TSL2591 light sensor, instantiated as tsl1, is plugged into STEMMA QT port 0 (tca[0]) and the second TSL2591 light sensor, instantiated as tsl2, is plugged into STEMMA QT port 1 (tca[1]).

# Create the PCA9546A object and give it the I2C bus
mux = adafruit_tca9548a.PCA9546A(i2c)

# For each sensor, create it using the PCA9546A channel instead of the I2C object
tsl1 = adafruit_tsl2591.TSL2591(mux[0])
tsl2 = adafruit_tsl2591.TSL2591(mux[1])

In the loop, the readings from the two light sensors are printed to the REPL every 0.1 seconds. 

while True:
    print(tsl1.lux, tsl2.lux)
    time.sleep(0.1)

Using the PCA9546 I2C multiplexer with Arduino involves wiring up the I2C multiplexer to your Arduino-compatible microcontroller and running the provided example code.

If you're curious why you'd need an I2C multiplexer, be sure to check out this guide that goes in depth on working with multiple copies of the same I2C device, which most likely have the same I2C address.

Wiring

Wire as shown for a 5V board like an Uno. If you are using a 3V board, like an Adafruit Feather, wire the board's 3V pin to the PCA9546 VIN.

Here is an Adafruit Metro wired up to the PCA9546 using the STEMMA QT connector, along with two VL53L4CD STEMMA boards plugged into port 0 and port 1 on the PCA9546:

  • Metro 5V to multiplexer VIN (red wire)
  • Metro GND to multiplexer GND (black wire)
  • Metro SCL to multiplexer SCL (yellow wire)
  • Metro SDA to multiplexer SDA (blue wire)
  • VL53L4CD 1 to multiplexer STEMMA port 0
  • VL53L4CD 2 to multiplexer STEMMA port 1

Library Installation

The Multi-Sensor example uses two VL53L4CD time of flight sensors. You can install the VL53L4CD library for Arduino using the Library Manager in the Arduino IDE.

Click the Manage Libraries ... menu item, search for VL53L4CD, and select the STM32duino VL53L4CD library:

I2C Scanner Example Code

// SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/**
 * PCA9546 I2CScanner.ino -- I2C bus scanner for Arduino
 *
 * Based on https://playground.arduino.cc/Main/I2cScanner/
 *
 */

#include "Wire.h"

#define PCAADDR 0x70

void pcaselect(uint8_t i) {
  if (i > 3) return;
 
  Wire.beginTransmission(PCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}


// standard Arduino setup()
void setup()
{
    while (!Serial);
    delay(1000);

    Wire.begin();
    
    Serial.begin(115200);
    Serial.println("\nPCAScanner ready!");
    
    for (uint8_t t=0; t<4; t++) {
      pcaselect(t);
      Serial.print("PCA Port #"); Serial.println(t);

      for (uint8_t addr = 0; addr<=127; addr++) {
        if (addr == PCAADDR) continue;

        Wire.beginTransmission(addr);
        if (!Wire.endTransmission()) {
          Serial.print("Found I2C 0x");  Serial.println(addr,HEX);
        }
      }
    }
    Serial.println("\ndone");
}

void loop() 
{
}

Upload the sketch to your board and open up the Serial Monitor (Tools -> Serial Monitor) at 115200 baud. You should see the 4 ports print to the Serial Monitor. If an I2C device is plugged into one of the ports, its address will be printed below the port number.

Multi-Sensor Example Code

// SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/**
 * PCA9546 I2C Multi Sensor Example
 *
 * Using two VL53L4CD sensors on ports 0 and 1
 *
 */

#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cd_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>

#define PCAADDR 0x70
#define DEV_I2C Wire

#define SerialPort Serial

// create two instances of the sensor
VL53L4CD tof1(&DEV_I2C, A1);
VL53L4CD tof2(&DEV_I2C, A1);

void pcaselect(uint8_t i) {
  if (i > 3) return;
 
  Wire.beginTransmission(PCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

// standard Arduino setup()
void setup()
{
    while (!Serial);
    delay(1000);

    Wire.begin();
    
    Serial.begin(115200);
    Serial.println("\nMultiSensor PCA9546");
    
    // define the port on the PCA9546 for the first sensor
    pcaselect(0);
    // setup the first sensor
    tof1.begin();
    tof1.VL53L4CD_Off();
    tof1.InitSensor();
    tof1.VL53L4CD_SetRangeTiming(200, 0);
    tof1.VL53L4CD_StartRanging();
  
    // define the port on the PCA9546 for the 2nd sensor
    pcaselect(1);
    
    // setup the 2nd sensor
    tof2.begin();
    tof2.VL53L4CD_Off();
    tof2.InitSensor();
    tof2.VL53L4CD_SetRangeTiming(200, 0);
    tof2.VL53L4CD_StartRanging();
      
}

void loop() 
{
  uint8_t NewDataReady = 0;
  VL53L4CD_Result_t results;
  uint8_t status;
  char report[64];
  
  // define port on the PCA9546
  pcaselect(0);

  // loop for time of flight sensor 1
  do {
    status = tof1.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);

  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    tof1.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    tof1.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "ToF 1 - Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
             results.range_status,
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.println(report);
  }

  // define port on PCA9546
  pcaselect(1);

  // loop for time of flight sensor 2
  do {
    status = tof2.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);

  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    tof2.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    tof2.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "ToF 2 - Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
             results.range_status,
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.println(report);
  }
}

Upload the sketch to your board and open up the Serial Monitor (Tools -> Serial Monitor) at 115200 baud. You should see readings from the two VL53L4CD time of flight sensors print to the Serial Monitor.

This guide was first published on Jan 13, 2023. It was last updated on Jul 21, 2024.