Arduino IDE, ESP32, I2C Timeout problem

I can't set the i2c timeout to values lower than 1000 millis.
wire.setTimeOut(n) with n<1000 also becomes 1000.
Why ?

Is your board a ESP32 ?

Which timeout are you using where and how and why do you want to use a timeout ?

The Arduino Uno has a timeout for the low-level I2C code: setWireTimeout() - Arduino Reference
The Stream class has a timeout for Serial communication (useless for the I2C bus): Stream.setTimeout() - Arduino Reference

The ESP32 uses the function name "setTimeOut" (as in the Stream class) and uses that for low level I2C code. I think that is wrong. arduino-esp32/Wire.cpp at master · espressif/arduino-esp32 · GitHub

The timeout of the Stream class has a default of 1 second (1000 milliseconds) and the timeout for low level I2C code for the Arduino Uno is in microseconds.

I think that somehow there is a mix between these things. Can you show your sketch ?
If you need a timeout, then you first have to fix your I2C bus. The I2C bus is not fault tolerant. You can not rely on a timeout to fix problems of the I2C bus.
I hope that you are not using the I2C bus between two Arduino boards, that would be a bad idea.

ESP32 is. In a sketch that does several tasks.
It also communicates with an i2C sensor module.
I2C blocks the ESP32 in I2Cread() for 1 sec if the sensor is disconnected.
I made the rest of the I2C operations non-blocking.
But I can't change the timeout to I2Cread() in case the sensor is disconnected.
1 sec. it is very much and unnecessary.

put the code to read the sensor in its own task. Have the result set of the read returned in a queue. Now the code can process other things while waiting for the i2c read to return a result set.

One can use the ESP32's I2C API, Inter-Integrated Circuit (I2C) - ESP32 - — ESP-IDF Programming Guide latest documentation, for greater I2C control.

Consider the following task

