Go Down

### Topic: ADC code needing more accurate results (Read 2519 times)previous topic - next topic

##### Apr 28, 2011, 12:12 amLast Edit: Apr 28, 2011, 12:16 am by Daniel Formosa Reason: 1
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:
ARDUINO MEGA
POWER SUPPLY
SOLENOID VALVE
RELAY
OPAMP

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

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

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:
Code: [Select]
`//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 A0double conversionvolt[10];      //array for average voltageint 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
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

#### AWOL

#1
##### Apr 28, 2011, 12:22 am
What happens if you do:
Code: [Select]
` Serial.println(conversionvoltaverage, 5); ` (for instance)?

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

Code: [Select]
` 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?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

#2
##### Apr 28, 2011, 12:23 am
So this gives 5 sig figures??

#### AWOL

#3
##### Apr 28, 2011, 12:24 am
No, it just prints more.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.
I speak for myself, not Arduino.

#4
##### Apr 28, 2011, 12:26 am
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

#### davekw7x

#5
##### Apr 28, 2011, 01:34 amLast Edit: Apr 28, 2011, 01:36 am by davekw7x Reason: 1
...just want to know why it doesnt correctly work out the equation.

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

#### gardner

#6
##### Apr 28, 2011, 02:10 am
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?

#7
##### Apr 28, 2011, 10:36 am
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

#### Fletcher_Chr

#8
##### Apr 28, 2011, 12:47 pm
Hi Daniel

Here you are makeing 10 superfast readings of the analog pin and calculate the average
Code: [Select]
`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:
Code: [Select]
` 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:

Code: [Select]
`int conversiontogas;`

-Fletcher

#### davekw7x

#9
##### Apr 28, 2011, 03:01 pmLast Edit: Apr 28, 2011, 04:08 pm by davekw7x Reason: 1

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.

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.

#10
##### Apr 28, 2011, 07:52 pm
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:
Code: [Select]
` 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:
Code: [Select]
`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
Code: [Select]
` 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

#### gardner

#11
##### Apr 28, 2011, 08:44 pmLast Edit: Apr 28, 2011, 08:53 pm by gardner Reason: 1

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

How's this?

Code: [Select]
`/* 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);}`

#12
##### Apr 28, 2011, 08:56 pm
I think I understood it..My question is this: uint8_t pin

tnx

#### gardner

#13
##### Apr 28, 2011, 11:15 pm

uint8_t pin

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

Code: [Select]
`  sensorValue = analogRead(analogInPin); `

You could do

Code: [Select]
`  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.

#14
##### Apr 28, 2011, 11:27 pm
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
Code: [Select]
`#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

Go Up