TWI/I2C - Arduino to Arduino, sending float

Due to the application demanding it.

You can still have half a revolution etc...

20000 RPM wont warrant it, but when using it for precise measurements like an encoder input (1000 pulses per rev), knowing parts of a revolution is required.

I was very happy to find this thread. I am experiencing the same problem. I want to transfer a float when that is requested by the I2C master. Both master and slave are Arduino's.

The example has not helped me out yet. Are you willing to share more of your code?

Hey,

See if this helps you at all.
I am reading 2 floats from the slave.

Master Arduino has this code somewhere in the loop, triggered as you see fit:

byte data[8];
float RPM;
float DRPS;

void loop()
{
    Wire.beginTransmission(2);
    Wire.requestFrom(2, 8);              // request 8 bytes from slave device #2
    if(Wire.available())
    {
      int i = 0;
      while(Wire.available())    // slave may send less than requested
      { 
        data[i] = Wire.receive(); // receive a byte as character  
        i = i + 1;
      }
      
      //A union datatypes makes the byte and float elements share the same piece of memory, which enables conversion from a byte array to a float possible
      union RPM_tag {byte RPM_b[4]; float RPM_fval;} RPM_Union;    
      RPM_Union.RPM_b[0] = data[0];
      RPM_Union.RPM_b[1] = data[1];
      RPM_Union.RPM_b[2] = data[2];
      RPM_Union.RPM_b[3] = data[3];    
      RPM = RPM_Union.RPM_fval * 60;
      
      union DRPS_tag {byte DRPS_b[4]; float DRPS_fval;} DRPS_Union;       //DRPS = Drum Revs per Second
      DRPS_Union.DRPS_b[0] = data[4];
      DRPS_Union.DRPS_b[1] = data[5];
      DRPS_Union.DRPS_b[2] = data[6];
      DRPS_Union.DRPS_b[3] = data[7];    
      DRPS = DRPS_Union.DRPS_fval;
    }
    Wire.endTransmission();
}

First float in this case is RPM, second is Drum Revs Per Second (DRPS). So make these whatever you like.

Slave Arduino has this code:

volatile byte* INPUT1FloatPtr;
volatile byte* INPUT2FloatPtr;
int Address = 2;  //This slave is address number 2

void setup()
{
  Wire.begin(Address);
  Wire.onRequest(requestEvent); // register event
}

void loop()
{
  lastINPUT1 = 12345.56    //your code here
  lastINPUT2 = 1234.12     //your code here
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  byte* Data;
  INPUT1FloatPtr = (byte*) &lastINPUT1;
  INPUT2FloatPtr = (byte*) &lastINPUT2;
  Data[0] = INPUT1FloatPtr[0]; 
  Data[1] = INPUT1FloatPtr[1]; 
  Data[2] = INPUT1FloatPtr[2]; 
  Data[3] = INPUT1FloatPtr[3]; 
  Data[4] = INPUT2FloatPtr[0];
  Data[5] = INPUT2FloatPtr[1];
  Data[6] = INPUT2FloatPtr[2];
  Data[7] = INPUT2FloatPtr[3];
  Wire.send(Data,8);
}

So in the above code, the first float I am sending is lastINPUT1, the second is lastINPUT2. They are both bundled up into a byte array called Data, and sent over Wire to the master.
The master then reads in each byte, and outputs the two floats.

Is that of any help?

Cheers
James

Hi James,

Thank you for your reply. Yet this has helped me to find my bug. You use Wire.onRequest() for registering the interrupt handler. That is correct. My issue is that I first want to send a command so that the correct float (I have several) is sent back. I am also using this command for controlling the satellite Arduino. You need Wire.onReceive() to do this.

I am still fighting to get these two handlers working together. But your example has helped my a lot.

Cheers,
Cronix

Hello

I had a PM from a user who couldnt reply to my original topic, due to the forum change I suspect, my original thread was here
http://arduino.cc/forum/index.php/topic,45445.0.html

He asked me how you would go about sending an array of 6 floats over I2C.

I havent done it before, only to the extent of what the post shows, 2 single floats, using two unions, sending the bytes etc.
I attempted to write him some code to try, but he said it didnt work.

Code is as follows. It compiled, but I did not test it. He said the following was received:

Float 1 Value: 740733.56
Float 2 Value: 1234.12
Float 3 Value: 12345.56
Float 4 Value: 0.00
Float 5 Value: 0.00
Float 6 Value: 0.00

Master Arduino:

