56  I2C Protocol

Key Concepts
  • I²C (Inter-Integrated Circuit): A two-wire synchronous serial bus using SDA (data) and SCL (clock) lines; supports multiple devices on the same bus with unique 7-bit or 10-bit addresses
  • Master/Slave Architecture: I²C uses a master device (typically a microcontroller) that initiates all transactions and one or more slave devices (sensors, ADCs) that respond
  • Open-Drain Signalling: I²C lines are open-drain; devices can only pull the line low; pull-up resistors (typically 4.7 kΩ) bring the line high when released
  • Address Conflict: Two I²C devices with the same address on the same bus cause communication failures; I²C address is typically fixed in hardware or set by address pins
  • Clock Stretching: A mechanism where a slave device holds SCL low to pause the master, giving the slave more time to prepare data
  • I²C Speed Modes: Standard mode (100 kbps), Fast mode (400 kbps), Fast-mode Plus (1 Mbps), High-speed mode (3.4 Mbps)
  • I²C Multiplexer: A device (e.g., TCA9548A) that allows multiple devices with the same I²C address to coexist on one bus by switching between sub-buses

56.1 In 60 Seconds

I2C (Inter-Integrated Circuit) is a 2-wire synchronous bus protocol using SDA (data) and SCL (clock) with pull-up resistors, supporting up to 112 usable devices on a single bus via 7-bit addressing (112 of the 128 possible addresses are available; 16 are reserved). It runs at 100 kHz (standard), 400 kHz (fast), or 3.4 MHz (high-speed) and is ideal for connecting multiple low-speed sensors and displays to a microcontroller with minimal wiring. Common issues include address conflicts, missing pull-ups, and clock stretching by slow devices.

56.2 Learning Objectives

By the end of this section, you will be able to:

  • Explain I2C Architecture: Describe the two-wire bus structure with SDA and SCL, and justify why open-drain outputs require pull-up resistors
  • Calculate Pull-Up Resistor Values: Apply the RC rise-time formula to select appropriate resistor values for a given bus capacitance and speed mode
  • Distinguish I2C Addressing: Identify how 7-bit addresses map to write and read bytes, and select device addresses to avoid bus conflicts
  • Implement I2C Transactions: Apply the read and write transaction sequences (START, address frame, ACK, data, STOP) in Arduino/ESP32 code
  • Diagnose I2C Failures: Analyze bus-level symptoms — missing ACK, slow rise times, address conflicts, clock stretching — and identify their root causes
  • Build I2C Applications: Construct working code to communicate with sensors (BME280) and displays (SSD1306) over a shared I2C bus

I2C (pronounced eye-squared-see) is a simple way to connect multiple sensors and chips using just two wires. Think of it as a shared phone line where each device has its own phone number – the main controller calls each device by its address and they take turns sharing information. It is one of the most common ways to connect sensors in IoT projects.

“I love I2C!” said Sammy the Sensor. “I only need two tiny wires – SDA for data and SCL for the clock – to talk to Max. And I am not alone on those wires. There can be up to 112 of us sensors sharing the same two wires!”

Max the Microcontroller explained how it works. “I am the master. When I want Sammy’s temperature reading, I send his address – say 0x48 – on the bus. Only Sammy responds. Then I send a request, and he sends back the data. Everyone else stays quiet because it was not their address.”

“The pull-up resistors are super important though,” warned Lila the LED. “The wires need them to work properly. If you forget the pull-ups, nothing talks! It is the number one beginner mistake with I2C. And sometimes two sensors accidentally have the SAME address – then you get conflicts.”

“I2C is not the fastest,” admitted Bella the Battery, “only 100 to 400 kHz for most IoT work. But it is incredibly simple – just two wires for dozens of sensors! Perfect for when you have a temperature sensor, a humidity sensor, a pressure sensor, and a display all connected to one microcontroller. Minimal wiring, maximum flexibility.”

56.3 Prerequisites

