Analog read very inaccurate readings from car (youtube vid)

Hello!

I have recently gotten into arduino, i put together a code wich converts the voltages from my two batterys into readings with max/min values on an lcd. My problem is that these readings are VERY inaccurate. If i do an crontroll with an multimeter my voltage is constant 14.3 volt with an slight differ of 0.05 volts over the time. But with ardu i got insane inaccurate readings whenever i got my car on or off.
I messure my voltages with the help of an 10kohm and an 100kohm resistor.

Video of readings: https://youtu.be/yebQ15oLUoE

My code wich is a bit messy:

    /*
    DC Voltmeter 
    */
    #include <LiquidCrystal.h>
    LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
    int analogInput = 0;
    float vout = 0.0;
    float vin = 0.0;
    float R1 = 100000.0; // resistance of R1 (100K)
    float R2 = 10000.0; // resistance of R2 (10K)
    int value = 0;

    int potPin = A1; //Potentiometer input pin
    int potValue1 = 0;
    int potValue2 = 0; // final display variable

    int analogInput2 = 2;
    float vout1 = 0.0;
    float vin1 = 0.0;
    float R11 = 100000.0; // resistance of R1 (100K)
    float R21 = 10000.0; // resistance of R2 (10K)
    int value1 = 0;

    float maxVal = 0;
    float minVal = 10;

    
    void setup(){
       pinMode(analogInput, INPUT);
       lcd.begin(20, 4);
       lcd.setCursor(0,4);
       lcd.print("GAIN");


    }
    void loop(){


    // read then divide the input(max 1020 in this case) by 10
    potValue1 = analogRead(potPin) / 10;
// divide by 1.02 (0.99 to get it accurate for me) to get percentage
    potValue2 = potValue1 / 0.99;

    potValue2 = min(potValue2, 100);
// set cursor to second row, first column
    lcd.setCursor(6, 3);
//display final percentage
    lcd.print(potValue2);
//print the percent symbol at the end
    lcd.print("%");
//wait 0.1 seconds
    delay(20);
//wipe the extra characters
    lcd.print(" ");
    delay(1);


      
       // read the value at analog input
       value = analogRead(analogInput);
       vout = (value * 5.0) / 1024.0;
       vin = vout / (R2/(R1+R2));
       if (vin<0.09) {
       vin=0.0;//statement to quash undesired reading
    }
    lcd.setCursor(0, 0);
    lcd.print("Batt 1= ");
    lcd.print(vin);


       // read the value at analog input
       value1 = analogRead(analogInput2);
       vout1 = (value1 * 5.0) / 1024.0;
       vin1 = vout1 / (R2/(R1+R2)); 
       if (vin1<0.09) 
       vin1=0.0;//statement to quash undesired reading

       lcd.setCursor(0, 1);
    lcd.print("Batt 2= ");
    lcd.print(vin1);

    lcd.setCursor(13
   , 0);
    lcd.print("Volt");

        lcd.setCursor(13
   , 1);
    lcd.print("Volt");

      if (vin < minVal) minVal = vin;
  if (vin > maxVal) maxVal = vin;

  // DISPLAY
  lcd.setCursor(0, 2);
  lcd.print("Min ");  // the extra parameter 2 indicates the decimals
  lcd.print(minVal, 2);
  lcd.print("  Max ");
  lcd.print(maxVal, 2);
            
    delay(400);
    }

Firstly, this has nothing to do with the Arduino, it has something to do with your project. (Although I understand you suspect the Arduino).

Electrical signals in a car are very "noisy", very short spikes of different voltages on all wires. Your Multimeter does an "averaging" reading; your Arduino code takes a much smaller snapshot of time, and thus sees more of these fluctuations. Also depending on your wiring the very eads from the battery to the arduino will act as tiny arials picking up the electrical noise.

The simplest way is to make a for-loop and add 5 readings together, then divide by 5 and thus you have an average. That will probably give you more stable readings.

The second is to add a simple RC filter (a small capacitor between your input pin and ground, and a low value resistor in series).

Also make sure no high voltage spikes get into your Arduino, for example by using a Zener diode to clamp the input voltage on your input wires. Maybe usinga shielded wire and earth the shield at both ends will reduce pickup, too.