#include <Wire.h>

byte data[24];
float floatData[6];
long lastSerialPrint = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
}

void loop()
{
    Wire.beginTransmission(2);
    Wire.requestFrom(2, 24);              // request 24 bytes from slave device #2
    if(Wire.available())
    {
      int i = 0;
      while(Wire.available())    // slave may send less than requested
      { 
        data[i] = Wire.read(); // receive a byte as character  
        i = i + 1;
      }
      
      //A union datatypes makes the byte and float elements share the same piece of memory, which enables conversion from a byte array to a float possible
      union Float1_tag {byte Float1_b[4]; float Float1_fval;} Float1_Union;       //Float 1
      Float1_Union.Float1_b[0] = data[0];
      Float1_Union.Float1_b[1] = data[1];
      Float1_Union.Float1_b[2] = data[2];
      Float1_Union.Float1_b[3] = data[3];    
      floatData[0] = Float1_Union.Float1_fval * 60;
      
      union Float2_tag {byte Float2_b[4]; float Float2_fval;} Float2_Union;       //Float 2
      Float2_Union.Float2_b[0] = data[4];
      Float2_Union.Float2_b[1] = data[5];
      Float2_Union.Float2_b[2] = data[6];
      Float2_Union.Float2_b[3] = data[7];    
      floatData[1] = Float2_Union.Float2_fval;
      
      union Float3_tag {byte Float3_b[4]; float Float3_fval;} Float3_Union;       //Float 3
      Float3_Union.Float3_b[0] = data[8];
      Float3_Union.Float3_b[1] = data[9];
      Float3_Union.Float3_b[2] = data[10];
      Float3_Union.Float3_b[3] = data[11];    
      floatData[2] = Float3_Union.Float3_fval;
      
      union Float4_tag {byte Float4_b[4]; float Float4_fval;} Float4_Union;       //Float 4
      Float4_Union.Float4_b[0] = data[12];
      Float4_Union.Float4_b[1] = data[13];
      Float4_Union.Float4_b[2] = data[14];
      Float4_Union.Float4_b[3] = data[15];    
      floatData[3] = Float4_Union.Float4_fval;
      
      union Float5_tag {byte Float5_b[4]; float Float5_fval;} Float5_Union;       //Float 5
      Float5_Union.Float5_b[0] = data[16];
      Float5_Union.Float5_b[1] = data[17];
      Float5_Union.Float5_b[2] = data[18];
      Float5_Union.Float5_b[3] = data[19];    
      floatData[4] = Float5_Union.Float5_fval;
      
      union Float6_tag {byte Float6_b[4]; float Float6_fval;} Float6_Union;       //Float 6
      Float6_Union.Float6_b[0] = data[20];
      Float6_Union.Float6_b[1] = data[21];
      Float6_Union.Float6_b[2] = data[22];
      Float6_Union.Float6_b[3] = data[23];    
      floatData[5] = Float6_Union.Float6_fval;
    }
    Wire.endTransmission();
    
    if(millis() > lastSerialPrint + 1000) //Like the Blink without delay example, true once a second
    {
      Serial.print("Float 1 Value: ");
      Serial.println(floatData[0]);
      Serial.print("Float 2 Value: ");
      Serial.println(floatData[1]);
      Serial.print("Float 3 Value: ");
      Serial.println(floatData[2]);
      Serial.print("Float 4 Value: ");
      Serial.println(floatData[3]);
      Serial.print("Float 5 Value: ");
      Serial.println(floatData[4]);
      Serial.print("Float 6 Value: ");
      Serial.println(floatData[5]);
      lastSerialPrint = millis(); //Snapshot of when this happened, in milli seconds
    }
}

Slave Arduino:

#include <Wire.h>

volatile byte* Float1ArrayPtr;  //Float 1 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float2ArrayPtr;  //Float 2 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float3ArrayPtr;  //Float 3 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float4ArrayPtr;  //Float 4 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float5ArrayPtr;  //Float 5 Array Pointer (series of bytes, 4 bytes each float)
volatile byte* Float6ArrayPtr;  //Float 6 Array Pointer (series of bytes, 4 bytes each float)
int Address = 2;  //This slave is address number 2
float floatData[6]; //Your float array

void setup()
{
  Wire.begin(Address);
  Wire.onRequest(requestEvent); // register event
}

