← all lessons
Microcontrollers · #11 of 48

I²C Communication

Addressing, ACK/NAK, Clock Stretching

Why it matters

I²C lets you connect many peripherals (IMUs, OLEDs, environmental sensors) using only two wires.

The idea

The core idea

I²C (Inter-Integrated Circuit) is a two-wire bus protocol that lets you connect many devices to a microcontroller:

Both lines are open-drain and require pull-up resistors (typically 2.2kΩ–10kΩ) to 3.3V.

Why Open-Drain?

Open-drain means devices can only pull the line LOW, never HIGH. The pull-up resistor pulls it HIGH when no device is pulling LOW. This allows multiple devices to share the same bus safely:

Addressing: How Devices Are Selected

Each I²C device has a unique 7-bit address (0x08 to 0x77). The master sends the address first:

Example: Address 0x3C (0b0111100) + Write (0) = byte 0x78 (0b01111000)

Protocol Flow

sequenceDiagram
    participant Master
    participant Slave
    Master->>Slave: START (SDA↓ while SCL high)
    Master->>Slave: Address + R/W (8 bits, MSB first)
    Slave-->>Master: ACK (SDA LOW on 9th clock)
    Master->>Slave: Data Byte 1 (8 bits)
    Slave-->>Master: ACK
    Master->>Slave: Data Byte 2 (8 bits)
    Slave-->>Master: ACK
    Master->>Slave: STOP (SDA↑ while SCL high)

ACK vs NACK

After every 8 data bits, there’s a 9th clock cycle for acknowledgment:

NACK can mean:

Clock Stretching

Sometimes a slave needs more time to process data. It can hold SCL LOW (clock stretching):

This allows slow devices (like EEPROMs) to work on fast buses.

Common I²C Devices

Many sensors and displays use I²C:

ESP‑WROOM‑32 Notes

Speed Limits

I²C has several speed modes:

Bus capacitance limits speed — longer wires = slower max speed. Keep wires short and use proper pull-ups.

Multi-Master

I²C supports multiple masters on the same bus:

Mini-lab

Increase NAK chance and see how a transaction aborts. Add clock stretching and watch SCL LOW extend. Try different addresses and observe how only the addressed device responds.

Demo

Top line is SCL, bottom is SDA. Watch for:

Key takeaways

Going deeper

Troubleshooting I²C Issues

Address Scanning

Scan addresses 0x08–0x77: send START + address + R/W, check for ACK. This identifies all devices on the bus — essential for debugging.

Pull-up Resistor Selection

Reading vs Writing

Math details

I²C frame (write, 7-bit address):
  START
  [A6 A5 A4 A3 A2 A1 A0 W]
  ACK
  [D7 D6 D5 D4 D3 D2 D1 D0]
  ACK
  STOP

Implementation

LLM Prompt: I²C Sensor Reading

Write Rust code for ESP32 I²C communication using esp-hal.
Connect to SHT31 sensor (address 0x44). Implement: write register,
read register, read temperature/humidity. Include error handling
for NACK and timeout. Use GPIO21 (SDA) and GPIO22 (SCL).

Lab Exercise

  1. Connect SHT31: VCC→3.3V, GND→GND, SDA→GPIO21, SCL→GPIO22
  2. Add 10kΩ pull-up resistors on SDA and SCL to 3.3V
  3. Implement I²C address scan — verify sensor at 0x44
  4. Read temperature register — verify reasonable values
  5. Add error handling for disconnected sensor (NACK)

full glossary →