Before diving into this chapter, you should be familiar with:

  • Wired Communication Fundamentals: Understanding of synchronous communication, multi-drop topology, master-slave architecture
  • Digital Electronics: Understanding of open-drain outputs and pull-up resistors

56.4 I2C Overview

I2C (IIC, I-squared-C) is a popular multi-device communication bus developed by Philips (now NXP) in the 1980s.

Pronunciation: “I-squared-C” or “I-two-C”

Full name: Inter-Integrated Circuit

56.4.1 Characteristics

Feature Value
Wires 2 only (SDA + SCL)
Topology Multi-drop (bus)
Sync/Async Synchronous (SCL = clock)
Duplex Half-duplex (SDA is bidirectional)
Speed 100 kHz (standard), 400 kHz (fast), 1 MHz (fast+), 3.4 MHz (high-speed)
Distance <1 meter (same PCB or short cable)
Devices Up to 112 (7-bit addressing) or 1024 (10-bit)
Master/Slave Multi-master capable

I2C physical wiring diagram showing SDA and SCL lines with pull-up resistors to VCC, connecting master and multiple slave devices in parallel bus configuration

I2C physical connections showing SDA and SCL with pull-up resistors
Figure 56.1: Physical I2C wiring: two lines (SDA and SCL) run across all devices on the bus. Pull-up resistors hold both lines HIGH by default; any device can pull a line LOW using its open-drain output.

56.6 I2C Signals

I2C bus wiring diagram showing SDA and SCL lines connecting one master microcontroller to multiple slave devices, with pull-up resistors on both lines to VCC, illustrating the multi-drop open-drain bus topology
Figure 56.2: I2C Bus Wiring with Pull-Up Resistors

SDA (Serial Data):

  • Bidirectional data line
  • Carries address and data
  • Open-drain (requires pull-up resistor)

SCL (Serial Clock):

  • Clock signal from master
  • Slaves can hold it LOW (“clock stretching”) to slow master
  • Open-drain (requires pull-up resistor)

56.7 Pull-Up Resistors

Critical requirement: Both SDA and SCL need pull-up resistors to VCC.

Typical values:

  • 4.7 kohm - Most common (400 kHz, few devices, short traces)
  • 2.2 kohm - For longer cables, many devices, or fast mode
  • 10 kohm - For very short cables, few devices, 100 kHz only

Why needed? I2C uses open-drain outputs - can only pull LOW, not HIGH. Pull-up resistors provide the HIGH level. See the detailed calculation in the Worked Example section for guidance on choosing the right value.


56.8 I2C Addressing

Each device on the bus needs a unique 7-bit address.

Address space:

  • 7-bit addresses: 0x08 to 0x77 (112 addresses)
  • Some addresses reserved (0x00-0x07, 0x78-0x7F)

Common I2C device addresses:

Device Address (Hex) Address (Decimal)
OLED Display (SSD1306) 0x3C or 0x3D 60 or 61
BME280 (Temp/Humidity) 0x76 or 0x77 118 or 119
MPU6050 (IMU) 0x68 or 0x69 104 or 105
RTC DS3231 0x68 104
EEPROM 24C256 0x50 - 0x57 80 - 87
Try It: I2C Address Frame Decoder

Finding device addresses:

// I2C Scanner
#include <Wire.h>

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Serial.println("I2C Scanner");

  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.print("Device found at 0x");
      if (addr < 16) Serial.print("0");
      Serial.println(addr, HEX);
    }
  }
  Serial.println("Scan complete");
}

void loop() {}


56.9 I2C Communication Protocol

Artistic representation of I2C signal timing showing START condition (SDA falling while SCL high), data transmission with clock synchronization, ACK/NACK bits after each byte, and STOP condition (SDA rising while SCL high). Demonstrates master-controlled clock stretching by slow slave devices

I2C Signal Timing
Figure 56.3: I2C protocol timing: START and STOP conditions frame each transaction, with data sampled on SCL rising edge. The acknowledgment bit after each byte confirms successful reception.

