Inaccurate voltage readings

Hi,

I am building a volt meter and would like to measure 0-100V but find that the readings are inaccurate, compared to 3 different brand digital multimeters, which all show roughly the same voltage.

The battery bank consist of 2x 12V batteries, which measures 25.6V on a DMM. On the Arduno serial monitor, however I get between 24.6V and 25.10V, measured every 1000ms.

The voltage divider consist of a 1Mohm resistor for R1, which measures 998000ohm on the DMM, and 3x 1Kohm resistors on R2 measuring 29200ohm. I used these values in the sketch as part of the calculation.

When I measure the values on A0 + ground, with my DDM set to "2V" I get readings of .609 - .701v, yet the serial console shows readings between 668.18 and 745

millivolt: 668.18
input_voltage: 24.94

millivolt: 722.73
input_voltage: 25.26

millivolt: 672.73
input_voltage: 25.58

millivolt: 731.82
input_voltage: 21.91

millivolt: 590.91
input_voltage: 27.66

millivolt: 786.36
input_voltage: 20.47

millivolt: 700.00
input_voltage: 24.94

millivolt: 704.55
input_voltage: 24.94

The sketch:

/*
  Voltage divider based on: http://www.lediouris.net/RaspberryPI/ADC/monitor-12v.html
  0V - 100V 
  R1: 1000Kohm
  R2: 3x 100Kohm
*/

float voltageOnA0 = 0.0;
float input_voltage = 0.0;
float temp=0.0;
float r1=998000.0;
float r2=29200.0;
// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  pinMode(A0, INPUT);   
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float millivolt = sensorValue * (4.65 / 1023.00) * 1000; // 4.65 was measured on the Arduino 5V and AREF pins

  Serial.print("millivolt: "); 
  Serial.println(millivolt);
  delay(1000);

  //Conversion formula
   int analog_value = analogRead(A0);
   temp = (analog_value * 4.65) / 1023.0; // 4.65 was measured on the Arduino 5V and AREF pins
   input_voltage = temp / (r2/(r1+r2));
   
   if (input_voltage < 0.1) 
   {
     input_voltage=0.0;
   } 
    Serial.print("input_voltage: ");
    Serial.println(input_voltage);
    Serial.println();
}

How do I get the same readings as on my DMM?

How do 3 * 1K measure 29200? Maybe 3 * 10K?

Put your delay at the end of the loop, and print sensorValue where the delay used to be.

Get rid of all your calculations inside loop() that involve constants and replace with pre-calculated constants, i.e. replace (r2/(r1+r2)) with 0.028427 or better yet, replace with 35.178 and change input_voltage = temp /... to temp * 35.178

Post new code and new output.

BTW your voltage divider needs to be rethought. For 0-100V, R2 needs to be about 50K. For 0-30V use about 200K.

RudiAhlers:
How do I get the same readings as on my DMM?

Maybe you need a fudge factor?

...R

Also, check your Arduino Vcc, which is what it uses as the reference.
Powered via usb, this may vary quite a bit...

Use this method to read the internal voltage of the arduino instead of using a constant.

How do you power this (unspecified) Arduino.
Is this a 3.3volt Arduino (guessing from the 1Meg:30k ratio).

Is this a continuation of this thread, with the MCP3208 and the ESP8266.
Leo..

DKWatson:
BTW your voltage divider needs to be rethought. For 0-100V, R2 needs to be about 50K. For 0-30V use about 200K.

I used some components I had in stock, but please explain this. I thought R1 should be the bigger one between the two?

DKWatson:
How do 3 * 1K measure 29200? Maybe 3 * 10K?

That was a type on my behalf. It is indeed 3x 10K resistors.

DKWatson:
Put your delay at the end of the loop, and print sensorValue where the delay used to be.

Moving the delay didn't do anything.

DKWatson:
Get rid of all your calculations inside loop() that involve constants and replace with pre-calculated constants, i.e. replace (r2/(r1+r2)) with 0.028427 or better yet, replace with 35.178 and change input_voltage = temp /... to temp * 35.178

Post new code and new output.

Please explain to me why this would make a difference? I am not challenging your suggestion, but want to learn.

