Inter-Integrated Circuit (I2C) is a two-wire communication protocol used for connecting microcontrollers to peripherals like sensors, displays, and memory devices.
I2C is a synchronous, multi-slave, multi-master protocol that is widely used for its simplicity and scalability.
This tutorial explains how I2C works, how to use it on Arduino, and provides practical examples.
Table of Contents
1. What Is I2C?
I2C (pronounced “I-squared-C”) is a protocol that allows multiple devices to communicate using only two wires:
- SCL (Serial Clock Line): Carries the clock signal.
- SDA (Serial Data Line): Transfers data between devices.
Key Features:
- Master-Slave Communication: One master initiates communication; multiple slaves can respond.
- Addressing: Each slave has a unique 7-bit or 10-bit address.
- Speed: Supports standard (100 kHz), fast (400 kHz), and high-speed (3.4 MHz) modes.
2. How Does I2C Work?
- Start Condition: Master pulls the SDA line low while SCL is high.
- Addressing: Master sends the 7-bit address of the target slave and a read/write bit.
- Acknowledgment (ACK): Slave acknowledges the address by pulling SDA low.
- Data Transfer: Master and slave exchange data in 8-bit packets.
- Stop Condition: Master releases the SDA line while SCL is high.
3. Setting Up I2C on Arduino
Most Arduino boards have dedicated I2C pins. The Wire library provides built-in support for I2C communication.
4. Arduino Wire Library
Include the Wire library to use I2C:
#include <Wire.h>
4.1 Wire.begin()
- Initializes the I2C bus.
- Use on the master device without parameters: Wire.begin().
- Use on the slave device with an address: Wire.begin(address).
4.2 Wire.requestFrom(address, quantity)
- Requests data from a slave device.
- Parameters:
- address: Slave device’s I2C address.
- quantity: Number of bytes to request.
4.3 Wire.beginTransmission(address)
- Starts communication with a slave device.
4.4 Wire.write(data)
- Sends data to the slave.
4.5 Wire.endTransmission()
- Ends the transmission to the slave.
4.6 Wire.read()
- Reads a byte of data from the slave.
5. I2C Pin Configuration
Board | SCL Pin | SDA Pin |
---|---|---|
Arduino Uno | A5 | A4 |
Arduino Mega | 21 | 20 |
Arduino Nano | A5 | A4 |
Arduino Due | 21 | 20 |
6. Practical Examples
6.1 Communicating Between Two Arduinos
Master Code
#include <Wire.h> void setup() { Wire.begin(); // Initialize as master Serial.begin(9600); } void loop() { Wire.beginTransmission(8); // Address of the slave Wire.write("Hello, Slave!"); // Send data Wire.endTransmission(); delay(1000); }
Slave Code
#include <Wire.h> void setup() { Wire.begin(8); // Initialize as slave with address 8 Wire.onReceive(receiveEvent); Serial.begin(9600); } void loop() { // Empty } void receiveEvent(int bytes) { while (Wire.available()) { char c = Wire.read(); // Read data Serial.print(c); } Serial.println(); }
6.2 Interfacing with an I2C Sensor
Read data from a temperature sensor (e.g., TMP102).
#include <Wire.h> const int sensorAddress = 0x48; void setup() { Wire.begin(); Serial.begin(9600); } void loop() { Wire.beginTransmission(sensorAddress); Wire.write(0x00); // Pointer to temperature register Wire.endTransmission(); Wire.requestFrom(sensorAddress, 2); // Request 2 bytes if (Wire.available() == 2) { byte msb = Wire.read(); byte lsb = Wire.read(); int temperature = ((msb << 8) | lsb) >> 4; // Combine bytes float celsius = temperature * 0.0625; // Convert to Celsius Serial.print("Temperature: "); Serial.print(celsius); Serial.println(" C"); } delay(1000); }
6.3 Controlling an I2C Display
Control an I2C OLED display (e.g., SSD1306).
- Install Adafruit SSD1306 Library:
- Go to Tools > Manage Libraries and search for “Adafruit SSD1306.”
- Connect the Display:
- Connect SDA and SCL to the appropriate pins.
- Code Example:
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { if (!display.begin(SSD1306_I2C_ADDRESS, 0x3C)) { Serial.println("SSD1306 allocation failed"); while (1); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("Hello, I2C Display!"); display.display(); } void loop() { // Empty }
7. Best Practices for Using I2C
- Use Pull-Up Resistors:
- Add 4.7kΩ resistors between SDA/SCL and VCC for stable communication.
- Scan for Devices:
- Use an I2C scanner sketch to find connected device addresses.
#include <Wire.h> void setup() { Serial.begin(9600); Wire.begin(); Serial.println("Scanning..."); for (byte address = 1; address < 127; address++) { Wire.beginTransmission(address); if (Wire.endTransmission() == 0) { Serial.print("I2C device found at address 0x"); Serial.println(address, HEX); } } Serial.println("Scan complete."); } void loop() { // Empty }
- Check Slave Acknowledgment:
- Ensure the slave device acknowledges data for reliable communication.
- Monitor Bus Speed:
- Match the I2C clock speed to the peripheral’s specification using:
Wire.setClock(400000); // Set clock to 400 kHz (fast mode)
- Match the I2C clock speed to the peripheral’s specification using:
- Debug with Serial Monitor:
- Print transmitted and received data to verify communication.
Conclusion
I2C is a powerful protocol for connecting multiple devices with minimal wiring. The Arduino Wire library simplifies I2C communication, making it easy to interface with sensors, displays, and other peripherals.
This tutorial provided a comprehensive introduction, from understanding the basics to practical examples of using I2C in Arduino projects.
For more details, visit the official Arduino I2C reference.