I2C is a great interface because it only requires two wires and can talk to a huge number of different sensors, memory chips, and other components. But since its address space is limited, it is possible to end up with more than one component on the same bus with the same address. Here are three ways of dealing with that.
Perhaps the most obvious is to use software I2C on separate pins via a library like SoftwareWire. This works because I2C doesn’t care about the stability of the clock or its precise frequency, as long as clock and data are in sync.
The downside is that you need two additional pins on your microcontroller, plus additional wiring to that particular sensor. Also, if you’re using a library on top of I2C to talk to your sensor (like the ones Adafruit provides), it probably assumes that you want to use the default I2C port so you’d have to modify that code.
Dual Analog Switch
If you want to stick with hardware I2C on the micro controller and only have one bus, you can use a switch to split it into two. The MAX4733 analog switch can be used to switch the SDA line between the devices. This is apparently one of the more common use cases, since they advertise that on the product page.
This does require switching to the correct line whenever the device in question is to be used, but any libraries would stay unmodified. This switch would have to sit right by the micro controller since it has to switch between the two buses, which might make wiring more challenging.
I2C Address Translator
The most interesting, and most specific, solution is to use the Analog Devices LTC4316 address translator to translate the address that’s transmitted to the device on the fly. This is actually quite interesting, because it has to understand the protocol to know when to flip bits and which ones. It can be configured to flip one or both of the least-significant bits. This means that all devices downstream from the translator appear under a different address to the micro controller, but nothing else needs to be done.
The upshot is that it doesn’t require any code changes other than the address you’re using to talk to the component. This translator also sits along the bus, so it doesn’t require additional wiring – the two parts of the bus are the one before and the one after the translator.