Arduino Mega 2560 ADC read Minivolts at ADC port A8

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am stuck up to find a way by reading analog input at port A8 of Arduino Mega 2560 at a resolution of 1 mV. I have a pH sensor which gives value of 1 mV which equal to 1 ppm and so on.

I have been referring to articles mentioned below:

 

http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

 

So will my code look like this:

 

int analogPin = A8;
int val = 0;           // variable to store the value read

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
     ADMUX = _BV(MUX5) | _BV(MUX0) ;
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  
  ADCSRB &= ~_BV(MUX5); 
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

 void setup() {
  Serial.begin(9600);
}


void setup()
{
  Serial.begin(9600);          //  setup serial
}

void loop()
{
  val = analogRead(analogPin);    // read the input pin
  Serial.println(val);             // debug value
}

Will I get the expected output means reading ADC with changes in 1mV?

 

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

prakash_prasad wrote:

...a resolution of 1 mV.

 

What accuracy are you needing?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I learned from here that http://arduino.cc/en/Reference/a...

 

Arduino has 10-bit analog to digital converter. This means that it will map input voltages between 0 and 5 volts into integer values between 0 and 1023. This yields a resolution between readings of: 5 volts / 1024 units or, .0049 volts (4.9 mV) per unit.

 

But in my case a reading of 1mV change at ADC A8 should be able to read it accurately using Arduino Mega 2560.

 

So I should be able to read values like:

 

1mV

2mV

3mV

4mV

....

 

I hope I am clear in my problem definition

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Shirley,  you should use analogReference() and analogRead().    God gave you the Arduino for a reason!

 

Yes,  you can scale the result to give you units in mV.    As it stands,   your results are 'almost' mV.   i.e. 1100mV returns 1023.  (with VREF=1100mV)

I would just scale the results by 1.074,   but you can do it in integer maths if you cast it to longs.

 

I can't understand how you can do pH in mV per ppm.

A neutral pH is 7.00.   A sample that is 8.00 will be +1.00 or +142857ppm 

If your sensor produces 143Volts,   it is going to blow up your AVR.

 

David.

Last Edited: Mon. Nov 24, 2014 - 10:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok Sorry its water conductivity meter - it will provide values calibrated to corresponding values from 1mV to 2V:

 

1 mV   1ppm

2mV    2ppm

...................

2000mV  2000ppm

 

So are you stating that I should use the below code:

 

analogReference(INTERNAL1V1) //a built-in 1.1V reference (Arduino Mega only)

 

So

1mV returns .93 or 93(Sorry for basic clarity)

2mV returns (1023/1100)*2(1.86 or 186)

.................

2000mV returns (1023/1100)*2000 (1.819 or 1819)

 

Please confirm

 

 

Last Edited: Mon. Nov 24, 2014 - 11:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

cheeky

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well I know my question is basic but please confirm if in case my understanding is correct

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why don't you start by being honest about your sensor?    Preferably giving its model number.

What is the output range?   e.g. 0mV - 500mV or 2000mV - 4000mV or ...

 

Yes,  the Arduino allows you to try anything very easily for yourself.

 

Personally,   I would be impressed by a short neatly formatted sketch that uses standard Arduino library methods.

 

I can't speak for your teacher.    She may like seeing reams of hand-crafted code.    

It is your school project.     You should be doing the work.

 

David.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Trying to measure an input of 0-2v and achieve 1mV of resolution across that range will not be possible using only the built in ADC on the Mega as it only has 10 bits of resolution. 

Your choices for Aref are 5v, 2.56v or 1.1v, give the following resolutions, 4.88mV, 2.5mV or 1mV,  up to but not more then the selected Aref voltage.  As you can see, only the 1.1v ref will give you the resolution you need, but not the range.

 

Perhaps, you can use the 1.1v ref for a lower level range, say 0-1v, then switch to the 2.56v Aref for the higher range above 1.0 volts, at a reduced resolution.

 

JC

 

 

 

 

 

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

As you can see, only the 1.1v ref will give you the resolution you need, but not the range.

