I have two arduino's who have to share info and one of them is the master.
For this I created a 32byte buffer which I want to keep in sync.
With a certain interval the master is pushing the data to the slave and polling the slave to see if the master side buffer the needs to be updated by the slave.
On both sides I have a flag (I2CbufLatch) which indicates if the buffer contant has changed because of performance reasons I only want to send the buffer when needed.
I'm not sure how the slave should respond when the buffer content hasn't changed. I can let the request timeout but is probably not the way how to do this and slowing down the process instead of making it as fast as possible.
As you can see I'm using Wire.write( data, 0 ) to let the master know: 'nothing to send for now' but i'm not sure if this is the correct way how to handle this situation or causing strange problems this way instead;
Is this the way how to do this or are there much better alternatives?
No, that is not the way.
Sending zero bytes is the same as sending no bytes at all.
It is allowed to send less data than requested, but the Master has to check that.
Is it okay to send 32 bytes at once ? Will that fit in the buffer of the Wire library ? I'm not sure about that.
You can use boolean variables to indicate that the buffer has changed.
boolean I2CbufLatch = false;
if (I2CbufLatch) ...
You don't need a return value (int ret) in the OnRequest handler.
If the Master requests data, why not always send the buffer to the Master ? The Master can discard the buffer or use it, that is up to the Master. Since the Master requested the data, the Master should know what to do with it.
You don't have to check for the Wire.availabe() in the OnReceive handler. The parameter "howMany" is the number of bytes in the buffer.
void receiveEvent(int howMany)
{
// Don't return more than the size of the buffer
int n = min (howMany, sizeof( I2Cbuf32 ));
for(int i=0; i<n; i++)
bufferByte[i] = Wire.read();
}
}
EDIT: the Wire.readBytes can be used: Wire.readBytes( bufferByte, n);
Maybe you should develop your own I2C protocol so the master can just request buffer status and get a unchanged/changed single byte reply. You could maybe expand on this so the master can set the read/write buffer address so only changed data is sent instead of sending the entire 32 byte buffer every time.
Riva, That is a good idea. Thinking about a protocol, I think a good protocol and a good description is needed in this situation. But to understand the data, I should know what the data is, how it is used, and so on.
What if the same data changes in the Master and the Slave at the same time ? One of the data changes will get lost. To avoid that, a double buffer is needed in the Master and the Slave.
When the Master is the Master, the Master should 'own' the data. That will avoid confusion.
It is also possible that the Master downloads the data every second to the Slave, regardless if it was changed or not. The Master also polls the Slave if new data has arrived, and the Slave does not change its own dataBuffer, but only tells the Master about the new data and the Master changes the dataBuffer.
A 32 byte buffer (0 to 31) needs a 5 bit address leaving 3 bits to play with in a single byte command.
One of the three remaining bits would need to be read/write address. Another bit could be used to request slave I2CbufLatch status/buffer access and you have a bit left over that could expand on command byte(s).
First byte sent from master determines what gets sent back and maybe what's to follow this first byte if writing slave buffer. As you always start with a control byte you will know what to expect (byte count) back from slave.
The buffer write command could be made to write a byte to slave buffer but it (the slave) returns the previous value stored there. This way as you update the slave it also returns it's current buffer. Doing it this way means you don't have to update the entire 32 bytes in one hit but can update next 1 or 2 bytes each time through loop() continually.
maybe I have to explain a bit more what I'm doing...
I'm using the I2CbufLatch to control if the buffer content is consistent or not, because a request from the master can popup any time.
The 32byte buffer is a Union of char buf[] and a struct of several fields.
buf[] is used to loop byte by byte through the buffer byte when I2C reading the buffer.
One of the fields in the struct is an Union (see below) of a status int and a struct of 16bitfields. Each field in the buffer has a corresponding status bit field.
when the content of that field has changed its bitfield is set also.
Union { unsigned char buf[]; struct {
int fld1;
long fld2;
etc..
etc..
char fld..n;
Union {
unsigned int status;*
struct (*
unsigned char u_fld1 :1;*
etc*
etc*
unsigned char u_fld..n :1;*
} bits;* } flds;
} I2Cbuf;
so as long I2Cbuf.flds.bits.status == 0 the buffer content is unchanged!
I2CbufLatch = 0; when:
the content of the buffer is unchanged since last transfer
or the buffer has changed but is inconsistent
I2CbufLatch = 1; when:
the content has changed (I2Cbuf.flds.bits.status != 0) but is not yet ready to transfer. (inconsistent)
Once I2CbufLatch == 1 at least one field has changed, the content is consitence so the buffer is ready to being send. As soon as the transfer of the whole buffer has finished I2CbufLatch is set to 0 again.
As I understood there is not much time difference in transfering a few bytes or the whole buffer at once because of the overhead of the protocol in setting up the connection etc.
Because I want as little as possible delay because of syncing the buffer I only want to do this when there is a real need for.
So when a request arrives I want to transfer the whole buffer at once only when the buffer is ready for tranfser or do nothing at all.
So:
when I2CbufLatch == 0 the content is in sync or not ready yet to be synced and I want to ignore the request from the master to prevent unnessesary delay because of the transfer.
What is happening at the master side though when ignoring it's request. wayting until a timeout occurs?
That is waisting time also because it's clear there is no reason to wait this time.
So I'm triying to find a way the slave can let the master know there is nothing to transfer and no need to wait until a timeout occurs and continue doing other urgent stuff.
When the buffer is transfered the receiving device will immediately clear I2CbufLatch, step through all fields with the update bit set, take neccesary actions for that cahnged field and when done clear the update bit until they are all done.
When that device changes one of the values of a buffer field the same procedure will be used to sync the buffer back to the other device. In this situation it will be pushed back to the slave.
About that request from the Master.
Suppose the Master requests 10 byte: int n = Wire.requestFrom (slave, 10);
The return value 'n' and Wire.available() tell how many bytes are in the buffer. That could be 0 if the Slave didn't send any (no Wire.write in the RequestEvent handler).
You can not stop a request, you have to complete the I2C session, but you can send less bytes than requested (even zero bytes), and the Master knows how many bytes are received.
This is part of the I2C protocol and no timeouts are used. The Slave finished the I2C session, when the buffer is emptied (the buffer inside the Wire library), regardless how many bytes were in the buffer.
Great Peter. Looks like this is the info I was looking for.
I tried to send 0 bytes by using a construction like Wire.write( buff, 0 ) in the assumption that would probably do the job but had the strong feeling this is not the correct way to do it. Couldn't find a better way though. As I understand from you, just leaving the handler does the job (closing the session without any transfer) which makes more sense. Didn't realize it was that simple.. 8)
Thanks a lot, I'm glad I took the step to create this post!!!!