I2C wire.write() question

Hello all,

I have two arduinos connected via I2C.
I can transfer data from one to another but can't pass float variables. It seems a limitation of wire.write to transfer only a byte at a time.
I realize that I have to convert the floats variables to byte type and wire.write() each of them or maybe alternatively create an array with all variables.
Since I have variables of type int and float to pass to the slave device (the other arduino), what's the best way to do the job?

thank you

It seems a limitation of wire.write to transfer only a byte at a time.

Not seems. Is.

However, you can make use of that capability more than once.

I realize that I have to convert the floats variables to byte type and wire.write() each of them

Convert is the wrong term. Using a union (of a float and 4 bytes), you can simply assign the float portion a value and send the byte portion. The float and the byte array occupy use the same memory, so sending the bytes is the same as sending the float.

Since I have variables of type int and float to pass to the slave device (the other arduino), what's the best way to do the job?

One way is to store all the data in fields on a struct, and then send the struct as a collection of bytes. The other is to convert all the values to strings, concatenate all the strings, and send the result.

Which one works best is for you to decide. The struct approach is easy, but it requires the same definition of the struct on both ends, and is hard to troubleshoot. The string approach is easy to debug, but more challenging in terms of converting the values to strings and the string pieces back to values.

HF_ATL:
Hello all,

I have two arduinos connected via I2C.
I can transfer data from one to another but can't pass float variables. It seems a limitation of wire.write to transfer only a byte at a time.
I realize that I have to convert the floats variables to byte type and wire.write() each of them or maybe alternatively create an array with all variables.
Since I have variables of type int and float to pass to the slave device (the other arduino), what's the best way to do the job?

thank you

The problem you are running into is that inside the onRequestEvent() function, only the last Wire.write() is used. so, only one call to Wire.write();

here is code to send a float (4 byte) variable:

volatile float F; //volatile because I am accessing it during and interrupt.

void onRequestEvent(){ // the maximum block that can be transmitted is 32bytes!
Wire.write((uint8_t*)&F,4);  // cast to byte pointer, length of 4
}

// code to send multiple variables.

volatile long bigNum;
volatile char msg[10];

void onRequestEvent1(){ // the maximum block that can be transmitted is 32bytes!
uint8_t buf[32]; // buffer in which to build block.
uint8_t len=0;
memmove(&buf[len],(uint8_t*)&F,sizeof(F));
len += sizeof(F);
memmove(&buf[len],(uint8_t*)&msg,sizeof(msg));
len += sizeof(msg);
memmove(&buf[len],(uint8_t*)&bigNum,sizeof(bigNum));
len += sizeof(bigNum);

Wire.write(buf,len);  
}

See if this doesn't work for you.

Chuck.

Dear all,

Thank you very much for both contribution.

Regarding the receiving side how can I separate each element in the array specially when dealing with float types to know where to find the digits after the decimal point?

HF_ATL:
Dear all,

Thank you very much for both contribution.

Regarding the receiving side how can I separate each element in the array specially when dealing with float types to know where to find the digits after the decimal point?

No,
capture each incoming byte, rebuild a float

float F;
uint8_t * b=(uint8_t*)&F;  // initialize b to point to the memory where F is stored.

Wire.requestFrom(slaveId,4); // all four bytes of the float

uint8_t i=0;
while(i<4){
   b[i++] = Wire.read();
   }

long L;

L = long((F-long(F))*10000); // 1 .. 99999 = .00001 .. .99999

chuck

For a easy implementation I'd prefer to use a Struct.

In the slave side (which will send the data) I got:

struct DataSD {
int errorstatus, alt, sats;
float lat, lon, temp_int, temp_ext;
};



void requestEvent()
{

 
struct DataSD data;

data.errorstatus=error;
data.gps_alt=alt;
data.sats=sats;
data.lat=lat;
data.lon=long;
data.temp_int=t1;
data.temp_ext=t2;

uint8_t *ptr= (uint8_t *)&data;
Wire.write(ptr,sizeof(data));

}

In the master side (which will get the slave data via I2C and have exactly the same structure definition DataSD) I got:

struct DataSD data_to_receive;


Wire.requestFrom(2,sizeof(data_to_receive)); 


uint8_t *ptr=(uint8_t*)&data_to_receive;

while(Wire.available()) {
*ptr=Wire.read();
}

Am I on the right path?
How can I proceed from here to extract the data to each structure member?

thank you again

You have to use the same struct in the Master and Slave :wink:
The Master can use a normal struct when writing and reading. For the Slave it depends, because the onReceive and onRequest handlers are interrupts handlers. If the struct data is used in the loop() of the Slave, perhaps a copy should be used.

Master

struct DataSD data_to_receive;

int numbytes = sizeof( data_to_receive);
int n = Wire.requestFrom(2, numbytes);
if( n = numbytes)   // check if the same amount received as requested
{
  readBytes( (char *) &data_to_receive, n);
}

For the Slave, I often keep that struct as a global (volatile) variable and fill the data as needed. In the onRequest handler, I just write that struct.
But it's okay to fill that struct in onRequest handler.

Thanks for the quick reply Peter.

I have modified the master code but I get: "'readBytes' was not declared in this scope"

