Go Down

Topic: TWI/I2C - Arduino to Arduino, sending float (Read 8468 times) previous topic - next topic

WanaGo

Sep 19, 2010, 03:18 am Last Edit: Sep 19, 2010, 03:20 am by WanaGo Reason: 1
Hi

I am just trying to send a float from 1 arduino to another over TWI/I2C and I have data coming over, but it doesnt look correct and need some help please.

I am getting the Master to request data from the Slave.

Code below are snippets just to show what im trying to do.

The slave has this code, which I stole from another post:
Code: [Select]
byte* RPMFloatPtr;

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

void requestEvent()
{
 RPMFloatPtr = (byte*) &lastRPM;
 Wire.send(RPMFloatPtr, 8); // respond with message of 4 bytes as expected by master
}


The Master does this:

Code: [Select]
void setup()
{
Wire.begin();
}

void loop()
{
Wire.requestFrom(2, 4);
float data;
while(Wire.available())    // slave may send less than requested
{
 data = Wire.receive(); // receive a byte as character  
}
Serial.print(data);
}


So the master is requesting 8 bytes of data from the slave, the slave is replying with 8 bytes in a byte array (float made into a byte array), and then the master is printing the incomming data as a float.

Im sure I have made a rookie mistake though.
Can someone assist?

The Slave has a float called 'lastRPM' which could be 0.00 up to 20000.00, and I just want the master to print that to the serial port.

Can someone help please

Regards
James

WanaGo

I found another post with this code in it (different variable names in some cases) - but this seems to work...

I made data an array of bytes first:

Code: [Select]
while(Wire.available())    // slave may send less than requested
   {
     data[i] = Wire.receive(); // receive a byte as character
     Serial.print("B: ");
     Serial.println(data[i]);
     i = i + 1;
   }


Then I used the code I found:

Code: [Select]
float RPM;

   union u_tag {
     byte b[4];
     float fval;
   } u;
   
   u.b[0] = data[0];
   u.b[1] = data[1];
   u.b[2] = data[2];
   u.b[3] = data[3];
   
   RPM = u.fval;


no idea what a union is yet or how/why this works though.

deSilva

A union is an "overlay" of variables, sharing the same memory cells.
In this case it is used as a re-interpret-cast.
It will only work if transmitting and sending devices have the same floating point model...

Senso

Why do you need floating point to represent rpms?

WanaGo

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.

cronix

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?

WanaGo

#6
Dec 05, 2010, 03:57 am Last Edit: Dec 05, 2010, 04:27 am by WanaGo Reason: 1
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:

Code: [Select]

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:
Code: [Select]

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

cronix

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

WanaGo

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:

Quote
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:
Code: [Select]
#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:
Code: [Select]
#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

Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

WanaGo


See: http://arduino.cc/forum/index.php?topic=104732.0

You could make a struct, put 6 floats in it and then use I2C_Anything to send the struct.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

WanaGo

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 ...

Code: [Select]

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.

http://www.gammon.com.au/i2c

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

Code: [Select]
if (Wire.requestFrom (2, 24) == 24)
  {
  // got a valid response
  }
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

#14
Nov 09, 2012, 09:54 am Last Edit: Nov 09, 2012, 10:08 am by Nick Gammon Reason: 1
This is the general idea without using the template function. Much shorter and easier to read don't you think?

Code: [Select]

#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.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up