void loop()
{
  //Your code with putting values into your float array
  floatData[0] = 12345.56;    //your code here
  floatData[1] = 1234.12;    //your code here
  floatData[2] = 12345.56;   //your code here
  floatData[3] = 1234.12;    //your code here
  floatData[4] = 12345.56;    //your code here
  floatData[5] = 1234.12;   //your code here
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  byte* Data;
  Float1ArrayPtr = (byte*) &floatData[0];
  Data[0] = Float1ArrayPtr[0]; 
  Data[1] = Float1ArrayPtr[1]; 
  Data[2] = Float1ArrayPtr[2]; 
  Data[3] = Float1ArrayPtr[3]; 
  Float2ArrayPtr = (byte*) &floatData[1];
  Data[4] = Float2ArrayPtr[0];
  Data[5] = Float2ArrayPtr[1];
  Data[6] = Float2ArrayPtr[2];
  Data[7] = Float2ArrayPtr[3];
  Float3ArrayPtr = (byte*) &floatData[2];
  Data[8] = Float3ArrayPtr[0]; 
  Data[9] = Float3ArrayPtr[1]; 
  Data[10] = Float3ArrayPtr[2]; 
  Data[11] = Float3ArrayPtr[3]; 
  Float4ArrayPtr = (byte*) &floatData[3];
  Data[12] = Float4ArrayPtr[0];
  Data[13] = Float4ArrayPtr[1];
  Data[14] = Float4ArrayPtr[2];
  Data[15] = Float4ArrayPtr[3];
  Float5ArrayPtr = (byte*) &floatData[4];  
  Data[16] = Float5ArrayPtr[0]; 
  Data[17] = Float5ArrayPtr[1]; 
  Data[18] = Float5ArrayPtr[2]; 
  Data[19] = Float5ArrayPtr[3]; 
  Float6ArrayPtr = (byte*) &floatData[5];
  Data[20] = Float6ArrayPtr[0];
  Data[21] = Float6ArrayPtr[1];
  Data[22] = Float6ArrayPtr[2];
  Data[23] = Float6ArrayPtr[3];
  Wire.write(Data,24); //Send the 24 bytes (6 floats)
}

If anyone has any advice to help him, it would be appreciated. I have PM'ed him back with the link to this thread.

Im not suggesting the way I wrote it is the best method by any means, but was the only way I knew that I had tried and had worked, but never to the extent of sending 6 floats.

Cheers
J

Threads merged.

Thanks Nick

See: Arduino Forum

You could make a struct, put 6 floats in it and then use I2C_Anything to send the struct.

Can you spot what might be wrong with the example I wrote? Im interested in the answer too.

Had a look at your post, looks good - still need to fully digest it though.

Cheers

You need to read up on doing loops. And making functions. That is just very repetitious.

Anyway ...

 Wire.beginTransmission(2);
    Wire.requestFrom(2, 24);              // request 24 bytes from slave device #2
    if(Wire.available())

You have to do the endTransmission first. In fact you aren't sending anything so the beginTransmission isn't needed.

Instead of Wire.available you should check you got 24 bytes back, eg.

if (Wire.requestFrom (2, 24) == 24)
  {
  // got a valid response
  }

This is the general idea without using the template function. Much shorter and easier to read don't you think?

#include <Wire.h>

const byte slaveAddress = 2;
const byte dataCount = 6;

union
  {
  float floatData [dataCount];
  byte  rawData [dataCount * sizeof (float)];
  } myData;

unsigned long lastSerialPrint = 0;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
}  // end of setup

void loop()
{
    if (Wire.requestFrom (slaveAddress, sizeof myData) == sizeof myData)
      {
      for (int i = 0; i < sizeof myData; i++)
         myData.rawData [i] = Wire.read ();
      }  // end if
         
    if(millis() - lastSerialPrint > 1000) //Like the Blink without delay example, true once a second
      {
      for (int i = 0; i < dataCount; i++)
        {
        Serial.print ("Float ");
        Serial.print (i);
        Serial.print (" Value: ");
        Serial.println (myData.floatData[i]);  
        } // end for
      lastSerialPrint = millis(); //Snapshot of when this happened, in milli seconds
      }  // end if
}  // ene of loop

Not tested, but it looks a lot shorter.

Nice work thanks.

As mentioned, what I wrote I never said was the best solution - I am not a programmer by any stretch of the imagination.
Just trying to help.

Appreciate the updated code.

Regards
James

I'm a bit late to this thread but you don't need floats to work with fractional values. Use large ints and work in 10ths or 100th of the units you need, then format accordingly when you present the data to a human.


Rob

