I2C (Inter-Integrated Circuit) is a synchronous communication bus running on two wires: SDA (the data) and SCL (the clock), shared by every device on the bus. One device acts as the controller (historically called the master) and paces the exchanges. Each peripheral device answers to a unique address, which is what lets you hang several of them off the same two wires.
How does it work?
Both lines work in open collector or open drain: no device ever drives the line high, each one can only pull it low. A pull-up resistor (typically ≈ 4.7 kΩ) brings the line back high when idle. This is what makes I2C unusual where logic levels are concerned: the line voltage is set by the pull-up, so a 3.3 V device and a 5 V device can sometimes share the same bus depending on the pull-up voltage chosen. That pull-up doubles as the line conditioning. There is no other termination to plan for.
Throughput is moderate and range is short, on the scale of a single board. In return, addressing lets you line up a good many sensors, displays or real-time clocks on just two wires. How many, at most? Seven-bit addressing offers a good hundred addresses in theory. In practice, it is the electrical capacitance of the bus that caps things well before that. Beyond a dozen devices, or a few tens of centimetres of wire, the edges degrade and the bus turns unstable.
On the wire
The controller opens with a START condition (SDA falls while SCL is high), sends the 7-bit address followed by the read/write bit, and the targeted peripheral answers with an acknowledgement bit (ACK). That is how you know it is actually there. Data bytes follow the same pattern (8 bits plus an ACK), through to the STOP condition. The SCL clock, for its part, is held by the controller from beginning to end. Putting it to work is covered in I2C on Arduino for Arduino, I2C on the ESP32 for the ESP32 and I2C in MicroPython for MicroPython.
Pitfalls
Forgetting the pull-up resistors. Without them the lines never come back high and the bus stays silent. Plenty of modules carry their own, but not all of them.
Address collision. Two devices sharing one address on the bus make each other unreachable. The address is worth checking on the datasheet, and some devices expose a few pins to change it.
See also
- Communication bus — hub: where I2C sits next to UART and SPI
- I2C on Arduino · I2C on the ESP32 · I2C in MicroPython — putting it to work, family by family
- SPI — a faster synchronous alternative, at the cost of one select wire per device
- UART — an asynchronous, point-to-point alternative
- Logic levels — the open-drain special case and pull-ups on the I2C bus