Language: EN

esp32-i2c

How to use the I2C bus on an ESP32

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 of I2C is that it allows us to connect multiple 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 supervises communication between devices over the I2C bus. This controller includes the following functions:

  • Master mode: Can initiate communication, read, and write bytes
  • Slave mode: Can read and write bytes, if previously communicated with by a master

The I2C bus on the ESP32

The ESP32, ESP32-S2, and ESP32-S3 have two I2C controllers, while the ESP32-C3 has one.

The use of I2C on the ESP32 is very similar to what we would find on a conventional Arduino or an ESP8266

The ESP32 Core in Arduino provides the Wire.h library with the functions available for I2C. On the other hand, the object that manages an I2C controller is called TwoWire.

Once the ground is prepared, you can start I2C communication in your code:

void setup() {
  // Start I2C communication
  Wire.begin();
}

The biggest 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

ModelSDASCL
ESP322122
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’s a good idea to always specify them 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 so on a conventional Arduino. Here are some examples.

Sending Data

byte dataToSend = 0x42;  // Data to send
byte slaveAddress = 0x50;  // Slave device address

Wire.beginTransmission(slaveAddress);  // Start transmission to the device
Wire.write(dataToSend);  // Send data
Wire.endTransmission();  // End transmission

Receiving Data

byte receivedData;
int bytesToRead = 1;  // Number of bytes to read

Wire.requestFrom(slaveAddress, bytesToRead);  // Request data from the device
if (Wire.available()) {
  receivedData = Wire.read();  // Read received data
}

Using multiple I2C buses on the ESP32

As we mentioned, the ESP32, ESP32-S2, and ESP32-S3 have two I2C controllers, allowing us to 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
}