Two wires, any number of devices, no clock recovery and no flow control beyond one hack. SDA and SCL are both open-drain: a device can pull the line low but never drive it high. The rising edge is done by a pull-up resistor, and almost every I²C problem is really a pull-up problem.

Bus topology

Every device hangs off the same two nets. Addresses are 7 bits, so the ceiling is 127 devices — but you will hit the capacitance limit long before the address limit.

Sizing the pull-ups

The spec caps total bus capacitance at 400 pF. The pull-up has to charge that within the rise-time budget:

and it must not sink more than the 3 mA a device is required to tolerate:

At 3.3 V and 100 kHz () with 200 pF of bus, that is roughly 1.5 kΩ to 5.9 kΩ. The 4.7 kΩ everyone reaches for lands inside the window, which is why it usually works and why it stops working the moment you add the fourth breakout board and a metre of ribbon cable.

Clock stretching is where the afternoon goes

A peripheral is allowed to hold SCL low to say “not ready”. Many MCU I²C peripherals implement this incorrectly — or a bit-banged driver forgets it entirely — and the controller sails on, sampling garbage. It surfaces as a sensor that reads fine at 100 kHz and returns nonsense at 400 kHz. Before blaming the sensor, scope SCL and look for a low period longer than the controller intended.

Reading a register

The near-universal pattern is a write of the register address, a repeated start rather than a stop, then a read:

sequenceDiagram
    autonumber
    participant C as Controller
    participant P as Peripheral 0x48
    C->>P: START + 0x48 + W
    P-->>C: ACK
    C->>P: register address
    P-->>C: ACK
    C->>P: REPEATED START + 0x48 + R
    P-->>C: ACK
    P->>C: data byte
    C-->>P: NACK (last byte)
    C->>P: STOP
int i2c_read_reg(int fd, uint8_t addr, uint8_t reg, uint8_t *buf, size_t n)
{
    struct i2c_msg msgs[2] = {
        { .addr = addr, .flags = 0,        .len = 1, .buf = &reg },
        { .addr = addr, .flags = I2C_M_RD, .len = n, .buf = buf  },
    };
    struct i2c_rdwr_ioctl_data xfer = { .msgs = msgs, .nmsgs = 2 };
 
    return ioctl(fd, I2C_RDWR, &xfer) < 0 ? -errno : 0;
}

The repeated start matters. Issue a stop between the two halves and another controller — or an interrupt handler using the same bus — can slip a transaction in and move the peripheral’s internal address pointer out from under you.

Recovering a wedged bus

If a peripheral was reset mid-byte it can sit holding SDA low forever. There is no reset line, so the standard escape is to bit-bang up to nine clock pulses on SCL until the peripheral finishes its byte and releases SDA, then issue a manual stop. Worth having in your init path on any board that gets power-cycled in the field.

See also