In this article, we’ll explore how to establish communication between two ESP32 devices using the I2C (Inter-Integrated Circuit) protocol. You’ll learn how to set up a master-slave connection, send and receive data, and implement simple I2C communication with Arduino code
To follow this article, you will need two Esp32s and two DuPont wires to connect pins 22 and 21:
Lets start with the master code. The first thing we need to do is to include the Arduino and Wire headers:
#include <Arduino.h>
#include <Wire.h>
Then we declare the I2C address of the slave and an uint32 variable to keep track of the number of packets sent:
#define I2C_DEV_ADDR 0x55
uint32_t i = 0;
On setup, initialize Serial and begin Wire:
void setup()
{
Serial.begin(115200);
Wire.begin(); // Begin as master
}
On the master’s loop is where we will manage our communication. On the first part of the loop, wait for 2 seconds to avoid spamming the serial, then begin a Wire transmission, send Hello with the packet number, end the transmission and log the return value:
void loop()
{
// Every 2 secs
vTaskDelay(2000 / portTICK_PERIOD_MS);
// Send hello and log result
Wire.beginTransmission(I2C_DEV_ADDR);
Wire.printf("Hello World! %u", i++);
uint8_t error = Wire.endTransmission(true);
Serial.printf("endTransmission: %u\n", error);
// ...
}
After sending our message we need to request a response, we do that with ‘requestFrom’, give it the slave address and the amount of bytes you want to read. After that, if the return is bigger than 0, then the slave sent something, read the bytes into an array and log it:
void loop()
{
// ...
// Request data and log it
uint8_t bytes = Wire.requestFrom(I2C_DEV_ADDR, 16);
Serial.printf("requestFrom: %u\n", bytes);
if (bytesReceived > 0)
{
uint8_t temp[bytes];
Wire.readBytes(temp, bytes);
log_print_buf(temp, bytes);
}
}
On the slave we will also include the same headers, use the same address and also create a packet number integer variable:
#include <Arduino.h>
#include <Wire.h>
#define I2C_DEV_ADDR 0x55
uint32_t i = 0;
We will then need to create a method that is called when we are requested data, in this example, we send the packet number:
void onRequest()
{
Wire.print(i++);
Wire.print(" Packets.");
Serial.println("onRequest");
Serial.println();
}
Then we need a method called when we receive data. This method takes in an integer argument with the size of the data received. While there is stuff to read from the buffer, we read and log it to Serial:
void onReceive(int len)
{
Serial.printf("onReceive[%d]: ", len);
while (Wire.available())
Serial.write(Wire.read());
Serial.println();
}
On Setup, begin Serial, register the I2C callbacks and begin Wire with the slave address. The loop function will remain empty:
void setup()
{
Serial.begin(115200);
Wire.onReceive(onReceive); // Register callbacks
Wire.onRequest(onRequest);
Wire.begin((uint8_t)I2C_DEV_ADDR); // Begin as slave
}
void loop() {}
After uploading your code to both your Esps, you should see something like the output below (left is the master and right is the slave):
And that’s all. Thanks for reading and stay tuned for more tech insights and tutorials. Until next time, and keep exploring the world of tech!