ADC code needing more accurate results

Dear all, as some of you might know (since i think i pissed many members here), I am design an automated refrigiration system for chargin an airconditioners (cars).
This system involves:
LOAD CELL
ARDUINO MEGA
POWER SUPPLY
SOLENOID VALVE
RELAY
OPAMP
LCD KEYPAD

All of these things have been succesfully tested and working as required :slight_smile:

My procedure is this:
Basically I just put a gas cylinder on top of my mounted load cell, and depending on the weight of the gas cylinder (therefore depending on the amount of gas in it), it gives a voltage. This voltage is transferred to the Arduinos ADC.

Many tests have been made in order to have accurate results on the load cell....(For example a full load gives 4.6V while a half load gives 2.3V)
Attached I have a graph having the grams (amount of gas) vs voltage. AND IT IS LINEAR :smiley:

Therefore I have an equation of the line of the graph; then I can enter the voltage in the equation and giving an amount in grams.
y = 4621.2x - 281.96 (y- GRAMS. x - VOLT)

This is my code:

//Testing adc with load cell and having pin 13 actuing as relay
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);

double maxvolt = 4.6;
int dec = 1023;

const int analogInPin = A0;
int gas_before;
double sensorValue = 0;        // value read from A0
double conversionvolt[10];      //array for average voltage

int conversiontogas;


void loop()
{
}


void setup()
{
  lcd.begin(16,2);                              //initialise LCD
  Serial.begin(9600);
  setupA();
}

void setupA()
{
  //TESTING
   lcd.print("TESTING");                //display on lcd
   delay(2000);                               //give some time for user to read
   
  digitalWrite(13,LOW);                  //RELAY CLOSED 
  //READ AMOUNT OF GAS BEFORE RELAY IS OPENED, I.E. NO GAS ESCAPED
  

 for(int x =0; x<10 ; x++)
 {
  sensorValue = analogRead(analogInPin);   
  
  conversionvolt[x] = sensorValue*(0.004643206);  //(conversionvolt of (4.7/1023)) + conversionvolt of (4.8/1023))/2  -NOTE THIS GIVES VERY GOOD RESULTS
  
 }
 //to find average
 double conversionvoltaverage = ((conversionvolt[0] + conversionvolt[1] + conversionvolt[2] + conversionvolt[3] + conversionvolt[4] + conversionvolt[5] + conversionvolt[6] + conversionvolt[7] + conversionvolt[8] + conversionvolt[9]) / 10); 
//To print the amount of voltage when cylinder is put on load cell 
Serial.print("A-");
 Serial.println(conversionvoltaverage); 
 
//Display voltage on lcd
 lcd.clear();
 lcd.print(conversionvoltaverage);
 delay(4000);
 lcd.setCursor(0,1);
 
  
  
  // Conversion from voltage to grams
  double conversiontogasstep1 = ((4621.2)*(conversionvoltaverage));        //y = 4.621.2x (part of equation)
  conversiontogasstep1 = (conversiontogasstep1 - 281.96);                     // - 281.96 (part of eqution)
  double conversiontogasstep2 = (conversiontogasstep1 - 326);                //minus weight of steel base of loadcell
  conversiontogas = (conversiontogasstep2 + 50);                                   //50 done under calibration since I put up a test and found that averagly there is a difference of -50grams than the origanal value of cylinder
  Serial.println(conversiontogas);
//DISPLAY LCD  
lcd.print(conversiontogas);
  delay(4000);
 
}

Therefore my question is this:
The voltage displayed on the multimeter is approximatle equal to the ADC voltage :slight_smile:
But when this voltage enters the equation y = 4621.2x - 281.96 (y- GRAMS. x - VOLT)[/b] It isn't calculated well. I dont understand why this happens.

For example:
If the multimeter displays 1.775V the Arduino displays 1.76V
But then it doesnt work the calculation correct; y = 4621.2x - 281.96

Why is this???

tnx

What happens if you do:
Serial.println(conversionvoltaverage, 5); (for instance)?

(You do know that for the Arduino, a "double" is precisely the same as a "float"?)

double conversionvoltaverage = ((conversionvolt[0] + conversionvolt[1] + conversionvolt[2] + conversionvolt[3] + conversionvolt[4] + conversionvolt[5] + conversionvolt[6] + conversionvolt[7] + conversionvolt[8] + conversionvolt[9]) / 10); What's wrong with a for loop?
Do you like typing or something?

So this gives 5 sig figures??

No, it just prints more.

KK didnt know that...

And yes i know, the code needs to be less.... but for now i just want to know why it doesnt correctly work out the equation.
tnx

