Problem while converting float

I am using an Arduino to read a signal coming from an op-amp. The op-amp is amplifying a signal from a photodiode. I had to use oversampling to get more resolution from the ADC. I intend to increase the resolution to 14-bits. For this I have to store the accumulated ADC readings into unsigned long datatype. This part of the program is without any bugs but when I later try to interpret the results i.e. convert the ADC values into volts, I am getting gibberish output. My assumption is that something is going wrong when I try to convert the unsigned long datatype to float. I think this because if I print the unsigned long variable I am getting no problems. I am attaching the code at the very end.

Converting the returned value to float by using the (float) command did not help.
I mean,
float Raw_ADC= (float)Oversample();
float volts = (Raw_ADC+0.5)*Vref/4096.0;

did not solve the problem.

Although I do not need to use unsigned long datatype to store 16, 12-bit values, I think I will need long for storing 256 such values when I try to increase the resolution to 14 bits.

Please help.


#include <avr/sleep.h>

const float Vref = 5.1;

void setup() {
 
 ADMUX = 0b01000000; // Use AVcc as ref and A0 as input
  ADCSRA |= 1<<ADEN; //enable ADC
 ADCSRA |= 1<<ADIE; //Generate an interrupt at end of ADC conversion to wake up from sleep
 ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));    //Prescaler at 128 so deviding 16Mhz by 128 we get a            clock  frequency of 125kHz
 sleep_enable();
 set_sleep_mode(SLEEP_MODE_ADC);
 sei();
 Serial.begin(9600);
}

void loop() 
{ // put your main code here, to run repeatedly:

float volts= (Oversample()+0.5)*Vref/4096.0;
Serial.println(volts,3);
//Serial.println(Oversample());
//Serial.println(LowNoiseRead());
//delay(1);

}

unsigned long LowNoiseRead()
{
 unsigned long RawADC = 0;
 sleep_cpu();   // going into sleep starts ADC conversion
 RawADC = ADC;
 return(RawADC);
 //delay(1);
}

ISR(ADC_vect)
{
 
}

unsigned long Oversample()
{
 unsigned long accumulatedReading =0;
 LowNoiseRead();
 for(int i=0;i<16;i++)
   {
     accumulatedReading += LowNoiseRead();   // adding 16 readings
     delay(1);
   }

 return (accumulatedReading >> 2);   // bit shifting to the right by 2

}

barbaric_baboon:
when I later try to interpret the results i.e. convert the ADC values into volts, I am getting gibberish output.

What do you get?

Please edit your post to place the code inside code tags to properly format it for the forum.

Please go back and put your posted code in code tags.

Consider unsigned long:unsigned long - Arduino Reference, float float - Arduino Reference, double double - Arduino Reference
the number of bytes in a unsigned long on a Uno, or Mega and the number of bytes of a float on a Uno/Mega, 4 bytes and a float or a double, 4 bytes, and that in your conversion process may be producing an overflow.


My I suggest that instead of using floats in this fashion float volts = (Raw_ADC+0.5)*Vref/4096.0;
that instead you get into the habit of declaring the explicit float vaules with a 'f' suffix; like so float volts = (Raw_ADC+0.5f)*Vref/4096.0f;.
I found not using the 'f' there is an opportunity of ending up with a int being used with the math.

wildbill:
What do you get?

