So I was implementing an i2c slave on the Rover's Arduinoes; the one that could be read from and written to. All Arduinoes are operating in slave mode, the bus master is a Raspberry Pi board.
For this apparently you have to install both onReceive and onRequest handlers; the "write" operation is simply handled by onReceive by receiving the register and immediately after that a new value; the "read" is a bit more complicated, as onReceive receives the register from the wire, and then onRequest that follows needs to send the data.
All that works quite fine until the speed at which Raspberry Pi queries the Arduinoes is skyrocketing to where Wire library apparently gets confused. In practice it is seen as if an Arduino gets stuck sending -1 to whatever request it receives.
It was bothering me for a while until I decided to figure it out yesterday. Since it's hard to debug an onReceive/onRequest handler via a Serial console (merely because doing Serial operations in an interrupt handler is impossible), I had to look through the Arduino libraries code trying to guess what's going on.
After a while I found a set of instructions that looked suspicious. Apparently Wire library is
not accepting any more data unless the buffer received from the previous i2c operation is completely read from!
So the solution looked simple to me; on onReceive, just read from the buffer until it's empty, to make sure it's called the next time data arrives. I'm not sure what exactly confuses Arduino so that it receives more data than Raspberry Pi sends, but it's good to clear the buffer as a safety measure in case someone writes more data by accident.
P.S. From the Wire library source code I also learned that the integer argument passed to onReceive is the same thing returned by Wire.available() before any reads are done. That's because they are computed in the same way, and onReceive, which is called from an interrupt handler, is non-interruptible, as any other Arduino interrupt handler.