If the master, in order to request (read) data from a slave, has to specify the amount of bytes it expects to receive; then how the slave knows that if the onRequest event doesn't even have any parameter that tells it? What happens if the slave doesn't transmit the exact amount of bytes the master requests?
The event triggers once per request or once per byte?
Why onReceive does have a parameter and onRequest does not? It would be more useful if the slave acknowledges how many bytes the master requested, instead of how many the slave received.
What happens if the slave doesn't transmit the exact amount of bytes the master requests?
The I2C protocol doesn't provide that information. It's possible to check for a stop condition and if no stop condition is reached just provide the next byte
but that's not possible using the Wire library, you would have to write your own low level calls.
Think about most I2C devices that already exist. They're temperature sensors and memory chips. They don't have a general-purpose microcontroller on board. If the datasheet says "write a 1 to this address and you will get 3 bytes back" then that's what you get. Basically any request becomes two requests: first tell the slave what you want or how many, then request again to receive the bytes.Look at an existing I2C port extender and just copy and extend that protocol to do what you want.
When the Master reads data, the Master is in charge of the clock, the acknowledge and the stop. The Slave just gives bytes, but does not know how many.
In the requestEvent() function, you may fill the buffer with 32 bytes. Just fill the buffer to be sure. The Master can do a Wire.requestFrom() and request 1 up to 32 bytes. If the Master stops, the Slave does not use the remaining bytes in the buffer.This makes sense if you have a array, just like the "registers" of a sensor. The Master could write the "register-address" (array index) and a requestFrom() starts reading from that address of the "registers".
To send commands to a "register" requires additional code, because most commands take time and should be handled in the loop() and not in the receiveEvent() function. You might need a boolean variable if a command is written in a special command byte of the array.
Have a look at this: https://github.com/thexeno/HardWire-Arduino-Library. I think that is exactly what you need if you don't use the "register" approach.
Wire.beginTransmission(2); // Hello slave address number 2Wire.Write(10); // set "register" address to 10 in the slaveWire.endTransmission(); // transmit it to the SlaveWire.requestFrom(2, 16); // request 16 bytes
I do not understand your last question. I think the HardWire-Arduino-Library might generate an interrupt for every byte, that would make it possible to count the number of bytes that the Master gets. But I have not looked into the HardWire-Arduino-Library that well.
How the slave can know when to stop sending bytes?
Yes clock stretching is a thing but most I2C devices like thermometers don't do it, so the Arduino library doesn't do it either.
If it does require a lot of work to provide what the master asked for then calculate it before the master asks.
A 100 kHz I2C-bus is not that slow for a 16 MHz Arduino Uno.
It is very easy to get to 100 µs and beyond.