Different power supplies change analog pin readings

I'm making a digital gauge for some sensors for my old VW. I got a pretty decent voltmeter done on my desk. At 12V is reads ~11.9V. Close enough for me.

When I hooked it up to the VW battery the voltage reading was off about a volt. The battery was 12.6V on measured with a Fluke and the arduino readout was about 11.5V.

In the house I was powering the Arduino from a powered USB hub. At the truck I powered it with a 12V wall wart.

Back in the house I tried the wall wart again (I tried 2 different ones) and the voltage reading is wrong on my display.

Also, on the wall wart the arduino starts repeatidly restarting after a minute or two.

Does anyone have any ideas what could cause this?

Unfortunately, while it seems to you that you have provided more than enough information, what is missing is your entire arduino system schematic and how exactly you are measuring 12V with an arduino running at 5V. Obviously there are two pieces of information missing for starters:

A. Voltage divider circuit to reduce 12V to <5V

B. Your code showing the actual analog counts read and your conversion code.

Your Application: VW Instrument Panel Voltmeter

I’m making a digital gauge for some sensors for my old VW. I got a pretty decent voltmeter done on my desk. At 12V is reads ~11.9V. Close enough for me.

When I hooked it up to the VW battery the voltage reading was off about a volt. The battery was 12.6V on measured with a Fluke and the arduino readout was about 11.5V.

In the house I was powering the Arduino from a powered USB hub. At the truck I powered it with a 12V wall wart.

Back in the house I tried the wall wart again (I tried 2 different ones) and the voltage reading is wrong on my display.

Also, on the wall wart the arduino starts repeatidly restarting after a minute or two.

We don’t need anything to explain the Fluke readings but everything else needs the above.

Also, “12V Wall Wart” is not sufficient description for troubleshooting.
Post a vendor link for the device and/or a photo showing any description of the device (ie: part number)

I don’t think it would be an exaggeration to say that your application is something people do every day,
(Google returns over a 1,000,000 hits) so it begs the question “What are you doing wrong ?”
I know it might seem unfair , but here on the forum, everyone is guilty until proven innocent…

vw_truck:
Does anyone have any ideas what could cause this?

Results of an A/D depend on two things.

  1. input voltage
  2. reference voltage

Arduino normally uses it’s power supply as reference voltage.
So if the 5volt supply goes up 3%, the readout goes down 3%.

Solution is to use a fixed reference voltage (not the supply) as Aref.
An Uno has one buildin. 1.1volt Aref.
Can be switched to in setup().
analogReference (INTERNAL); // switch to ~1.1volt Aref
The voltage divider must be calculated to drop to 1volt.
And the code must be adjusted for the voltage divider and reference voltage.
Example attached.
Leo…

/*
  0 - ~17volt voltmeter
  works with 3.3volt and 5volt Arduinos
  uses the stable internal 1.1volt reference
  10k resistor from A0 to ground, and 150k resistor from A0 to +batt
  (1k8:27k or 2k2:33k are also valid 1:15 ratios)
  100n capacitor from A0 to ground for stable readings
*/

unsigned int total; // holds readings
float voltage; // converted to volt

void setup() {
  analogReference(INTERNAL); // use the internal ~1.1volt reference | change (INTERNAL) to (INTERNAL1V1) for a Mega
  Serial.begin(9600);
}

void loop() {
  total = 0; // reset
  for (int x = 0; x < 64; x++) { // 64 analogue readings for averaging
    total += analogRead(A0); // add each value
  }
  voltage = total * 0.0002567; // convert readings to volt | calibrate by changing the last digit(s)

  Serial.print("The battery is ");
  Serial.print(voltage);
  Serial.println(" volt");
  delay(1000); // use a non-blocking delay when used with other code
}

vw_truck:
At the truck I powered it with a 12V wall wart.

Also, on the wall wart the arduino starts repeatidly restarting after a minute or two.

Most likely an overheating regulator.
An Uno can't supply more than about 100mA at that voltage difference.
Leo..

An Uno can't supply more than about 100mA at that voltage difference.

