I2C from ESP32 to Arduino

Hi all,

I'm trying to send some data through I2C from an ESP32-CAM to an Arduino based board.
ESP32 is acting like a sender(slave) with the address 0x04 and Arduino is the reader (master).
The code:
ESP32. Based on the example "slave_sender" from GitHub - gutierrezps/ESP32_I2C_Slave: I2C slave library for ESP32.

#include <Arduino.h>
#include <Wire.h>
#include <WireSlave.h>

#define I2C_SDA 13
#define I2C_SCL 12
#define I2C_SLAVE_ADDR 0x04
int table[] = {0, 0, 0};
void requestEvent();

void setup()
{
  Serial.begin(115200);

  WireSlave.begin(I2C_SDA, I2C_SCL, I2C_SLAVE_ADDR);
  Serial.printf("Slave joined I2C bus with addr #%d\n", I2C_SLAVE_ADDR);
}

void loop()
{
  int x = 120;
  int y = 1200; // gets converted to an strange # because is larger than 255
  int z = 3;
  table[0] = x;
  table[1] = y;
  table[2] = z;

  WireSlave.onRequest(requestEvent); // register event

}

// function that runs whenever the master sends an empty packet.
// this function is registered as an event, see setup().
// do not perform time-consuming tasks inside this function,
// do them elsewhere and simply read the data you wish to
// send inside here.
void requestEvent()
{
  uint8_t Buffer[3];
  Buffer[0] = table[0];
  Buffer[1] = table[1];
  Buffer[2] = table[2];
  WireSlave.write(Buffer, 3);
}

Arduino. Based on the example "master_reader" from the library Wire.h:


#include <Arduino.h>
#include <Wire.h>
#include <WireSlaveRequest.h>

#define I2C_SLAVE_ADDR 0x04
#define MAX_SLAVE_RESPONSE_LENGTH 64
int table[] = {0, 0, 0, 0};
void setup() {
  Wire.begin();   // join i2c bus
  Serial.begin(115200);  // start serial for output
}

void loop() {
  static unsigned long lastReadMillis = 0;

  // request data from Slave every 1000 ms
  if (millis() - lastReadMillis > 1000) {
    // first create a WireSlaveRequest object
    // first argument is the Wire bus the slave is attached to (Wire or Wire1)
    WireSlaveRequest slaveReq(Wire, I2C_SLAVE_ADDR, MAX_SLAVE_RESPONSE_LENGTH);

    // optional: set delay in milliseconds between retry attempts.
    // the default value is 10 ms
    slaveReq.setRetryDelay(10);

    // attempts to read a packet from an ESP32 slave.
    // there's no need to specify how many bytes are requested,
    // since data is expected to be packed with WirePacker,
    // and thus can have any length.
    bool success = slaveReq.request();

    if (success) {

      for (int i = 0; i < 4; i++) //organizes the data from the slave in the table
      {
        uint8_t c = slaveReq.read(); // receive a byte as character
        table[i] = c;
      }
      //displays the data
      Serial.print('\n');
      Serial.print(table[0]);
      Serial.print('\t');
      Serial.print(table[1]);
      Serial.print('\t');
      Serial.print(table[2]);
      Serial.print('\t');
      Serial.print(table[3]);
      Serial.print('\n');

      delay (500);

    }
    else {
      // if something went wrong, print the reason
      Serial.println(slaveReq.lastStatusToString());
    }

    lastReadMillis = millis();
  }
}

The final purpose is to send the local IP that ESP32 is connected to, to my main PCB.

Thank you, Pablo.

You must use a logic level shifter when connecting 3.3V and 5V devices.

You declare integer values, and then place them in a single byte buffer value. Why do you do that?

The size of an integer is different between the two platforms.
4 bytes on the ESP32 and 2 bytes on the Arduino, but you are sending and reading single bytes for you your values.

You need to write and read the same number of bytes on both platforms the number of bytes needs to reflect the actual size of your values.

Always have to say - if you have an ESP-32, why would you ever need an Arduino?

I've ordered one, but I'm not sure if that's the problem as I think SDA/SCL pins from ESP32 are 5v tolerant. Also the arduino detects the ESP32, if I run an i2c scanner it will tell that there's a device with address 0x04 connected.

Can you give a source for this belief ?

The library you're using is an obsolete workaround which won't be understood by most people here, so the easiest solution would be to install the latest core for the esp32 (ESP32 Arduino 2.0.1), which provides slave functionality for the esp32, using the basic wire.h library (Release ESP32 Arduino 2.0.1 based on ESP-IDF 4.4 · espressif/arduino-esp32 · GitHub).

You can install 2.0.1 using these instructions; https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html. .

Then use one of the the following examples; Use I2C for communication between Arduinos - #2 by Robin2 All sketches should compile for both the esp32 and arduino nano, however I would avoid example 2, as it's not reliable to have 2 masters.

And yes, you need a level shifter for your setup. See here for an example hookup; Bi-Directional Logic Level Converter Hookup Guide - SparkFun Learn

Also, since esp32 and arduino have different sizes for "int" variables, I believe you can simply just replace each int variable with "uint16_t" on both the master and the slave.

2 Likes

ESP32's pins are NOT 5V tolerant.

1 Like

You may have already damaged the ESP32 by exposing the pins to 5V. Even if it seems to work, you can't trust it now.

They are cheap, so order another.

Suggest you make self stick labels that say 3.3V Only ! and put them on all 3.3v devices.

It sucks to get old :older_man: .

why not? :thinking:

What is the Arduino doing that the ESP32 couldn't ?

Because - as Bob points out - the ESP is a far more capable processor, and can almost always do everything you might want an Arduino to do while doing whatever else the ESP needs to do.

And using a second processor massively complicates matters by requiring a communications protocol, whether by serial, I²C, SPI or whatever - as well as the logic level problems mentioned.

People sometimes imagine they need an Arduino to provide more I/O, but "port expanders" and other peripherals do this, possibly better than the Arduino. These connect to the ESP via I²C or SPI, using few shared I/O pins.

I think it's just they often think "My Arduino needs WiFi" and they never realize that the 'WiFi module' they've bought is actually an Arduino on a speedball of steroids and uncut cocaine. They think it's like the relay module they got in the same bag with it.

Yeah.

:rofl:

Thanks. Well, I think the main problem people have is getting other asynchronous tasks to run, while having wifi running on the ESP32. I know there's dual core on the esp32, but I've struggled to wrap my head around how to implement it.

Don't worry about dual core or the task scheduler. Odds are the task you want to execute are loght weight anyway and can simply be "multitasked" using millis and state machine logic.

Perhaps you could start a thread asking about how to implement the OS, freeRTOS, on a ESP32?

Ok thanks, that's handy to know.

To the main discussion at hand though, we have ZERO idea how heavy OP's IoT application is. Therefore I provided a solution to get the master-slave configuration working, in post #7 (I2C from ESP32 to Arduino - #7 by GigaNerdTheReckoning) . We also have to consider that he/she is using an ESP-CAM, with a lot less I/O pins, so such a configuration might provide some convenience.

1 Like

The Arduino is not the final board, I'm using it for prototyping. I need the ESP32 to stream video. I can't let the ESP32 do everything as I need BLE and WIFI to run simultaneously. Also, I have other sensors connected to the main board that I can't connect to the ESP32. Trust me I would love to have only one micro.