Start and Stop Conditions:

     SDA  ____        ________________        ____
              \_______/                \______/

     SCL  _________                ____________
                   \_______/\_____/

          <-START-> <-DATA-> <-STOP->

START: SDA goes HIGH → LOW while SCL is HIGH

STOP: SDA goes LOW → HIGH while SCL is HIGH

Data Transfer (1 byte + ACK):

Bit:  7   6   5   4   3   2   1   0  ACK
     ___     ___         ___     ___ ___
SDA:    \___/   \_______/   \___/      (Data bits + ACK)

     _   _   _   _   _   _   _   _   _
SCL: _|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_ (Clock pulses)

     <--------- 8 Data Bits -------> <A>
                                      |
                                     ACK (0=success, 1=fail)

Data transfer rules:

  • SDA can only change when SCL is LOW
  • SDA is sampled when SCL is HIGH
  • Data transmitted MSB first (most significant bit first)
Complete I2C transaction timing showing start condition, 7-bit slave address, R/W bit, acknowledge, data bytes with ACK after each byte, and stop condition for write and read operations
Figure 56.4: I2C communication protocol timing showing complete transaction

56.10 I2C Write Transaction

I2C write transaction sequence diagram showing master sending START condition, 7-bit slave address with write bit (0), register address byte, data byte, each followed by slave ACK, then STOP condition
Figure 56.5: I2C Write Transaction Sequence

Steps:

  1. Master sends START
  2. Master sends slave address + write bit (0)
  3. Slave acknowledges (ACK)
  4. Master sends register address
  5. Slave ACKs
  6. Master sends data byte
  7. Slave ACKs
  8. Master sends STOP

56.11 I2C Read Transaction

I2C read transaction sequence diagram showing master sending START, slave address with write bit, register address byte with ACK, then REPEATED START, slave address with read bit, slave sending data bytes, master sending NACK on last byte, then STOP condition
Figure 56.6: I2C Read Transaction with Restart Condition

Steps:

  1. Master sends START
  2. Master sends slave address + write bit (0)
  3. Slave ACKs
  4. Master sends register address to read from
  5. Slave ACKs
  6. Master sends REPEATED START (restart without releasing bus)
  7. Master sends slave address + read bit (1)
  8. Slave ACKs
  9. Slave sends data byte(s); master ACKs each except the last
  10. Master sends NACK on final byte (signals end of read)
  11. Master sends STOP

The Repeated START (step 6) is essential: it lets the master switch from write mode (selecting the register) to read mode (receiving data) without another device claiming the bus in between.


56.12 Arduino/ESP32 I2C Example

#include <Wire.h>

// I2C device address
#define MPU6050_ADDR 0x68

void setup() {
  Serial.begin(115200);
  Wire.begin();  // Initialize I2C

  // Wake up MPU6050 (write 0 to PWR_MGMT_1 register)
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x6B);  // Register address
  Wire.write(0);     // Wake up
  Wire.endTransmission(true);

  Serial.println("MPU6050 Initialized");
}

void loop() {
  // Read accelerometer X-axis (registers 0x3B-0x3C)
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x3B);  // Starting register
  Wire.endTransmission(false);  // Repeated start

  Wire.requestFrom(MPU6050_ADDR, 2, true);  // Request 2 bytes

  if (Wire.available() >= 2) {
    int16_t accelX = Wire.read() << 8 | Wire.read();

    Serial.print("Accel X: ");
    Serial.println(accelX);
  }

  delay(500);
}

Wiring (ESP32):

ESP32 GPIO 21 (SDA) -> Sensor SDA
ESP32 GPIO 22 (SCL) -> Sensor SCL
ESP32 3.3V          -> Sensor VCC
ESP32 GND           -> Sensor GND

(Add 4.7k pull-up resistors on SDA and SCL if not on sensor board)

56.13 Clock Stretching

Problem: Slave needs more time to process data.

Solution: Slave holds SCL LOW to pause communication.