Your original code should work.

In cases like this, I typically use a low-level function to isolate the user code from the actual protocol, like this:

//send a byte with a protocol
void send_byte(unsigned char dat) {
//implement your protocol here
}

//send a word with send_byte()
void send_word(unsigned short wrd) {
  unsigned char i=sizeof(wrd);
  unsigned char *ptr=(unsigned char *) &wrd;
  while (i--) send_byte(*ptr++);
}

//send a float with send_byte()
void send_float(float flt) {
  unsigned char i=sizeof(flt);
  unsigned char *ptr = (unsigned char *) &flt;
  while (i--) send_byte(*ptr++);
}

You can pretty much expand it to cover any other data types.

It has the advantage of providing some stability to your code: if you wish to change the protocol through which the data is sent, you just change send_byte(), as everything else is built on top of it.

Hi,

I was asking for the code. I tried the above code and it works perfect! I just have to change few things ( master has to request slave to send).

Thank you so much! :slight_smile:

Bye

Hello.

Could someone please take a look at my code -simplified as given below-, and say what's the problem here? I am using 2 DUEs, and I am almost out of my mind! Please help me...

Additionally, I wonder if using volatile and union together like;

  • volatile union T {byte b[8]; double d;} T;,
  • union T {volatile byte b[8]; volatile double d;} T; or
  • union T {byte b[8]; double d;} volatile T;

is possible or not?

MASTER:

    #include <Wire.h>

    volatile double t, x, y;

// I2C BUS:

// Constants:
    const byte slaveAddress = 8;
    const byte dataCount = 24;

// Variables:
    union T {byte b[8]; double d;} T;
    union X {byte b[8]; double d;} X;
    union Y {byte b[8]; double d;} Y;

// Functions:
    void readPositions()
    {
      if (Wire.requestFrom(slaveAddress, dataCount) == dataCount)
      {
        for (byte i = 0; i < 8; i++)
        {
          T.b[i] = Wire.read();
          X.b[i] = Wire.read();
          Y.b[i] = Wire.read();
        }

        t = T.d; x = X.d; y = Y.d;
      }
    }

void setup()
{
  Wire.begin();
}

void loop()
{
  // Here is some code using t, x and y values...
}

SLAVE:

    #include <Wire.h>

    volatile double t, x, y;

// I2C BUS:

// Constants:
    const byte myAddress = 8;
    const byte dataCount = 24;

// Variables:
    byte* ArrayPtrT;
    byte* ArrayPtrX;
    byte* ArrayPtrY;
    byte* data;

// Functions:
    void writePositions()
    {
      ArrayPtrT = (byte*) &t;
      ArrayPtrX = (byte*) &x;
      ArrayPtrY = (byte*) &y;

      for (byte i = 0; i < 8; i++)
      {
        byte j = i * 3;
        data[j] = ArrayPtrT[i];
        data[j + 1] = ArrayPtrX[i];
        data[j + 2] = ArrayPtrY[i];
      }

      Wire.write(data, dataCount);
    }

void setup()
{
  Wire.begin(myAddress);
  Wire.onRequest(writePositions);
}

void loop()
{
  t = t + 0.1;
  x = x + 0.1;
  y = y + 0.1;
}

Why are you going out of your mind? Does it compile? Does it work? Why not use I2C_Anything?

I will try to implement I2C_Anything tomorrow. However, I want to learn how to use union, and also learn how to deal with things in bytes level...

My code is actually semi-working:

I serial-print data[0] to data[23] just before the wire.write line. It prints something like: 1,2,3,...,22,23,24

I also serial-print T.b[0] to T.b[23] + X.b[0] to X.b[23] + Y.b[0] to Y.b[23] just after wire.read commands. It should print exactly the same series, but it doesn't. It prints: 0,1,2,...,21,22,8

And if I print T.d, X.d and Y.d, I see that they are all 0.00. Sometimes, for some values I thing, they may be nan or vof...

I don't understand what the problem here is...

    byte* data;

      for (byte i = 0; i < 8; i++)
      {
        byte j = i * 3;
        data[j] = ArrayPtrT[i];
        data[j + 1] = ArrayPtrX[i];
        data[j + 2] = ArrayPtrY[i];
      }

You haven't allocated any memory for data, therefore you are corrupting memory. Try:

    byte data [dataCount];

Also your sending and receiving ends are non-symmetrical which is confusing.

Thank you for your quick reply. I will try this asap. But what do you mean by saying non-symmetrical ends?