Is there a problem with float/double data types?

I'm working on a pulse oximeter and with some success everything seems to be fine until my arduino acts weirdly when processing double and float data types. I'm calculating some ratios(100 values!) and printing them. Now when i set the base value types to double or float, half the serial.prints i give do not execute, in addition the led i'm glowing glows at a rapid pace. however if i change the base value type to int everything seems to work fine. I presume that the size of the double variable (i have an array of 100) must have some effect on this. Here is the code(beware no comments! its a bare draft to ensure working!) what else could be the possible causes for this weird behaviour? i used an Atmega168 on a hand built serial board(the board works grt with a lot of other programs)

#define sensor 1
#define ir_pin 5
#define led_pin 6
#define pwm 230
#define swtch 12
int ir[100], led[100];
long int ir_max =0, ir_min = 0, led_min = 0, led_max=0;
float ratio1,ratio2;

int ir_base=0, led_base=0;
//double ir[100], led[100];

double ir_ratio=0, led_ratio=0;
double ratio[100], ratio_avg=0;
double ir_base_avg =0, led_base_avg = 0;
int count =0, ir_count = 0, led_count=0, ratio_count = 0;
int time =0;

void setup()
{
  pinMode(ir_pin,OUTPUT);
  pinMode(led_pin,OUTPUT);
  pinMode(swtch,INPUT);
  analogWrite(ir_pin,pwm);
  analogWrite(led_pin,0);
  time = millis();
  while(millis()-time<300)  
  {
  ir_base_avg = ir_base_avg + analogRead(sensor);
  count = count + 1;
  }
  ir_base = int(ir_base_avg/count);
  count = 0;
  analogWrite(ir_pin,0);
  analogWrite(led_pin,pwm);
  time = millis();  
  while(millis()-time<300)
  {
    led_base_avg = led_base_avg + analogRead(sensor);
    count = count + 1;
  }
  led_base = int(led_base_avg/count);
  count = 0;
  analogWrite(led_pin,0);
  Serial.begin(19200);
  Serial.print((int)led_base);
  Serial.println((int)ir_base);  
}

void loop()
{
 if(digitalRead(swtch)==1)
{
check_pulse();
display_pulse();
reset_variables();
} 
}

void reset_variables()
{
  for(count = 0; count<100; count++)
  {
   ir[count] = 0;
   led[count]=0;
   ratio[count]=0;    
  }
  ratio_avg = 0;
  count = 0;
  ir_count = 0;
  led_count = 0;
  ratio_count = 0;
  time = 0;
}

void check_pulse()
{
  ir_count = 0;
  led_count = 0;
  count = 0;
  analogWrite(ir_pin,pwm);
  analogWrite(led_pin,0);
  time = millis();
  while(millis()-time<300 && ir_count<100)
  {
    ir[ir_count] = analogRead(sensor);
    ir_count = ir_count + 1;
  }
  analogWrite(ir_pin,0);
  analogWrite(led_pin,pwm);
  time = millis();
  while(millis()-time<300 && led_count<100)  
  {
    led[led_count] = analogRead(sensor);
    led_count = led_count + 1;
  }
  analogWrite(led_pin,0);
  ir_count = ir_count - 1;
  led_count = led_count - 1;
  
  if(ir_count > led_count)
    ratio_count = led_count;
  else
    ratio_count = ir_count;
  ir_min = ir[0];
  led_min = led[0];
  
  for(count=0; count <= ratio_count; count++)
  { ir_ratio = (double)ir[count]/ir_base;
    led_ratio = (double)led[count]/led_base;
    ratio[count]= led_ratio/ir_ratio;
    if(ir[count] > ir_max)
      ir_max = ir[count];
    if(led[count] > led_max)
      led_max = led[count];
    if(ir[count] < ir_min)
      ir_min = ir[count];
    if(led[count] < led_min)
      led_min = led[count];
    ratio_avg = ratio_avg + ratio[count];
  }
  ratio_avg = ratio_avg/count;
}

void display_pulse()
{
  Serial.print(ir_max);
  Serial.print(" ");
  Serial.print(ir_min);
  Serial.print(" ");  
  Serial.print(led_max);
  Serial.print(" ");  
  Serial.print(led_min);
  
  for(count=0; count <= ratio_count; count++)
  {
    Serial.print("IR Value: ");
    Serial.println((int)ir[count]);
    Serial.print("LED Value: ");
    Serial.println((int)led[count]);
    printdouble(ratio[count],100);
    Serial.println(" ");
  }
  
  ratio1=(float)ir_max/(float)led_min;
  Serial.println((int)(ratio1*10));
  ratio2=(float)ir_min/(float)led_max;
  Serial.println((int)(ratio2*10));
}

void printdouble( double val, unsigned int precision){
// prints val with number of decimal places determine by precision
// NOTE: precision is 1 followed by the number of zeros for the desired number of decimial places
// example: printDouble( 3.1415, 100); // prints 3.14 (two decimal places)

    Serial.print(int(val));  //prints the int part
    Serial.print("."); // print the decimal point
    unsigned int frac;
    if(val >= 0)
      frac = (val - int(val)) * precision;
    else
       frac = (int(val)- val ) * precision;
    int frac1 = frac;
    while( frac1 /= 10 )
        precision /= 10;
    precision /= 10;
    while(  precision /= 10)
        Serial.print("0");
    Serial.println(frac,DEC);
}