Master releases SCL (expects HIGH)
Slave holds SCL LOW (clock stretching)
Master waits...
Slave releases SCL (ready to continue)
Communication resumes

Use case: Slow sensors (temperature readings take time), EEPROM write operations.


Practical Tips

Avoiding Address Conflicts:

  • Check device datasheets before buying
  • Many sensors have address select pins (A0, AD0) to change address
  • Use I2C scanner sketch to verify addresses
  • Plan device selection to avoid conflicts

Common Conflicts:

  • 0x68: MPU6050, DS1307/DS3231 RTC → Use MPU6050 AD0 pin for 0x69
  • 0x76/0x77: BMP280, BME280, BME680 → Use SDO pin
  • 0x27/0x3F: LCD backpack modules → Check board

Pull-up Resistors:

  • 4.7k: Most common (400 kHz)
  • 2.2k: Long cables or many devices
  • 10k: Short cables, few devices

Pull-up resistor values aren’t arbitrary—they’re calculated from RC time constant requirements.

For I2C fast mode (400 kHz), the rise time must be under 300 ns (from 0 V to 70% of VCC, the I2C sampling threshold). With typical bus capacitance \(C_{bus} = 50\) pF (3 devices + PCB traces):

\[t_r = 0.8473 \times R_{pull} \times C_{bus}\]

Solving for maximum resistance: \[R_{max} = \frac{t_r}{0.8473 \times C_{bus}} = \frac{300 \times 10^{-9}}{0.8473 \times 50 \times 10^{-12}} = 7{,}080 \text{ Ω}\]

The I2C spec also requires minimum current drive capability of 3 mA at 0.4 V LOW level. With 3.3 V supply: \[R_{min} = \frac{V_{DD} - V_{OL}}{I_{sink}} = \frac{3.3 - 0.4}{0.003} = 967 \text{ Ω}\]

This gives a range of 967 Ω to 7,080 Ω for a 50 pF bus. The standard 4.7 kΩ falls well within this range—which is why it works reliably for most setups. With 100 pF bus capacitance (many devices or long traces), the maximum drops to ~3.5 kΩ, so 2.2 kΩ pull-ups are needed.


56.14 Hands-On Lab: I2C Scanner

Objective: Find all I2C devices connected to your ESP32/Arduino.

#include <Wire.h>

void setup() {
  Serial.begin(115200);
  Wire.begin();

  Serial.println("\nI2C Scanner");
  Serial.println("Scanning for devices...\n");

  byte count = 0;

  for (byte i = 8; i < 120; i++) {
    Wire.beginTransmission(i);
    if (Wire.endTransmission() == 0) {
      Serial.print("Found device at address: 0x");
      if (i < 16) Serial.print("0");
      Serial.print(i, HEX);
      Serial.print(" (");
      Serial.print(i);
      Serial.println(")");
      count++;
    }
  }

  Serial.print("\nTotal devices found: ");
  Serial.println(count);
}

void loop() {}

Expected output:

I2C Scanner
Scanning for devices...

Found device at address: 0x3C (60)
Found device at address: 0x76 (118)

Total devices found: 2

56.15 Hands-On Lab: BME280 Temperature Sensor

Objective: Read temperature from BME280 sensor via I2C.

#include <Wire.h>
#include <Adafruit_BME280.h>

Adafruit_BME280 bme;  // I2C

void setup() {
  Serial.begin(115200);

  if (!bme.begin(0x76)) {  // I2C address
    Serial.println("BME280 sensor not found!");
    while (1);  // Halt
  }

  Serial.println("BME280 Ready");
}

void loop() {
  float temperature = bme.readTemperature();
  float humidity = bme.readHumidity();
  float pressure = bme.readPressure() / 100.0F;

  Serial.print("Temp: ");
  Serial.print(temperature);
  Serial.print(" C, Humidity: ");
  Serial.print(humidity);
  Serial.print(" %, Pressure: ");
  Serial.print(pressure);
  Serial.println(" hPa");

  delay(2000);
}