If I do the math, i.e. (r2/(r1+r2)) using Windows calculator, I get 0.028446261682243. Please explain how you got 0.028427 or 35.178 ?

I have made the suggested changes though, yet it doesn't improve the accuracy.

/*
  Voltage divider based on: http://www.lediouris.net/RaspberryPI/ADC/monitor-12v.html
  0V - 100V 
  R1: 1000Kohm
  R2: 3x 100Kohm
*/

float voltageOnA0 = 0.0;
float input_voltage = 0.0;
float temp=0.0;
float r1=998000.0;
float r2=29200.0;
// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  pinMode(A0, INPUT);   
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float millivolt = sensorValue * (4.65 / 1023.00) * 1000; // 4.65 was measured on the Arduino 5V and AREF pins
  Serial.print("millivolt: "); 
  Serial.println(millivolt);
  Serial.print("sensorValue: "); 
  Serial.println(sensorValue);

  //Conversion formula
   int analog_value = analogRead(A0);
   temp = (analog_value * 4.65) / 1023.0; // 4.65 was measured on the Arduino 5V and AREF pins
//   input_voltage = temp / (r2/(r1+r2));
  input_voltage = temp * 35.178; 
   
   if (input_voltage < 0.1) 
   {
     input_voltage=0.0;
   } 
    Serial.print("input_voltage: ");
    Serial.println(input_voltage);
    Serial.println();
    delay(1000);
}

Serial Console output:

millivolt: 713.64
sensorValue: 157
input_voltage: 25.10

millivolt: 690.91
sensorValue: 152
input_voltage: 24.30

millivolt: 718.18
sensorValue: 158
input_voltage: 25.26

millivolt: 718.18
sensorValue: 158
input_voltage: 24.94

millivolt: 709.09
sensorValue: 156
input_voltage: 25.10

millivolt: 672.73
sensorValue: 148
input_voltage: 24.30

millivolt: 690.91
sensorValue: 152
input_voltage: 24.78

millivolt: 713.64
sensorValue: 157
input_voltage: 24.94

westfw:
Also, check your Arduino Vcc, which is what it uses as the reference.
Powered via usb, this may vary quite a bit...

I have, as I have noted in my code :wink:

  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float millivolt = sensorValue * (4.65 / 1023.00) * 1000; // 4.65 was measured on the Arduino 5V and AREF pins
  Serial.print("millivolt: ");

This is on a Arduino Nano, powered from my laptop's USB, by the way. But the voltage, both on the AREF and 5V pins are very stable, generally at 4.6-4.7V

Wawa:
How do you power this (unspecified) Arduino.
Is this a 3.3volt Arduino (guessing from the 1Meg:30k ratio).

It's an Arduino Nano, powered from my laptop's USB with a stable voltage of 4.6-4.7V, which I have used in my code as reference.

Wawa:
Is this a continuation of this thread, with the MCP3208 and the ESP8266.
Leo..

Yes, but I am not there yet. I just want to get the basics working before I move onto the ESP8266 and MCP3208 - which I probably don't need anymore if it works well at the 0V-3.3V range.

RudiAhlers:
I used some components I had in stock, but please explain this. I thought R1 should be the bigger one between the two?

R1 is 1M.

DKWatson:
R1 is 1M.

Yes, I know R1 = 1Mohm, but then R2 should be 3Kohm for a 100V:3v ratio.

RudiAhlers:
If I do the math, i.e. (r2/(r1+r2)) using Windows calculator, I get 0.028446261682243. Please explain how you got 0.028427 or 35.178 ?

If R1=998000 and R2=29200 then R1 + R2 = 1027200. 29200/1027200 = 0.02842679127725856697819314641745 (Windows calculator)
1 divided by that thing is 35.178082191780821917808219178082.

Ultimately, multiplication takes a lot fewer resources than division.

RudiAhlers:
Yes, I know R1 = 1Mohm, but then R2 should be 3Kohm for a 100V:3v ratio.

Mate, you need a new calculator or a refresher in basic algebra.

1M:3K is 333:1

DKWatson:
Mate, you need a new calculator or a refresher in basic algebra.

1M:3K is 333:1