the last function to print a double i picked it here from the forum and presume that is not causing the trouble. :-/

Creating 2 arrays of 100 doubles is 800 bytes of RAM.

The 168 processor has only 1k Of RAM, some of this is used for internal structures, so you might have hit the RAM limit.

Using more RAM than available usually leeds to totally unpredictable results.

Try to make the double arrays 50 elements each and see if that helps.

Serial can print doubles and floats directly so you don't need that print double routine anymore.

If you are using Arduino 0013 or later you can do this:

Serial.print(ratio[count]);

I have encountered similar problems when I tried to use arrays with many elements for smoothing analog input values.

Is there a way to know how much RAM size the sketch consumes?
Do I have to calculate the size by reading the sketch?

tasasaki,

[ch12381][ch12428][ch12399][ch12385][ch12423][ch12387][ch12392]... (It's a bit, um, ...)

Complicated.

All of the global variables, all of the values that initialize them, all of the string constants, each consume RAM from the bottom up. As you call functions, each call and any local variables used inside the function are added to the stack, which consumes RAM from the top down. If you use malloc() routines, these consume RAM between the two. Only the bottom RAM can easily be accounted for by looking at the source code. A small trick can measure the stack usage at a given instant, but that can vary with other paths your program takes. And computing free malloc() heap is harder still.

Wow! Japanese word!

halley, now I understood how difficult the calculation. If I were a compiler, I could done that job.
There seem to be only a way with trial-and-error method.

reduced the variables and fixed it. Would have to do more sampling and analyse it... :frowning:

pracas, if you want to use more big SRAM, why not consider the following options:

  1. Latest Arduino Duemilanove which has ATmega328P with 2KBytes SRAM. (ATmega 168 has 1KBytes SRAM.)

  2. Arduino MEGA which has ATmega1280 with 8KBytes SRAM.

Did you get any far with your PulseOximeter project?

tec

Are you still working on this project?

Hi Pracas,

I've been working towards this goal too - with success in receiving values for incident light and working up toward Pulse Oximeter.
I have an idea for an interactive artform that will hopefully be installed in a children's hospital.
I then found your code (thanks for posting) and have it printing out values like this:

IR Value: 254
LED Value: 245
72.34 %
IR Value: 254
LED Value: 242
71.46 %
IR Value: 255
LED Value: 241
70.88 %

Seems good, however the readings don't vary much with application or removal of the paired LEDs (not more than a few percent).
Did you manage to obtain similar readings? - or was there greater variance?
Hoping you're able to help (I feel like I'm 95% there!) :slight_smile:
Thanks!
Rich.

What type of IR LEDs are you using? Are you using a commercial disposable/ reusable sensor, or are you building your own? What wavelengths are you using?

You need two IR LEDs in different parts of the spectrum to get an 02 saturation. at the moment I cannot remember which part but it might be just a normal red LED and a normal IR LED that you get get at Radio Shack. You would also want to have filters on the sensor so that it only detects red/IR light. Otherwise you would get noise from the environmental light.

Hello!,

thanks for the code. Now I´m working in a pulseoximeter with arduino and a Nellcor probe, but the results aren´t good.

The Nellcor probe has the following configuration:

Pin 1, 4, 6, 8 - no connection
Pin 2 - anode of the IR LED, cathode of the red LED - usually red wire
Pin 3 - cathode of the IR LED, anode of the red LED - usually black wire
Pin 5 - photodiode anode - usually white wire
Pin 7 - shield, connects to copper shield over the photodiode
Pin 9 - photodiode cathode - usually green wire

Pins 2/3 are connected to D3* and D5* (analog).
Pin 5 (photodiode anode) is connected to A7 in order to read the values.
Pin 9 and 7 are connected to GND.

The values are the followings:

IR Value: 82
LED Value: 70
0.97

IR Value: 82
LED Value: 70
0.97

IR Value: 85
LED Value: 66
0.93

IR Value: 85
LED Value: 68
0.96

IR Value: 84
LED Value: 73
0.97

IR Value: 83
LED Value: 73
0.98

I think that these values are erroneus....

IR Value: 82
LED Value: 70
0.97

IR Value: 82
LED Value: 70
0.97

IR Value: 85
LED Value: 66
0.93

IR Value: 85
LED Value: 68
0.96

IR Value: 84
LED Value: 73
0.97

IR Value: 83
LED Value: 73
0.98

Those values are correct. The annode of the probe produces a signal of ~.40v (Red) or ~.35v (IR) depending on lighting conditions. This signal only varies by a few millivolts. Since the defaults ADC assigns a value from 0 to 1023 for values of 0v - 5v, a reading of '80' would be 0.39v, which is what is expected. The problem is that the signal only varies by a few mV, but the step size for the ADC is ~5mV! That means the values read will only range from ~75-85!

You have to do 1 (or both) of 2 things.

  1. Set the AnalogReference to EXTERNAL and apply ~.75v to the ARef pin.
  2. Amplify the signal's AC components before feeding to the to analog input.

Please explain the connection of your LED (IR and RED) to the arduino and the photosensor to the arduino.
About the reading from the digital pin, i think it was not correct.