ESP32 I2C problem

Hello, i tried to connect one esp8266(master) and one esp32(slave) with the I2C protocol, but i noticed a problem when i request at the slave some bytes. When the data i receive from the slave they are one "step backwards". The firts data are always all 0xFF the second time i request it return the first data i request. thi is the code:

Slave(esp32)

#include "Arduino.h"
#include "Wire.h"

#define SDA 21
#define SCL 22
#define ADDRESS (uint8_t)0x08


void event(){
	Wire.beginTransmission(8);
	Wire.write("123456");
	Wire.endTransmission();
}

void setup() {
	Wire.begin(ADDRESS, SDA, SCL);
}

void loop() {
	Wire.onRequest(event);
	delay(100);

}

Master(esp8266):

#include "Arduino.h"
#include "Wire.h"


#define ADDRESS 8
#define SDA 5
#define SCL 2



void setup(){
	Serial.begin(460800);
	Wire.begin(SDA, SCL);
}

{
	Wire.requestFrom(ADDRESS, 6);
	while(Wire.available()){
		char c = Wire.read();
		Serial.print(c);
	}
	Serial.println();
	
	delay(1000);
}

The onRequest callback is called when another master requests data. The slave is not allowed to do begin/endTransmission() in that callback. See the slave_sender example.

1 Like

DrDiettrich is right and another issue is that you should call onRequest() only once during setup.

However the problem that you are reporting is unrelated.

ESP32 slave mode is awkward, that's why it took so long to have it at all in the Arduino code base, and Espressifs current documentation doesn't help clarifying things, particularly their examples lead you to think that it works just like we are used to while it doesn't.

I'm not an expert and did not test it fully yet, but here's what I deducted from my tests so far:

ESP32 needs the buffer prefilled before the I2C interrupt happens. So whatever you write in the event() ISR comes too late, it will only be sent in the next occurrence. As a consequence you

  1. need to prefill the buffer in the main loop before the interrupt happens,
  2. run the risk of incomplete replies because of that, and
  3. are not able to generate the data for sending on the fly. E.g. if you want your slave to return the value of some pin's state you cannot read it on the fly, but find some other way to have the current value ready.

I briefly mentioned this problem only recently here. In case you read German, the problem is also discussed here. Here is the current version of how I tried to solve the problem in my AccelStepperI2C firmware:

Here the buffer is prefilled and here is the ISR, which is effectively empty for the ESP32.

Jan

1 Like

I see 2 possible workarounds:

  1. On a read request the transmission is held by clock stretching
    until the response buffer is filled.
  2. Every other read request is aborted. Then the buffer is filled and can be sent on a repeated read request.
1 Like

thank you very much

Thank you. I have a question: how can i do the first workaraounds?

If you ask then you can't do. It requires knowledge of the ESP32 hardware and firmware before the firmware can be modified.

Thank you very much for the patient

It seems it can't be done at all, as it seems ESP32 hardware does not support clock stretching.

For a variation of option 2 see my above example code. Just think of the slave as a real slave device which only acts on a previous command, i.e. some data with the desired behavior sent by the master immediately before the request. Timing and/or sanity checks are crucial, though, as the slave could send bogus or incomplete data if it didn't get enough time to prepare the desired data before receiving the request. That's one of the reasons I added CRC8 checksums in my example above.

Jan

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.