56.16 Knowledge Check: I2C Multi-Master Arbitration

Scenario: You’re designing a robotics platform with two microcontrollers (ESP32 and Arduino) that both need to control a shared OLED display (I2C address 0x3C) and BME280 sensor (0x76). Both MCUs operate as I2C masters on the same bus with 4.7k pull-ups.

Think about:

  1. What happens if both masters try to send a START condition simultaneously?
  2. Why doesn’t this cause electrical damage to the devices?

Key Insight: I2C uses wired-AND arbitration through open-drain outputs. When multiple masters transmit simultaneously, any device pulling SDA LOW wins (LOW overrides HIGH). During arbitration, masters compare transmitted bits to actual bus state:

  • Master transmits 1 (releases line HIGH)
  • Bus reads 0 (another master pulled LOW)
  • First master detects mismatch → backs off gracefully
  • No electrical conflict because open-drain outputs never drive HIGH (only pull LOW or float)

This elegant mechanism prevents bus damage. Push-pull outputs (like SPI) would short VCC to GND if two devices drive opposite levels, potentially destroying the chips. The trade-off: RC charging through pull-up resistors limits rise time, restricting I2C to ~400 kHz vs SPI’s 80+ MHz.

Verify Your Understanding:

  • Calculate arbitration delay: With 400 pF bus capacitance and 4.7 kΩ pull-ups, rise time = 0.8473 × 4,700 Ω × 400 pF = 1.59 µs. Since the I2C standard-mode clock period is 10 µs (100 kHz), this rise time is marginal; for reliable multi-master operation at 400 kHz (2.5 µs period), the rise time budget of 300 ns would be exceeded—requiring 2.2 kΩ pull-ups or reducing bus capacitance.
  • Why faster rise times require smaller pull-ups: At 100 kHz (standard mode), 10k resistors work. At 400 kHz (fast mode), 2.2k needed for adequate rise time.

56.17 Quiz: I2C Protocol



Complete I2C protocol overview showing physical layer (two-wire bus), data layer (address frame with R/W bit, data frames with ACK), and logical layer (master-slave communication with clock stretching and arbitration for multi-master scenarios)

I2C Protocol Overview
Figure 56.7: I2C provides a simple, efficient interface for connecting low-speed peripherals like sensors, EEPROMs, and displays. Its two-wire design minimizes PCB routing complexity while supporting multiple devices.

56.18 Worked Example: Pull-Up Resistor Calculation

Choosing the right pull-up resistor value is the single most common source of I2C failures. Too high and the bus cannot rise fast enough; too low and the devices cannot sink enough current to pull the bus low.

The constraints:

  1. Minimum resistor (max current): I2C spec limits sink current to 3 mA at VOL = 0.4 V. With 3.3 V supply: R_min = (VDD - VOL) / Isink = (3.3 - 0.4) / 0.003 = 967 Ω ≈ 1 kohm
  2. Maximum resistor (rise time): The bus must rise from 0V to 70% of VCC within the rise time specification. Rise time depends on bus capacitance.

Rise time formula:

\[t_r = 0.8473 \times R_{pull} \times C_{bus}\]

Where the maximum rise time is 1000 ns for standard mode (100 kHz) and 300 ns for fast mode (400 kHz).

Calculating bus capacitance:

Typical capacitance sources:
  Each I2C device input: 5-10 pF
  PCB trace: 1-2 pF/cm
  Wires/jumpers: 50-100 pF/m

Example: ESP32 + BME280 + SSD1306 OLED + 15 cm PCB traces
  ESP32 SDA/SCL pins: 10 pF
  BME280: 5 pF
  SSD1306: 8 pF
  15 cm PCB trace x 2 pF/cm: 30 pF
  Total: 53 pF

Maximum resistor for 400 kHz (fast mode):

