ESP32 I2C Master and Slave (I2C Communication Between Two ESP32) – Arduino IDE

In this guide, you’ll learn how to exchange data between two ESP32 boards using I2C communication protocol. One ESP32 board will be set as an I2C master and the other board as an I2C slave. The ESP32 boards will be programmed using Arduino IDE.

ESP32 I2C Master and Slave I2C Communication Between Two ESP32 boards Arduino IDE

Do you need a wireless communication protocol? Try ESP-NOW communication protocol with the ESP32 to exchange data between boards.

Table of Contents

In this guide, we’ll cover the following topics:

Introducing I2C

I²C means Inter Integrated Circuit (it’s pronounced I-squared-C), and it is a synchronous, multi-master, multi-slave communication protocol. You can connect :

  • Multiple slaves to one master: for example, your ESP32 reads from a BME280 sensor using I2C and writes the sensor readings in an I2C OLED display.
  • Multiple masters controlling the same slave: for example, two ESP32 boards writing data to the same I2C OLED display.

ESP32 I2C Bus Interfaces

The ESP32 supports I2C communication through its two I2C bus interfaces that can serve as I2C master or slave, depending on the user’s configuration. Accordingly to the ESP32 datasheet, the I2C interfaces of the ESP32 supports:

  • Standard mode (100 Kbit/s) 
  • Fast mode (400 Kbit/s) 
  • Up to 5 MHz, yet constrained by SDA pull-up strength 
  • 7-bit/10-bit addressing mode 
  • Dual addressing mode. Users can program command registers to control I²C interfaces, so that they have more flexibility

For a more detailed introduction to I2C communication with the ESP32, read our guide:

ESP32 Master and ESP32 Slave

For I2C communication between two ESP32 boards, we’ll use

  • The default Wire.h library
  • The default I2C pins on both boards (these may vary depending on your board model)

Connecting two ESP32 Boards via I2C

Start by connecting the two ESP32 boards with each other. Use the default I2C pins for the boards you’re using. Don’t forget to connect the GND pins together.

Connecting two ESP32 Boards via I2C master slave communication
ESP32 MasterESP32 Slave
SDA (GPIO 21)*SDA(GPIO 21)*
SCL (GPIO 22)*SCL (GPIO 22)*
GNDGND

* in my case, I’m testing this with the ESP32 DOIT V1 board. The default I2C pins are GPIO 21 (SDA) and GPIO 22 (SCL). If you’re using an ESP32-C3, ESP32-S3, or other model, the default I2C pins might be different. Please check the pinout for the board you’re using.

I2C Communication Between 2 ESP32 boards

Here’s how I2C communication between two ESP32 boards works:

ESP32 MasterESP32 Slave
Sets its I2C address
Sets callback functions:
– to read received messages;
– to handle requests.
Initializes I2C bus on the slave I2C address
Starts I2C communication on the I2C bus
Sends a message via I2C (Starts transmission)
Reads the received message
Requests data from the slave
Sends data to the master

ESP32 I2C Slave – Arduino Code

To test setting the ESP32 as an I2C slave, we’ll use the default example from the Wire.h library.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-i2c-master-slave-arduino/
  ESP32 I2C Slave example: https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/examples/WireSlave/WireSlave.ino
*********/

#include "Wire.h"

#define I2C_DEV_ADDR 0x55

uint32_t i = 0;

void onRequest() {
  Wire.print(i++);
  Wire.print(" Packets.");
  Serial.println("onRequest");
  Serial.println();
}