But no accuracy (see data sheet).

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rig up a rail to rail opamp with a gain of 2.5. This will amplify your 0V to 2V input up to 5V, and you have 1023 counts, so about 2mv per count.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

prakash_prasad wrote:

I learned from here that http://arduino.cc/en/Reference/analogRead:

 

Arduino has 10-bit analog to digital converter. This means that it will map input voltages between 0 and 5 volts into integer values between 0 and 1023. This yields a resolution between readings of: 5 volts / 1024 units or, .0049 volts (4.9 mV) per unit.

 

But in my case a reading of 1mV change at ADC A8 should be able to read it accurately using Arduino Mega 2560.

 

So I should be able to read values like:

 

1mV

2mV

3mV

4mV

....

 

I hope I am clear in my problem definition

No...

 

VRef = 5VDC @ 10 Bit ADC Resolution values:
----------------------
0b0000000000 = 0mV
0b0000000001 = 4.9mV
0b0000000010 = 9.8mV
0b0000000011 = 19.6mV
0b0000000100 = 39.2mV
.
.
.
0b1111111100 = 4985.3mV
0b1111111101 = 4990.2mV
0b1111111110 = 4995.1mV
0b1111111111 = 5000.0mV

If you want 1mV resolution, as well as full range of the sensor from a 10 bit ADC you will probably have to select between available VRef values and selectable gain via an amplifier between the sensor output and the ADC input.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Thu. Dec 11, 2014 - 06:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks - my code implementation as per understanding is mentioned below:

float ppm = 0.0;
int x=0,y=0;
int check()
{
  ppm = (analogRead(A8)) * 4.9; // Sensor is connected to A8 ADCport
// hence a value 1 read from A8 port would mean sesnor is sending ~5 value
  return 0;  
}

void setup()
{
 analogReference(INTERNAL1V1); // Arduino Mega 2560 Board
}

