Skip to main content I2C | IoT Worker

I2C

When a board needs to connect a few low-speed peripherals, the first problem is often not “how do we move more data?” but “how do we use fewer wires and fewer pins while still letting multiple chips work together?” Temperature sensors, EEPROMs, RTCs, and power-management chips are not high-throughput devices, but they all need to be accessed by the host. If every extra chip needs its own dedicated wire set, cost, routing, and packaging pressure rise quickly.

I2C solves exactly that kind of problem: multiple devices share one low-speed control bus, and the host does not need a dedicated data channel for each peripheral. It optimizes first for pin cost, wiring simplicity, and coexistence among multiple devices, not for bandwidth.

The most useful first mental model is simple: I2C is a way to compress “who is talking, who it is talking to, whether the other side received it, and whether multiple masters will collide” into one shared timing model on two wires. Start bits, address bits, ACK, arbitration, and clock stretching are all there to close those loops.

How Two Wires Can Still Carry Many Devices

I2C has only two signal lines:

  • SCL: the clock line
  • SDA: the data line

Unlike SPI, it does not give each slave its own chip-select line. Instead, all devices are wired directly onto the same SCL and SDA pair. That only works because the electrical rules are different.

I2C uses open-drain outputs. A line’s high level is not actively driven by any device; it is pulled up by a resistor. A device can only do two things:

  • pull the line low
  • release it so the line returns high

That matters because it makes shared-bus conflicts manageable. If two devices act at the same time, the bus simply stays low when any one of them pulls low. You do not get a dangerous push-pull fight where one side forces high and another forces low.

The tradeoff is direct:

  • rise time depends on pull-up resistors and bus capacitance, so the speed cannot go too high
  • line length, loading, and capacitance all affect waveform quality directly
  • it is better for board-level, short-distance, low-speed control than for long-distance communication

So the first constraint in I2C is not a protocol field. It is the reality that this is meant to be a cheap, shared, low-speed, short-range bus.

How a Transfer Starts and Ends

One thing shared buses fear most is ambiguity about whether a bit sequence belongs to a new transaction. I2C uses START and STOP to define that boundary.

  • START: when SCL is high, SDA goes from high to low
  • STOP: when SCL is high, SDA goes from low to high

Those conditions are not ordinary data bits. During normal data transfer, SDA should change only while SCL is low. That way, every device on the bus can recognize them as “transaction start” and “transaction end.”

The main I2C flow is therefore:

  1. The master sends START
  2. The master sends the device address and the read/write bit
  3. The selected slave acknowledges
  4. The two sides continue transferring one or more bytes, with an ACK after each byte
  5. The master sends STOP at the right time

If the master does not want to release the bus and wants to move immediately to the next phase, it can send a Repeated START. That is common when you first write a register address and then immediately read data, because it avoids opening a gap in which another master might cut in.

Addresses Are Not for Classification, They Are for Calling Out a Device on a Shared Wire

Without a dedicated chip-select wire, I2C has to answer “who are we talking to this round?” using an address.

The most common form is a 7-bit address plus a 1-bit read/write direction bit:

  • 0 means the master is going to write
  • 1 means the master is going to read

Many descriptions make I2C sound like an “address protocol,” but the address itself is not the point. The point is that it replaces a dedicated wire with a name on the shared bus. The master broadcasts an address on the bus, every slave listens, and only the one that matches continues with the transaction.

The benefits are clear:

  • many devices share just two wires
  • when you add a new peripheral, you do not need another chip-select pin
  • the software access model is usually uniform: address + register/data

The limits are also clear:

  • the address space is limited, so collisions appear when too many devices are used
  • many chips expose only a few configurable address bits, so board-level expansion space is limited
  • the address only names the target; it does not discover topology, authenticate devices, or solve higher-layer compatibility

Address conflict is a very common field problem. If two devices on the bus use the same default address, the protocol will not coordinate for you. Typical solutions are to use a variant with configurable addresses, change hardware straps, or add an I2C mux.

What ACK Is Solving

Another core problem on a shared bus is how the master knows the other side really accepted a byte instead of just leaving the line alive.

I2C answers that by adding a 9th clock after every 8 data bits:

  • the receiver pulls SDA low to say ACK
  • the receiver leaves it high to say NACK

That small mechanism carries several different meanings.

The first layer is device presence. After the master sends an address, if no device pulls SDA low during the acknowledge bit, that usually means no one is listening at that address, or the target device is not ready to accept this transfer.

The second layer is byte-level flow control. During a write, the slave can tell the master whether a byte was accepted. During a read, the master can also send NACK after the last byte to say “I do not want any more data now,” and then end the transfer.

The third layer is minimal error exposure. Unlike heavier protocols, I2C does not have a complex built-in retransmission or sequence negotiation model. It keeps confirmation at the per-byte ACK level. That keeps the implementation simple, but it leaves more recovery responsibility to the higher-level driver and device protocol.

So ACK is useful, but do not overread it. It usually only says:

  • someone responded on the bus at that moment
  • the byte was accepted at the link layer

It does not guarantee:

  • the device has already finished the internal action
  • a write has reached nonvolatile storage
  • the returned data is valid in business semantics

For example, EEPROM writes often have a write cycle after the data is sent. During that time the device may NACK subsequent access until the internal write completes. That NACK does not mean the link is broken. It means the device is still busy.

