Using the Teensy 3 as a master device, should the timeout feature in endTransmission and requestFrom allow code to not hang if the slave device SDA or SCL (or power) lines are physically disconnected, as in the case where the slave device is a sensor which has been removed entirely? Using the attached code, when I pull the SDA or SCL line out, the Teensy hangs unrecoverably.
A few things to try:
First, the timeout is intended only to prevent a particular command from hanging right at that point. The initial part of your readByte() routine is writing to the device. However the way I2C works, if the device is not present it will not ACK - so the Master (in your case the T3) will interpret it as a NAK. It will not timeout, since ACK/NAK is a normal part of the I2C communication it will occur very quickly. Timeouts are more for the case where you send a long data set to a Slave and it takes longer than it is supposed to (due to clock stretching, interference, whatever).
Looking at your code I don't think the Write section is needed at all. I can see that you are testing if the device is present, but it is not needed, the requestFrom() routine will get a NAK if the device is missing.
Also, the readByte() routine appears to be keeping the bus in a non-released state. I don't know if that can cause a problem during disconnect (if you get glitches on the wire I'm not sure what would happen). I would try changing I2C_NOSTOP to I2C_STOP on the requestFrom() command.
Finally, your routine looks like it will hang unless it gets read data (this is probably the problem). The following:
Code:
while(!Wire.available());
will hang if the Rx buffer is empty.
You need to decide what return value you want from the readByte() routine in cases where there is no device present, or for whatever reason it doesn't read anything. If you are ok with a zero return value in those cases then the routine can simplify to this:
Code:
byte readByte(byte address)
{
Wire.requestFrom(VCNL4000_ADDRESS,1,I2C_STOP,1000); // this is a blocking command, so no need to poll Wire.available()
return Wire.readByte(); // will return 0 if buffer empty
}
IIRC, the old Arduino lib used to return a signed int equal to -1 in this case. If you want a -1 value in cases where the buffer is empty, then use this:
Code:
int readByte(byte address)
{
Wire.requestFrom(VCNL4000_ADDRESS,1,I2C_STOP,1000); // this is a blocking command, so no need to poll Wire.available()
return Wire.read(); // will return -1 if buffer empty
}
Check if either of those can work for you.
EDIT: Sorry I see that your readByte() routine is feeding a memory address into the device, not just checking for connectivity. Try this instead:
Code:
// readByte(address) reads a single byte of data from address
byte readByte(byte address)
{
Wire.beginTransmission(VCNL4000_ADDRESS);
Wire.send(address);
Wire.endTransmission(I2C_NOSTOP,1000);
if(Wire.status() == I2C_WAITING)
{
Wire.requestFrom(VCNL4000_ADDRESS,1,I2C_STOP,1000); // this is a blocking command, so no need to poll Wire.available()
return Wire.readByte(); // will return 0 if buffer empty
}
return 0; // some kind of error occured (NAK, Timeout), return 0
}