RobTillaart / PCF8575

Arduino library for PCF8575 - 16 channel I2C IO expander
MIT License
73 stars 10 forks source link

read from a non-present device #20

Closed sgrizzi closed 2 years ago

sgrizzi commented 2 years ago

Hi, I am using a modular setup with an esp32, which may have one, two or three pcf8575 connected to the i2cbus. Using a for-loop I always perform "reads" out of three pcf8575, expecting no valid data from a device which is not present. Unfortunately a pcf.read() out of a non present device makes the code behave weird. Is a read function not simply aborted if the device responds with a nack on being addressed? or shall I always use the isconnected() check before attempting a read()?

RobTillaart commented 2 years ago

Thanks for the question,

You need to call bool isConnected(); to see if a device is present.

sgrizzi commented 2 years ago

Thanks for the prompt answer. Indeed I can use isConnected(). This is addressing the device and checking if ack or nack is returned, and I am using this elsewhere in the code. When performing a read(), the very same operation is carried out, so I would expect that the read() is aborted when receiving a nack on addressing the device. Instead it appears that the sw jams the i2c interface, which behaves weird for a while... If there is no alternative... I will use isConnected()

RobTillaart commented 2 years ago

If the device is dynamically present or not you will get race conditions anyway, as at any moment in time the connection can be broken. So even in the middle of a read() call while e.g. having fetched the first byte and halfway second.

To protect against this you need a detection mechanism - think interrupt based - to see if the device is connected all time during the transaction. Fail safe programs is level of its own.

// dummy code
volatile bool deviceOnliine ;
bool valueValid = false;

...
valueValid = false;
deviceOnliine = dev.isConnected();
if (deviceOnliine )
{
  value = dev.read();
  if (deviceOnliine )  valueValid = true;
}

if (validValue) process(value);

...

void ISR()    // triggers if device is disconnected 
{
  deviceOnliine  = false;
}
sgrizzi commented 2 years ago

True indeed. That is also the reason why I was expecting a read() to abort if a nack is received when not due. Fair enough, if this is the way the code works, then I will use accordingly. Thanks for your support! (I have another related question but maybe I should start a new issue)

RobTillaart commented 2 years ago

If there is no alternative... I will use isConnected()

You can check the error flag See this line in read() _error = PCF8575_I2C_ERROR; have you tried that?

data = device.read();
if (device.lastError() == PCF8575_OK)
{
  process(data);
}
else
{
  sound the alarm bells.
}

update

See this line in read16() _error = PCF8575_I2C_ERROR;

RobTillaart commented 2 years ago

(I have another related question but maybe I should start a new issue) I prefer separate issues, but if related, let it come!

sgrizzi commented 2 years ago

I just see your update. Will try and see if it works. The problem is that, using an oscilloscope to actually check what is going on on the i2c, I can see that the read() itself seems to jam the interface. But let's not pre-empt the answer - I will try it out. Thanks.

sgrizzi commented 2 years ago

This is the very simple code I used.

  Serial.printf("pcf8575 = %d\n\r",PCF.read16());
  Serial.printf("pcf8575 = %d\n\r",PCF1.read16());
  Serial.printf("pcf8575 = %d\n\r",PCF2.read16());

The 3rd device is not there. I checked again with the scope and what apparently happens at the execution of the 3rd read16 is that indeed the interface reads a nack on the bus, but the bus remains "busy" for ca. 1 second. Then a stop condition is issued (a timeout?) and then the bus resumes again. I hope the screenshot is readable. Unfortunately the 1s "busy time" will crash my code elsewhere. Will use isConnected()....🙂 (probably, again, not related to your library)

image

RobTillaart commented 2 years ago

https://www.arduino.cc/reference/en/language/functions/communication/wire/setwiretimeout/

could be interesting ... don't know if ESP supports it ?

RobTillaart commented 2 years ago

https://github.com/espressif/arduino-esp32/issues/5934

some backgrounder?

RobTillaart commented 2 years ago

I hope the screenshot is readable.

100%

sgrizzi commented 2 years ago

Very good hints, thanks! Setwiretimeout is indeed supported by the esp32 but it does not look like a good solution to me. Fine if you need to recover from an unexpected problem, but here I have a structural implementation "variable" and I think that using the isConnected() is a cleaner solution, even though it uses the bus one extra time.

The second link does indeed describe the very same problem, and the solution may indeed need to go quite deep in the system. Thanks again!

RobTillaart commented 2 years ago

you're welcome