56 I2C Protocol
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
For Beginners: The I2C Protocol
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.
Sensor Squad: The Two-Wire Wonder!
“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 |
56.5 Why I2C is Popular
Advantages:
- Only 2 wires (minimal pin usage)
- Multiple devices on same bus
- Simple addressing (each device has unique address)
- Bidirectional (master can read/write to slaves)
- Well-supported by sensors, displays, EEPROMs
56.6 I2C Signals
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 |
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
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)
56.10 I2C Write Transaction
Steps:
- Master sends START
- Master sends slave address + write bit (0)
- Slave acknowledges (ACK)
- Master sends register address
- Slave ACKs
- Master sends data byte
- Slave ACKs
- Master sends STOP
56.11 I2C Read Transaction
Steps:
- Master sends START
- Master sends slave address + write bit (0)
- Slave ACKs
- Master sends register address to read from
- Slave ACKs
- Master sends REPEATED START (restart without releasing bus)
- Master sends slave address + read bit (1)
- Slave ACKs
- Slave sends data byte(s); master ACKs each except the last
- Master sends NACK on final byte (signals end of read)
- 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
Putting Numbers to It
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
Understanding 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:
- What happens if both masters try to send a START condition simultaneously?
- 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
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:
- 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
- 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.
Common Pitfalls
1. Using the Wrong Pull-Up Resistor Value
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.
2. Forgetting That I²C Devices Must Have Unique Addresses
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.
3. Not Handling I²C Bus Lockup in Firmware
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 |