Go Down

Topic: Float I2c (Read 3 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.

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

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