I meant 30Kohm. At 100V, with 1Mohm on R1, and 30Kohm on R2 I get a 100V:2.91V ratio.

But you're right, at 1V it is indeed 333:1. But I'm working with 100V input

But this doesn't tell me why the readings about 0.5V off from the DMM's readings. Changing the way the math is don't doesn't really improve the accuracy, it just simplified the math.

Your full scale reading with the divider you have is 163V. You are measuring a range of a couple of volts. Straight away that's low resolution. Also you're normalizing against a Vref you claim is 4.65. I've got about 30 Arduinos sitting in front of me and none of them show Vcc that low. I would seriously question some of your readings, less so had the math and other details proven flawless.

If you want accurate readings, scale your divider to take advantage of the range of the ADC. Your inputs are not shielded, in all likelihood your device is not properly grounded, dupont cables with pins and headers are not the best connectors. There's a bucket load of influencing factors that can lead to unstable readings. At the end of the day, Robin's advice might be best (#3).

RudiAhlers:
The battery bank consist of 2x 12V batteries, which measures 25.6V on a DMM. On the Arduno serial monitor, however I get between 24.6V and 25.10V, measured every 1000ms.

How do I get the same readings as on my DMM?

We can meet the same readings through 2-point calibration of the measurement system including the external Battery and the voltage-divider circuit where the DMM is playing the role of Secondary Calibrator. The procedures are:

1. Connect the DC (+) terminal of the voltage-divider with the (24V) point of the Battery. Use DMM and the measure the voltages at the following points:
(a) Battery Terminal (24V-point) and record its as: VinA.
(b) A0-point (ADC Ch-0) and record it as VDA.
(c) The first known point is: A(VinA, VDA).

2. Connect the DC (+) terminal of the voltage-divider with with the (12V) point of the Battery. Use DMM and the measure the voltages at the following points:
(a) Battery Terminal (12V-point) and record its as: VinB.
(b) A0-point (ADC Ch-0) and record it as VDB.
(c) The second known point is: B(VinB, VDB).

3. The third unknown point is: C(Vin, VD).

4. Find equation for Vin in term of VD using the known points A(VinA, VDA) and B(VinB, VDB)

Vin = mVD + c //where m and c are known values;

5. Assume VREF of ADC of the ATmega328P MCU of UNO Board is at DEFAULT (5V); transform VD from its analog domain to its equivalent binary domain --
VD = (5/1024)*analogRead(A0)

6. Now, Vin of Step-4 can be written as --

Vin = m*(5/1024)*analogRead(A0) + c

7. The execution of the following codes should bring the readings of the DMM and UNO very close to each other.

float Vin;
Vin = (float)m*(5/1024)*analogRead(A0) + c;  // m and c must be replaced by their numerical values
Serial.println(Vin, 2);
delay(2000);

RudiAhlers:
It's an Arduino Nano, powered from my laptop's USB with a stable voltage of 4.6-4.7V, which I have used in my code as reference.

Yes, but I am not there yet. I just want to get the basics working before I move onto the ESP8266 and MCP3208 - which I probably don't need anymore if it works well at the 0V-3.3V range.

Remove the word "stable".
Not a problem to use a Nano on USB power with ratiometric sensors,
but not very wise to use it to measure voltages.

The ESP8266 should be more "stable", since it has an internal 1volt reference.
The Nano also has, but you're not using it.

The MCP3208 is another problem, since it doesn't have a reference voltage at all.
Breakout boards like the ADS1115 do.
Leo..

Wawa:
Remove the word "stable".
Not a problem to use a Nano on USB power with ratiometric sensors,
but not very wise to use it to measure voltages.

Why not? It doesn't drift much, it's just off by a couple millivolt.

Wawa:
The ESP8266 should be more "stable", since it has an internal 1volt reference.
The Nano also has, but you're not using it.

How would I use the 1.1V reference with such high input voltages?

I have hooked up the ESP8266 but get a lot of fluctuations, hence trying to get it fixed on the Arudino Nano first.

Wawa:
The MCP3208 is another problem, since it doesn't have a reference voltage at all.
Breakout boards like the ADS1115 do.
Leo..

Let's forget about the MCP3208 for a moment. I have it at hand and could use it, but don't need to.