\[R_{max} = \frac{300 \text{ ns}}{0.8473 \times 53 \text{ pF}} = \frac{300 \times 10^{-9}}{44.9 \times 10^{-12}} = 6.68 \text{ kohm}\]

Result: For this 3-device bus at 400 kHz, use pull-ups between 967 Ω and 6.68 kohm. The standard 4.7 kohm falls comfortably in the middle – which is why 4.7 kohm is the universal default recommendation.

When 4.7 kohm does NOT work:

Scenario Problem Fix
Long cable (>30 cm) Bus capacitance >200 pF, rise time too slow at 4.7k Use 2.2 kohm or reduce speed to 100 kHz
Many devices (>8) Combined capacitance >100 pF Use 2.2 kohm pull-ups
3.3V bus with 5V tolerant device Logic thresholds mismatch Use level shifter, not just pull-up changes
High-speed mode (3.4 MHz) Rise time budget only 40 ns (≤ 100 pF load); passive pull-ups cannot charge fast enough Use current-source active pull-ups; passive resistors cannot meet the HS-mode rise-time spec
Breadboard prototype Breadboard adds 20-50 pF per row Use 2.2 kohm and limit to 100 kHz

Debugging tip: If I2C works intermittently or only at 100 kHz but fails at 400 kHz, the pull-up resistor value is almost always the cause. An oscilloscope on SDA/SCL will show rounded rising edges (too slow) or ringing (too fast), confirming the diagnosis.

Try It: I2C Pull-Up Resistor Calculator

Common Pitfalls

Pull-up resistors that are too weak (>10 kΩ) cause slow rising edges that fail at higher speeds. Resistors too strong (<1 kΩ) consume excessive power and may damage open-drain outputs. Fix: calculate the pull-up value based on bus capacitance and target speed; 4.7 kΩ is a safe default for 100 kbps on short buses.

Adding a second I²C temperature sensor with the same address as the first causes them to respond simultaneously, corrupting data. Fix: check address settings (hardware address pins or software configuration) before combining multiple devices of the same type on one bus.

A power glitch during an I²C transaction can leave the SDA line stuck low, locking the bus. The master cannot reset the bus without clocking SCL 9 times to free the stuck slave. Fix: implement a bus recovery routine in firmware that generates 9 SCL pulses to reset any locked slave device.

56.19 Summary

This chapter covered I2C (Inter-Integrated Circuit) protocol:

  • Two-wire bus (SDA for data, SCL for clock) supports multiple devices
  • Open-drain outputs with pull-up resistors (typically 4.7k) enable multi-master operation
  • Pull-up value depends on bus capacitance – 4.7k works for most setups, use 2.2k for long cables or many devices
  • 7-bit addressing allows up to 112 devices on a single bus
  • Synchronous protocol with master-controlled clock and optional clock stretching
  • Half-duplex communication with START, STOP, and ACK/NACK conditions
  • Common devices: Temperature sensors (BME280), displays (SSD1306), IMUs (MPU6050), RTCs (DS3231)

56.20 What’s Next

Topic Chapter Description
Wired Protocol Foundations Wired Communication Fundamentals Review synchronous vs asynchronous, open-drain topology, and master-slave architecture before moving to SPI
High-Speed Peripherals SPI Protocol Learn the 4-wire full-duplex SPI bus used for SD cards, displays, and ADCs that need speeds far beyond I2C’s 3.4 MHz limit
Long-Distance Serial UART and RS-232 Serial Communication Explore point-to-point asynchronous serial communication used for GPS modules, debug consoles, and PC interfaces
Wired Protocol Survey Wired Communication Protocols Compare I2C, SPI, UART, CAN, and 1-Wire side-by-side to select the right protocol for your IoT design
LAN Connectivity Wired Access: Ethernet Extend from chip-level buses to LAN-scale wired networking with Ethernet, switches, and IEEE 802.3 standards
Protocol Stack Context OSI and TCP/IP Layered Models Situate I2C at the physical and data-link layers of a full networking stack to understand how it fits into larger IoT architectures