Go Down

Topic: Float I2c (Read 3326 times) previous topic - next topic

woody_unreal

Is there a nice simple way to Pass Float Value over I2C -to a second arduino-  (example 3.12 for Volts). All ive seen is elaborate unions and pointers ,, Seems WAY over complexe so i skipped reading further. Any thoughts/ideas /Examples/referances :) ?

woody_unreal

Wow thers a first, never had one with out some kind of responce :)

Nick Gammon

You need a union. You said you don't want to use one. Not much left to say. They are not particularly elaborate BTW.
Please post technical questions on the forum, not by personal message. Thanks!

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

woody_unreal

Nick Gammon , you sem to the goto for I2C ,, have your had the chance to run "unions" before ?

Nick Gammon

#4
May 08, 2012, 01:59 am Last Edit: May 08, 2012, 02:01 am by Nick Gammon Reason: 1
OK, here's how to do it. Based on the EEPROM_Anything idea I did this ...




I2C_Anything.h

Make a new tab in the IDE called "I2C_Anything.h". Put this in it:

Code: [Select]
// Written by Nick Gammon
// May 2012

#include <Arduino.h>
#include <Wire.h>

template <typename T> int I2C_writeAnything (const T& value)
 {
   const byte * p = (const byte*) &value;
   unsigned int i;
   for (i = 0; i < sizeof value; i++)
         Wire.write(*p++);
   return i;
 }  // end of I2C_writeAnything

template <typename T> int I2C_readAnything(T& value)
 {
   byte * p = (byte*) &value;
   unsigned int i;
   for (i = 0; i < sizeof value; i++)
         *p++ = Wire.read();
   return i;
 }  // end of I2C_readAnything


Now here is an example of sending a float and a long to a slave:




Master

Code: [Select]

// Written by Nick Gammon
// May 2012

#include <Wire.h>
#include "I2C_Anything.h"

const byte SLAVE_ADDRESS = 42;

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

void loop()
{

long foo = 42;

for (float fnum = 1; fnum <= 10; fnum += 0.015)
   {  
   Wire.beginTransmission (SLAVE_ADDRESS);
   I2C_writeAnything (fnum);
   I2C_writeAnything (foo++);
   Wire.endTransmission ();
     
   delay (200);
   }  // end of for

}  // end of loop





And here is the slave:

Slave

Code: [Select]

// Written by Nick Gammon
// May 2012

#include <Wire.h>
#include "I2C_Anything.h"

const byte MY_ADDRESS = 42;

void setup()
{
 Wire.begin (MY_ADDRESS);
 Serial.begin (115200);
 Wire.onReceive (receiveEvent);
}  // end of setup

volatile boolean haveData = false;
volatile float fnum;
volatile long foo;

void loop()
{
 if (haveData)
   {
   Serial.print ("Received fnum = ");
   Serial.println (fnum);  
   Serial.print ("Received foo = ");
   Serial.println (foo);  
   haveData = false;  
   }  // end if haveData

}  // end of loop

// called by interrupt service routine when incoming data arrives
void receiveEvent (int howMany)
{
if (howMany >= (sizeof fnum) + (sizeof foo))
  {
  I2C_readAnything (fnum);  
  I2C_readAnything (foo);  
  haveData = true;    
  }  // end if have enough data

}  // end of receiveEvent


The template in the .h file will convert any data type (eg. float, int, long) into a pointer to a series of bytes and send (or receive) those one at a time via I2C.

Example output:

Code: [Select]

Received fnum = 6.52
Received foo = 410
Received fnum = 6.53
Received foo = 411
Received fnum = 6.55
Received foo = 412
Received fnum = 6.56
Received foo = 413
Received fnum = 6.58
Received foo = 414
Received fnum = 6.59
Received foo = 415
Received fnum = 6.61
Received foo = 416
Received fnum = 6.62
Received foo = 417
Received fnum = 6.64
Received foo = 418
Received fnum = 6.65
Received foo = 419
Received fnum = 6.67
Received foo = 420
Please post technical questions on the forum, not by personal message. Thanks!

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

woody_unreal

#5
May 08, 2012, 02:10 am Last Edit: May 08, 2012, 02:25 am by woody_unreal Reason: 1
Holy Crap, Ty for the info -I would have never expected a complete sketch  tyvm :) . Now i have to break it down to get the idea of how its works, then to incorporate into my increasingly growing sketch. thank you again :)

rombokas

#6
Jan 04, 2013, 12:35 am Last Edit: Jan 04, 2013, 12:51 am by rombokas Reason: 1
I'm having trouble using I2C_Anything with Wire.requestFrom, and any help would be greatly appreciated.

I'd like the master to request data from the slave, but all my floats arrive as nan .  I've tried sending individual bytes and it works.  I'm confident in my wiring because I can get I2C_Anything to work as written in this post.  There's something about how I'm using requestFrom which is breaking something!


Here are the files:

Master, an Uno which uses Wire.requestFrom to ask for the data
Code: [Select]

// Eric Rombokas
// Jan 3 2013
// Arduino is i2c master, arduIMU is i2c slave .  arduino dumps requested i2c data to serial
// Using gammon I2C_readAnything.h for translating I2C bytes to other data types

