espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.68k stars 7.42k forks source link

ESP32 S3 I2C issues when working as slave #10145

Open JorgeMALopes opened 3 months ago

JorgeMALopes commented 3 months ago

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. scope_2 scope_1 scope_0

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

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

-----------------------------------Slave code:-----------------------------

#include "Wire.h"             // i2c library

#define I2C_DEV_ADDR 0x30     // i2c adress, needs to be the same as in the lua script

typedef union {         // union for sending int via i2c
  int i;
  byte b[4];
} Dados_I;

Dados_I batata;

void setup() {
  Serial.begin(115200);        // start the serial port, used for debuging
  Serial.setDebugOutput(true);

  Wire.begin(I2C_DEV_ADDR,13,14,0);// start the i2c channel with the I2C_DEV_ADDR adress
  Wire.onRequest(onRequest);    // if there is a i2c request call then onRequest function is called
  Serial.println("setup");

  batata.i=0x01FF0306;
}

void loop() {
  // put your main code here, to run repeatedly:
}

void onRequest() {          // on request function, sends data via i2c depeding on the request
 Wire.write(batata.b, 4);
}

-----------------------------------master code:-----------------------------

#include "Wire.h"

#define I2C_DEV_ADDR 0x30

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

void loop() {
  delay(100);

  uint8_t bytesReceived = Wire.requestFrom(I2C_DEV_ADDR, 2);
  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);
  }
}

Debug Message

--------------ESP32 master:-------------

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1288
load:0x40078000,len:13872
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3048
entry 0x40080590
[    16][D][esp32-hal-cpu.c:264] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
=========== Before Setup Start ===========
Chip Info:
------------------------------------------
  Model             : ESP32
  Package           : D0WD-Q5
  Revision          : 1.44
  Cores             : 2
  CPU Frequency     : 240 MHz
  XTAL Frequency    : 40 MHz
  Embedded Flash    : No
  Embedded PSRAM    : No
  2.4GHz WiFi       : Yes
  Classic BT        : Yes
  BT Low Energy     : Yes
  IEEE 802.15.4     : No
-----------------------------------------INTERNAL Memory Info:
------------------------------------------
  Total Size        :   382260 B ( 373.3 KB)
  Free Bytes        :   352228 B ( 344.0 KB)
  Allocated Bytes   :    22980 B (  22.4 KB)
  Minimum Free Bytes:   346716 B ( 338.6 KB)
  Largest Free Block:   118772 B ( 116.0 KB)
------------------------------------------
Flash Info:
------------------------------------------
  Chip Size         :  4194304 B (4 MB)
  Block Size        :    65536 B (  64.0 KB)
  Sector Size       :     4096 B (   4.0 KB)
  Page Size         :      256 B (   0.2 KB)
  Bus Speed         : 80 MHz
  Bus Mode          : DIO
------------------------------------------
Partitions Info:
------------------------------------------
                nvs : addr: 0x00009000, size:    20.0 KB, type: DATA, subtype: NVS
            otadata : addr: 0x0000E000, size:     8.0 KB, type: DATA, subtype: OTA
               app0 : addr: 0x00010000, size:  1280.0 KB, type:  APP, subtype: OTA_0
               app1 : addr: 0x00150000, size:  1280.0 KB, type:  APP, subtype: OTA_1
             spiffs : addr: 0x00290000, size:  1408.0 KB, type: DATA, subtype: SPIFFS
           coredump : addr: 0x003F0000, size:    64.0 KB, type: DATA, subtype: COREDUMP
------------------------------------------
Software Info:
------------------------------------------
  Compile Date/Time : Aug 12 2024 10:15:22
  Compile Host OS   : windows
  ESP-IDF Version   : v5.1.4-497-gdc859c1e67-dirty
  Arduino Version   : 3.0.3
------------------------------------------
Board Info:
------------------------------------------
  Arduino Board     : ESP32_DEV
  Arduino Variant   : doitESP32devkitV1
  Arduino FQBN      : esp32:esp32:esp32doit-devkit-v1:UploadSpeed=921600,FlashFreq=80,DebugLevel=debug,EraseFlash=none
============ Before Setup End ============
[   515][I][esp32-hal-i2c.c:109] i2cInit(): Initializing I2C Master: sda=21 scl=22 freq=100000
=========== After Setup Start ============
INTERNAL Memory Info:
------------------------------------------
  Total Size        :   382260 B ( 373.3 KB)
  Free Bytes        :   349208 B ( 341.0 KB)
  Allocated Bytes   :    25552 B (  25.0 KB)
  Minimum Free Bytes:   343696 B ( 335.6 KB)
  Largest Free Block:   118772 B ( 116.0 KB)
