I²C Communication
Shared Wires, Pull-Ups, Addresses, ACKs, and Bus Debugging
The robot hand needs an IMU in the wrist, a real-time clock for logs, an OLED for bench status and maybe an EEPROM for calibration data. If every part needed a separate parallel data bus, the harness would become a mess. I2C is the opposite idea: many small devices share two signal wires, and the microcontroller talks to one device at a time by naming its address.
That convenience is not free. The two wires are analog nodes with capacitance, pull-up resistors, rise times, address collisions and the occasional device that holds the bus low. I2C is simple enough to wire in minutes and subtle enough to lose an afternoon debugging.
I2C stands for Inter-Integrated Circuit. In normal embedded systems it is a short-board or short-cable bus for slow peripherals: sensors, real-time clocks, display controllers, I/O expanders, EEPROMs and power monitors. It is not a magic two-wire network. It is a carefully timed agreement about how shared open-drain lines behave.
By the end, you can
- Explain why I2C uses open-drain SDA and SCL lines with pull-up resistors
- Identify START, STOP, address, R/W bit, data byte and ACK/NACK in an I2C transaction
- Convert a 7-bit device address into the 8-bit address byte seen on the wire
- Explain what NACK, bus stuck low and clock stretching mean during debugging
- Choose practical first checks for pull-ups, logic level, address and bus length
- Distinguish I2C from UART and SPI by the way devices share wires and timing
Two wires, many listeners
An I2C bus has two named signal lines:
- SCL, the serial clock.
- SDA, the serial data line.
Every device on the bus connects to those same two wires. The controller, often called the master in older datasheets, creates transactions. Target devices, often called slaves in older datasheets, listen for their address and respond only when selected.
This is why I2C is popular on small boards. A temperature sensor, RTC, IMU and OLED can share the same two signal traces. Each still needs power and ground, and each still adds capacitance and possible failure modes, but the pin count stays low.
Open-drain is the safety trick
The reason many devices can share SDA and SCL is that they do not fight to drive the line high. I2C outputs are open-drain or open-collector. A device can pull a line low. To send high, it releases the line and lets a pull-up resistor bring it back to Vdd.
That creates wired-AND behavior:
- If every device releases the line, the pull-up makes it HIGH.
- If any device pulls the line down, the shared line is LOW.
- No device should actively drive HIGH into another device that is pulling LOW.
This is not just protocol elegance. It is electrical self-defense. Without open-drain, two devices could drive opposite levels and short a supply through their output transistors. With open-drain, the low state wins safely.
Why can multiple I2C devices share the same SDA wire without fighting each other?
-
Correct. Open-drain devices never actively drive HIGH on the bus. LOW wins, HIGH comes from the pull-up.
-
Different high voltages would be a logic-level problem, not a sharing method.
-
SDA is an electrical node, but the protocol depends on valid digital levels.
-
All devices are physically connected; addressing decides which one participates.
A transaction is address, bits and acknowledgments
An I2C transaction starts with a START condition: SDA falls while SCL is high. It ends with a STOP condition: SDA rises while SCL is high. During data bits, SDA is allowed to change while SCL is low, and it is sampled while SCL is high.
After START, the controller sends an address byte. In the common 7-bit form, the first seven bits are the device address and the final bit says read or write:
For device address 0x3C, a write transaction sends:
A read transaction to the same device sends:
That detail explains a common datasheet confusion. Some datasheets list the 7-bit
address, like 0x3C. Others list the already-shifted write byte, like 0x78. If your
scanner sees 0x3C but sample code says 0x78, both may be describing the same
device from different layers.
After every 8 bits, a ninth clock appears for acknowledgment:
- ACK: the receiver pulls SDA low on the ninth clock.
- NACK: the receiver leaves SDA high.
NACK can mean no device at that address, wrong wiring, wrong voltage, device busy, read complete, or a transaction error. It is not one diagnosis. It is the bus saying, "that byte was not accepted here."
An I2C display has 7-bit address 0x3C. What address byte appears on the wire for a write?
-
Correct. 0x3C shifted left one bit is 0x78, and write uses R/W = 0.
-
The 7-bit address is not the same as the 8-bit byte on the wire.
-
0x79 is the read byte for address 0x3C.
-
0x77 is an address-range endpoint, not this device's encoded byte.
Drive the transaction
The widget below slows the bus down so the waveform is readable. Top trace is SCL, bottom trace is SDA. Watch the START condition, address bits, ACK slot, data byte and STOP condition.
Try these moves:
- Change address and notice the address byte changes but the transaction shape remains.
- Switch read/write and watch the final address bit flip.
- Raise NAK chance past 0.5. The address is not acknowledged and the data byte does not follow.
- Add clock stretching. SCL stays low longer because the target is holding the bus until it is ready.
Clock stretching is one of I2C's clever compromises. A slow device can hold SCL low to pause the controller. The controller must read the actual line state, not merely assume its own clock output went high. Some devices use stretching rarely; some do not support it; some firmware stacks time out if it lasts too long. When debugging, clock stretching is a fact to measure, not a theory to argue about.
The bus is a measurement problem
I2C bugs often look like software bugs until you put a scope or logic analyzer on the lines. A scan returns no addresses. A sensor works alone but fails when the OLED is connected. Reads work at 100 kHz and fail at 400 kHz. A device vanishes only when a motor runs. Each symptom can come from code, but each also has an electrical version.
Use this first-pass debug order:
- Power and ground: verify each module has the correct supply and common ground.
- Voltage level: 5 V pull-ups on a 3.3 V microcontroller can damage pins.
- Pull-ups: confirm there is a pull-up on SDA and SCL, not only weak internals.
- Address: check the 7-bit address and any address-select jumpers.
- Speed: try 100 kHz before assuming a board can handle 400 kHz.
- Waveform: inspect rise time, stuck-low lines, ACK slots and stretching.
- Isolation: remove devices until the bus works, then add them back one at a time.
I2C is not UART and not SPI
I2C, UART and SPI all move bits, but their failure modes are different.
UART has no shared clock line. Both ends agree on a baud rate and timing tolerance. It is good for logs and point-to-point links.
SPI has a clock and separate chip-select lines. It is fast and simple electrically, but each device usually needs its own select signal.
I2C shares clock and data among addressed devices. It saves pins and board space, but rise time, address conflicts and stuck-bus recovery become part of the design.
Design the bus before the code
For a small ESP32 sensor bus, a sane first design looks like this:
- Use 3.3 V devices or proper bidirectional level shifting.
- Put external pull-ups on SDA and SCL. Start around 4.7 kΩ for a short 100 kHz or 400 kHz board bus, then measure.
- Keep wiring short and avoid routing beside noisy motor or switcher nodes.
- Check every device address before buying two modules that conflict.
- Use ADC1/GPIO notes, boot-strapping notes and pin ownership notes before picking pins.
- Add timeouts and stuck-bus recovery in firmware.
- Verify with a scope or analyzer before blaming the sensor library.
A sensor has 7-bit I2C address 0x44. What address byte appears on the wire for a read
transaction?
Show worked solution
Shift the 7-bit address left one bit, then set R/W to 1 for read:
So the wire byte is 0x89. The write byte for the same device would be 0x88.
An I2C address scan finds no devices. Name four checks you should make before changing the sensor driver code.
Show worked solution
Check power, ground, voltage level, SDA/SCL wiring, external pull-ups, the expected 7-bit address, address-select jumpers and bus speed. Then look at the waveform: if SDA or SCL is stuck low, the problem is not the register-reading code.
A sensor reads correctly at 100 kHz but often NACKs or returns bad data at 400 kHz. What electrical cause should you suspect first, and what would you measure?
Show worked solution
Suspect slow rising edges from too-weak pull-ups, too much bus capacitance or long wiring. Measure SDA and SCL rise time on a scope. If the released line has not reached a valid high level before the next sampling point, reduce speed, lower the pull-up resistance within sink-current limits, shorten the bus or reduce capacitance.
Key takeaways
- I2C uses shared SDA and SCL lines. Devices pull LOW or release; pull-up resistors create HIGH.
- START is SDA falling while SCL is high. STOP is SDA rising while SCL is high.
- A 7-bit address becomes an 8-bit wire byte by shifting left and adding the R/W bit.
- ACK means the receiver pulled SDA LOW on the ninth clock. NACK is a symptom, not a complete diagnosis.
- Pull-ups, capacitance, logic level, address conflicts and stuck-low devices are first-class parts of I2C design.
I2C feels friendly because it reduces a bus to two wires. The better mental model is a shared analog conversation with digital rules. Once you can see the pull-ups, the address byte, the ACK slot and the rising edge, I2C stops being a library call and becomes a circuit you can debug.