Oops :-[ It is Wire.readBytes(). Sorry.
In the past, there was only Wire.read(), but now there is Wire.readBytes().

HF_ATL:
For a easy implementation I'd prefer to use a Struct.

In the master side (which will get the slave data via I2C and have exactly the same structure definition DataSD) I got:

struct DataSD data_to_receive;

Wire.requestFrom(2,sizeof(data_to_receive));

uint8_t ptr=(uint8_t)&data_to_receive;

while(Wire.available()) {
*ptr=Wire.read();
}




Am I on the right path?
How can I proceed from here to extract the data to each structure member?

thank you again

close, in the Wire.read() you need to increment the ptr

*ptr++=Wire.read();

Else all of the read() data will be written into the first byte of the structure.

chuck.

Thank you for all your contribution, really appreciated!

Now it is working as expected!

Hello again,

I realized that sometimes arduino hangs on I2C communication and freezes there forever, sometimes it works as expected and get the data writting it in the SD card.

I have two devices communicating with arduino via I2C: another arduino uno with 0x02 address given and the MS5611 with 0x77. Using Serial.println across the code I found out that the MS5611 gettemp and getpressure routines sometimes could not start. Above that part in the code is the I2C communication (communicatin with the another arduino as mentioned). Is there any known bug in OneWire? Seems like there is no timeout...

Thank you

Also, for instance, the MS5611 sometimes initialize as expected but other times it doesn't and I realized that pressing the reset button of arduino help and get the ms5611 to initialize properly. It does not happen always but I would rather prefer to debug the issue than solve it by reset button press

HF_ATL:
Also, for instance, the MS5611 sometimes initialize as expected but other times it doesn't and I realized that pressing the reset button of arduino help and get the ms5611 to initialize properly. It does not happen always but I would rather prefer to debug the issue than solve it by reset button press

The way the Wire library is written, if the SCL line is held LOW the Arduino will wait for it to return to HIGH. If something is (holding the bus LOW, like your MS5611, or the buss is not released; the other Arduino's last I2C command was endTransmission(false), or requestfrom(i2caddr,count,false). It will hang waiting for the buss to clear. I changed my copy of Wire to incorporate timeouts for these conditions. With the timeouts, I have to code for these error conditions, instead of just assuming no errors happen.

Chuck.

Thanks for the quick reply Chuck.
But I guess that with timeout condition the freeze state will not going to happen but the ms5611 can stay for a long time without providing real-time information?
I really suspect that is the MS5611 because in setup() it is the first i2c comm established and many times in the startup it does not initialize thus the program just freezes there. The other arduino I believe is working like it should.
It will be preferable to use a modded OneWire or try another ms5611, or other solution?
If I had an oscilloscope that would be much easy to debug.

HF_ATL:
Thanks for the quick reply Chuck.
But I guess that with timeout condition the freeze state will not going to happen but the ms5611 can stay for a long time without providing real-time information?
I really suspect that is the MS5611 because in setup() it is the first i2c comm established and many times in the startup it does not initialize thus the program just freezes there. The other arduino I believe is working like it should.
It will be preferable to use a modded OneWire or try another ms5611, or other solution?
If I had an oscilloscope that would be much easy to debug.

If you have an extra Arduino UNO or MEGA you can have yourself a Logic analyzer that you can uses to identify this problem.
Gillham OLS implementation

It works pretty good.
Here is a image of I2C traffic I captured with it. :confused: PostImage.org is having a problem so I'll just append it.

Chuck.

I have an extra arduino uno and mega so it will be good to visualize the I2C traffic.
Following that link I couldn't find the process of implementing it.
It is like installing logic sniffer, upload the sketch to arduino and communicate via serial between arduino and logic sniffer?

HF_ATL:
I have an extra arduino uno and mega so it will be good to visualize the I2C traffic.
Following that link I couldn't find the process of implementing it.
It is like installing logic sniffer, upload the sketch to arduino and communicate via serial between arduino and logic sniffer?

Download the three .ino files, put them in a folder named logic_analyzer in you Arduino projects folder.
Select the correct Arduino board, to match the one you are using(from the Tools/board)

compile and upload the code to you Arduino

from Gillham's readme: pins and options

This SUMP protocol compatible logic analyzer for the Arduino board supports
5 channels consisting of digital pins 8-12, which are the first 5 bits (0-4)
of PORTB. Arduino pin 13 / bit 5 is the Arduino LED, bits 6 & 7 are the
crystal oscillator pins.

Uncomment CHAN5 below if you want to use the LED pin as an input and have
6 channels.

On the Arduino Mega board 8 channels are supported and 7k of samples.
Pins 22-29 (Port A) are used by default.

SUMP protocol Client web page

Here is a direct link to the SUMP Client download 0.9.7.2

unzip this packing, I put it in Documents\logic. inside this folder it will create a ols-0.9.7.2 folder.
Under this is a plugins folder, copy the two .cfg from Gillham's github.

To start it double click on the run.bat from the ols-0.9.7.2 folder.

Select Capture/device -> open Bench
Select Capture/begin capture
Connection type -> serial port
analyzer port -> your arduino Serial port
Devicetype -> Arduino generic (uno) or Arduino Mega (mega)
Select show device metadat, this will talk to the Arduino, verify comms
Select Acquisition
Sampling rate -> your choice
recording size -> your choice, limited by which Arduino you have.
Select Triggers
Uncheck trigger enabled, this will start sampling as soon as you click capture
With the Arduino, only SIMPLE type is possible, and you can only use bits 0..4 or 5 for Uno, 0..7 for mega Mask select which 'channels' to monitor for trigger, Value is check=High unchecked=LOW

Chuck.