#include <math.h>
#include <Wire.h>
#include "I2C_writeAnything.h"

const int SLAVE_ADDRESS = 10;

volatile float volatileYaw;

void setup() {
 Serial.begin(115200);
 Wire.begin(); // begin with no address specified joins the bus as a master
}

void loop() {
 
 delay(1000);  
 
 if (Wire.requestFrom(SLAVE_ADDRESS, 4, true) == 4) {
   I2C_readAnything(volatileYaw);
   Serial.println(volatileYaw);
 }  


}



And slave, which uses I2C_writeAnything in its requestEvent()

Code: [Select]


// Eric Rombokas
// Jan 3 2013
// ArduIMU runs as an i2c slave, querying freeIMU for yaw, pitch, roll when asked to by the i2c master
// Adapted from Fabio Varesano's FreeIMU_Yaw_pitch_roll RIP
// i2c float/byte stuff adapted from "WanaGo" on arduino forum

#include <ADXL345.h>
#include <bma180.h>
#include <HMC58X3.h>
#include <ITG3200.h>
#include <MS561101BA.h>
#include <I2Cdev.h>
#include <MPU60X0.h>
#include <EEPROM.h>

//#define DEBUG
#include "DebugUtils.h"
#include "CommunicationUtils.h"
#include "FreeIMU.h"
#include "I2C_writeAnything.h"
#include <Wire.h>
#include <SPI.h>
#include <math.h>

volatile float volatileYaw = 42.42;

const int I2CSlaveAddress = 10;

void setup() {

 
 Wire.begin(I2CSlaveAddress);
 Wire.onRequest( requestEvent ) ; // declare function handle to be called when i2c master requests
 
}

void loop() {
 

}

// called by onRequest interrupt
void requestEvent() {

 I2C_writeAnything(volatileYaw);
   
}



Any ideas?

Thanks,
Eric R

(edited to remove superfluous stuff)

rombokas

An update.  Sending multiple bytes at all during a requestEvent results in error.

I'm running on two Uno R3 arduinos.  

Master uses requestFrom to ask for bytes.  If I ask for one, it works, but if I ever ask for more than 1, they come back corrupted.

Master code:

Code: [Select]

#include <Wire.h>

int numReceived = 0;

void setup()
{
Wire.begin();        // join i2c bus (address optional for master)
Serial.begin(115200);
}

void loop()
{
 numReceived = 0;
 Wire.requestFrom(2, 2);

 while(Wire.available())
 {
 byte c = Wire.read();
 Serial.print("Received: ");
 Serial.print(c);        
 Serial.println(" . ");
 numReceived++;
 }

 delay(500);
 Serial.print(" Received ");
 Serial.print(numReceived);
 Serial.println(" bytes.  ");
}


Slave code:

Code: [Select]

#include <Wire.h>

byte val1 = 0;
byte val2 = 1;

void setup()
{
Wire.begin(2);                // join i2c bus with address #2
Wire.onRequest(requestEvent); // register event
}

void loop()
{
delay(100);
}

void requestEvent()
{
 Wire.write(val1);
 Wire.write(val2);
}


Output is :

Received: 1 .
Received: 255 .
Received 2 bytes. 


What is happening here?

-Eric

PaulS

Try this:
Code: [Select]
byte vals[2];

void requestEvent()
{
  vals[0] = 14;
  vals[1] = 172;

  Wire.write(vals, 2);
}

rombokas

Thanks for the reply! 

It works correctly using the single call to Wire.write().  If I split it into two calls it fails:

eg
Code: [Select]

Wire.write(vals[0]);
Wire.write(vals[1]);


Produces

Received 2 bytes. 
Received: 172 .
Received: 255 .

Is there something about the Wire library that means only a single write() should be used in an interrupt?  I2C_writeAnything() uses multiple Wire.write() calls in a for-loop, so that would mean it can't be used in an onRequest handler.

Perhaps I could modify it to fill a byte array, then send using a single write() call, like this:

Code: [Select]

float someFloat = 1.2;
byte vals[4];

byte * p = (const byte*) someFloat;
unsigned int i;
for (i = 0; i < sizeof someFloat; i++)
  vals[i] = *p++;

Wire.write(vals, 4);


Or something like that?

-Eric





rombokas

That worked. 

I added the following function to I2C_Anything.h

Code: [Select]


template <typename T> int I2C_singleWriteAnything (const T& value) {
  int size = sizeof value;
  byte vals[size];
  const byte* p = (const byte*) &value;
  unsigned int i;
  for (i = 0; i < sizeof value; i++) {
    vals[i] = *p++;
  }
 
  Wire.write(vals, size);
  return i;
}


And it works perfectly, even in the onRequest handler. 

Thanks 4.2 zillions!

Eric Rombokas

conamore78

Thanks a lot! it works perfectly with the last addition. MasterWrite works great even with Due. But requestEvent is not working with Due :( it always prints only "0.00", it doesn't matter if you connect a slave to the master..help please! =(

rudydarmawan

thannks for respon,  :)

Go Up