Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C wrong callback order on repeated start command #6793

Closed
1 task done
YehorFedorov opened this issue May 24, 2022 · 14 comments
Closed
1 task done

I2C wrong callback order on repeated start command #6793

YehorFedorov opened this issue May 24, 2022 · 14 comments
Assignees
Labels
Status: Awaiting triage Issue is waiting for triage

Comments

@YehorFedorov
Copy link

Board

ESP32 C3

Device Description

ESPRESSIF ESP32 C3 mini 1

Hardware Configuration

GPIO 5 - SDA
GPIO 6 - SCL

Version

v2.0.3

IDE Name

Arduino IDE

Operating System

macOS Big Sur v 11.6.5

Flash frequency

80 Mhz

PSRAM enabled

no

Upload speed

460800

Description

I'm trying to establish communication via i2c between 2 esp32 c3 devices. The idea is simple, I want to send non stop request to slave (with repeated start), where master write 2 bytes first and read data from slave. And it's working somehow but not in the way I expected because slave firstly run onRequest callback and then onReceive. This looks like a bug for me (correct me if I'm wrong)

As a result I'm receiving data from previous request...

Attaching my wiring and logic analyzer data for 2 requests
wiring
first_req
second_req

Sketch

### MASTER
#include "Wire.h"

#define I2C_DEV_ADDR 0x10

uint32_t i = 0;
int func = 0;
int reg = 10;

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  bool res = Wire.begin(5, 6, (uint32_t)400000);
  Serial.print("Connected: ");
  Serial.println(res ? "true" : "false");
}

void loop() {
  delay(5000);
  unsigned int first, second;

  //Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.write(func);
  Wire.write(reg);
  Wire.endTransmission(false);
  Wire.requestFrom(I2C_DEV_ADDR, 2, true);
  first = Wire.read();
  second = Wire.read();

  Serial.print("First: ");
  Serial.println(first);
  Serial.print("Second: ");
  Serial.println(second);

  Serial.print("Func: ");
  Serial.println(func);
  Serial.print("Register: ");
  Serial.println(reg);

  func++;
  reg++;
}


### SLAVE
#include "Wire.h"

#define I2C_DEV_ADDR 0x10

uint32_t i = 0;
uint8_t arr[2] = {0, 0};

void onRequest(){
  Wire.slaveWrite(arr, 2);
}

void onReceive(int len){
  arr[0] = Wire.read();
  arr[1] = Wire.read();
}

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.onReceive(onReceive);
  Wire.onRequest(onRequest);
  Wire.begin((uint8_t)I2C_DEV_ADDR,5,6,(uint32_t)400000);
  Serial.println("SETUP");
}

void loop() {

}

Debug Message

Connected: true

// FIRST REQUEST //
First: 0
Second: 0
Func: 0
Register: 10
// END //

// SECOND REQUEST //
First: 0
Second: 10
Func: 1
Register: 11
// END //

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@YehorFedorov YehorFedorov added the Status: Awaiting triage Issue is waiting for triage label May 24, 2022
@SuGlider
Copy link
Collaborator

@me-no-dev PTAL, thanks.

@me-no-dev
Copy link
Member

Resolution is rather simple. Do not use repeated start :)
The ESP I2C slave peripheral will not execute the transaction until you also requestFrom which can cause some issues in the ISR and order of things. Not using repeated start will ensure that onReceive is called first.
Any reason to chose repeated start?

@YehorFedorov
Copy link
Author

I just thought it's a better soulution rather than 2 separate requests :)
I'm just wondering if it's only esp related problem?

Because I've spend a lot of time researching this issue and I saw that there are some i2c slave devices that working with repeated start

@me-no-dev
Copy link
Member

It is ESP related "problem" when both devices are ESPs. Catching the repeated start as Slave is rather complicated and that coupled with the way that the master sends the transaction could get you in such situation. Using separate write and read transactions (not calling Wire.endTransmission with false) would allow the slave to properly detect each step, extract/fill that data you want and so on.

@YehorFedorov
Copy link
Author

Thanks, @me-no-dev !

Closing this issue

@albertlt
Copy link

albertlt commented Jul 11, 2022

Hi, I have exactly the same problem. Basically, after writing to slave, master always receive previous result. I debugged using serial output on the slave's side and everything seems to be in correct order but master always receive previous "requestFrom" result. Is this not fixed? I used Wire.endTransmission() and tried both requestFrom(I2C_DEV_ADDR, 2) and requestFrom(I2C_DEV_ADDR, 2, true) without success. So frustrated, I hope someone can shed some lights.

@albertlt
Copy link

Also in my case, I did write and read from master twice consecutively and it seems that the slave haven't finish preparing the data on the second request and I received partly "⸮⸮⸮⸮⸮" jibberish data. Adding delay(1000) in between each write read sequence seems to fix the issue although the order of result is still incorrect as described above. Is there an elegant way for Master device to properly wait until slave is ready instead of predicting the processing time using delay?

@YehorFedorov
Copy link
Author

Hi @albertlt!
What boards do you use for master and slave?

@albertlt
Copy link

albertlt commented Jul 11, 2022 via email

@YehorFedorov
Copy link
Author

YehorFedorov commented Jul 11, 2022

@albertlt I think this is an answer to you question - #6677 (comment)

I can't explain you the difference between esp32, c3, s2 and s3 chip and why this is an issue but I saw some comments that i2c slave mode don't work well or as we expect on esp32 chip but on newest chips like s2, s3 and c3 it works fine

@albertlt
Copy link

I used: "esptool.py chip_id" and found out the master is: ESP32-D0WDQ6-V3 (revision 3) and slave is: ESP32-PICO-D4 (revision 1). Are they affected by the issue that you mentioned?

@albertlt
Copy link

By the way, is there any method in Wire to clear everything in it's buffer? I tried Wire.flush() on slave at the end of each session but it doesn't seem to work as it still gave me the previous result every time I start a new request from master.

@albertlt
Copy link

The flow of my program is as follow:

  1. Master send cmd 1.
  2. Slave decode in onReceive() and put result in a global variable: strResult.
  3. Master request result of cmd 1 ( Assume strResult = "abc" )
  4. Slave send the content of strResult in onRequest().
  5. Master send clear cmd after retrieving the result.
  6. Slave set strResult = "" and execute Wire.flush() in onReceive().

Several minutes later, master send cmd 2 with expected result = "def" but I still get "abc" even though strResult was already cleared at the end of previous iteration. It seems that "previous result" was still stored somewhere in the Wire library. I'd like to clear it if possible.

@RaghuRamaBL
Copy link

hello hi i am also facing same issue can you please let me know whether you have got any solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Awaiting triage Issue is waiting for triage
Projects
None yet
Development

No branches or pull requests

5 participants