Wire library issues with lost bus arbitration

This is a continuation of a discussion that started in this thread:
http://forum.arduino.cc/index.php?topic=400402.0
And mainly about the issue I'm experiencing with the Wire library describe here:
http://forum.arduino.cc/index.php?topic=400402.msg2754212#msg2754212

The main issue being that a misbehaving slave that asserts ACK during a read (not allowed by i2c) causes the AVR code to see a loss of bus arbitration.
The AVR code never recovers and if you do any further reads/writes to the slave the AVR will hang down in the twi code as it will never aquire the bus because it is some state waiting for something that never happens.

The only way I've been able to recover from this is using the Arduino 1.6.0+ Wire.end() call to disable the h/w and restart it.
On the AVR, if you get less than the number of bytes you expected from Wire.requestFrom(), then the next attempt to use the i2c will hang the processor.
When i detect the condition,
The only sequence that I've found that can get things going again on the avr is.

Wire.end();
Wire.begin();
Wire.beginTransmission(i2caddr);
Wire.endtransmission();

If you attempt to do a read or write without doing this "dummy" address phase, even after doing end() and begin() the library will hang.
So end() and begin() are not fully cleaning things up to a clean state after this condition occurs.


Koepel:
@bperrybap, the bytes do not come in after the Wire.requestFrom() with multi-master. When the Arduino is busy, the other Master should wait. Suppose it does not wait, and jumps in, then the Arduino detects that, and the I2C transaction fails, it should return with a number that is less than the number of bytes that was requested.

I think every I2C device (sensor, eeprom, rtc) will not continue after it sees a start or stop from another Master during a I2C transaction.

I don't like it, but the whole Wire library will only work, when the Wire.endTransmission() and Wire.requestFrom() are waiting.

Please start a new topic ( for example "I2C LCD not conform I2C standard" ) in the Network/I2C section ( Networking, Protocols, and Devices - Arduino Forum ) and refer to this topic.

I would argue that if you have an API function like Wire.requestFrom() that is synchronous (waits for completion), that it should not return early if there was a loss of bus arbitration during the reads as loss of bus arbitration is not a fatal error.

There have been a vew big discussions about bus arbitration on AVRFreaks a few times.
And I think one of things that comes up is that loss of bus arbitration is not really an error.
It is something that can occur and does occur when multiple masters exist and should not be treated as a fatal error and should be handle gracefully.
Yes the specific case I'm dealing with right now is a misbehaving slave, but still, loss of bus arbitration is something that needs to be dealt with in the library to make multi-master work.

And while I do believe that having Wire.requestFrom() be a synchronous/blocking API function (in the Wire library) is a good thing, I don't believe that it is technically necessary for Wire.requestFrom() to be fully blocking in order to function.
In other words, I think that making the API call synchronous is easer, and makes the API easier to use , but I don't think that it is a technical requirement.
To some extent all this is related to what Wire provides vs what the twi code provides.
The Wire code should always take extra steps to make things easier in cases where certain things may not be provided by the twi code.

For now, lets just limit discussion to a synchronous Wire.requestFrom()

In terms of recovering after loss of bus arbitration,
it isn't that that a slave would continue after the master detached from losing bus arbitration, (that isn't how i2c works), it is that that the master could and should pickup and continue the transfers to complete the application's request.
And technically that is what the twi code should do to ensure it works in multi master environments.
And if the twi code doesn't/won't do it, then the Wire library code should handled it, so it is hidden from the application.

losing bus arbitration is not a fatal error and it is not a condition that should be handled up in the application, this should be handled by the library, either in twi or in Wire.
The job of a good i/o library is to hide these kinds of things so that every application doesn't have to re-implement this type of exception handling.

I haven't tested multi master yet but the twi code should handle it by restarting and continuing the operations. If it doesn't, then I consider this a bug or at least something that is missing in Wire code.

Currently the Wire.requestFrom() code calls the twi code twi_readFrom() to request some bytes.
The twi code does pretty much everything from the ISR and the foreground code simply waits for the operation to complete by looking at a s/w status.
The twi code uses its own buffers.
As the bytes come in the twi ISR copies them to its internal buffers.
If there is a loss of bus arbitration, the foreground operation in twi_readFrom() completes with fewer than the requested bytes to return to Wire.requestFrom()
Wire.requestFrom() is really simplistic. All it does is blindly return whatever twi_readFrom() returned.

However, and this is not particularly clear, is whether the twi code picks up and continues reading its bytes on as soon as it can reacquire the bus - even though the foreground requestFrom() API call completed pre-maturely.
(After looking closer, it looks like the twi code does not restart after losing arbitration)

The twi code could be smart enough to restart and run in the background to complete the original read operation since it is using its own internal RX buffer and is fully interrupt driven.
If it does, then Wire.available() could be used to "see" the remaining bytes as they come in.
However, that would then not be a fully synchronous Wire.requestFrom() call which makes things more complex in the application.

And it is this situation, that I was saying that the Wire library should handle transparently for the application in Wire.requestFrom()

So from an API perspective, to keep the interface simpler,
Wire.requestFrom() could ensure that it never returned until all the bytes were read unless there was a fatal error - loss of bus arbitration is not a fatal error.

This would break down into one of two cases:

1)If twi causes twi_readFrom() to return early but then restarts in the background to complete the read, then Wire.requestFrom() should hide this by polling twi to detect when the i/o really completed before returning back to the applcation.

2)if twi does not restart the i/o after a lost bus arbitration, the Wire.requestFrom() code should restart the read operation to continue and complete the i/o requested by the application.

This ensures that Wire.requestFrom() would only return early on fatal errors, not for something like lost bus arbitration.

--- bill

In the bigger picture, I'm still trying to figure out why, after this goofy slave stomped on the i2c bus to create a fake loss of bus arbitration event, that Wire.end() and Wire.begin() doesn't clean things back up to a working state.

--- bill

Hi, have you figured out how to handle multi-master bus arbitration?
I'm running into a similar problem Two-way communication on multimaster I2C bus - Arduino Stack Exchange