Help with mixed variable types

Well, after a long time away from experimenting with microcontrollers (haven't touched them since the Basic Stamp 2sx came out!), I've been bitten by the Arduino bug... and hard.

Anyways, I'm working on a little something involving a Sensirion SHT15 temperature and humidity sensor. I've been experimenting with the library provided by Jonathan Oxer and Maurice Ribble, but I've discovered a bug that's preventing me from getting the proper readings for humidity (my temperature is still a bit off, but I'll get to that when I can).

Basically, the error comes from converting the sensor data to real-world values. The code pulls in a 12-bit value for humidity (though anything over about decimal 2500 would be above 100%RH), which fits nicely into the int data type. To convert from the sensor data to Relative Humidity, you use the formula as follows:

RHlinear = c1 + c2*SORH + c3*SORH2

Where RHlinear is the Relative Humidity, SORH is the sensor output,c1 is -2.0468, c2 is 0.0367, and c3 is -.0000015955.

The code ends up looking something like this:

float readHumidity() {
int val;
float humidity;
const float C1 = -2.0468;
const float C2 =  0.0367;
const float C3 = -0.0000015955;

val = get_SO_RH();

humidity = C1 + C2 * val + C3 * val * val;

return humidity;
}

The first thing I notice is we are multiplying some rather small floats with some large ints. If I return val and manually figure out the correct value, my sensor is dead-on (I have it in a special cigar bag with a "Humidipak" that's calibrated to 69% RH; the sensor is showing 68.6 which is well within the 2% margin of error). My concern is that multiplying val * val may be causing an overflow (as it's well above a 16-bit number) and therefor throwing my numbers off (or it could be the mix of all these datatypes or the limitation of Arduino's handling of float).

Even though that value for C3 may be small, since it's being multiplied by a rather large number, it significantly affects the output- around 5% at 70%RH. If worse comes to worst, there's an alternate method that should work, but that comes at the expense of precision and thus something I am trying to avoid.

Any insight? Your help is greatly appreciated!

if float doesnt work, double might work...

this formula is faster and doesnt do any integer arith (just like urs: the expression is evaluated from left to right):
humidity = C1 + (C2 + C3 * val) * val;

can u show us a tabular of results for val=0, 500, 1000, 2500 ?

-arne

if float doesnt work, double might work...

On the Arduino, float and double are the same size.
From: http://arduino.cc/en/Reference/Double

The double implementation on the Arduino is currently exactly the same as the float, with no gain in precision.

A better solution is to change the type of val from int to long.

From: http://arduino.cc/en/Reference/Long

Long variables are extended size variables for number storage, and store 32 bits (4 bytes), from -2,147,483,648 to 2,147,483,647.

As long as val is less than 46,340, val * val will fit in a long.

Using an intermediate variable to hold val * val will ensure that the long type is what is used in C3 * val * val.

On the other hand, since the multiplication property is associative, the equation is evaluated left to right, so C3 and val will be multiplied, and the result multiplied by val, so you really don't need to do anything.

You can ensure that C3 * val occurs first (rather than val * val) by adding parentheses around C3 * val in the statement.

ohoh
no double on arduino... giggle

here
http://arduino.cc/en/Reference/Float
we say, that the float has just 6-7 digits...

although 6-7 digits r not much, it should suffice for that formula...

-arne

we could try it like this (with pure integer arithmetics):
-2.0468+0.0367V-.0000015955VV
= -20,468/10,000 + V
367/10,000 - VV15955/10,000,000,000
= (-20,468 + V367 - VV15955/1,000,000)/10,000
= (-20,468
1,000 + V*(3671,000,000 - V15,955)/1,000)/10,000,000
~=~ "(V*(367000UL - (V*15955UL)/1000UL) - 20468000UL) / 10000UL" thousandths (1/1,000ths) of the result with some little error

that should be faster (on my big box, which has hardware support for division and multiplication, the integer method is more than 2 times faster)...

simulation on my big box:

#include <stdio.h>
#include <stdint.h>

int main() {
   for (uint16_t V=100; V<=2500; V+=400) {
      const uint32_t c1 = 367000, c2 = 15955, c3 = 20468000, c4 = 1000, c5 = 10000;
      const uint32_t r = (V*(c1 - (V*c2)/c4) - c3) / c5;
      const double R = -2.0468+0.0367*V-.0000015955*V*V;
      printf("V=%4d r=%3d.%03d/%7.3f (error:%.3fppm)\n",
             V,r/1000,r%1000,R,(1-r*1e-3/R)*1e6);
   }
   return 0;
}

output:

V= 100 r= 1.607/ 1.607 (error:152.435ppm)
V= 500 r= 15.904/ 15.904 (error:20.435ppm)
V= 900 r= 29.690/ 29.691 (error:28.460ppm)
V=1300 r= 42.966/ 42.967 (error:18.735ppm)
V=1700 r= 55.732/ 55.732 (error:3.678ppm)
V=2100 r= 67.987/ 67.987 (error:0.662ppm)
V=2500 r= 79.731/ 79.731 (error:4.076ppm)

-arne (who didnt have to do anything else)

Hey guys, thanks for all your help. I was a little busy with work the last few days, and finally got back around to the project.

It turns out the values returned from the Arduino are fine; the error is on my part. When I was testing what the values "should" be, I was missing the negative sign on the C3 term :-/ Of course, it just so happened that these incorrect values were what I was expecting them to be. Total mea culpa on this one!

Now I have to check somehow to see if my calibrated humidity source is still good, and I should probably pick up some ones with other values just to be sure.

riddick, thanks for the integer math conversion, I may have to give that a try, though in this application the speed advantage is trivial compared to the accuracy requirements.

Thanks again everyone.