void fParseDewPointWindChill( void *pvParameters )
{
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  struct stu_message px_message;
  String sDewPoint = "";
  String sWindChill = "";
  sDewPoint.reserve( payloadSize );
  sWindChill.reserve( payloadSize );
  for (;;)
  {
    if ( xQueueReceive(xQ_WindChillDewPoint, &px_message, portMAX_DELAY) == pdTRUE )
    {
      sDewPoint = px_message.payload;
      int commaIndex = sDewPoint.indexOf(',');
      sWindChill.concat ( sDewPoint.substring(0, commaIndex) );
      sDewPoint.remove( 0, (commaIndex + 1) );
      xSemaphoreTake ( sema_eData, portMAX_DELAY );
      x_eData.WindChill = sWindChill.toFloat();
      xSemaphoreGive( sema_eData );
      xEventGroupSetBits( eg, evtDewPoint );
      sDewPoint = "";
      sWindChill = "";
    }
    //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}

That just sits there waiting for data. Data is requested elsewhere, when the data becomes available the data is put into a Queue. When the queue has data the queue signals the task of the presence of data, the task then runs.

A request for data was made, code processing continues instead of waiting for the data to become available, other things are done in the meantime.

Ok and thanks @Idahowalker ,
I can put i2c_read in another task, but I don't understand why the timeout was limited to a minimum of 1000 millis.
I only need to read 4 bytes to see that I have communication.
Create_task for a simple i2c ?

What are the functions "I2Cread()" and "i2c_read" ?
Can you show the sketch ?

I can mention dozens of problems, but it would be easier if you show the sketch instead of me trying to guess what your sketch is doing.
My guess is that you see the I2C bus as a bus for serial data and use the Arduino stream class to wait for something when there is nothing to wait for. Some use waiting after a Wire.requestFrom(), perhaps that uses the maximum timeout if there is no data available. Some create an extra object TwoWire parallel with the existing one. Seriously, I can really mention dozens of problems.

The I2C bus is not plug-and-play. You are not supposed to disconnect a sensor.

There is nothing special in my sketch.

TwoWire::requestFrom(..., timeOutMillis, ...) from Wire.cpp
who calls:
i2cRead(uint8_t i2c_num, uint16_t address, uint8_t* buff, size_t size, uint32_t timeOutMillis, size_t *readCount) from i2c-hal-i2c.c

next:
i2c_master_read_from_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_RATE_MS) from libdriver.a

timeOutMiilis cannot be smaller that 1000.

This is my problem. Why ?

You are using something very very very very very (5 times very) special :bangbang:

I have not heard of "TwoWire::requestFrom(..., timeOutMillis, ...)" before and even Google can not find the file "i2c-hal-i2c.c".

Mayby you are using the second parameter for the number of bytes as timeout :question:

The ESP32 has a Wire library that is compatible with the Arduino Wire library: arduino-esp32/Wire.h at master · espressif/arduino-esp32 · GitHub
However, there is a problem with the low-level timeout versus the Stream timeout that I described. It might have to do with your problem if you hit the 1 second timeout.

Can you show your sketch ? I would like to see the usage of the library and the initialization and the includes.
Can you show a photo of your board ? I start to doubt if you have a ESP32.

perhaps looking at the I2C code may provide reason why.

So I used the words "arduino wire library github" to get to the library. Which gets me to ArduinoCore-avr/Wire.cpp at master · arduino/ArduinoCore-avr · GitHub where you can look at how the wire library does its thing. Where with some reading you may find out the why or not. Good luck with your research and let us know what you find.

Here is another useful link

It's important to learn how to do your own research.

I think I am not understood. My mistake. My english.
The question was simple:
Why does the i2c arduino ide implementation for ESP32 not allow a read timeout lower than 1000 milliesc?
Did someone else succeed under 1 sec ?

@18f We understand your question. But your question raises an eyebrow here and there. You are asking about something that does not seem to exist. The ESP32 has a timeout, but you do not show a sketch that uses it. The parameter for a timeout in the Wire.requestFrom() function does not seem to exist.
Did you write the code ? Perhaps you have code from someone else who wrote bad code. Please show your sketch.

We would add a check with Wire.beginTransmission() and Wire.endTransmission() to check if something is still connected to the I2C bus.

You have also not explained why you want to disconnect something from the I2C bus. It is not that kind of bus.

Hi, have you found a solution? Hope the eyebrows didn't scare you away :slight_smile:

It seems I have a similar (or even the same?) problem and would be interested to hear your status.

My usecase: ESP32 reading gyro and acceleration data from an MPU6050. It mostly works, but if I have to wait 1s on errors, all hope to recover is lost. Unfortunately that is exactly what's happening:
I have occasional read timeouts of 1003 to 1004 ms where my usual loop time is ~3 ms.

Hard to dig in because the library doesn't do much error handling. I think about writing the code to talk to the 6050 myself, at least the register read loop. But not yet...

What I found out so far:

If I do a Wire.setTimeOut(30) at the beginning of setup() I can do a Wire.getTimeOut() at the end of initializing the 6050 and still see the 30ms (standard is 50ms). The actual timeout on errors (simulated by toggling scl/sda contact) stays at 1s which somewhat matches your description.

If I do the Wire.setTimeOut(30) after the device init just before entering the read loop, I see shorter timeouts. But then toggling scl/sda contact sometimes isn't even noticed anymore (except for receiving wrong data). At least better than nothing because you can act on it and maybe it helps you.

Thanks to Koepel for pointing out Stream::setTimeout(). Its default of 1s is pretty suspicious. Setting that too will be my next test...

Hi,
I put I2C in other Core :slight_smile:

The I2C bus is not a fault tolerant bus. You have to fix your I2C bus.
The I2C bus was not designed to go through a cable.

Recovering from something bad on the I2C bus is not part of the I2C standard, because that is not supposed to happen. So there is no need to do error recovery.

Well, some things can be detected, and sometimes a retry is possible, but that is like navigating between the icebergs, there are no guarantees.

The solution: See the I2C bus as this: one error a day means your I2C bus is 100% bad.
Can you show a photo with the wires ? How is everything powered ? Are the voltage conflicts on the I2C bus ? and so on, and so on.

Is it working now ?
Does that mean that there is a bug somewhere that caused a conflict ?

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