-
Notifications
You must be signed in to change notification settings - Fork 7.6k
ESP32 S3 I2C issues when working as slave #10145
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
Comments
The ESP32's Slave works differently than all other chips we have. ESP32 needs data to be PRELOADED before the request is received. Fore reference are these lines in the slave example. That is why there is no delay between the address and the data. All other chips wait for the request to be handled and data written to the fifo, which is then returned. |
The issue i am having is with the ESP32-S3 as a slave. This is also happening if i program the S3 with IDF. |
I tried the code. the only change I made was to write two bytes, because that is how many you read in your master sketch and the result is as expected
|
Thank you. As a further experiment into this, if you request 5 bytes and the slave writes 4 bytes the response fluctuates: requestFrom: 5 Why is the slave address appearing in the data? |
where is it appearing? the slave address is |
Please make sure that you write as many bytes as will be read. That is important, else you are reading random bytes in the fifo |
After some playing around i noticed that if the ESP32 S3 slave does not write to the fifo then what is read by the ESP32 master is the left bit shifted address of the slave + 1. Does this mean that on the previous examples the ESP32 S3 slave is to slow to populate the fifo? I hope i am not being annoying, am truly trying to figure this out. Thanks for all the help. |
You must read as many bytes as you wrote. That is all. No other tricks are necessary |
I will try that. Thank you. |
Same issue happens irregularly. Master code: #include "Wire.h"
#define I2C_DEV_ADDR 0x30
typedef union { // union for sending int via i2c
int i;
byte b[4];
} Dados_I;
typedef union { // union for sending float via i2c
float f;
byte b[4];
} Dados_F;
typedef union { // union for sending a boolean via i2c
bool bo;
byte b[1];
} Dados_Bool;
// values that are requested from the smart battery
const int req_health = 1; // battery health
const int req_voltage = 2; // total battery voltage
const int req_cell_count = 3; // cell count
const int req_rem_capacity_pct = 4; // remaining capacity in percentage
const int req_cycle_count = 5; // cycle count
const int req_current = 6; // battery current
const int req_consumed_mah = 7; // consumed capacity in mah
const int req_consumed_wh = 8; // consumed capacity in wh
const int req_temperature = 9; // battery temperature
const int req_cell_voltage_1 = 11; // cell voltages for each of the cells
const int req_cell_voltage_2 = 12;
const int req_cell_voltage_3 = 13;
const int req_cell_voltage_4 = 14;
const int req_cell_voltage_5 = 15;
const int req_cell_voltage_6 = 16;
const int req_cell_voltage_7 = 17;
const int req_cell_voltage_8 = 18;
const int req_cell_voltage_9 = 19;
const int req_cell_voltage_10 = 20;
const int req_cell_voltage_11 = 21;
const int req_cell_voltage_12 = 22;
const int req_cell_voltage_13 = 23;
const int req_cell_voltage_14 = 24;
const int req_cell_voltage_15 = 25;
const int req_cell_voltage_16 = 26;
const int req_cell_voltage_17 = 27;
const int req_cell_voltage_18 = 28;
const int req_cell_voltage_19 = 29;
const int req_cell_voltage_20 = 30;
const int req_cell_voltage_21 = 31;
const int req_cell_voltage_22 = 32;
const int req_cell_voltage_23 = 33;
const int req_cell_voltage_24 = 34;
Dados_Bool health; // local variable where the batttery health is stored
Dados_F voltage; // local variable where the batttery voltage is stored (V)
Dados_I cell_count; // local variable where the batttery cell count is stored
Dados_I rem_capacity_pct; // local variable where the batttery capacity is stored (%)
Dados_I cycle_count; // local variable where the batttery cycle count is stored
Dados_F current; // local variable where the batttery current count is stored (A)
Dados_F consumed_mah; // local variable where the batttery consumed mah is stored (mah)
Dados_F consumed_wh; // local variable where the batttery consumed wh is stored (wh)
Dados_F temperature; // local variable where the batttery temperature is stored (c)
Dados_I cell_voltage_1; // local variables where the batttery cell voltages are stored (mv)
Dados_I cell_voltage_2;
Dados_I cell_voltage_3;
Dados_I cell_voltage_4;
Dados_I cell_voltage_5;
Dados_I cell_voltage_6;
Dados_I cell_voltage_7;
Dados_I cell_voltage_8;
Dados_I cell_voltage_9;
Dados_I cell_voltage_10;
Dados_I cell_voltage_11;
Dados_I cell_voltage_12;
Dados_I cell_voltage_13;
Dados_I cell_voltage_14;
Dados_I cell_voltage_15;
Dados_I cell_voltage_16;
Dados_I cell_voltage_17;
Dados_I cell_voltage_18;
Dados_I cell_voltage_19;
Dados_I cell_voltage_20;
Dados_I cell_voltage_21;
Dados_I cell_voltage_22;
Dados_I cell_voltage_23;
Dados_I cell_voltage_24;
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Wire.begin();
}
void loop() {
delay(500);
health.b[0] = i2cBoolGet(req_health);
voltage.f = i2cFloatGet(req_voltage);
cell_count.i = i2cIntGet(req_cell_count);
rem_capacity_pct.i = i2cIntGet(req_rem_capacity_pct);
cycle_count.i = i2cIntGet(req_cycle_count);
current.f = i2cFloatGet(req_current);
consumed_mah.f = i2cFloatGet(req_consumed_mah);
consumed_wh.f = i2cFloatGet(req_consumed_wh);
temperature.f = i2cFloatGet(req_temperature);
cell_voltage_1.i = i2cIntGet(req_cell_voltage_1);
cell_voltage_2.i = i2cIntGet(req_cell_voltage_2);
cell_voltage_3.i = i2cIntGet(req_cell_voltage_3);
cell_voltage_4.i = i2cIntGet(req_cell_voltage_4);
cell_voltage_5.i = i2cIntGet(req_cell_voltage_5);
cell_voltage_6.i = i2cIntGet(req_cell_voltage_6);
cell_voltage_7.i = i2cIntGet(req_cell_voltage_7);
cell_voltage_8.i = i2cIntGet(req_cell_voltage_8);
cell_voltage_9.i = i2cIntGet(req_cell_voltage_9);
cell_voltage_10.i = i2cIntGet(req_cell_voltage_10);
cell_voltage_11.i = i2cIntGet(req_cell_voltage_11);
cell_voltage_12.i = i2cIntGet(req_cell_voltage_12);
cell_voltage_13.i = i2cIntGet(req_cell_voltage_13);
cell_voltage_14.i = i2cIntGet(req_cell_voltage_14);
cell_voltage_15.i = i2cIntGet(req_cell_voltage_15);
cell_voltage_16.i = i2cIntGet(req_cell_voltage_16);
cell_voltage_17.i = i2cIntGet(req_cell_voltage_17);
cell_voltage_18.i = i2cIntGet(req_cell_voltage_18);
cell_voltage_19.i = i2cIntGet(req_cell_voltage_19);
cell_voltage_20.i = i2cIntGet(req_cell_voltage_20);
cell_voltage_21.i = i2cIntGet(req_cell_voltage_21);
cell_voltage_22.i = i2cIntGet(req_cell_voltage_22);
cell_voltage_23.i = i2cIntGet(req_cell_voltage_23);
cell_voltage_24.i = i2cIntGet(req_cell_voltage_24);
Serial.println("=====================");
Serial.print("health.bo = "); Serial.println(health.b[0]);
Serial.print("voltage.f = "); Serial.println(voltage.f);
Serial.print("cell_count.i = "); Serial.println(cell_count.i);
Serial.print("rem_capacity_pct.i = "); Serial.println(rem_capacity_pct.i);
Serial.print("cycle_count.i = "); Serial.println(cycle_count.i);
Serial.print("current.f = "); Serial.println(current.f);
Serial.print("consumed_mah.f = "); Serial.println(consumed_mah.f);
Serial.print("consumed_wh.f = "); Serial.println(consumed_wh.f);
Serial.print("temperature.f = "); Serial.println(temperature.f);
Serial.print("cell_voltage_1.i = "); Serial.println(cell_voltage_1.i);
Serial.print("cell_voltage_2.i = "); Serial.println(cell_voltage_2.i);
Serial.print("cell_voltage_3.i = "); Serial.println(cell_voltage_3.i);
Serial.print("cell_voltage_4.i = "); Serial.println(cell_voltage_4.i);
Serial.print("cell_voltage_5.i = "); Serial.println(cell_voltage_5.i);
Serial.print("cell_voltage_6.i = "); Serial.println(cell_voltage_6.i);
Serial.print("cell_voltage_7.i = "); Serial.println(cell_voltage_7.i);
Serial.print("cell_voltage_8.i = "); Serial.println(cell_voltage_8.i);
Serial.print("cell_voltage_9.i = "); Serial.println(cell_voltage_9.i);
Serial.print("cell_voltage_10.i = "); Serial.println(cell_voltage_10.i);
Serial.print("cell_voltage_11.i = "); Serial.println(cell_voltage_11.i);
Serial.print("cell_voltage_12.i = "); Serial.println(cell_voltage_12.i);
Serial.print("cell_voltage_13.i = "); Serial.println(cell_voltage_13.i);
Serial.print("cell_voltage_14.i = "); Serial.println(cell_voltage_14.i);
Serial.print("cell_voltage_15.i = "); Serial.println(cell_voltage_15.i);
Serial.print("cell_voltage_16.i = "); Serial.println(cell_voltage_16.i);
Serial.print("cell_voltage_17.i = "); Serial.println(cell_voltage_17.i);
Serial.print("cell_voltage_18.i = "); Serial.println(cell_voltage_18.i);
Serial.print("cell_voltage_19.i = "); Serial.println(cell_voltage_19.i);
Serial.print("cell_voltage_20.i = "); Serial.println(cell_voltage_20.i);
Serial.print("cell_voltage_21.i = "); Serial.println(cell_voltage_21.i);
Serial.print("cell_voltage_22.i = "); Serial.println(cell_voltage_22.i);
Serial.print("cell_voltage_23.i = "); Serial.println(cell_voltage_23.i);
Serial.print("cell_voltage_24.i = "); Serial.println(cell_voltage_24.i);
Serial.println();
}
byte i2cBoolGet(int command) {
Wire.beginTransmission(I2C_DEV_ADDR); // transmit to device #4
Wire.write(command); // sends one byte
Wire.endTransmission(); // stop transmitting
byte value[1];
Wire.requestFrom(I2C_DEV_ADDR, 1);
Wire.readBytes(value, 1);
return value[0];
}
int i2cIntGet(int command) {
Wire.beginTransmission(I2C_DEV_ADDR); // transmit to device #4
Wire.write(command); // sends one byte
Wire.endTransmission(); // stop transmitting
Dados_I value;
Wire.requestFrom(I2C_DEV_ADDR, 4);
Wire.readBytes(value.b, 4);
return value.i;
}
float i2cFloatGet(int command) {
Wire.beginTransmission(I2C_DEV_ADDR); // transmit to device #4
Wire.write(command); // sends one byte
Wire.endTransmission(); // stop transmitting
Dados_F value;
Wire.requestFrom(I2C_DEV_ADDR, 4);
Wire.readBytes(value.b, 4);
return value.f;
} Slave code: #include "Wire.h" // i2c library
#define I2C_DEV_ADDR 0x30 // i2c adress, needs to be the same as in the lua script
int opcode = 0; // received operation code
int i2cCommand = 0; // received i2c command
typedef union { // union for sending int via i2c
int i;
byte b[4];
} Dados_I;
typedef union { // union for sending float via i2c
float f;
byte b[4];
} Dados_F;
typedef union { // union for sending a boolean via i2c
bool bo;
byte b[1];
} Dados_Bool;
// values that are requested from the smart battery
const int req_health = 1; // battery health
const int req_voltage = 2; // total battery voltage
const int req_cell_count = 3; // cell count
const int req_rem_capacity_pct = 4; // remaining capacity in percentage
const int req_cycle_count = 5; // cycle count
const int req_current = 6; // battery current
const int req_consumed_mah = 7; // consumed capacity in mah
const int req_consumed_wh = 8; // consumed capacity in wh
const int req_temperature = 9; // battery temperature
const int req_cell_voltage_1 = 11; // cell voltages for each of the cells
const int req_cell_voltage_2 = 12;
const int req_cell_voltage_3 = 13;
const int req_cell_voltage_4 = 14;
const int req_cell_voltage_5 = 15;
const int req_cell_voltage_6 = 16;
const int req_cell_voltage_7 = 17;
const int req_cell_voltage_8 = 18;
const int req_cell_voltage_9 = 19;
const int req_cell_voltage_10 = 20;
const int req_cell_voltage_11 = 21;
const int req_cell_voltage_12 = 22;
const int req_cell_voltage_13 = 23;
const int req_cell_voltage_14 = 24;
const int req_cell_voltage_15 = 25;
const int req_cell_voltage_16 = 26;
const int req_cell_voltage_17 = 27;
const int req_cell_voltage_18 = 28;
const int req_cell_voltage_19 = 29;
const int req_cell_voltage_20 = 30;
const int req_cell_voltage_21 = 31;
const int req_cell_voltage_22 = 32;
const int req_cell_voltage_23 = 33;
const int req_cell_voltage_24 = 34;
Dados_Bool health; // local variable where the batttery health is stored
Dados_F voltage; // local variable where the batttery voltage is stored (V)
Dados_I cell_count; // local variable where the batttery cell count is stored
Dados_I rem_capacity_pct; // local variable where the batttery capacity is stored (%)
Dados_I cycle_count; // local variable where the batttery cycle count is stored
Dados_F current; // local variable where the batttery current count is stored (A)
Dados_F consumed_mah; // local variable where the batttery consumed mah is stored (mah)
Dados_F consumed_wh; // local variable where the batttery consumed wh is stored (wh)
Dados_F temperature; // local variable where the batttery temperature is stored (c)
Dados_I cell_voltage_1; // local variables where the batttery cell voltages are stored (mv)
Dados_I cell_voltage_2;
Dados_I cell_voltage_3;
Dados_I cell_voltage_4;
Dados_I cell_voltage_5;
Dados_I cell_voltage_6;
Dados_I cell_voltage_7;
Dados_I cell_voltage_8;
Dados_I cell_voltage_9;
Dados_I cell_voltage_10;
Dados_I cell_voltage_11;
Dados_I cell_voltage_12;
Dados_I cell_voltage_13;
Dados_I cell_voltage_14;
Dados_I cell_voltage_15;
Dados_I cell_voltage_16;
Dados_I cell_voltage_17;
Dados_I cell_voltage_18;
Dados_I cell_voltage_19;
Dados_I cell_voltage_20;
Dados_I cell_voltage_21;
Dados_I cell_voltage_22;
Dados_I cell_voltage_23;
Dados_I cell_voltage_24;
void setup() {
Serial.begin(115200); // start the serial port, used for debuging
Serial.setDebugOutput(true);
Wire.begin(I2C_DEV_ADDR, 42, 41, 0 ); // start the i2c channel with the I2C_DEV_ADDR adress
Wire.onReceive(onReceive); // if there is a i2c receive call then onReceive function is called
Wire.onRequest(onRequest); // if there is a i2c request call then onRequest function is called
Serial.println("setup");
}
void loop() {
setValues();
}
void onReceive(int len) { // on receive function, used to update local variables or activate local functions
Serial.print("receive[");
Serial.print(len);
Serial.print("]: ");
i2cCommand = 0; // resets the command
opcode = Wire.read(); // reads the operation code
Serial.print(opcode);
if (len == 2) { // if there are 2 bytes
i2cCommand = Wire.read(); // the second byte is the command
Serial.print(", ");
Serial.print(i2cCommand);
}
Serial.println();
}
void onRequest() { // on request function, sends data via i2c depeding on the request
Serial.print("request: ");
Serial.println(opcode);
loadValues();
}
void setValues(){
health.bo = true;
voltage.f = 35.5;
cell_count.i = 10;
rem_capacity_pct.i = 55;
cycle_count.i = 10;
current.f = 40.35;
consumed_mah.f = 20.2;
consumed_wh.f = 11.3;
temperature.f = 30.2;
cell_voltage_1.i = 3000;
cell_voltage_2.i = 3000;
cell_voltage_3.i = 3000;
cell_voltage_4.i = 3000;
cell_voltage_5.i = 3000;
cell_voltage_6.i = 3000;
cell_voltage_7.i = 3000;
cell_voltage_8.i = 3000;
cell_voltage_9.i = 3000;
cell_voltage_10.i = 3000;
cell_voltage_11.i = 4;
cell_voltage_12.i = 4;
cell_voltage_13.i = 4;
cell_voltage_14.i = 4;
cell_voltage_15.i = 4;
cell_voltage_16.i = 4;
cell_voltage_17.i = 4;
cell_voltage_18.i = 4;
cell_voltage_19.i = 4;
cell_voltage_20.i = 4;
cell_voltage_21.i = 4;
cell_voltage_22.i = 4;
cell_voltage_23.i = 4;
cell_voltage_24.i = 4;
}
void loadValues(){
switch (opcode) {
case req_health:
Wire.write(health.b, 1);
break;
case req_voltage:
Wire.write(voltage.b, 4);
break;
case req_cell_count:
Wire.write(cell_count.b, 4);
break;
case req_rem_capacity_pct:
Wire.write(rem_capacity_pct.b, 4);
break;
case req_cycle_count:
Wire.write(cycle_count.b, 4);
break;
case req_current:
Wire.write(current.b, 4);
break;
case req_consumed_mah:
Wire.write(consumed_mah.b, 4);
break;
case req_consumed_wh:
Wire.write(consumed_wh.b, 4);
break;
case req_temperature:
Wire.write(temperature.b, 4);
break;
case req_cell_voltage_1:
Wire.write(cell_voltage_1.b, 4);
break;
case req_cell_voltage_2:
Wire.write(cell_voltage_2.b, 4);
break;
case req_cell_voltage_3:
Wire.write(cell_voltage_3.b, 4);
break;
case req_cell_voltage_4:
Wire.write(cell_voltage_4.b, 4);
break;
case req_cell_voltage_5:
Wire.write(cell_voltage_5.b, 4);
break;
case req_cell_voltage_6:
Wire.write(cell_voltage_6.b, 4);
break;
case req_cell_voltage_7:
Wire.write(cell_voltage_7.b, 4);
break;
case req_cell_voltage_8:
Wire.write(cell_voltage_8.b, 4);
break;
case req_cell_voltage_9:
Wire.write(cell_voltage_9.b, 4);
break;
case req_cell_voltage_10:
Wire.write(cell_voltage_10.b, 4);
break;
case req_cell_voltage_11:
Wire.write(cell_voltage_11.b, 4);
break;
case req_cell_voltage_12:
Wire.write(cell_voltage_12.b, 4);
break;
case req_cell_voltage_13:
Wire.write(cell_voltage_13.b, 4);
break;
case req_cell_voltage_14:
Wire.write(cell_voltage_14.b, 4);
break;
case req_cell_voltage_15:
Wire.write(cell_voltage_15.b, 4);
break;
case req_cell_voltage_16:
Wire.write(cell_voltage_16.b, 4);
break;
case req_cell_voltage_17:
Wire.write(cell_voltage_17.b, 4);
break;
case req_cell_voltage_18:
Wire.write(cell_voltage_18.b, 4);
break;
case req_cell_voltage_19:
Wire.write(cell_voltage_19.b, 4);
break;
case req_cell_voltage_20:
Wire.write(cell_voltage_20.b, 4);
break;
case req_cell_voltage_21:
Wire.write(cell_voltage_21.b, 4);
break;
case req_cell_voltage_22:
Wire.write(cell_voltage_22.b, 4);
break;
case req_cell_voltage_23:
Wire.write(cell_voltage_23.b, 4);
break;
case req_cell_voltage_24:
Wire.write(cell_voltage_24.b, 4);
break;
default:
break;
}
} Notice the decimal 97 =0x61 when i request the health byte and the 768097 value, if you convert it into hex you get 0x0B, 0xB8 and 0x61, the value that is the slave adress bit shifted left + 1. There are no tricks, I send the same amount of bytes that are requested. It fixes itself if i reset the slave. This is not optimal. |
I don't know why the code breaks when i paste it into the code tags. I'm sorry for that. |
One suggestion for your use case. Use void onReceive(int len) { // on receive function, used to update local variables or activate local functions
i2cCommand = 0; // resets the command
opcode = Wire.read(); // reads the operation code
if (len == 2) { // if there are 2 bytes
i2cCommand = Wire.read(); // the second byte is the command
}
loadValues(); // load the values here
}
void onRequest() {
}
void loadValues(){
switch (opcode) {
case req_health:
Wire.slaveWrite(health.b, 1);
break;
case req_voltage:
Wire.slaveWrite(voltage.b, 4);
break;
............ |
Wonderful. Thank you. |
@JorgeMALopes any news? |
It worked with the code as is. But stops working when i add the rest. As soon as i add the BQ769142 code to it the communication with the flight controller breaks. Thanks for all the help. |
Still no success with the regular ESP32. Can an ESP32 be an i2c master on one bus and obtain data from a bunch of sensors, do some calculations and then have it available as a slave on a separate bus? Thanks for all the help. |
yes it can |
Ok, going to try and figure that out. |
Figured it out. Tried it with a S3, it works. Modified my main code, did not work with either esp32 or S3. Start hunting down the culprit. Found it. It was trying to write to a position outside of an array. There is no bug in the I2C bus on the S3, it just behaves differently when compared to the regular ESP32, but still works fine as long as you ask as many bytes as you write on the other side. I think i can finish the rest of the code. I think this thread can be closed now. |
Board
ESP32 S3 wroom 1
Device Description
Just an USB port, and a TI battery monitor.
Hardware Configuration
Currently only working with just the CPU, but has a TI battery monitor connected to the other I2C port.
Version
v3.0.3
IDE Name
Arduino IDE
Operating System
windows 10
Flash frequency
80MHz
PSRAM enabled
no
Upload speed
921600
Description
When i connect an ESP32S3 as an I2C slave to an ESP32 or a drone flight controller, as a master, the data comes out wrong.
When i reverse the code and the S3 is the master and the ESP32 is the slave then the data comes out correct.
I made a simple sketch where the slave returns 4 bytes when there is a request, 0x01FF0306 or 0x01FF03FF. The master requests a variable amount of bytes, sometimes more, sometimes less. When the S3 is the slave the first byte received from it is the slave address followed by part of the slave data. When the ESP32 is the slave then only data is returned.
I have asked a friend to lend me his oscilloscope so i can check the data lines for each case.
These are the lines for the S3 as a slave, notice the long time after the request and the repetition of the slave address.



This is the lines for ESP32 as slave, the delay is minimal and there is no repetition of the slave address.

The most stable case was then the returned bytes where of the same quantity as the requested ones, but sometimes the device address appears in the beginning and the only way to fix it is to reset the S3 which is not a viable solution.
I have tested with wire and wire1 and tested with other connections on the S3.
Sketch
Debug Message
Other Steps to Reproduce
No response
I have checked existing issues, online documentation and the Troubleshooting Guide
The text was updated successfully, but these errors were encountered: