Search code examples
esp32i2carduino-esp32nanopb

Is there a way to detect the length of an incoming I2C message on an ESP32 (Arduino)?


I'm running a system with ESP32s: One acts as master, the other ones (count doesn't matter for now) as slave. Communication is done with I2C and my messages are encoded with nanopb.

I develop with the Arduino framework (in PlatformIO).

I'd like to send messages in both directions with arbitrary sizes (not too big, but not fixed size).

For decoding the messages I need to know about their length. When I request a message from the master's side with Wire.requestFrom() I need to say how many bytes I want to read. However, it is not possible to know about the length before executing the request.

My idea was to send the length in the first byte. But that is only half the battle. For decoding purposes it is enough. However, I still don't know how many bytes I should read.

I've seen that there is 0xFF or 255 after the last data byte. But can I use this information to reliably detect the end of the message?

(I guess this is more a theoretical question, so I didn't supply any code example. Anyways, feel free to ask.)


Solution

  • When writing to I2C devices (master->slave), there is no limit to the number of bytes you can write. The receiver (slave) can signal that it doesn't want (or can't receive) more by responding with a NACK instead of an ACK after the last byte.

    The same is true of reading; you can read bytes forever from a slave device; there is no defined mechanism for telling the master how many bytes are available.

    The Arduino API tries to intervene in the reading process by having you tell it beforehand how many bytes you intend to read from the slave device. It goes ahead and reads these bytes into an internal buffer, then allows you to read them at your own pace by your code. The slave device has sent the bytes and the transaction finished before it returns from your call to requestFrom().

    The normal method of using I2C is to have a defined set of internal "registers" that can be read with known length and purpose. You can define your own register protocol which can tell the slave the type and length of the data you're requesting. For example, let's say that you have 5 different message types and each can respond with a length of 1 to 16 bytes. You can assign them a "register" of 0x00 to 0x40 (message type 1 to 5) and the length is the low nibble of the register. A full example:

    MASTER writes "register" 0x25 (3rd message type, length 6 requested) MASTER then reads 6 bytes from the SLAVE

    The master can also write an arbitrary number of bytes to each slave with the message type and length at the beginning of the transaction.

    You may need to do a back-and-forth protocol to get the effect you want, but there's nothing in the I2C protocol to do exactly what you need.