Problem with I2C and float data transfer

Hi everyone,
First of all, Yes, i'm 13 years old, and i'm Italian so please don't judge me if my English skills won't be really good. (i'm not going to use Google Translate)

I'm making a project with 2 Arduino's (ONE) and a temperature sensor
One of the Arduino's is the sleve and takes the data from the temperature sensor as a float value, this value now has to be transferred to the Master witch made the request (if that makes any sense)

The problem is that the Wire library won't let me transfer these kind of data (float, for example 24.12)

This is the code i wrote for the Sleve

#include "DHT.h"
#define DHTPIN 2 
#define DHTTYPE DHT22 
DHT dht(DHTPIN, DHTTYPE);
#include <Wire.h>
int x;
void setup()
{
  Serial.begin(9600);
  Wire.begin(2);                
  dht.begin();
  Wire.onRequest(requestEvent); 
  Wire.onReceive(receiveEvent);
}


void loop() {}


void requestEvent()
{
  switch (x) {
    case 0:
    break;
    case 1:
    float t = dht.readTemperature();
   Wire.write(t);
    break;
    case 2:
    Wire.write("Come va?");
    break;
    case 3:
    Wire.write("il mio nome e' SlaveID 2");
    break;
  }     
  x = 0;
}

void receiveEvent(int howMany)  //Arriva la domanda
{
  x = Wire.read();              //Archivia la domanda
}

The problem is on the live where i try to send the float data via the Wire library, returning this error:
"call of overloaded 'write(float&)' is ambiguous"

Any ideas on how to fix it?

UP

It looks like wire.write doesn't have an overload that takes a float. To get it to compile at least, you can cast the float to an int. Also, move the declaration of the float to the top of the function; the compiler doesn't like it inside the switch as it is.

Your English is perfect, you do not need to translate UNO to ONE :wink:

For readability of code you might add an empty line to separate code in logical sections
and press CTRL-T for auto indenting, See code below. It also contains a possible solution for your float.

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

#define DHTPIN 2 
#define DHTTYPE DHT22 

DHT dht(DHTPIN, DHTTYPE);
int x;

void setup()
{
  Serial.begin(9600);
  Wire.begin(2);                
  dht.begin();
  Wire.onRequest(requestEvent); 
  Wire.onReceive(receiveEvent);
}

void loop() {
}

void requestEvent()
{
  switch (x) {
  case 0:
    break;
  case 1:
    float t = dht.readTemperature();
    int whole = t;
    int fract = (t-a)*10; 
    Wire.write(whole);
    Wire.write('.');
    Wire.write(fract);
    break;
  case 2:
    Wire.write("Come va?");
    break;
  case 3:
    Wire.write("il mio nome e' SlaveID 2");
    break;
  }     
  x = 0;
}

void receiveEvent(int howMany)  //Arriva la domanda
{
  x = Wire.read();              //Archivia la domanda
}

In my humble opinion it's a really bad idea to be returning float values from the temperature sensor. Much better to return a scaled integer, e.g. if the temperate is 25.24C then return 2524. This can be transferred as two byte values over I2C. I'm assuming there's some reason you can't just transfer the actual integer ADC value read from the sensor.

The problems with using floating point are many: (1) the AVR doesn't have native floating point, so using it means linking in software floating point and hence wasting memory. (2) Floating point formats vary between CPUs, especially when one of those is a device which is as restricted as a AVR. (3) Floating point is inherently fuzzy, you can't do the precise comparisons you can with integer. (4) Floating point may give the naive user an illusion of precision, but it's all fake: you can't be more precise than what you get from the ADC.

@DonMilne
your arguments make sense, however converting all to integer has its drawbacks too.

The DHT sensor itself returns 2 bytes that need to be interpreted as a float - see datasheet - creating an int from it can give trouble per sensor. Some sensors give 1 decimal, others 2 and some even 4. If converted to an int I loose the decimal places information, and yes I can edit that myself in the code but it makes my code less portable.

E.g a DS18B20 can have 4 decimals => converted to an int e.g 1.0625 gives me a long 10625. That is hard to distinguish from another sensor that does 2 decimals and returns 10625.

Thanks a lot guys, i'm gonna try it now

I'm trying this code now:

#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
#include <Wire.h>
int x;
void setup()
{
  Serial.begin(9600);
  Wire.begin(2);
  dht.begin();
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);
}


void loop() {}


void requestEvent()
{
  switch (x) {
    case 0:
      break;
    case 1:
     
      break;
    case 2:
       float t = dht.readTemperature();
    int whole = t;
    int fract = (t-whole)*10; 
    Wire.write(whole);
    Wire.write('.');
    Wire.write(fract);
      break;
    case 3:
      Wire.write("il mio nome e' SlaveID 2");
      break;
  }
  x = 0;
}

void receiveEvent(int howMany)  //Arriva la domanda
{
  x = Wire.read();              //Archivia la domanda
}

for a strange reason it gives an error on the 'case 3:' line, it goes 'jump to case label'

????

replace this function (it has additional {} in case 2: ) give it a try

void requestEvent()
{
  switch (x) 
  {
  case 0:
    break;
  case 1:
    break;
  case 2:
    {
      float t = dht.readTemperature();
      int whole = t;
      int fract = (t-whole)*10; 
      Wire.write(whole);
      Wire.write('.');
      Wire.write(fract);
      break;
    }
  case 3:
    Wire.write("il mio nome e' SlaveID 2");
    break;
  }
  x = 0;
}

robtillaart:
The DHT sensor itself returns 2 bytes that need to be interpreted as a float - see datasheet

Hmm. I just did read the datasheet and it turns out the sensor is sending back a scaled 2 byte integer exactly as I suggested above. There is absolutely no need to treat this as a float. Storage, transmission and comparison etc will work just as well using appropriately scaled integers.

DonMilne:

robtillaart:
The DHT sensor itself returns 2 bytes that need to be interpreted as a float - see datasheet

Hmm. I just did read the datasheet and it turns out the sensor is sending back a scaled 2 byte integer exactly as I suggested above. There is absolutely(1) no need to treat this as a float. Storage, transmission and comparison(2) etc will work just as well using appropriately scaled integers.

(1)Yes, of course
a computer only work with integers, even floats are on their lowest level just an array of bits.

(2)not true,
you cannot compare the 2 bytes of the DHT internal representation with a string that represent a float that is typed in by a user - user want to set the temperature to switch on heater or so - so you need to convert to a common format and yes that might be the scaled integer format, however scaled integers are not standardized without exponent (oops isn't that a float :wink:

Eh?? I made no mention of strings. I said "an appropriately scaled integer". You can't compare a string with anything except another string, so I don't see why that remark is relevant at all.

The DHT sensor returns temperatures scaled up by 256. So, for example, to compare the returned value against 25degC you might say "if (val_read >= ( 25<<8 )) {". On the AVR one must of course be careful to consider overflows when working with 16 bit signed ints.

And no, floating point has a very specific definition which is quite different from fixed point, which is really what we're discussing here. An implied exponent makes it fixed point, to be floating point it needs an explicit exponent term (and sign bits, if you mean a standard floating point format).

p.s. Just for what it's worth: I've been a fulltime professional C programmer for 30 years. On the other hand, what we're discussing here is very much at a beginners level. You might like to bear that in mind before trying to score silly points off me.

DonMilne:
Eh?? I made no mention of strings. I said "an appropriately scaled integer". You can't compare a string with anything except another string, so I don't see why that remark is relevant at all.

The remark is an example that there need to be conversion. The scaled integer of the DHT needs to be converted to a standard supported data type to be comparable. The raw scaled integer cannot be compared to anything except its own format, and even that may not be true as operators are not defined, they might work by accident.
I used the string as an (apparently too) extreme example that conversion is needed before compare without using float as example.

The DHT sensor returns temperatures scaled up by 256. So, for example, to compare the returned value against 25degC you might say "if (val_read >= ( 25<<8 )) {". On the AVR one must of course be careful to consider overflows when working with 16 bit signed ints.

So what you say is that you need to convert the value 25 to something other before it can be compared to the internal fixed point format of the sensor. That is exactly the point I want to make one way or the other you need to convert internal formats. The easiest and the logical way is to convert the internal representation to a standard supported format (which can be float).

So I would write that line of code as "if ( ( val_read >> 8 ) >= 25 ) {".
Or even "temperature = ( val_read >> 8 ); if ( temperature >= 25 ) {"

That makes it more explicit that 25 is the value compare to. 25 has a semantic meaning outside the code, ( 25 << 8 ) has not. There is imho just no need to stick with the internal format of the sensor when the data is "out of the box". What if you have 2 different sensors with 2 different internal fixed point representations, which representation should I use?. The standard design pattern is to normalize first (OK not necessary to float) and then do a compare.

And no, floating point has a very specific definition which is quite different from fixed point, which is really what we're discussing here. An implied exponent makes it fixed point, to be floating point it needs an explicit exponent term (and sign bits, if you mean a standard floating point format).

Agree 100%, but the Arduino does not support a fixed point data type.

p.s. Just for what it's worth: I've been a fulltime professional C programmer for 30 years. On the other hand, what we're discussing here is very much at a beginners level. You might like to bear that in mind before trying to score silly points off me.

Sorry if you feel offended, that is not my intention at all, it is not my intention "to score" silly points off you.

I just disagree with your arguments to use a proprietary fixed point data types in code longer than strictly needed. Imho they should be converted to supported data types asap (unless in optimizing mode). You have arguments to not do that, OK, agree to disagree

In my opinion this discussion is not only beginners level, as I see (too) much professional programmers with years of experience still making mistakes with data types. It should be in the design (role of the architect != beginner) to decide what data formats to use and why. Thinking about consequences of design decisions is done too few times.

robtillaart:
The remark is an example that there need to be conversion. The scaled integer of the DHT needs to be converted to a standard supported data type to be comparable.

It's already in a standard data type: integer, which furthermore is native to the AVR. The only thing being scaled here is the units of temperature. Why you believe that makes it necessary or wise to use floating point... escapes me.

Anyway, 'nuff said. I'm trying to give the OP practical advice, not have a debate.