void onReceive(int len) {
  Serial.printf("onReceive[%d]: ", len);
  while (Wire.available()) {
    Serial.write(Wire.read());
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.onReceive(onReceive);
  Wire.onRequest(onRequest);
  Wire.begin((uint8_t)I2C_DEV_ADDR);

/*#if CONFIG_IDF_TARGET_ESP32
  char message[64];
  snprintf(message, 64, "%lu Packets.", i++);
  Wire.slaveWrite((uint8_t *)message, strlen(message));
  Serial.print('Printing config %lu', i);
#endif*/
}

void loop() {
  
}

View raw code

How does the code work?

Let’s take a quick look at how the slave code works.

Including the Wire Library

First, you need to include the Wire.h library.

#include "Wire.h"

Define the I2C address you want to give to your I2C device. In this case, it’s 0x55, but you can define any other address as long as you use the same one on the master device.

#define I2C_DEV_ADDR 0x55

Assign Callback Functions

In the setup(), you must assign two callback functions: one for when the board receives data from the master and another one for when the board receives a request from the master (send data to the master).

Wire.onReceive(onReceive);
Wire.onRequest(onRequest);

onReceive() Callback Function

The onReceive() function will read the data sent from the master and prints it on the Serial monitor.

void onReceive(int len) {
  Serial.printf("onReceive[%d]: ", len);
  while (Wire.available()) {
    Serial.write(Wire.read());
  }
  Serial.println();
}

You check if there’s data available to read using Wire.available().

while (Wire.available()) {

Then, you read the received bytes with Wire.read().

Serial.write(Wire.read());

onRequest() Callback Function

The onRequest() function will send data to the Master when requested.

void onRequest() {
  Wire.print(i++);
  Wire.print(" Packets.");
  Serial.println("onRequest");
  Serial.println();
}

To send data to the master, we use Wire.print().

Wire.print(i++);
Wire.print(" Packets.");

In the documentation, it mentions that you need to use Wire.slaveWrite() in the case of the ESP32, so that it writes the response to the buffer before receiving a message from the master. However, in our case, it works well without Wire.slaveWrite() (that’s why we have that section commented).

Depending on your board, you may need to use the slaveWrite() method as follows.

/*#if CONFIG_IDF_TARGET_ESP32
  char message[64];
  snprintf(message, 64, "%lu Packets.", i++);
  Wire.slaveWrite((uint8_t *)message, strlen(message));
  Serial.print('Printing config %lu', i);
#endif*/

Initialize I2C

In the setup(), you must also initialize I2C communication on the I2C address defined earlier. Use the begin() method as follows. This will initialize I2C on the ESP32 default I2C pins.

Wire.begin((uint8_t)I2C_DEV_ADDR);

You can also pass the I2C pins and bus frequency to this method:

bool Wire.begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency);

ESP32 I2C Master – Arduino Code

To test setting the ESP32 as an I2C master, we’ll use the default example from the Wire.h library.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-i2c-master-slave-arduino/
  ESP32 I2C Master Example: https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/examples/WireMaster/WireMaster.ino
*********/

#include "Wire.h"

#define I2C_DEV_ADDR 0x55

uint32_t i = 0;

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.begin();
}

void loop() {
  delay(5000);

  // Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.printf("Hello World! %lu", i++);
  uint8_t error = Wire.endTransmission(true);
  Serial.printf("endTransmission: %u\n", error);

  // Read 16 bytes from the slave
  uint8_t bytesReceived = Wire.requestFrom(I2C_DEV_ADDR, 16);
  
  Serial.printf("requestFrom: %u\n", bytesReceived);
  if ((bool)bytesReceived) {  //If received more than zero bytes
    uint8_t temp[bytesReceived];
    Wire.readBytes(temp, bytesReceived);
    log_print_buf(temp, bytesReceived);
  }
}

View raw code

How does the code work?

Let’s take a quick look at how the ESP32 I2C Master code works.

Including the Wire Library

First, you need to include the Wire.h library.

#include "Wire.h"

Slave I2C Address

Set the I2C address of the slave device (set on the previous code):

#define I2C_DEV_ADDR 0x55

Initialize I2C

In the setup(), initialize I2C communication using Wire.begin(). This will initialize I2C on the default I2C pins.

Wire.begin();

You can also pass the I2C pins and bus frequency to this method:

bool Wire.begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency);

Start Communication with the Slave

In the loop(), we send a message to the slave to inform that we’ll start I2C transmission:

// Write message to the slave
Wire.beginTransmission(I2C_DEV_ADDR);
Wire.printf("Hello World! %lu", i++);
uint8_t error = Wire.endTransmission(true);
Serial.printf("endTransmission: %u\n", error);

First, you need to call the beginTransmission() method and pass the slave address before writing a message.

Wire.beginTransmission(I2C_DEV_ADDR);

