Pages: [1] 2 3   Go Down
Author Topic: TWI/I2C - Arduino to Arduino, sending float  (Read 5136 times)
0 Members and 2 Guests are viewing this topic.
NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
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
« Last Edit: September 18, 2010, 08:20:31 pm by WanaGo » Logged

NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Edison Member
*
Karma: 0
Posts: 1103
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Portugal
Offline Offline
God Member
*****
Karma: 5
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Why do you need floating point to represent rpms?
Logged

NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 22
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
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
« Last Edit: December 04, 2010, 10:27:51 pm by WanaGo » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 22
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
#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
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Threads merged.
Logged

NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Nick
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

NZ
Offline Offline
Sr. Member
****
Karma: 0
Posts: 390
Turtle in a hard shell
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Anyway ...

Code:
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:
if (Wire.requestFrom (2, 24) == 24)
  {
  // got a valid response
  }
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
#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.
« Last Edit: November 09, 2012, 04:08:36 am by Nick Gammon » Logged

Pages: [1] 2 3   Go Up
Jump to: