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
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.
-
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– user884342023年01月13日 12:35:36 +00:00Commented 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– user884342023年01月13日 13:37:44 +00:00Commented Jan 13, 2023 at 13:37
// 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.
-
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– Emil2023年01月13日 07:32:18 +00:00Commented Jan 13, 2023 at 7:32
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.
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!
-
-
@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– user884342023年01月13日 14:20:27 +00:00Commented Jan 13, 2023 at 14:20