Arduino run is halted while waiting for I2C slave to respond

In this project, an Arduino Nano master is controlling two Arduino Nano slaves using I2C.
Mostly it is working well.
The project is disassembled and assembled often, also works fine, unless due to a human mistake one of the slaves is not well connected to the system.

On setup phase, there is a special check to ensure all is connected.
Yet in case it is not, i notice the program is waiting forever for the response.

The code of the check routine at the master is below.
Is there a way to tell the endTransmission to limit the waiting time for a response from a device that may not exist?

// ****************** I2C_check **********************
boolean I2C_check(int dev)
{
  byte error;

  Wire.beginTransmission(dev);
  error = Wire.endTransmission();

  if (error == 0)
    return true;
  else
    return false;
}

Yes it does. The Wire.endTransmission() halts when SDA or SCL is shortcut to GND or shortcut to each other. Because of this, I think that Arduino is still a hobby project. Being able to stop a sketch with an external event is a shame.

How long are your wires ?

Others have made libraries with timeouts, but then a few bugs were fixed in the Arduino Wire libary and the other libraries didn't keep them up to date. At this moment I don't know a good library with timeouts that I can recommend.

You could do a check before Wire.begin().

The next code is something that I made up just now. It is not a proven method.

// The pins SDA and SCL are defined and are the pins of the I2C bus.
// The weak internal pullup resistor should be enough to make it high.
// A delay is added to give it time to go high with a weak internal pullup resistor.
// A byte needs 9 clock pulses, to be sure 20 clock pulses are given.
// While keeping the SDA high, a rising SCL is also a STOP on the bus.
// The SCL is never made a hard HIGH for safety, it toggles as if it was a open-collector output.

// Check if SDA or SCL is stuck to GND
pinMode( SDA, INPUT_PULLUP);
pinMode( SCL, INPUT_PULLUP);
delay( 1);
if( digitalRead( SDA) == LOW)
  Serial.println( "Error, SDA is low");
if( digitalRead( SCL) == LOW)
  Serial.println( "Error, SCL is low");

// Check if SDA and SCL are shortcut to each other
digitalWrite( SCL, LOW);
pinMode( SCL, OUTPUT);
delay( 1);
if( digitalRead( SDA) == LOW)
  Serial.println( "Error, SDA is shortcut to SCL");
pinMode( SCL, INPUT_PULLUP);

// Give clock pulses while keeping SDA high.
for( i=0; i<20; i++)
{
  digitalWrite( SCL, LOW);     // preset output LOW, also disable internal pullup resistor
  pinMode( SCL, OUTPUT);   // OUTPUT and also a hard LOW
  delay( 1);
  pinMode( SCL, INPUT_PULLUP);   // INPUT with weak internal pullup resistor.
  delay( 1);
}

// Final check, there is no more that we can do.
if( digitalRead( SDA) == LOW || digitalRead( SCL) == LOW)
  Serial.println( "Error, I2C bus is not okay");

Wire.begin();

Thanks Koepel

Few answers and questions:

  • I don't think it the halt is due to shorts. I checked physically. I believe it occurs when the master is asking the the slave that is not connected to respond.

  • My wires were 10 meters long in previous version, and 50cm in this version (32 feet and 1.6 feet). The protocol was working fine before and now. The master is halting also the same.

  • Your code is interesting. I didn't think of changing pins IO direction on the fly. Can be interesting. I also never used the internal pull-ups. Can they be used to de-bounce input switched? I need to check. Maybe to weak.

  • At first I didn't understand the code, but after some reading I noticed that when changing the SCL to output when was pull-up before, it remains HIGH. Cool.

  • I think that a timeout is the best solution. I did it before, not with Arduino. I suppose I can use an internal timer, and cause an interrupt to report a problem. Something like:

start timer
wire.begin()
end timer

When a Slave is not connected at all, that should be solved in the sketch. That should be no problem. Can you show your sketch ?

When for example the GND of the Slave is not well connected, that could be seen as a shortcut between SDA and SCL.
When for example the 5V of the Slave is not well connected, then SDA and SCL are probably stuck low. That could be caused by internal protection diodes to 5V (which is 0V when not connected).

50 cm is okay, until you put the signals in a cable or a flat ribbon cable. The SDA and SCL next to each other in a flat ribbon cable is the worst.

What are your pullup resistors ?

When the SCL is OUTPUT and LOW and you measure 5V on its pin, then the SCL is shortcut to 5V and there is a shortcut current of 40mA that could damage the SCL pin.

I think that the Wire.begin() does not halt. The Wire.endTransmission() and Wire.requestFrom() could halt if something is wrong on the I2C bus. You could use the WatchDog and update the WatchDog in the loop(). When the loop() is not run, then after a while the Arduino can be reset by the WatchDog.

Using a timer with a interrupt might not work. When something is wrong and the Wire library halts, then it halts in a interrupt routine (I think, I'm not sure). Other interrupts might not run and sending a message with the Serial library might not work because that library uses also interrupts.

yigalb:
Is there a way to tell the endTransmission to limit the waiting time for a response from a device that may not exist?

Add time out codes say for 500 ms -- if the Master does not find 0x00 as error code for that period, it will print the message "check for slave connection or for bad I2C lines".

Hi Guys. Not sure why. but I can't recreate the problem. All is working well under all situations. I will try damaging it slowly until I can investigate it deeply.