void loop()
{
    check();
    x=y=0;
    x = (ppm - x);
    y = (ppm -x)*100;
    //x and y variable holds the after and before decimal place values or rather the actual values for us to refer from sensors??
    delay(2000);
}

   Please correct my understanding

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
prakash_prasad wrote:
void loop() {     check();     x=y=0; // If X is zero here...     x = (ppm - x); // X = (ppm - 0); So, X = ppm     y = (ppm -x)*100; //  Y = (ppm - ppm) * 100 = 0 because ppm - ppm = 0     //x and y variable holds the after and before decimal place values or rather the actual values for us to refer from sensors??     delay(2000); }

I don't get it...

 

How about telling us the maximum span of the parameter being measured by this sensor. so, we know that it's a 1ppm/mV sensor but, haw many ppm's is this sensor suppose to measure over it's stated operating range?

 

Then we could simply calculate: PPM = ppm_max * (ADC_value / 1023).

 

But understand, depending on which Vref you use, the resolution will be between 4.9ppm per ADC step and 1.07ppm, depending on what Vref can actually be selected.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Fri. Dec 12, 2014 - 09:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kindly note that I found that Arduino has bad integration with float and double data types esp when I want to make use of sprintf library function.

 

x and y are integer variables while ppm if a float type. ppm variable can have a value like 4.993 or something else.

 

Now the result of ppm - x is being store into an integer variable so all the after decimal portion value would get truncated  which is same like x = ppm. As such the value of variable x would be 4.

 

The integer variable y would store the 2 decimal places values of ppm - (4.993 -4) * 100 = 99.  The objective of getting the the digits before / after  decimal places of float / double data types variable is that manipulating values eases a lot of issues for me.

 

Please explain

 

ppm_max * (ADC_value / 1023).

 

 

is it that ppm_max = max volts * 4.96 =   5000 * 4.96 ?

 

Last Edited: Fri. Dec 12, 2014 - 10:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok...

 

Sensors ----- ALL TYPES OF SENSORS ----- have specifications.  All you've told us about your sensor is that it outputs 1ppm/mV.

 

What about two other parameters ---> Maximum Range of the output voltage and Maximum Range of the physical you are attempting to quantify?

 

Maximum Range of output voltage relates to interface needs of the circuitry the sensor will be connected to, I.E. circuitry that requires 3.3 volts max, 5.0 volts max, 10 volts max, etc.  Voltage is an interface issue.  While you have stated the resolution or transfer function of the sensor, you haven't told us the maximum voltage the interface circuitry expects to see.

 

The Maximum Range of the physical property is the maximum ppm that the sensor can reliably and accurately detect and represent via it's output.

 

So, a typical ppm sensor parameter specification might be:

 

Maximum ppm = 5000 ppm ---> a reasonable value for this explanation.

Sensitivity = 1ppm/mV ---> The sensitivity that you've stated for this sensor.

Absolute Maximum Input Operating Voltage = 6.0 VDC. ---> The maximum input voltage the device tan take without being damaged.

Output voltage at Maximum ppm = 5.0 VDC. ---> The sensor output voltage when it is detecting the maximum ppm.

Number of ADC bits = 10, equating to a maxim count 2^10 = 0 to 1024.  But, ADC = 0 is a valid number (or place holder) in the ADC count so, the ADC range is 0 to 1023.

 

So then, you know that for a 10 bit ADC, the minimum ppm change you will be able to detect is about 4.9 volts.  But for this sensor, the real world property it is measuring is a ratio of two liquids or gasses that is quantified as PPM or Parts Per Million and output some electrical (in this case...voltage) representation of that ration.  The output voltage is nothing more than a a way to represent what the sensor is attempting to quantify.  Once it is determined that the electrical output specifications meet the input requirements of the ADC, you are done with it.  Because you have an operating range (example 0ppm to 5000ppm) for this sensor and you have a range (bit specification) of the ADC, you are done with any voltage requirements - FINISHED !!!

 

Everything from here on involves PPM and raw DC values.

 

You seem to caught up in measuring the sensors output voltage, but to what effect?  You still don't know what the ppm level is until you convert the captured voltage from the sensor into ppm.  that means that you spend more time in unnecessary calculations.

 

So then, lets get to it, shall we???

 

The equation you are after is: PPM = PPM_MAX * (ADC / 1023).  Plain & simple.

 

PPM_MAX is the maximum ppm value that this sensor can detect over it's output voltage specification.

 

ADC / 1023 is the fractional representation of the ADCs maximum input.

 

If ADC input value is 1023 then... 1023 / 1023 = 1 and so PPM_MAX * 1 = PPM_MAX or 5,000 PPM.

 

If the  ADC input value is 511 then... 511/1023 = 0.4995 and so, PPM_MAX * 0.4995 = 2,497 PPM

 

If the ADC input value is 1 then... 1/1023 = 0.000978 and so, PPM_MAX * 0.000978 = 4.8876 PPM

 

The use of ADC / 1023 results in a fractional result and requires floating point to get the best accuracy.

 

But this can be done entirely in integer math with one small change.

 

Rather than find the fractional part first and then multiplying the result by MAX_PPM, let shuffle thing up a bit.

 

 

If we change the order of multiplication and division, we don't have to deal with fractions or floating point math, that is, rearranging the equation...

 

from:

PPM = PPM_MAX * (ADC / 1023)

 

to:

 PPM = (PPM_MAX * ADC) / 1023

 

there is no fractional part to worry about.  The catch is that, (PPM_MAX * ADC) must be able to fit in an INT or a long INT.

 

So 5000 * 1023 = 5,115,000.  But an INT can only handle up to 56635 so it will not work for us.  A long INT handles up to 4,294,967,296 so, long INT it will have to be.

 

So if you simply specify what the maximum PPM this sensor can detect over its operating output voltage range, it all boils down to one simply equation...

 

      PPM_MAX * ADC
PPM = -------------
          1023

Now, in the time I've spent explaining this, I could have written the code and be displaying accurate PPM on a 16 x1 LCD display with a range of 0 to 5000 PPM with a resolution of about 4.9 PPM, using the exact method I've described here.

 

No more nonsense.  I've told you everything you need to know.

 

Read it, and then read it again, study it, and then study it some more and... you might eventually understand it...

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Fri. Dec 12, 2014 - 05:33 PM