Overview
The TCA9548A is an 8-channel bidirectional I2C switch from Texas Instruments that lets a single microcontroller talk to up to 8 I2C devices that share the same address. If you have ever tried to wire two BME280 sensors to the same bus and watched them collide at 0x76, this is the chip that solves that problem cleanly without bit-banging a second software I2C bus.
The way it works is simple. The master MCU connects to the upstream SDA/SCL pins and addresses the TCA9548A itself at 0x70 (default). You write a single control byte where each bit enables one of the 8 downstream channels (SD0/SC0 through SD7/SC7). Whichever channels you enable get electrically connected to the upstream bus, and the others are isolated. Read your sensor, switch channels, read the next sensor, and so on.
Beyond duplicate-address fanout, the TCA9548A is also handy for mixed-voltage I2C (each child channel is independently level-translated against VCC), for isolating noisy devices from clean sensors, and for building hot-swappable I2C trees. The three address pins A0/A1/A2 let you put up to 8 muxes on the same parent bus, giving you a theoretical 64 child channels from a single MCU. That is a lot of BME280s.
At a Glance
Specifications
| Parameter | Value |
| IC | Texas Instruments TCA9548A |
| Operating Voltage | 1.65V to 5.5V (3.3V and 5V logic friendly) |
| Number of Channels | 8 (SD0/SC0 to SD7/SC7) |
| I2C Address | 0x70 default, A0/A1/A2 set 0x70–0x77 |
| Maximum SCL Frequency | 400 kHz |
| Supply Current (Operating) | ~80 µA typical |
| Standby Current | ~1 µA |
| Channel On-Resistance | ~4 Ω typical |
| Reset Pin | Active-low, internal pull-up |
| Mux-per-Bus Limit | Up to 8 (via A0/A1/A2) |
| Operating Temperature | -40°C to +85°C |
| Package on Module | TSSOP-24 on breakout PCB |
Pinout Diagram
Wiring Guide
Arduino Uno / Nano (5V logic):
- TCA9548A VIN → Arduino 5V
- TCA9548A GND → Arduino GND
- TCA9548A SDA → Arduino A4 (SDA)
- TCA9548A SCL → Arduino A5 (SCL)
- TCA9548A RESET → leave floating or tie to 5V
- A0/A1/A2 → tie to GND for default 0x70
- Wire each child sensor to SDn/SCn (channel 0–7) plus VIN and GND
Most I2C sensor breakouts already have 4.7k pull-ups, so you usually do not need to add more. If you see flaky reads on long wires, add 4.7k pull-ups to 3.3V or 5V on each child channel.
ESP32 DevKit (3.3V logic):
- TCA9548A VIN → ESP32 3.3V
- TCA9548A GND → ESP32 GND
- TCA9548A SDA → GPIO21 (default SDA)
- TCA9548A SCL → GPIO22 (default SCL)
- RESET → floating, or tie to a GPIO for software reset
- A0/A1/A2 → GND for address 0x70
ESP32 has internal pull-ups but they are weak. The TCA9548A breakout usually has them on the master side already.
Raspberry Pi (3.3V logic):
- TCA9548A VIN → Pi 3.3V (pin 1)
- TCA9548A GND → Pi GND (pin 6)
- TCA9548A SDA → Pi GPIO2 / SDA1 (pin 3)
- TCA9548A SCL → Pi GPIO3 / SCL1 (pin 5)
- A0/A1/A2 → GND for 0x70
Enable I2C with sudo raspi-config → Interface Options → I2C. Verify with i2cdetect -y 1 and look for 0x70.
Raspberry Pi Pico (3.3V logic):
- TCA9548A VIN → Pico 3V3(OUT) (pin 36)
- TCA9548A GND → Pico GND
- TCA9548A SDA → GPIO4 (I2C0 SDA, pin 6)
- TCA9548A SCL → GPIO5 (I2C0 SCL, pin 7)
- A0/A1/A2 → GND for 0x70
I2C0 and I2C1 are both available on the Pico, and you can remap to almost any pin. Match these to whatever you use in machine.I2C(...).
Code Examples
Arduino — Channel select helper and scan all 8 channels:
#include <Wire.h>
#define TCA_ADDR 0x70
void tcaSelect(uint8_t ch) {
if (ch > 7) return;
Wire.beginTransmission(TCA_ADDR);
Wire.write(1 << ch);
Wire.endTransmission();
}
void setup() {
Serial.begin(115200);
Wire.begin();
delay(100);
for (uint8_t ch = 0; ch < 8; ch++) {
tcaSelect(ch);
Serial.print("Channel "); Serial.print(ch); Serial.println(":");
for (uint8_t addr = 1; addr < 127; addr++) {
if (addr == TCA_ADDR) continue;
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print(" Found 0x"); Serial.println(addr, HEX);
}
}
}
}
void loop() {}
ESP32 — Two BME280 sensors at the same address on different channels:
#include <Wire.h>
#include <Adafruit_BME280.h>
#define TCA_ADDR 0x70
Adafruit_BME280 bme;
void tcaSelect(uint8_t ch) {
Wire.beginTransmission(TCA_ADDR);
Wire.write(1 << ch);
Wire.endTransmission();
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
tcaSelect(0);
if (!bme.begin(0x76)) Serial.println("BME on ch0 not found");
tcaSelect(1);
if (!bme.begin(0x76)) Serial.println("BME on ch1 not found");
}
void loop() {
tcaSelect(0);
Serial.printf("Ch0: %.2f C\n", bme.readTemperature());
tcaSelect(1);
Serial.printf("Ch1: %.2f C\n", bme.readTemperature());
delay(1000);
}
Raspberry Pi (Python, smbus2):
from smbus2 import SMBus
import time
TCA_ADDR = 0x70
BUS = 1
def tca_select(bus, channel):
if channel > 7: return
bus.write_byte(TCA_ADDR, 1 << channel)
with SMBus(BUS) as bus:
for ch in range(8):
tca_select(bus, ch)
time.sleep(0.05)
print(f"Channel {ch}:")
for addr in range(0x03, 0x78):
if addr == TCA_ADDR:
continue
try:
bus.read_byte(addr)
print(f" Found 0x{addr:02X}")
except OSError:
pass
Raspberry Pi Pico (MicroPython):
from machine import Pin, I2C
import time
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
TCA_ADDR = 0x70
def tca_select(channel):
if 0 <= channel <= 7:
i2c.writeto(TCA_ADDR, bytes([1 << channel]))
for ch in range(8):
tca_select(ch)
time.sleep_ms(50)
devs = i2c.scan()
print(f"Channel {ch}: {[hex(d) for d in devs if d != TCA_ADDR]}")
Frequently Asked Questions
1 << channel to 0x70 first, then scan again to see the device on that channel.0b00000011 you enable channel 0 and 1 simultaneously. But if both channels host the same I2C address (which is usually why you bought this), you will get bus contention. Enable one channel at a time for duplicate-address devices.