How does an I2C slave know the data length in an onReceive() event?

I’m using the standard code in the examples.

The master requests a number of bytes and it’s all working, changing the number of bytes works seamlessly.

It’s like one person wrote onReceive() and another wrote onRequest().

This was asked in an old post ( 'TWI/I2C Wire Library' Clarification Question ..’) but it was not answered by anyone other than in guesses.

Is there an answer? It is just one call, all data is sent back to the request.

The slave doesn’t really “figure out” the length while receiving. With the Wire library the full message is already buffered and the handler is called after the STOP condition, so the length is known at that point (the howMany parameter).

And as far as I know any additional protocol (like a length byte) is more about structuring the data than about the I²C mechanism itself.

Hi @chrisgadg
The master must tell the slave how many bytes it should receive or the number of bytes needs to be fixed, otherwise the slave has no idea.

Yes I get that bit, the requesting is fine.

But the onRequest() which is in the slave code appears to have no idea yet it does.

My solution is to send a transmission from the master to say how many bytes will be requesting in the proceeding request, then request, and at this the slave code has the number of requested bytes so I can act on it.

Not really, it's interrupt driven. It will receive as many bytes as the master sends, no more no less.

My solution is to send a transmission from the master to say how many bytes will be requesting

That is what I said.

If you are interested in I2C you may read this

https://www.ti.com/lit/pdf/slva704

In paragraph 2.2 you will find this line

Any number of data bytes can be transferred from the
master to slave between the START and STOP conditions.

As already posted here the number of bytes transferred is defined by the controller application. This application which starts and stops a specific transmission has to make sure that the correct number is sent or received.

As a slave application you read or send after START until the bus goes into a STOP mode (or a repeated START, see 2.1.1 and 2.1.2 in the linked paper).

If you write a sketch for both sides you can create your own protocol on top of I2C, e.g. by defining that the first data byte will contain the number of following bytes or a command byte that tells the other side how many and what kind of data are following. (That's what @jim-p also wrote!)

Edit: This is probably the most comprehensive explanation for I2C use

https://www.gammon.com.au/i2c

It includes a lot of short examples and has been created by Nick Gammon, one of the most respected experts in this field.

I know but this is not what I asked.

The number requested within the slave’s interrupt for requested data, is elusive. Unlike in the onReceive() interrupt.

No it is not.
If think you are expecting this to be more complicated than it actually is. See post #5 and #3

There is nothing to tell the slave how many bytes to send. The requestFrom() from the master will always receive the requested number of bytes, regardless of how many the slave sends, if the slave sends less then the master sees a high level bus and assumes that represents data. The slave generally will either send a fixed amount of data, or however much is required based on a command previously sent from the master.

The onRequest interrupt just tells the slave device that the master is ready for reception. Nothing more.

The master device will read as many bytes as it expects.

Usually the number is implicitly known by both sides. If not it can be made depending on a previous transmission of the master which sends a command that explicitly or implicitly defines how many data shall be transferred. Or - the other way around - the slave sends a length byte as the first data. But that's application not I2C!

I get it now, it’s very basic compared to the more helpful I2C code away from Arduino code.

As I already said you were trying to make this much more complicated than it needs to be.

Feel free to read also this thread

https://forum.arduino.cc/t/use-i2c-for-communication-between-arduinos/653958

It comes with example code for two boards where a complete struct is transferred from one to the other.

Have fun!
ec2021

You do need to worry about overflowing the I2C buffer. For most Arduinos it 32 bytes.

The coding presentaton of the above text:

Assume that the I2C Master has executed the following lines:

Wire.beginTransmission(slaveAddess);
Wire.write(0x5C);
Wire.write(0x2A);
Wire.endTransmission();

In the Save Sketch, the following function declaration is made in te setup() function:

Wire.onReceive(receiveEvent);    //receiveEvent is name of callback function
void receiveEven(int howMany)
{
      Serial.print(howMany, DEC);  //shows 2 = bytes received from Master
}

Note: Whenever a data byte arrives at the slave from the master, the slave is interrupted. Therefore, the slave can count the number of interrupts that occur until the STOP condition appears on the I²C bus. The howMany argument is then loaded with the number of received bytes, excluding the slave address (i.e., number of interrupts minus one).

How does the slave know this?
Also what if the master sends a repeated start?

OnReceive() is a method that embeds the callback function: receiveEvent(int howMany(){} wich is called upon once STOP condition is asserted on the I2C bus by the Master. The variable howMany holds the number of bytes received by Slave from Master.

The I2C logic continuously monitors the bus states, and the corresponding actions prescribed by the I2C protocol are automatically carried out. The following diagram illustrates the START (SDA= 0, SCL = 1) and STOP (SDA = 1, SCL = 1) conditions of the I²C bus.


......................................................................................................................
......................................................................................................................

onRequest() is a method at the Slave side to declare/register a callback function: requestEvent(){} which is called upon when STOP condition is asserted on the I2C bus by the master after the execution of the following command:

Wire.requestFrom(slaveAddress, 3);

The Slave enters into the callback function and writes exactly 3 bytes (arg2 of requestFrom() method) into the I2C Buffer for onward transmission to Master. How does the Slave know this number is not known to me.

void requestEvent()    //no argument for requestEvent()
{
    Wire.write(0x12);
    Wire.write(0x34);
    Wire.write(0x56);
    
]

The same way, as slave knows any other data from master. By deciphering protocol going on clock-data lines.