[quote author=Daniel Formosa link=topic=59686.msg430023#msg430023 date=1303943172]...just want to know why it doesnt correctly work out the equation.

[/quote]

You haven't told us a couple of important things:

Say you get a reading of 1.775 Volts on your multimeter and you see 1.76 on the first row of the LCD...

What output do you expect to see on the second row of the LCD?

What output do you see on the LCD?

I mean, it takes the calculation from the formula and subtracts something and adds something and truncates the floating point value to an integer, right?

Regards,

Dave

I compiled your code for the final conversion and it converts 1.76 into 7,575 grams. This looks like the right answer to me. What does your program print?

davekw7x - Well if for example on the LCD i see 1.76V, then on the other row i need to see: y = 4621.2x - 281.96; i.e. 7851.32 (calculation).
But it doesnt give me this number. It gives me a number near to it but not good like 7950 for example.....I cant understand why...

gardner - How did you get that answer?? Is there something wrong with my code??

tnx

Hi Daniel

Here you are makeing 10 superfast readings of the analog pin and calculate the average

for(int x =0; x<10 ; x++)
 {
  sensorValue = analogRead(analogInPin);   
  
  conversionvolt[x] = sensorValue*(0.004643206);  //(conversionvolt of (4.7/1023)) + conversionvolt of (4.8/1023))/2  -NOTE THIS GIVES VERY GOOD RESULTS
  
 }
 //to find average
 double conversionvoltaverage = ((conversionvolt[0] + conversionvolt[1] + conversionvolt[2] + conversionvolt[3] + conversionvolt[4] + conversionvolt[5] + conversionvolt[6] + conversionvolt[7] + conversionvolt[8] + conversionvolt[9]) / 10);

Why? Have you seen instability in the voltage signal to justify an average calculation?

Here you are doing undocumentet manipulation with the equation:

 double conversiontogasstep2 = (conversiontogasstep1 - 326);                //minus weight of steel base of loadcell
  conversiontogas = (conversiontogasstep2 + 50);

I thought you were only interested in the weight change hence the absolute (true) weight value of the bottle with CFS gas is unimportent (unless it will run dry during the filling ofcorse). Furthermore you are storing a double (float) + 50 in a int construct:

int conversiontogas;

-Fletcher

[quote author=Daniel Formosa link=topic=59686.msg430239#msg430239 date=1303979815]
davekw7x - Well if for example on the LCD i see 1.76V, then on the other row i need to see: y = 4621.2x - 281.96; i.e. 7851.32 (calculation).
But it doesnt give me this number.[/quote]
No, it doesn't give you that number, and I would not expect it to.

Have you looked (yes looked) at the calculations in the program?

Well, without knowing what your real expectations from the program are, and without worrying about whether the program correctly implements what it needs to do to meet your expectations, and without caring about why it has been written the way that it was to do what it does, I'll step away from the Arduino for a minute, and I will go through the calculations that the program actually performs, but I will do them with my 10-digit calculator. (See Footnote.)

The calculation in the program gives, approximately, the (truncated) integer value of (1.76*4621.1-281.96-326+50), so I would expect to see something "in the neighborhood of" 7575 on the second line of the LCD.

In fact, if the number shown on the first line of the LCD is 1.76, it represents a number that was rounded to two significant digits, so the actual value could be anything between 1.755 and 1.765. Repeat the actual calculation with these numbers, and you can see that value on the second row could be anything from 7552 to 7598.

Regards,

Dave

Footnote:
The floating point calculations in the Arduino are carried out with a precision of something like six or seven significant decimal digits. For this particular formula, roundoff error does not alter the results significantly, so I would expect the same output on the LCD that I show above for my calculator.

Dear all, I made some arrangments. First off all I didnot know that the arduino uses up to 6 or 7 sig fig... Therefore this is what I came up with. This all depends on this line:

 sensorValue = analogRead(analogInPin);   
  Serial.print(sensorValue);
  Serial.print("\n");
             //AT THAT MOMENT THE VOLT ON MULTIMETER WAS 1.954V, THEREFORE BY LOOKING AT SERIAL.PRINT(SENSORVALUE), ONE FOUND (X/1023)TIMES 415.8 (AVERAGE ADC VALUE) = 1.954
                  conversionvolt[x] = sensorValue*(0.00469592);

The number 0.00469592 is the main thing I think, changing it to 0.00467351 will give A REALLY DIFFERENCE IN SHOWING THE VOLTAGE.
The number 0.00467351 came from doing various averaging of the analogvalue and that is the best I came up with.

Now I need to construct a loop because this is my new code for reading the voltage and averaging:

for(int y = 0; y<10;y++)
{
 for(int x =0; x<10 ; x++)
 {
  sensorValue = analogRead(analogInPin);   
  Serial.print(sensorValue);
  Serial.print("\n");
                   conversionvolt[x] = sensorValue*(0.00469592);  //Maxvoltafe/1023   4.782V Vmax 2 BESTTTTTTTTTTTTTTTTTTTTTTTTTT
  
  
  Serial.print(conversionvolt[x], 3);
  Serial.print("\n");
 }
 //to find average
 conversionvoltaverage[y] = ((conversionvolt[0] + conversionvolt[1] + conversionvolt[2] + conversionvolt[3] + conversionvolt[4] + conversionvolt[5] + conversionvolt[6] + conversionvolt[7] + conversionvolt[8] + conversionvolt[9] 
                                 ) / 10); 
}
double conversionvoltaverageaverage = ((conversionvoltaverage[0] + conversionvoltaverage[1] + conversionvoltaverage[2] + conversionvoltaverage[3] + conversionvoltaverage[4] + conversionvoltaverage[5] + conversionvoltaverage[6] + conversionvoltaverage[7] + conversionvoltaverage[8] + conversionvoltaverage[9] 
                                 ) / 10);


 Serial.print("A-");
 Serial.println(conversionvoltaverageaverage, 3);
 Serial.print("\n");

I need help in such a simple loop since I need to take a lot of values to have an accurate reading.

double conversiontogasstep2 = (conversiontogasstep1 - 326); //minus weight of steel base of loadcell That is since the loadcells base (were the cylinder is put weighs 326g)

AND THE CODE conversiontogas = (conversiontogasstep2 + 50);
IS WRONG, THAT WAS AN AVERAGE ERROR WHICH I THOUGHT WOULD HAVE HELPED ME, forget it...

Please I need help in reading more values; i.e. just a big loop which I think will help me more accurate

tnx

How's this?

/* Sample analog in pin a given number of times and
 * return the floating point mean of the samples.
 * The summing of samples is done using a long, which
 * is subject to overflow for very large counts.
 */
float read_average_analog(uint8_t pin, int count)
{
  int i;
  long sum;

  /* Impossible counts yield a reading of 0.0
   */
  if (count < 1)
    return 0.0;

  sum = 0;
  for (i = 0; i < count; i++)
      sum += analogRead(pin);

  return ((float) sum) / ((float) count);
}

I think I understood it..My question is this: uint8_t pin

Is that the pin in which the Analog input enters?
tnx

Yes, this is the pin number that is being passed to analogRead(). Where you have:

  sensorValue = analogRead(analogInPin);

You could do

  sensorValue = read_average_analog(analogInPin, 10);

Oversampling in this way would likely help with quantization noise and may help improve your overall accuracy. The A2D only has a 1 part in 1024 resolution, and it's accuracy is +/-2 LSB -- this means about +/-10 mV in a range of 5 V. And as davekw7x worked out earlier, this is an error of around +/-50 grams. There are likely other accuracy effects to look out for -- temperature stability of the load cell and amplifier, hysteresis in the load cell and such -- that will knock another ~100 ppm off your accuracy.

I think you have some work to do to understand the simple arithmetic you are doing and the way in which that is implemented in C though. Simple issues like using an integer when you wanted a float are going to mess you up until you get them right, and no amount of oversampling is going to fix it.

Yes I understant the %error. As an amplifier I used a chopper opamp which has a very low offset volt so that change in temp doesnt effect the volt amplified...
Ok and I managed to get o how many averages as I want with this code

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
const int analogInPin = A0;
int count = 300;



void setup()
{
  lcd.begin(16,2);                              //initialise LCD
  Serial.begin(9600);
  setupA();
}

void loop()
{
}


void setupA()
{
  lcd.print("TESTING");                //display on lcd
   delay(2000); 
   digitalWrite(13,LOW);                  //RELAY CLOSED 
   read_average_analog(count);
}


/* Sample analog in pin a given number of times and
 * return the floating point mean of the samples.
 * The summing of samples is done using a long, which
 * is subject to overflow for very large counts.
 */
double read_average_analog(int count)
{
  int i;
  double sum = 0;
  double average;
  double voltage;
  double conversiontogas;
  int gas;


  
    sum = 0;
    for (i = 0; i < count; i++)
    {
        sum += analogRead(analogInPin);
        Serial.print(sum, 3);
        Serial.print("\n");
    }
      
   average = ((sum)/(count));
   voltage = average*(0.00469592); 
   Serial.print("A");
   Serial.println(average, 3);
   Serial.println(voltage, 3);
   
   conversiontogas = ((4621.2)*(voltage) - 607.96); 
  gas = conversiontogas;    
  Serial.println(conversiontogas);
  Serial.println(gas);
  
   
        
  
  return gas;
}

I havenet tried this in real becuse I dont have the system near me....

I was thinking: Since I am always using a tank; when empty the tank is approx 7kgs which gives 1.576V. Therefore can I trick the ADC of having a range of from 1.576 to 5V??

tnx

The problem with comments is that they don't automatically re-write themselves to reflect what you are actually doing.

As someone else mentioned, using the type double is not doing what it appears to be doing. You are better off using float, as that is what you will get. This will also help avoid disappointment if you test your code on a real computer (that has long float) and then later run it on your Arduino.

By the way, you've read this document, right?

AVR121: Enhancing ADC resolution by oversampling

A long time ago haha...Holy...I had no idea I had to add for example some white noise to my signal and use PWM and shit to have more accurate results.....

I really hope not..