I2C Multiplexers for Makers: TCA9548A Wiring and Reading Multiple Identical-Address Sensors
The I2C wiring guide on this site covers the bus fundamentals, but it doesn't solve one of the most common problems makers actually run into: what happens when you want four identical distance sensors on a robot, or three BME280s in different rooms, and every single one shares the same fixed I2C address? You can't just wire them all to the same bus — the first one to respond is the only one you'll ever talk to. The TCA9548A I2C multiplexer is the standard fix, and this guide covers wiring it, addressing it, and the gotchas that trip people up the first time.
The Problem It Solves
Many I2C sensors have a fixed address burned into silicon with no way to change it (or, at best, one or two address-select pins giving you two or four possible addresses). The VL53L0X time-of-flight sensor is a classic example on Adafruit/generic breakouts — most inexpensive breakout boards default to the same 0x29 address with no easy hardware address change. Put two on the same bus and only one will ever answer. Your options are: separate physical I2C buses (the ESP32 has two hardware I2C peripherals, and you can bit-bang more in software, but this doesn't scale well past 2-3 buses), sensors with hardware address-select pins wired to different levels (works for some parts, not others), or a multiplexer that switches which downstream bus segment is connected to the host at any given moment. For more than two or three identical sensors, the multiplexer is almost always the cleanest answer.
How the TCA9548A Works
The TCA9548A is itself an I2C device (default address range 0x70-0x77, selected by three address pins A0-A2) that acts as an 8-way switch for the I2C bus. You talk to the mux first, over the main bus, telling it which of its eight downstream channels to connect. Only that one channel is electrically joined to the main SDA/SCL lines at any moment — the others are isolated. You then talk to whatever device sits on that channel exactly as if it were the only device on the bus, because as far as it and the host controller are concerned, it is. Switch channels, and you're now talking to the sensor on the next channel, with all eight downstream channels able to host identical-address devices without conflict.
Wiring
- Main bus: SDA and SCL from your ESP32 or Raspberry Pi go to the mux's SDA/SCL pins, with pull-up resistors (4.7kΩ is a safe default) on this main side if your dev board doesn't already provide them.
- Downstream channels (SD0-SD7, SC0-SC7): Each channel needs its own pull-up resistors too — this is the detail most people miss. The mux electrically isolates each channel, so pull-ups on the main bus don't carry through to the downstream side. Each active channel needs its own 4.7kΩ pull-ups on both its SDA and SCL lines, either on the sensor breakout itself (many already include them) or added separately.
- Address pins (A0-A2): Tie these high or low to set the mux's own I2C address if you need more than one mux on the same main bus (giving you up to 64 downstream devices across 8 muxes × 8 channels). For a single mux, tying all three low (address 0x70) is the simplest default.
- Reset pin: Pull this high for normal operation; it's active-low and can be tied to a GPIO if you want the ability to reset a hung mux state in firmware without power-cycling the whole board.
Software Pattern
The core sequence in firmware is always: write a single byte to the mux's address with only one bit set (selecting the channel), then perform your normal I2C transaction with the target sensor's address. Libraries like Adafruit's TCA9548A driver wrap this into a simple tcaSelect(channel) call you make before every read from a given sensor. A typical polling loop looks like: select channel 0, read sensor, select channel 1, read sensor, and so on — the mux adds a small amount of latency per switch (a single I2C write, generally well under a millisecond) but this is negligible for sensor polling at typical rates.
Bus Capacitance and Speed
Because each channel is isolated when not selected, you don't pay a capacitance penalty for having many devices wired up simultaneously the way you would on one giant shared bus — only the currently selected channel's capacitance matters. This is one of the mux's underrated benefits: it lets you run a longer aggregate wiring harness across many sensor locations (useful for a robot with sensors on multiple arms, or a multi-zone environmental monitor) without the bus speed and reliability problems that come from a single very long, heavily loaded I2C bus.
Common Mistakes
SymptomCauseFix All sensors return the same (or stale) dataForgot to call the channel-select function before each read, or selected the wrong channelAlways explicitly select the channel immediately before each transaction — don't assume the mux "remembers" a previous selection across other bus activity One channel works, others don't respondMissing pull-up resistors on that specific downstream channelVerify pull-ups exist on both SDA and SCL for every channel you use, not just channel 0 Mux itself doesn't respond at its addressAddress pins floating instead of tied firmly high or low, or address conflict with another deviceExplicitly tie A0-A2 with pull-up or pull-down resistors rather than leaving them floating Intermittent bus lockupsA downstream sensor hangs the bus (holding SDA low) while its channel is selectedAdd a bus-recovery routine, or use the mux's reset pin to force all channels back to a known de-selected stateWhen You Don't Need a Multiplexer
If you only need two of the same sensor and the part has an address-select pin (many BME280, BNO055, and similar breakouts do), just set one high and one low and skip the mux entirely — it's fewer parts and one less thing to debug. Reserve the multiplexer for cases with three or more identical-address devices, or where you specifically want physical bus isolation between sensor groups regardless of address availability.
Once wired correctly, a TCA9548A is close to invisible in normal operation — the extra channel-select call is a minor firmware addition for the ability to run a nearly unlimited number of otherwise address-conflicting I2C sensors off a single microcontroller.