0

When I need to send multiple bytes via Wire, for example a long int, I cast it to a byte array and specify a length

long int i;
Wire.write((byte*)&i, 4);

But if I want to send bytes from more than 1 variable I need to create a buffer

byte i;
byte j;
byte message[2] = {i, j};
Wire.write(message, 2);

If I want to send them without copying the data I can put them in a struct

struct message {
  byte i;
  byte j;
};
Wire.write((byte*)&message, 2);

But this would require me to refactor existing code.

Is there any c voodoo that I can perform to send bytes of different places of my code over Wire without copying them in a buffer and without refactoring the code?

4 Answers 4

4

No, this function overwrite of Wire.write() is meant for buffers, meaning connected spaces in memory, which can be covered by simply incrementing the buffer. That is not the case for distinct variables.

But that begs the question, why you want to do that in one call to Wire.write() in the first place. This function does not do any communication on the I2C lines, it just writes to the libraries internal buffer. The data gets actually send in Wire.endTransmission() or in the onRequest interrupts in the background (depending on where you have those write statements). Thus you already have a buffer and don't need another one. Just call Wire.write() multiple times, once for each variable. That will build up the data sequentially inside the internal buffer.

2
  • Well my device is operating in slave mode and my Wire.write() gets called (perhaps not directly but down the line) from an onRequest handler. In my experiments I found that once I call Wire.write() I can not call it again or I get rubbish. Is it possible to use multiple writes in my case?
    – user88434
    Jan 13, 2023 at 12:35
  • I got to the bottom of it! But it was too long for a comment so I added an answer. Thanks again!
    – user88434
    Jan 13, 2023 at 13:37
1
// Written by Nick Gammon
// May 2012

template <typename T> unsigned int I2C_writeAnything (const T& value)
  {
  Wire.write((byte *) &value, sizeof (value));
  return sizeof (value);
  }  // end of I2C_writeAnything

template <typename T> unsigned int I2C_readAnything(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = Wire.read();
    return i;
  }  // end of I2C_readAnything

Then in the body of your code you can:

I2C_writeAnything(message);

Similarly your other end can I2C_readAnything.

There is a small amount of refactoring required, changing the function call.

1
  • I think there may be issues with struct padding or endianness mismatch in this code if you are unlucky, but maybe it works most of the time.
    – Emil
    Jan 13, 2023 at 7:32
1

No, you cannot.

C does not guarantee any allocation properties of variables. Even if you write one variable definition right after another, both variables can have addresses assigned that have other memory in between.

So, if you want a "packet" sent via Wire that contains values of multiple variables, you need to fill a buffer with these values.

Even though C maintains the order of members of a structure, please be aware that depending on the target's architecture there can be gaps between the members for alignment. However, some compilers have language extensions to tackle this issue.

1

As @chrisl said, there is an internal buffer so the best I can do is just use multiple Wire.write calls, but in my case that wasn't working. I got only the last written value or rubbish data.

I dug through the source of the Wire library and found that the version in github (as of this writing) makes it possible to use multiple writes by appending to the buffer:

// set length and copy data into tx buffer
for(i = 0; i < length; ++i){
  twi_txBuffer[twi_txBufferLength+i] = data[i];
}
twi_txBufferLength += length;

But the one I have installed locally looks like this:

// set length and copy data into tx buffer
twi_txBufferLength = length;
for(i = 0; i < length; ++i){
  twi_txBuffer[i] = data[i];
}

So that explains a lot.

In case someone else also has this problem, you just need to update.

I was even too lazy to update, so I just replaced the function. Probably not the best decision, but it works!

Thanks!

2
  • fixed 6 years ago
    – Juraj
    Jan 13, 2023 at 13:56
  • @Juraj yeah I got curious and checked it too. Probably someone else was also as lazy as me and didn't update! I can't remember where I got my arduino sdk from but it was about 2 years ago, so definitely plenty of time has been had for updates, lol.
    – user88434
    Jan 13, 2023 at 14:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.