measuring the battery voltage using the ADC on Mini 3V3 8MHz

Hi all,

I'm trying to measure the voltage of the battery I use to power my Arduino Pro 3V3.
I have a resistor divider across the battery terminals (100K and 390K) and connect to A0, and set the analog reference to internal. I believe that the internal ref is 1.1V.

My code looks like this

int A0adc;
float A0Volts;

analogReference(INTERNAL);
A0adc = analogRead(A0);
delay(50);
A0Volts = A0adc / 1023.0 * 1.1;
battVolts = A0Volts * (100.0+390.0)/100.0;
Serial.print("; ADC counts "); Serial.print(A0adc);Serial.print("; battVolts "); Serial.print(battVolts);

I get the following output

ADC Counts 224; battVolts 1.18

Clearly this is not right. I measured the battery under load as 3.5V. I measured the voltage divider as 0.7V which is close enough given the tolerance of the resistors.

Can anyone tell me what I am doing wrong? Is my assumption that the internal ref is 1.1V?

thanks

Hi, and welcome to the forum.

An A/D count of 224 with an input voltage of 0.7volt indicates that Aref hasn't switched to 1.1volt (yet).
Because 1024/224 * ~0.7 = ~3.2volt.
It should be ~750.

This is on the Aref page:
"After changing the analog reference, the first few readings from analogRead() may not be accurate"

The A/D is optimised for input impedances <10kohm.
With those high resistor values, you also need a 100n cap from the analogue input to ground.
And/or read the A/D twice, and use the second reading.

Your code could be compressed to

int A0adc;
float battVolts;

in void setup()
analogReference(INTERNAL);
A0adc = analogRead(A0); // dummy read

in void loop()
A0adc = analogRead(A0); // dummy
A0adc = analogRead(A0); // reading
battVolts = A0adc * 0.004567; // calibrate here
Serial.print.....

Leo..

Thanks for your suggestions Leo
George

There is another approach, that avoids the external voltage divider completely. It uses the battery voltage as the reference and measures the internal voltage reference instead.

Every Arduino needs to be individually calibrated, because the internal reference (although very stable) is not calibrated.

From Gammon on power saving techniques, a great reference page.

const long InternalReferenceVoltage = 1062;  // Adjust this value to your board's specific internal BG voltage
 
// Code courtesy of "Coding Badly" and "Retrolefty" from the Arduino forum
// results are Vcc * 100
// So for example, 5V would be 500.
int getBandgap () 
  {
  // REFS0 : Selects AVcc external reference
  // MUX3 MUX2 MUX1 : Selects 1.1V (VBG)  
   ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
   ADCSRA |= bit( ADSC );  // start conversion
   while (ADCSRA & bit (ADSC))
     { }  // wait for conversion to complete (toss this measurement)
   ADCSRA |= bit( ADSC );  // start conversion
   while (ADCSRA & bit (ADSC))
     { }  // wait for conversion to complete
   int results = (((InternalReferenceVoltage * 1024) / ADC) + 5) / 10; 
   return results;
  } // end of getBandgap

That measures MCU supply against 1.1volt Aref.

Works if you have the battery connected to the 5volt pin.
Does not work if you run the battery through the voltage regulator.
In that case you can only get a warning when the battery drops below MCU supply plus the dropout voltage of the regulator.
Leo..