The I2C bus is a synchronous communication protocol that allows interaction between microcontrollers and devices (such as sensors, memories, analog-to-digital converters, among many others).
One of the most interesting points about I2C is that it allows us to connect several devices using only two lines:
- SDA (Serial Data Line)
- SCL (Serial Clock Line)
This allows us to save I/O pins on the microcontroller and communication lines in our project. Both lines require pull-up resistors.
The I2C controller manages communication between devices over the I2C bus. This controller includes the following functions:
- Main mode: Can initiate communication, read and write bytes
- Follower mode: Can read and write bytes, if a master previously communicates with it
The I2C Bus on the ESP32
The ESP32, ESP32-S2, and ESP32-S3 have two I2C controllers, while the ESP32-C3 has one.
Using I2C on the ESP32 is very similar to what you would find on a conventional Arduino or an ESP8266
The ESP32 Core for Arduino provides the Wire.h library with the functions offered for I2C. On the other hand, the object that manages an I2C controller is called TwoWire.
Once the groundwork is laid, you can start I2C communication in your code:
void setup() {
// Start I2C communication
Wire.begin();
}
The main difference is that, in the case of the ESP32, we can remap the I2C controller without performance loss.
In some models, the I2C controller is preconfigured by default to certain pins. But for example, in the ESP32-S3
| Model | SDA | SCL |
|---|---|---|
| ESP32 | 21 | 22 |
| ESP32-S3 | - | - |
No problem, we can always decide which pins to use
Wire.begin(I2C_SDA, I2C_SCL);
In fact, for compatibility and to avoid errors, it doesn’t hurt to always specify it explicitly.
How to Use I2C on ESP32 in the Arduino Environment
Using the I2C bus on the ESP32 in the Arduino environment is very similar to doing it on a conventional Arduino. Here are some examples.
Send Data
byte dataToSend = 0x42; // Data to send
byte followerAddress = 0x50; // Follower device address
Wire.beginTransmission(followerAddress); // Start transmission to the device
Wire.write(dataToSend); // Send data
Wire.endTransmission(); // End transmission
Receive Data
byte receivedData;
int bytesToRead = 1; // Number of bytes to read
Wire.requestFrom(followerAddress, bytesToRead); // Request data from the device
if (Wire.available()) {
receivedData = Wire.read(); // Read received data
}
Using Multiple I2C Buses on the ESP32
As we said, the ESP32, ESP32-S2, and ESP32-S3 have two I2C controllers, so we can create two simultaneous device networks.
#include <Wire.h>
// replace with the pins you want to use
const int SDA_1 = 0;
const int SCL_1 = 0;
const int SDA_2 = 0;
const int SCL_2 = 0;
TwoWire MyI2C_1 = TwoWire(0);
TwoWire MyI2C_2 = TwoWire(1);
void setup() {
MyI2C_1.begin(SDA_1, SCL_1, freq1);
MyI2C_2.begin(SDA_2, SCL_2, freq2);
}
void loop()
{
delay(1000); // do nothing
}