I have not studied your code, apart from seeing that it is straightforward and you are not (yet) doing an averaging of your input values.

From the ATmega328p datasheet: "The ADC is optimized for analog signals with an output impedance of approximately 10 k or less." I suspect your 100k resistor is too high a value.

Also, dividing by 11 gets you a nominal input of 1.3V which is a waste of a large portion of your input rage (0-5V). Let's figure a maximum expected voltage of 15.0V. To read that on a 5V input we would require a "divide by three" where the signal side resistance is twice the ground side resistance. Use three 10k resistors. Put two in parallel to get 5k and use that for the ground side of the divider. Then you can calculate the battery voltage with: float voltage = (analogRead(voltagePin) / (1024.0/5.0)) * 3.0;

" potValue1 = analogRead(potPin) / 10;"
That is reducing your precision right off the bat. I suggest using the full reading, and running it through the map() function.

I suspect you need more caps. On the analog pin to ground, on the power rails, etc.
How are you powering the arduino?

Thanks for the good answers. im powering the arduino right from battery no1. will look more into these answers tomorrow since its realy late here.

And no, i am not doing any averaging yet. so an average of 5 readings is the optimal?
From my simple view what stops me from doing an sample of 500 / second and averaging 100 each and printing it 5 times a second?

Arduino’s 5volt supply is by default used as reference for the A/D converter.
That default Aref makes A/D readings potentially unstable.
Better to use the stable internal 1.1volt Aref.
Here is a sketch to try.
It uses pre-reading, averaging and 1.1volt Aref.
Leo…

/*
0 - ~17volt voltmeter
displays the voltage on the serial monitor and/or LCD shield
works with 3.3volt and 5volt Arduinos
uses the internal 1.1volt reference
10k resistor from A1 to ground, and 150k resistor from A1 to +batt
(1k8:27k or 2k2:33k are also valid 1:15 ratios)
100n capacitor from A1 to ground for stable readings
*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // your LCD pins could be different
//int ledPin = 10; // backlight pin
float Aref = 1.075; // change this to the actual Aref voltage of ---YOUR--- Arduino (1.000 - 1.200), or adjust to get accurate voltage reading
unsigned int total; // A/D output
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); // ---set serial monitor to this value---
  //analogWrite(ledPin, 255); // optional dimming
  lcd.begin(16, 2); // shield with 2x16 characters
  lcd.setCursor(0, 0); // first line
  lcd.print("Voltmeter"); //info text
  lcd.setCursor(0, 1); // second line
  lcd.print("0-16 volt");
  delay(2000); // info display time
  lcd.clear(); // clear
  lcd.setCursor(0, 0); // first line
  lcd.print("Battery is"); // print this once
}

void loop() {
  analogRead(A1); // one unused reading to clear old sh#t
  for (int x = 0; x < 16; x++) { // 16 analogue readings and 1/16 voltage divider = no additional maths
    total = total + analogRead(A1); // add each value
  }
  voltage = total * Aref / 1023; // convert readings to volt
  // print to LCD
  lcd.setCursor(0, 1); // second line
  lcd.print(voltage);
  lcd.print("volt ");

  // print to serial monitor
  Serial.print("The battery is ");
  Serial.print(voltage);
  Serial.println(" volt");
  total = 0; // reset
  delay(1000); // readout delay
}

Msquare:
The second is to add a simple RC filter (a small capacitor between your input pin and ground, and a low value resistor in series).

Also make sure no high voltage spikes get into your Arduino, for example by using a Zener diode to clamp the input voltage on your input wires.

  1. A voltage divider plus a 100n cap from analogue-in to ground is a filter.

  2. A zener adds non-linearity and temp dependency. Bad advice.
    A 10k:150k divider protects to ~200volt (<1mA pin protection diode current). No zener needed.

johnwasser:
“The ADC is optimized for analog signals with an output impedance of approximately 10 k or less.” I suspect your 100k resistor is too high a value.

Also, dividing by 11 gets you a nominal input of 1.3V which is a waste of a large portion of your input rage (0-5V).

  1. A 100n cap from analogue input to ground is a very low impedance to ground compared to the ~12pf sampling cap.
    A 10Megohm resistor would still work.

  2. Not if you’re going to use the 1.1volt Aref.
    Leo…