3

I am going to use MCP23017 chips, and some of them will be cabled with a 1 meter max unshielded cable. I am expecting that certain transmissions may toss errors. So I want to tackle this problems by using the built in error detection of I2C, polling the inputs frequently (every 20ms or so) and by debouncing the received data in code by comparing the previous state to the current state of every input pin.

If you write bytes to an I2C slave with the Wire library. You can use the return value of Wire.endTransmission(); to test if the slave has acknowledged the message.

How does this work when you are requesting bytes using Wire.requestFrom( address, 3 ); for example. This function does return the amount of received bytes. Can we check this value with the value of the amount of bytes we requested?

I have read the TI document over how I2C works

The master will continue sending out the clockpulses, but will release the SDA line, so that the slave can transmit data. At the end of every byte of data, the master will send an ACK to the slave, letting the slave know that it is ready for more data. Once the master has received the number of bytes it is expecting, it will send a NACK, signaling to the slave to halt communications and release the bus. The master will follow this up with a STOP condition

So the master either ACKs or NACKs a read-in byte. Does the Wire library automatically make several attempts with reading? Oris there something in the Wire library which we can use to detect a failed read operation?

Is it perhaps so that the requestFrom() function may return a different amount of bytes than you requested with which would indicate that atleast 1 read operation failed?

So could I attempt something like

doItAllAgain:

for(int i = 0 ; i < 5 ; i ++ )
{
    Wire.beginTransmission( address ) ;
    Wire.write( register ) ;
    if( !Wire.endTransmission() ) break ; // if no error, break out of for loop
    Serial.println(F("write operation failed");
}

Serial.println(F("write operation succes");

byte requestedBytes = 3 ;
byte returnedBytes = Wire.requestFrom( address, requestedBytes ) ;

if( requestedBytes != returnedBytes )
{
    Serial.println(F("read operation failed");
    goto doItAllAgain ;
}
else
{
    Serial.println(F("read operation also succes");
    byte byte1 = Wire.read() ;
    byte byte2 = Wire.read() ;
    byte byte3 = Wire.read() ;
}
3
  • You can only get little information about unsuccessful transmission as long as e.g. a slave which is actually there does not acknowledge. I2C has no way of figuring out if transmitted data is corrupted or not. This is the mayor problem because instead of not reacting, slaves can behave erratically. To deals with that you have to implement error checks yourself, for example by validating a checksum.
    – Sim Son
    Commented Apr 20, 2021 at 9:57
  • From what I understand. If the dataline has a flank change while the clockline is high, there will be a NACK and this should work in 2 directions. Regardless how could I possibly add a checksum while reading out an Mcp23017 device? To have a checksum system, the slave device should be able to accept and transmitt checksum bytes correct? I have a bare arduino at my desk with no I2C devices, I will check what the requestFrom() function returns when there is no device
    – bask185
    Commented Apr 20, 2021 at 10:10
  • Ok, so Wire.requestFrom() returns 0 when no device is connected. This means that this function does incorporate a check on NACK bits.
    – bask185
    Commented Apr 20, 2021 at 10:25

1 Answer 1

3

The Wire library does not give you any possibility to get error states, when requesting bytes from a slave. Basically you are stuck with the number of bytes being read.

Though you can read some information from that. If the number of bytes is zero, the slave didn't answer (either because its not there, or because it cannot send any byte currently). This equals a NACK by the slave after the address byte.

Other than that the I2C protocol doesn't give any more information. On requesting the slave is sending data and the master is acknowledging it. That means, that the slave cannot generate a NACK in this process. In fact that also means, that the returned number of bytes can only be zero (on NACK on address) or equal to the number of requested bytes, since after the address ACK from the slave the master will continue to generate clock pulses, until it has read all bytes, that it wanted. If the slave doesn't have any more data to send before that, it cannot stop the transmission. It would just stop driving the data line, resulting in all high data bytes (0xFF).

Also I doubt, that it would be really useful in your case. I2C is made for small distances, mostly on the same PCB. It can be used for longer distances, but your mileage may vary. If your setup really can corrupt the communication on the I2C lines, your biggest problem will be a stuck bus. The I2C protocol is generally not prepared for noisy environments and it incorporates states, where the devices on the bus can wait infinitely. This is also reflected in the Wire library. That means, that big noise on the lines will often lead to the bus being stuck in the middle of a transaction, needing you to reset the devices (not only the slaves, but also the master) to get the bus free again. I would go so far to say, that is it more likely to get the bus stuck, than to get corrupted data. So any information about errors is not really helping you here, since you won't get out of requestFrom() in the first place. Your code can very well get stuck there.

I suggest, that you test your setup thoroughly with the wanted run times. If the bus gets stuck too often for you, then you should make your setup more robust (shielded cables, or using an extra MCU with the I2C slave and using a more robust communication interface between main MCU and extra MCU).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.