Understanding I2C Protocol, Addressing, and How to Build an I2C Scanner for ESP Boards

Larbi OUIYZME
5 min readOct 6, 2023

--

Introduction

The Inter-Integrated Circuit (I2C) is a short-distance, bi-directional communication protocol developed by Philips. It’s a simple way to communicate between a master device and multiple slave devices using only two wires: Serial Data Line (SDA) and Serial Clock Line (SCL).

In this article, we’ll delve into the fundamentals of I2C, its specific circuits, and addressing mechanisms. Additionally, we’ll provide you with a simple I2C scanner code that can discover and display the addresses of connected peripherals. The code will be compatible with ESP boards.

I2C Protocol Basics

Master-Slave Communication

In an I2C communication setup, one device functions as the master, initiating and controlling the data transfer. The other devices act as slaves, receiving commands and data from the master.

Two-Wire Setup

  • SDA (Serial Data Line) : This is the line where the data is sent and received.
  • SCL (Serial Clock Line) : The master controls this line to synchronize the communication.

Specific Circuits and Pull-up Resistors

It’s essential to connect pull-up resistors to both SDA and SCL lines. A common value for these resistors is between 4.7kΩ and 10kΩ. These resistors ensure that the lines default to a ‘high’ voltage level, providing a clear signal and avoiding data corruption.

Addressing in I2C

Each slave device on an I2C bus must have a unique 7-bit or 10-bit address. The master uses this address to communicate with a particular slave device. The addressing can either be hardware-based or software-configurable.

Limitations on the Number of Devices in an I2C Bus

Certainly! The I2C (Inter-Integrated Circuit) protocol uses a 7-bit address for each device connected to the bus. This means it can theoretically handle up to 128 different devices (from address 0x00 to 0x7F in hexadecimal notation). However, some of these addresses are reserved for special purposes:

  1. The 0x00 address is generally reserved for general call or broadcasting.
  2. Addresses from 0x01 to 0x07 and from 0x78 to 0x7F are also reserved for special functions or future use.

Taking these reservations into account, the practical number of devices that you can connect to a single I2C bus is around 112.

It’s also possible to use 10-bit I2C addresses, which would theoretically increase the number of devices you could connect. However, not all devices and controllers support 10-bit addressing.

Also, keep in mind the limitations related to cable length, data transmission speed, and power requirements when connecting multiple devices to a single I2C bus.

How to Connect Multiple Devices on a Single I2C Bus

The I2C (Inter-Integrated Circuit) protocol is inherently designed to allow multiple devices to communicate with a single master device over a shared bus. This feature simplifies the design and reduces the wiring complexity of multi-device setups, making it an ideal choice for connecting various sensors, LCD screens, and EEPROMs together.

Hardware Setup :

  1. Master Device : This could be your Arduino, ESP32, ESP8266, or NodeMCU. Connect its SDA and SCL pins to the corresponding SDA and SCL lines of the I2C bus.
  2. Slave Devices : Connect the SDA and SCL pins of each sensor, LCD, or EEPROM to the same SDA and SCL lines on the bus.
  3. Pull-up Resistors : Use pull-up resistors (typically between 4.7kΩ and 10kΩ) on both the SDA and SCL lines to ensure signal integrity.

Addressing :

Make sure each slave device has a unique I2C address. Some modules allow you to change their default I2C addresses; consult your device’s datasheets for information on how to do this.

Troubleshooting Tips

  1. Check the Wiring : Make sure all your devices are correctly wired to the I2C bus.
  2. Address Conflicts : Use an I2C scanner code to confirm that no two devices share the same address.
  3. Power Requirements : Ensure that all devices have the appropriate voltage levels and power supply.
  4. Golden Rule : multiple I2C devices on a single bus is a straightforward and efficient way to augment the capabilities of your microcontroller setup. Just make sure to handle the addressing and power requirements properly, and you’re good to go.

I2C Scanner Code

Now, let’s get to the practical part. Here’s a simple I2C scanner code compatible with ESP32, ESP8266, and Arduino.

/*
Author: Larbi OUIYZME
Version: 1.0
Date: October 6, 2023
Title: Simple I2C scanner
*/

// Include the Wire library to communicate with I2C devices
#include <Wire.h>

// This function runs once when you turn your Arduino or ESP board on or press the reset button
void setup() {
// Initialize the I2C communication
Wire.begin();

// Initialize the Serial communication with a baud rate of 115200
Serial.begin(115200);

// Print a message to the Serial Monitor to indicate the I2C scanning will begin
Serial.println("\nI2C Scanner");
}

// This function loops continuously after the setup() has been run
void loop() {
// Declare variables to store the error status and address
byte error, address;

// Variable to store the number of devices found
int nDevices = 0;

// Print "Scanning..." to the Serial Monitor
Serial.println("Scanning...");

// Loop through the range of possible I2C addresses (from 1 to 126)
for (address = 1; address < 127; address++) {

// Begin transmission to the device at the specified address
Wire.beginTransmission(address);

// End the transmission and store the return status in 'error'
error = Wire.endTransmission();

// If 'error' is 0, it means that a device has been found at that address
if (error == 0) {

// Print the address to the Serial Monitor
Serial.print("I2C device found at address 0x");

// If the address is less than 16, print an additional "0" for formatting
if (address < 16) Serial.print("0");

// Print the address in hexadecimal format
Serial.println(address, HEX);

// Increment the device count
nDevices++;
}
}

// If no devices were found, print a message to the Serial Monitor
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
} else {
// If one or more devices were found, print "Done" to the Serial Monitor
Serial.println("Done\n");
}

// Wait 5 seconds before the next scan
delay(5000);
}

Copy and paste this code into your Arduino IDE, and upload it to your board. Open the serial monitor to see the addresses of connected I2C devices.

Conclusion

I2C is a robust and straightforward communication protocol that finds its applications in various systems. Understanding its fundamentals and addressing can streamline your embedded projects significantly. The I2C scanner code is a valuable tool to identify and troubleshoot your I2C network.

--

--

Larbi OUIYZME

I'm Larbi, from Morocco. IT trainer and Chief Information Security Officer (CISO), I'm committed to share knowledge. Also, Ham Radio CN8FF passionate about RF