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
If you don’t know what the I2C bus is or need more help, check out these entries:
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
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’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
}