Then, you write a message to the buffer using the printf() method.

Serial.printf("endTransmission: %u\n", error);

Finally, to send the buffered message, you need to use the endTransmission() method.

uint8_t error = Wire.endTransmission(true);

Request Data From the Slave

Then, we request data from the slave using Wire.requestFrom(). Pass as argument the address of the slave and the number of requested bytes.

// Read 16 bytes from the slave
uint8_t bytesReceived = Wire.requestFrom(I2C_DEV_ADDR, 16);

Then, concatenate all the bytes and print the received data.

Serial.printf("requestFrom: %u\n", bytesReceived);
if ((bool)bytesReceived) {  //If received more than zero bytes
  uint8_t temp[bytesReceived];
  Wire.readBytes(temp, bytesReceived);
  log_print_buf(temp, bytesReceived);
}

Exchange Data Between Two ESP32 Boards via I2C – Demonstration

Upload the master and slave I2C sketches to your ESP32 boards. Open two instances of the Arduino IDE to see the Serial Monitor for both boards simultaneously.

You should see the master initializing a communication with the slave and receiving the data packets.

This is what you should get on the slave (a Hello World message! followed by a counter and the onRequest function being triggered):

Exchange Data Between Two ESP32 Boards via I2C Demonstration

On the Master, you’ll receive the packets sent from the slave.

Exchange Data Between Two ESP32 Boards via I2C Demonstration Master Slave

Wrapping Up

In this tutorial, we’ve shown you how to set the ESP32 as an I2C slave and as an I2C master and how to exchange data between two EPS32 boards using I2C communication protocol.

For more details about using the Wire library for I2C communication, check the official documentation: Arduino-ESP32 I2C API Documentation.

We have other I2C tutorials with the ESP32 that you may find useful:

If you want to exchange data between two ESP32 boards wirelessly, we recommend using ESP-NOW instead:

We hope this tutorial is useful. Learn more about the ESP32 with our resources:



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

15 thoughts on “ESP32 I2C Master and Slave (I2C Communication Between Two ESP32) – Arduino IDE”

  1. The “Master/Slave” terminology is out-of-date because of the slavery connotation. Please consider changing to Parent/Child or some other equivalent. Thanks!

    Reply
      • And just because you’ve done it one way for 50 years doesn’t mean it’s the best way to do it today.
        wired.com/story/tech-confronts-use-labels-master-slave/

        Reply
        • Thanks to everyone for flagging this.

          Yes, there are lots of things that were fine 50 years ago that aren’t now. It’s called progress. Let’s all celebrate it. I think the Wired link above is a good reference.

          Sender -> Receiver is perfectly good
          Conductor -> Performer is okay

          Plenty of options.

          Reply
        • You don’t get to redefine industry terms. we have male and female connectors (no non-binary) motherboards and daughter cards, and yes, master / slave processes.

          Reply
      • Me too. How many lines of code and comments have master/slave in them. It is history we can’t change it now but we can learn from it.

        Reply
      • agree with you. there is nothing wrong with the words master and slave. they aptly describe the relationship here and have nothing to do with human slavery. people are just looking for something to be upset about

        Reply
        • We know it’s nothing but virtue signalling. But the truly sad part is that while you and I just roll our eyes, the people who actually profit from human trafficking today are laughing their posteriors off. They absolutely LOVE the distraction because the more effort spent on bickering about worthless activities like this, the less effort is focused on actually going after them. Slavery is alive in well in the world today in places and forms that would surprise us all, and the ‘masters’ relish these futile attempts to change their hearts and minds. But if someone wants to think they are leading the army against injustice by changing some words in a source file, then gee whiz, I’m sure that’ll really show those bad guys a thing or two!

          Reply
  2. Thanks for this tutorial.
    Is the Master ESP32 I2C port locked for the communication with the Slave ESP32 ? I mean, can the Master ESP32 still communicate with other I2C devices like sensors while connected at the same time to the Slave ESP32 ?

    Reply
  3. Hi Sarah, I read this tutorial because I used the I2C communication protocol on other devices. Do you know the maximum length of wire line before raising of transmission errors? Thanks!

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.