Why I2C Can Support Multiple Masters

Once the bus is shared, the harder question inevitably comes up: if more than one master exists, what happens when they try to start at the same time?

I2C does not solve that by pre-booking the bus. It uses the physical behavior of open-drain lines for arbitration. The rule is: while sending, the master also reads the bus level back continuously.

If a master tries to send high but reads back low, that means another device pulled the line low. Since low dominates on the wire, that master immediately knows it lost arbitration and must withdraw from the transaction.

That has several important implications:

  • arbitration happens during the transfer, not in a separate negotiation phase
  • the winning side can continue sending the current message
  • the losing side does not turn the bus into an unrecoverable electrical conflict

The cost is that multi-master support, while part of the spec, is not common in many real systems. That is not because the mechanism is fake; it is because the system complexity rises:

  • software must handle retries after arbitration loss
  • timing analysis and failure reproduction become harder
  • in most board-level systems, a single MCU master is already enough

So “the protocol supports multi-master” and “multi-master is commonly deployed” are not the same thing. Many driver stacks are built around a single-master mental model by default.

Why the Clock Can Also Be Held Back by a Slave

At first glance, SCL sounds like it should be entirely under the master’s control. The ideal flow is not that simple, because a slave may not always be ready to keep up.

I2C allows clock stretching: a slave can hold SCL low when it needs more processing time, and the master must wait.

That solves the problem of “low-cost, slow peripherals need to keep up with one common bus rhythm.” Without it, the master would have to use extremely conservative timing, or every device would need its own separate waiting scheme.

But clock stretching also brings real cost:

  • not every controller fully supports it, especially in some SoCs or bridges
  • it can be confused with a dead bus or a fault during debugging
  • the higher layer needs timeouts, or one misbehaving slave can freeze the whole bus

That is a very typical piece of I2C field experience: a flexibility mechanism that seems to simply “wait a bit” is also one of the easiest places to leave a hidden bug.

What a Typical Register Read Flow Looks Like, and Why It Often Uses a Repeated Start

Many sensors, RTCs, and PMICs look like they are “reading a register,” but the actual wire-level flow is usually:

  1. START
  2. Send the slave address with write direction
  3. Send the register address or internal offset
  4. Repeated START
  5. Send the slave address again, this time with read direction
  6. Read the data bytes continuously
  7. After the last byte, the master sends NACK
  8. STOP

That is a very important pattern to remember because it combines several I2C ideas at once:

  • the address names the target
  • the write phase tells the slave which register to look at
  • the repeated start keeps the transaction continuous without releasing the bus
  • the read phase lets the slave drive data onto SDA
  • the master uses the final NACK to end the read

When you inspect a capture, if you cannot reconstruct even this basic flow yet, do not jump straight to the register values. First check whether the address is wrong, the direction bit is wrong, the repeated start is missing, the ACK behavior is wrong, or the ending is wrong.

What I2C Is Most Easily Mistaken For

I2C is often mistaken for “SPI with two wires,” or for “a simpler serial bus.” Both readings skew later engineering judgment.

It is not like SPI because:

  • there is no dedicated chip select for each slave
  • the read/write direction and target device are embedded in the timing
  • the bus uses wired-AND sharing, not push-pull exclusivity

It is not like UART because:

  • it is a shared bus, not a point-to-point link
  • it cannot run by itself with just a loose byte stream; it depends on start, stop, ACK, and addressing semantics
  • the electrical layer and protocol layer are designed together, not as a pure byte pipe

The more accurate view is: I2C is a shared control bus for board-level peripheral interconnect. It accepts limited speed, capacitance sensitivity, and weaker error recovery in exchange for fewer wires and lower cost.

What To Check First in Engineering

When implementing, capturing, or debugging I2C, the most valuable thing is usually not “copy every bit.” It is to check a few high-information points first.

First, see whether the bus is actually idle and whether both SCL and SDA can return high. If one line stays low for a long time, check pull-ups, wiring, device lock-up, or controller state first.

Second, check whether the address phase gets an ACK. Many problems can be separated right there:

  • no ACK: check the address, power, wiring, pull-ups, and device busy state first
  • ACK is present but data later looks wrong: move on to register semantics or the driver state machine

Third, when a register read fails, check whether the repeated start is missing or the direction bit is wrong. Many “the device does nothing” bugs are really transaction-shaping bugs.

Fourth, when there are occasional timeouts or the whole bus gets stuck, do not look only at software logic. Bring clock stretching, pull-up resistors, bus capacitance, line length, and total shared load into the same analysis.

Fifth, do not merge protocol guarantees with device behavior. ACK only means link-layer response. It does not replace the busy status, conversion time, or register constraints in the device datasheet.

When It Fits and When It Does Not

I2C is a good fit for:

  • short board-level connections
  • multiple low-speed sensors or configuration chips sharing one bus
  • situations where pin count and routing are more important than throughput

I2C is not a good fit for:

  • long-distance communication
  • large data movement
  • buses with heavy capacitance and many devices when you still need high-frequency stability
  • scenarios with stronger real-time and error-recovery requirements

So choosing I2C is not about it being “simple.” It is about being a good tradeoff for a class of problems: lots of low-speed peripherals, limited resources, short distance, and a desire to keep access shared with minimal wiring.

Further Reading