------------------------------------------
GPIO Info:
------------------------------------------
  GPIO : BUS_TYPE[bus/unit][chan]
  --------------------------------------  
     1 : UART_TX[0]
     3 : UART_RX[0]
    21 : I2C_MASTER_SDA[0]
    22 : I2C_MASTER_SCL[0]
============ After Setup End =============

-----------------esp32 S3 slave------------

CPU Frequency     : 240 MHz
setup
=========== Before Setup Start ===========
Chip Info:
------------------------------------------
  Model             : ESP32-S3
  Package           : 0
  Revision          : 2
  Cores             : 2
  CPU Frequency     : 240 MHz
  XTAL Frequency    : 40 MHz
  Embedded Flash    : No
  Embedded PSRAM    : No
  2.4GHz WiFi       : Yes
  Classic BT        : No
  BT Low Energy     : Yes
  IEEE 802.15.4     : No
------------------------------------------
INTERNAL Memory Info:
------------------------------------------
  Total Size        :   396660 B ( 387.4 KB)
  Free Bytes        :   364480 B ( 355.9 KB)
  Allocated Bytes   :    27292 B (  26.7 KB)
  Minimum Free Bytes:   359432 B ( 351.0 KB)
  Largest Free Block:   327668 B ( 320.0 KB)
------------------------------------------
Flash Info:
------------------------------------------
  Chip Size         : 16777216 B (16 MB)
  Block Size        :    65536 B (  64.0 KB)
  Sector Size       :     4096 B (   4.0 KB)
  Page Size         :      256 B (   0.2 KB)
  Bus Speed         : 80 MHz
  Bus Mode          : QIO
------------------------------------------
Partitions Info:
------------------------------------------
                nvs : addr: 0x00009000, size:    20.0 KB, type: DATA, subtype: NVS
            otadata : addr: 0x0000E000, size:     8.0 KB, type: DATA, subtype: OTA
               app0 : addr: 0x00010000, size:  3072.0 KB, type:  APP, subtype: OTA_0
               app1 : addr: 0x00310000, size:  3072.0 KB, type:  APP, subtype: OTA_1
               ffat : addr: 0x00610000, size: 10112.0 KB, type: DATA, subtype: FAT
           coredump : addr: 0x00FF0000, size:    64.0 KB, type: DATA, subtype: COREDUMP
------------------------------------------
Software Info:
------------------------------------------
  Compile Date/Time : Aug 12 2024 10:26:11
  Compile Host OS   : windows
  ESP-IDF Version   : v5.1.4-497-gdc859c1e67-dirty
  Arduino Version   : 3.0.3
------------------------------------------
Board Info:
------------------------------------------
  Arduino Board     : ESP32S3_DEV
  Arduino Variant   : esp32s3
  Arduino FQBN      : esp32:esp32:esp32s3:UploadSpeed=921600,USBMode=hwcdc,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,CPUFreq=240,FlashMode=qio,FlashSize=16M,PartitionScheme=app3M_fat9M_16MB,DebugLevel=debug,PSRAM=disabled,LoopCore=1,EventsCore=1,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default
============ Before Setup End ============
[  1291][I][esp32-hal-periman.c:141] perimanSetPinBus(): Pin 19 already has type USB_DM (45) with bus 0x3fc965c4
[  1291][I][esp32-hal-periman.c:141] perimanSetPinBus(): Pin 20 already has type USB_DP (46) with bus 0x3fc965c4
[  1300][I][esp32-hal-i2c-slave.c:250] i2cSlaveInit(): Initializing I2C Slave: sda=14 scl=21 freq=100000, addr=0x30
[  1310][D][esp32-hal-i2c-slave.c:514] i2c_slave_set_frequency(): Fifo thresholds: rx_fifo_full = 28, tx_fifo_empty = 4
setup
=========== After Setup Start ============
INTERNAL Memory Info:
------------------------------------------
  Total Size        :   396660 B ( 387.4 KB)
  Free Bytes        :   358416 B ( 350.0 KB)
  Allocated Bytes   :    33052 B (  32.3 KB)
  Minimum Free Bytes:   353320 B ( 345.0 KB)
  Largest Free Block:   327668 B ( 320.0 KB)
------------------------------------------
GPIO Info:
------------------------------------------
  GPIO : BUS_TYPE[bus/unit][chan]
  --------------------------------------  
    14 : I2C_SLAVE_SDA[0]
    19 : USB_DM
    20 : USB_DP
    21 : I2C_SLAVE_SCL[0]
    43 : UART_TX[0]
    44 : UART_RX[0]
