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.

The I2C bus consists of two lines: the serial data line (SDA) and the serial clock (SCL). Both lines require pull-up resistors.

What makes I2C more interesting is its ability to connect multiple devices through only two lines: SDA (Serial Data Line) and SCL (Serial Clock Line).

This allows us to save I/O pins on the microcontroller, and communication lines in our project. This facilitates the implementation of networks of devices and sensors.

The I2C controller supervises communication between devices through the I2C bus. This controller includes the following functions:

  • Read and write bytes in Master mode.
  • Slave mode.
  • Read and write to registers that are in turn read/written by the master.

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 we would find on a conventional Arduino or an ESP8266.

The ESP32 Core in Arduino offers 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 initiate 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 loss of performance.

In some models, the I2C controller comes 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 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 slaveAddress = 0x50;  // Slave device address

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

Receive Data

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

Wire.requestFrom(slaveAddress, bytesToRead);  // Request data from 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, 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
}