Wawa is referring to 12V-5V = 7V (that must be dissipated as HEAT by the regulator.

Running the arduino off a lower external dc voltage source (not greater than 9V) would reduce the Heating.
The Ideal input for the external dc barreljack is 7.5V supplied by FIVE 1.5V alkaline batteries (one 4pack +1 single battery holder , wired IN SERIES. This provides the OPTIMUM input voltage with the MINIMUM amount of heating. Is it a pain in the ass to have to make up a dual battery pack like that ? (maybe, but
if you do that your regulator won't heat up and that's one less thing to worry about.

I bought 3 of these.

You can prove out what Wawa has explained with a simple test.

Assuming your board is an Uno, connect the board to USB power and using your Fluke meter, measure and record the voltage between the +5 volt pin and the ground pin on the 8 pin header connector. Disconnect USB and power with the wall wort. Measure the 5 supply again and compare to the value measured when powered by the USB port. You should find the ratio of those two 5 volt readings should be the same ratio of the two 12 volt measurements.

For the best analog performance, you must use a stable voltage reference like the internal 1V1 ref or an external reference that must be somewhere between zero and the vcc supply voltage. The Arduino design favors simplicity over accuracy but it’s easy to fix once you understand the inherent limitations.

A cigarette lighter USB charger is (should be) designed for a potentially 'dirty' car supply.
Easy to just plug in the USB lead of an (assuming) Uno.
Leo..

Thanks for all the quick replies! That is a lot of great information.

Here's a quick video I just did showing the setup and results. I used Wawa's suggestion and powered it off a cigarette lighter I installed. That seemed to have fixed the rebooting problem.

Also, here is the code. Please don't beat me up too bad! :smiley: I'm not a programmer. I just started this project because I thought it'd be cool to have some feedback from the vehicle without installing a bunch of gauges. Embarrassingly enough it took me about a week to just get this far on the voltage. I started with the fuel gauge, but had trouble with smoothing. They I found a bunch of great examples for this so I switched over to voltage.

/*
This is a program used to read in the battery voltage in an old VW and display that on
a Nextion display
It uses a voltage splitter to reduce the voltage in to the Arduino to 2.52V at 14V
I may add smoothing someday
*/

int sensorPin2 = A2;  // Vout from voltage splitter circuit
int sensorValue2;  // Variable to store the value sensor reading

void setup() { 

 Serial.begin(9600);  // Start serial communication at baud=9600
}

 void loop(){
   
 float Vout; // voltage that will be read from pin A2
 float Vin; //This will be the calculated voltage from the splitter circuit - Troubleshooting
 float Realvolts = 0; // Calculated battery voltage
 const float correctionNum = .0047; // I got this by dividing the voltage reading on the fluke by the voltage splitter calculation

 //Variables resistors ohms in voltage divider. I will change these numbers to the actual tested resistance later
 int R1 = 10000; 
 int R2 = 2200;
 
 Vout = analogRead(sensorPin2); // Read in the analog sensor pin value
 Realvolts = Vout * ( R1 + R2 ) / R2 * correctionNum;  // Calculate battery the voltage from the sensor value
 Vin = Vout * correctionNum; //Calculate the volatege in from the splitter - Troubleshooting

 // Print to the Nextion display

 String printVolts = "volts.txt=\""+String(Realvolts)+"\""; // Shows the calcuated volts
 String printSensorA2 = "sensorA2.txt=\""+String(Vout)+"\""; // Prints the raw value from the sensor pin A2 - Troubleshooting
 String printVin = "Vin.txt=\""+String(Vin)+"\""; // Prints the calculated voltage in from the splitter circuit - Troubleshooting
 
 Serial.print(printVolts);

 Serial.write(0xff);
 Serial.write(0xff);
 Serial.write(0xff);

 Serial.print(printSensorA2);// Troubleshooting

 Serial.write(0xff);
 Serial.write(0xff);
 Serial.write(0xff);

 Serial.print(printVin) // Troubleshooting

 Serial.write(0xff);
 Serial.write(0xff);
 Serial.write(0xff);

 delay(200);

}

Vin = 12.72
R1 = 10000
R2 = 2200
Vo = Vin*(R2/(R1 + R2))

Voltage_Divider
Vin =
12.7200
R1 =
10000
R2 =
2200
Vo =
2.2938

It is obvious from anyone reading your post that :

A. They are probably NOT 1% resistors (since you would have mentioned that)

B. If R1 really is 10000, then R2 = 2195.5 ohms

It uses a voltage splitter to reduce the voltage in to the Arduino to 2.52V at 14V

Vin = 14
Vo = 2.52
R1 = 10000
Vo = Vin* R2/(R1 + R2)
2.52 = 14* R2/(10000 + R2)
2.52*(10000 + R2)/14 = R2
R2 = (2.52/14)(10000 + R2)
R2 = 0.1800(10000 + R2)
R2 = 1800 + 0.1800
R2
R2-0.1800R2 = 1800
R2
(1-0.1800) = 1800
0.8200*R2 = 1800
R2 = 1800/0.8200
R2 = 2195.5

//Variables resistors ohms in voltage divider

HINT: NEVER use variable resistors for a voltage divider, (especially cheap ones like those) !
(they will NEVER be consistent !)
As soon as the temperature changes the voltage will change.

Take the time to find some proper resistors and do the math.

Just use common/cheap 1% metalfilm (not carbon) 1/4watt resistors.
Absolute value and tolerance of the resistors in a voltage divider is irrelevant. Only stability is important.
No need to measure the resistors, and no need (silly) to use expensive 0.1% resistors.
The several variables that lead to voltage can (must) be calibrated out in the end with code (to 0.1%) against a good DMM.

Don't expect two digits precision if you drop the battery voltage to 50% of the Aref voltage (using half of the A/D). You only have 1024 values to work with. Use them wisely.
Leo..

Thanks for all the help! I have a decent voltmeter working now! Next it's oil pressure, oil temperature, fuel gauge and RPM.

Here is the code that I ended up using.

/*
This is a program used to read in the battery voltage in an old VW and display that on
a Nextion display
It uses a voltage splitter to reduce the voltage in to ~2.52V at 14V 2200 & 10000 om resistors
This uses mapping so the voltage is displayed as an integer

Credit to InterlinkKnight for posting the sketch that I based this on.

8/20/2019
*/

int sensorPin2 = A2;  // Vout from voltage splitter circuit
int sensorValue2;  // Variable to store the value sensor reading

// Varibles for the high and low end of the gauge range
int lowEndvolts = 0; // Low
int highEndvolts = 20; // High

//Variables for sensor pin range A2 from testing
int sensorLowValue = 0; // at 0v
int sensorHighValue = 780; // high end at 20V - then tweaked to get accurate reading

// Calibration for smoothing volts:
const int numReadings = 20;     // number of samples for smoothing. The higher, the more smoothing, but slower to react. Default: 20

// Calibration for volts deadzone:
const int voltsDeadzoneSamples = 2;  // amount of samples for dead zone (1 would be no dead zone). Default: 2

// Calibration for volts limit:
const int voltsLimitAmount = 20;  // how many cycles we are going to wait when volts reach limit value, before showing that value. Default: 20

// Variables for smoothing volts:
int readings[numReadings];  // the input
int readIndex = 0;  // the index of the current reading
long total = 0;  // the running total
int average = 0;  // the average

// Variables for deadzone for volts:
int voltsWithDeadzone;

// Variables for volts limit:
int voltsLimitCounter1 = 0;  // counter to wait for how long we see a limit value for volts, before showing that value
int voltsLimitCounter2 = 0;  // counter to wait for how long we see a limit value for volts, before showing that value

// Variable to store the volts value after remaped:
int voltsRemaped; 

//Variable to store volts
int Realvolts = 0;

void setup() { 

Serial.begin(9600);  // Start serial comunication at baud=9600

 // I am going to change the Serial baud to a faster rate.
delay(500);  // This delay is just in case the nextion display didn't start yet, to be sure it will receive the following command.
Serial.print("baud=115200");  // Set new baud rate of nextion to 115200, but it's temporal. Next time nextion is power on,
                              // it will retore to default baud of 9600.
                              // To take effect, make sure to reboot the arduino (reseting arduino is not enough).
                              // If you want to change the default baud, send the command as "bauds=115200", instead of "baud=115200".
                              // If you change the default baud, everytime the nextion is power ON is going to have that baud rate, and
                              // would not be necessery to set the baud on the setup anymore.
Serial.write(0xff);  // We always have to send this three lines after each command sent to nextion.
Serial.write(0xff);
Serial.write(0xff);

Serial.end();  // End the serial comunication of baud=9600

Serial.begin(115200);  // Start serial comunication at baud=115200

}  // End of setup

void loop(){

delay(20);  // I put this delay because without it, the timer on the display would stop running.
            // Aparently we shouldn't send data to the display too often.

// Read in raw value from sensor pin then map in range
sensorValue2 = analogRead(sensorPin2);
Realvolts = map (sensorValue2, sensorLowValue, sensorHighValue, lowEndvolts, highEndvolts);  // Remap sensor reading to volts
Realvolts = constrain(Realvolts, lowEndvolts, highEndvolts);  // Constrain the value so it doesn't go below or above the limits

//No Smoothing - commented out
//int voltsRemapedWithoutSmoothing = map(Realvolts, lowEndvolts, highEndvolts, lowEndvolts, highEndvolts);  // Remap the raw volts to match the volts value range
//voltsRemapedWithoutSmoothing = constrain(voltsRemapedWithoutSmoothing, lowEndvolts, highEndvolts);  // Constrain the value so it doesn't go below or above the limits

// Smoothing volts:
// subtract the last reading:
total = total - readings[readIndex];
// read speed:
readings[readIndex] = Realvolts;  // takes the value we are going to smooth
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;

// if we're at the end of the array...
if (readIndex >= numReadings) {
  // ...wrap around to the beginning:
  readIndex = 0;
}

// calculate the average:
average = total / numReadings;  // the average value it's the smoothed result

voltsRemaped = map (average, lowEndvolts, highEndvolts, lowEndvolts, highEndvolts);  // Remap the smoothed volts to match the volts value range
voltsRemaped = constrain(voltsRemaped, lowEndvolts, highEndvolts);  // Constrain the value so it doesn't go below or above the limits

// Deadzone for volts:
// This is another layer of smoothing the volts. It adds some dead zone so it doesn't go back and forward to the same
// adjacent values. This is because volts sensors have some error range and jump between different values for the same speed.
voltsWithDeadzone = voltsWithDeadzone + ((voltsRemaped - voltsWithDeadzone)/voltsDeadzoneSamples);

// By putting dead zone on volts, the limits can't be reached anymore. Because of this we need to check min and max limits:
// Min limit:
if (voltsRemaped == 0)  // if volts is 0
{
  if(voltsLimitCounter1 == voltsLimitAmount)  // if we wait long enough and still is 0
  {
    voltsWithDeadzone = 0;  // show real volts as 0
  }
  else  // since we didn't wait long enough if volts it's still 0
  {
    voltsLimitCounter1++;  // count to wait if volts it's going to remain 0
  }
}
else  // since volts its not 0
{
  voltsLimitCounter1 = 0;  // reset counter
}

// Max limit:
if (voltsRemaped == highEndvolts)  // if volts is 20 (the maximun limit) highEndvolts
{
  if(voltsLimitCounter2 == voltsLimitAmount)  // if we wait long enough and still is 20
  {
    voltsWithDeadzone = highEndvolts;  // show real volts as 20
  }
  else  // since we didn't wait long enough if volts it's still 20
  {
    voltsLimitCounter2++;  // count to wait if volts it's going to remain 20
  }
}
else  // since volts its not 20
{
  voltsLimitCounter2 = 0;  // reset counter
}

// Print to the Nextion display

String printVolts = "volts.txt=\""+String(voltsRemaped)+"\""; // Shows the calculated volts

Serial.print(printVolts);

Serial.write(0xff);
Serial.write(0xff);
Serial.write(0xff);

}

vw_truck:
Thanks for all the help!

Pitty you ignored most of it.
You also didn't read the 'how to post' thread of the forum.
Leo..

On the forum home page I didn't see an intro to the forum link. I do see it now under Community - Website and Forum - 5th post down, then click the link to "How to use the forum". Most forums I visit have that section posted in an obvious place. Now I know.

I fixed my code posts.

This is a work in progress. I did follow some of the suggestions. Some I am still working on and trying to understand. I'm new to electronics and am slow on the uptake.

Suggestions:

Wawa:
Just use common/cheap 1% metalfilm (not carbon) 1/4watt resistors.

I ordered some and expect them this week.

Wawa:
A cigarette lighter USB charger is (should be) designed for a potentially 'dirty' car supply.
Easy to just plug in the USB lead of an (assuming) Uno.
Leo..

I did this and it's documented in the video.

Wawa:
Pitty you ignored most of it.
You also didn't read the 'how to post' thread of the forum.
Leo..

I didn't ignore anything. People you are the reason I stay out of most forums.

vw_truck:
I didn't ignore anything. People you are the reason I stay out of most forums.

I assume you come to a forum to learn. Every answer given is carefully thought over (most of the time). If you don't understand a topic/sentence, you should ask. If you do, and don't want to use the answer, then at least say so. Ignoring the answers is hurting the guru (we have feelings too).

Wawa:
Results of an A/D depend on two things.

  1. input voltage
  2. reference voltage

You still use the potentially unstable 5volt supply as reference in your last code.

Wawa:
You only have 1024 values to work with. Use them wisely.

Your voltage divider to ~2.5volt only uses half of the A/D.

Q: Why the voltage limitations in the code.
The A/D won't output more than 1023 anyway, limiting voltage display automagically.
Leo..

I appreciate the information. Truth is that I don’t understand much of what was suggested. But, I’m not the type of person that immediately asks what something is before doing my due diligence and trying to learn about it first. I am new to electronics and programming so I am learning the jargon. I’ll keep plugging away at this.

vw_truck:
Truth is that I don’t understand much of what was suggested.

Not a problem. We all started with zero knowledge. Asking will get you there faster.

Wawa:
Results of an A/D depend on two things.

  1. input voltage
  2. reference voltage

That should be easy to understand.
If you measure the hight of a person (input voltage), with a tape measure (the reference), then you get a certain value.
If you measure that same person with a rubber band (bad reference), then every time you measure you could get a slightly different result.

The 5volt supply (default reference) is not that stable.
It depends on car supply and on other loads on the 5volt pin (LCD backlight PWM dimmer?).
If that 5volt supply dips with just 50mV (rubber band gets shorter), then A/D result goes up with the same ratio (more rubber band distances on the person).
Worse actually. Because of the voltage divider, voltage result goes also up with the same ratio of the voltage divider.
Therefore it’s best to use a ‘better’ reference voltage, and the Uno has one buildin.
Now study the code/comments in post#2 again.
Leo…

Gotcha. That makes sense. The rubber band example cleared that up!

I don't have the resistors yet. They should be here today. I have a 104 capacitor. If I understand correctly, that's a 100,000 pF = 100nF.

I'll let you know when I get that all connected and running.

Well, I screwed up.

First, I got the resistors hooked up. Then I hooked up the power adapter that came with the Nextion so it won't be powered by the arduino any more. Then I plugged in the external power supply that simulates the battery to the + & - strips down the center of the breadboard. After loading the code provided by Wawa I powered on the power supply. I saw that it was drawing over .5 amps so I shut it off. I forgot to remove the hookup wires run from the + & - strips down the center of the breadboard from the Arduino. I put 14 volts into the 5v pin.

Now the arduino program says this: "avrdude: ser_open(): can't open device "\.\COM5": The system cannot find the file specified."

I think I fried it. :frowning: I ordered a couple more.

Question. Can you check the schematic to see if it's correct?

I think I fried it.

Needless to say, if you really did put 14V on the arduino (ATMega328) 5V pin
you did indeed fry it.

Secondly, even technicians with many years experience do a cursory sanity check voltage reading across the power supply rails BEFORE applying power to a micro processor.

You are probably asking yourself "What do you mean ?"
What I mean is that the jumper from the Vcc to the microprocessor chip Vcc pin (or analog pin) is FIRST removed.

THEN (and ONLY then) is it safe to turn ON the power (because it is NOT connected to the arduino)

THEN the power rail voltage is checked with a meter. If all is ok (within allowable limits), then the jumper that connects the microcontroller to the power rail is installed.

I know you are probably thinking ("are you serious ? You really go through this routine every time ?"
Well, since most have us have done what you just did at least once, yes , we do.

raschemmel,
That makes sense. I will do that in the future. Thank you for the suggestion!