============ After Setup End =============

Other Steps to Reproduce

No response

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

JorgeMALopes commented 2 months ago

esp32 S3

I got a pc based logic analyzer and an ESP32 S3 devkitc devboard and using the same code the same issue happens.

me-no-dev commented 2 months ago

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. In the code above, ESP32 returns the previous Wire.write.

JorgeMALopes commented 2 months ago

The issue i am having is with the ESP32-S3 as a slave. The first byte that the S3 slave sends is the slave address and not the data that was requested and it is trying to write with Wire.write(batata.b, 4); in the onRequest(). This happens even when the master is an STM arm based flight controller.

This is also happening if i program the S3 with IDF.

me-no-dev commented 2 months ago

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

requestFrom: 2
0x06, 0x03, // ..
JorgeMALopes commented 2 months ago

Thank you. I have noticed that works in this case. The code i put here is a very simplified version of what i am making and in that sometimes the data comes clean and sometimes it comes contaminated with a random byte. What confuses me is that the random byte that appears is the slave address byte. Is this a normal thing?

As a further experiment into this, if you request 5 bytes and the slave writes 4 bytes the response fluctuates:

requestFrom: 5 0x61, 0x61, 0x06, 0x03, 0x03, // aa... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x06, 0x03, 0x03, 0x03, 0x03, // ..... requestFrom: 5 0x61, 0x61, 0x61, 0x61, 0x61, // aaaaa requestFrom: 5

Why is the slave address appearing in the data?

me-no-dev commented 2 months ago

where is it appearing? the slave address is 0x30 not 0x03

me-no-dev commented 2 months ago

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

JorgeMALopes commented 2 months ago

The slave address is 0x30, if you bit shift it left once and put in the read bit it becomes 0x61. This follows when i change the slave address, 0x10 becomes 0x21. This is the data that is poluting the response.
Here is a capture of the data, notice that the first byte that is received as data is the same as the address byte plus the read bit. adrees in data

Also I got it to get that random byte to appear even when i request the same size of data that the slave is writing.

bad data 4

I did one request for 1 byte, uploaded to the esp32 master and then changed the code and requested 4 bytes and uploaded to esp32 master, now it is locked in this situation. It comes out of this situation when i reset the S3.

Also it is kind of weird that if i place 4 known bytes in the fifo and read 3 bytes the first byte is the device address bit shifted left once plus the read bit.

JorgeMALopes commented 2 months ago

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.

Capturarar

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. I am trying to avoid having to replace the ESP32 S3 with something else that is more stable.

Thanks for all the help.

me-no-dev commented 2 months ago

You must read as many bytes as you wrote. That is all. No other tricks are necessary

JorgeMALopes commented 2 months ago

I will try that. Thank you.

JorgeMALopes commented 2 months ago

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;
  }
}

What i should be getting is: original

I am getting this: outputdata

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.

JorgeMALopes commented 2 months ago

I don't know why the code breaks when i paste it into the code tags. I'm sorry for that.

me-no-dev commented 2 months ago

I have slightly revised your example above. Mostly prints and it's functionally the same. I use the same pins and all.

Screenshot 2024-09-11 at 12 27 34
me-no-dev commented 2 months ago

One suggestion for your use case. Use Wire.slaveWrite(data, len) instead of Wire.write(data, len). Then you can load the values directly in the onReceive callback. That will make communication faster too. Also do not print inside the callbacks. It takes time and slows things down.

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;
 ............
JorgeMALopes commented 2 months ago

Wonderful. Thank you. I will try it now.

me-no-dev commented 2 months ago

@JorgeMALopes any news?

JorgeMALopes commented 2 months ago

It worked with the code as is. But stops working when i add the rest. In the device i am making the ESP32S3 is an interpreter for a BQ769142. The S3 controls and gets the data from the BQ769142, via I2C as a master, does some simple calculations and then feeds it via I2C to a flight controller as slave.

As soon as i add the BQ769142 code to it the communication with the flight controller breaks. I am going to desolder the S3 from the board and wire up a standard ESP32 and try that. If i have success i will inform. For now i think the S3 is not a viable device for an i2c slave.

Thanks for all the help.

JorgeMALopes commented 2 months ago

Still no success with the regular ESP32. I have a question but i am unsure if i should create a new thread elsewhere or ask here so i can figure out how to fix this or if this is fixable.

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?

links

Thanks for all the help.

me-no-dev commented 2 months ago

yes it can

JorgeMALopes commented 2 months ago

Ok, going to try and figure that out. Thanks for everything.

JorgeMALopes commented 2 months ago

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. Thanks for all the help!!