Reading mV range voltages using analog read

I am trying to measure a voltage reading using analog input with Arduino UNO. It is a simple program and yet the results are askew to the requirement.

int analogPin = A0;
int val = 0;  
double voltage = 0;
String command;
void setup() {
  Serial.begin(9600); 
  analogReference(INTERNAL);  
}

void loop() {
  

  if (Serial.available()) {
    command = Serial.readStringUntil('\n');
           // debug value
    command.trim();
    val = analogRead(analogPin);  
    voltage = ((float(val) + 0.5)*1100)/1024;
    Serial.print("Command: ");
    Serial.println(command);    
    if (command.equals("READP")) {
      //Serial.print("Voltage(mV) : ");
      //Serial.println(voltage);
      Serial.print("Value : ");
      Serial.println(val);
    }
    else {
      Serial.println("bad command");
    }
    
  }
}

used this reference link to understand the issue with analog reference
1.1 V(used as analog ref) maps correctly to 1023. But it throws an error of atleast 27 mV.
Is there a way to increase it's accuracy since it's resolution itself is actually 1.1/1023 = 1.075 mV.

Welcome to the forum.

Do you have a good multimeter ?
The internal reference can be from 1.0 to 1.2. You have to determine the actual value for each Arduino Uno board.

Set the internal reference and do a analogRead():

void setup() 
{
  analogReference(INTERNAL);
  analogRead(A0);  
}

void loop() {}

Then measure the voltage at the AREF pin. Use that voltage in the calculation.

int analogPin = A0;
float measuredReference = 1.15;       // just an example, measure it

void loop() {
  val = analogRead(analogPin);
  voltage = (float(val) + 0.5) * measuredReference / 1024.0;
  Serial.println(voltage);
  delay(200);
}

I read the webpage of your link, but it is missing a few practical things.
To get a good measurement, a number of samples should be captured and the average should be calculated. With only a few samples, a lot of electronic noise is reduced.
I wrote a averageRead.ino a while ago. It tries to keep every bit and only change to floating point when needed. By making use of the noise, the resolution increases beyond the 10-bit of the ADC. The overall accuracy is not increased, because that depends on the linearity and other things.

I say this only because you are focused on accuracy. This is not technically correct. The resolution would be 1.1/1024 = 1.074 mV. I understand this doesn't address your problem but I feel it's better to work with the most accurate formulas.

Actually, you did perform the calculation correctly in the voltage calculation line, although it is over-optimistic about input precision. The input is actually imprecise enough that the floating point calculation (and rounding) is not really worth it, you could

    long mv = val * 1100L / 1024;

except that after calibration, you would need to substitute something like 1093 instead of 1100 if the reference was really 1.093V for example.

Edit - afterthough... also the compiler will optimize the /1024 to a left shift operation. This completely cuts out the division as well as the floating point. Multiplication is very fast compared with division when there is no FPU.

1 Like

Another refinement, if you're intending to read more than one AI, and using the abovementioned multiple-read averaging to improve accuracy. Bear in mind that, when you change channel, the first one or two readings will be impacted by charging the input stage, particularly if your voltage source is high-impedance. Therefore, it might be best when trying for a really accurate reading from any AI to read a couple of throw away values, then capture your set for averaging. I.e. "read(1), read(1), read(10)" will achieve a better result than "read(10)".
Best of luck. I've always found trying to read more "accuracy" than the native resolution to be fraught with difficulties.
C

Right, another suggestion, use a long variable instead of an int as an accumulator, which would allow you to take for example 200 samples instead of the current 20 in the same time period.

It's not commented, but 20 is a seat of the pants value close to the maximum before overflow occurs.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.