I got this on the Serial Monitor
dogÀ^dolÀ^dp`À^dosÀ^dokÀ^dogÀ^dogÀ^dogÀ^dosÀ^dopÀ^dogÀ^docÀ^dogÀ^d

Idahowalker:

My I suggest that instead of using floats in this fashion

float volts = (Raw_ADC+0.5)*Vref/4096.0;

that instead you get into the habit of declaring the explicit float vaules with a 'f' suffix; like so

float volts = (Raw_ADC+0.5f)*Vref/4096.0f;

.
I found not using the 'f' there is an opportunity of ending up with a int being used with the math.

Tried this, but got back slightly different gibberish:-
gdcÀ^gddÀ^gddÀ^gcdÀ^goÀ^gsÀ^gchÀ^gddÀ^gdcÀ^gcdÀ^gccÀ^gcdÀ

And you serial monitor is the same as your serial.begin(9600)? A common error and worth a mention.

Idahowalker:
And you serial monitor is the same as your serial.begin(9600)? A common error and worth a mention.

Yes. I've checked that.
Could it be that float cannot handle a large data like long? Like an overflow problem?
I tried using double instead of float but got the same error.

Have you tried a simple ASCII “Hello world”?

barbaric_baboon:
Could it be that float cannot handle a large data like long? Like an overflow problem?

No, float can store a much bigger range than long, albeit with less precision.

I tried using double instead of float but got the same error.

On small Arduinos, float and double are the same size.

It still looks like a baud rate issue to me. Can you print "Hello World" to serial in setup?

TheMemberFormerlyKnownAsAWOL:
Have you tried a simple ASCII "Hello world"?

Is that great minds think alike or fools seldom differ?

Have you tried

Serial.println( "hello world");

Yeah, Hello World prints fine.

I have tried the following code which is basically substituting the value returned by the Oversample() function in my original code with a value.

void setup() {
Serial.begin(9600);
unsigned long a= 3456;
float vref=5.1;
float v= ((float)a+0.5)*vref/4096.0;
Serial.print(v,DEC);// put your setup code here, to run once:
//prints 4.3037476539
}

void loop() {
  // put your main code here, to run repeatedly:

}

This works fine and i get 7 digits precision. So my chip can process floating point math correctly.
The problem mysteriously arises when i try using the value from a function like this:

float Raw_ADC= (float)Oversample();
float volts = (Raw_ADC+0.5)*Vref/4096.0;

When I print oversample() i get a perfectly normal value

Possibly sleep_cpu() is affecting the Serial. If I put Serial.flush() before the print statements in the original code (after un-commenting them) the output looks ok.

try

float qaz=1.001f;
Serial.println( qaz,6 );

Does that print?

Hiii..... I don't know if you will believe me but somehow, including a substantial delay in the void loop solved the problem. I know it might sound crazy and I have no idea why it is behaving like this but adding a 100 ms delay made it work.

void loop() 
{ 
 float volts = ((float)(Oversample())+0.5)*Vref/4096.0;
 Serial.println(volts,5);
 delay(100);
 
}

I promise I made absolutely no change in the rest of the program. Just this. And I have no idea how this worked. I was experimenting with converting each term in the "volts" equation and printing out the result over the serial and I found that even displaying strings over the serial would awry after a floating point operation.
So, i had a really stupid code like this:

void loop() 
{ // put your main code here, to run repeatedly:
int unsigned long v1 = Oversample();

Serial.println("OverSample");
Serial.println(v1);

float v2 =(float)(v1)+ 0.5;

Serial.println("Overvalue + 0.5");
Serial.println(v2);

float v3 = (Vref);

Serial.println("float Vref");
Serial.println(v3);

float v4 = (float)(v2);
delay(10);
Serial.println("float V2");
Serial.println(v2);

float v5 = ( v4 * v3 ) / 4096.0;

Serial.println("Final Value");
Serial.println(v5);


 //Serial.println(Oversample());
 //Serial.println(LowNoiseRead());
 //delay(1);
 
}

This just replaced the void loop() the rest of the code remained the same as I was convinced that there was no bug in the functions.
This code would give this interesting output:

240
Overvalue + 0.5
240
float Vref
(5.10
ÄïõâìÅÄ@V2
240
Final Value
0.30
OverSample
249
Overvalue + 0.5
249
float Vref
(5.10
ÄïõâìÅÄ@V2
249
Final Value
0.31

Notice that "float v2" is printed as rubbish although it is a string. I guess that this could be because the floating-point math was too much for the chip so I included a little delay and that affected how the string was printed over the serial. It was mostly garbage but a different type of garbage so I concluded that floating-point math was somehow interfering with other instructions. I included the delay in my original code and it worked!

Now can anyone tell me why it is the way it is? It is really really strange.
And please if you have a spare Arduino with you try doing the same experiment and share the results. I would love to know if this behaviour varies from chip to chip. It is really interesting!

Try removing the delay, but putting Serial.flush(); before the line where you call Oversample(), that will delay until all the serial data has been sent. I think your problem is that sleeping the processor while reading the ADC is interfering with the serial output.

Hmmm... I will try that and let you know in just a moment. But I've already tried removing the read while sleep part and just using analogRead() for sampling and that did not remove the problem. But thanks for the idea I will it.

The Serial.flush() works just fine.

I think this will be really helpful for anyone who needs to sample quickly.
Thanks for the help.
I will go with using the Serial.flush(); but I dont think that sleep mode and interrupts are the culprit here.

barbaric_baboon:
The Serial.flush() works just fine.

I think this will be really helpful for anyone who needs to sample quickly.
Thanks for the help.
I will go with using the Serial.flush(); but I dont think that sleep mode and interrupts are the culprit here.

Prove it. Make a sketch that duplicates the problem, but doesn't use sleep mode or interrupts.