Measuring battery voltage conditionally

I have a Pro Mini running off 3xAA (3 x 1.5V) alkaline batteries.
I want to measure the voltage of the batteries.

I found 2 relevant threads this one from 2013 and this one from 2012 which I will use for the basis of my code.

From those two threads I understand I can determine the battery voltage by feeding the supply voltage into one of the analog pins.

However, I have a question - is there any benefit to be gained in how the connections are made and in when the battery measuring code is run.

So, comparatively speaking, which scenario would have the least amount of battery drain due to voltage measurement :

  1. Battery connected direct to A0 and a sketch + battery measuring code running every second.
  2. Battery connected direct to A0 and a sketch running every second with battery measuring code running every 24 hours.
  3. An output pin (pulled high by battery measuring code) connected to A0 and a sketch running every second with battery measuring code running every 24 hours.

Option 3 also begs the question - is this a viable approach?

The analog input pins have a very high impedance and won't take any appreciable amount of energy from the battery so you can measure the voltage as often as you wish. Keep it simple.

One thing you do need to watch for is the reference voltage for measurement. AFAIK if you are using the battery voltage as a reference you won't get any useful data. It will always seem "full". You can get around this by using the internal 1.1 reference voltage, but then you will need a voltae divider (two resistors) so that the measured battery voltage is in the range 0 to 1.1v.

...R

I was trying to avoid the use of a voltage divider so as not to add to battery drain.

FWIW my main objective is to get a low battery alarm - say at 10% above min required operating voltage.

Are u saying without the voltage divider I won't get that level of accuracy?

If the Pro Mini is powered directly from batteries this is a better method for measuring the supply voltage...
http://forum.arduino.cc/index.php?topic=38119.0

That thread is from 2011.
The 2012 thread I referenced has code by your "partner" (retrolefty) from the 2011 thread.

The 2013 thread has somewhat similar code.

So if my understanding is correct, I can use the following code with "no external parts" i.e. no voltage divider and by following the step in Post #3 of the 2011 thread to determine the internal voltage and AREF.

// Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference
// This demonstrates ability to read processors Vcc voltage and the ability to maintain A/D calibration with changing Vcc
// Now works for 168/328 and mega boards.
// Thanks to "Coding Badly" for direct register control for A/D mux
// 1/9/10 "retrolefty"

int battVolts;   // made global for wider avaliblity throughout a sketch if needed, example a low voltage alarm, etc

void setup(void)
    {
     Serial.begin(38400);
     Serial.print("volts X 100");
     Serial.println( "\r\n\r\n" );
     delay(100);
    }
    
void loop(void)
    {
     battVolts=getBandgap();  //Determins what actual Vcc is, (X 100), based on known bandgap voltage
     Serial.print("Battery Vcc volts =  ");
     Serial.println(battVolts);
     Serial.print("Analog pin 0 voltage = ");
     Serial.println(map(analogRead(0), 0, 1023, 0, battVolts));
     Serial.println();    
     delay(1000);
    }

int getBandgap(void) // Returns actual value of Vcc (x 100)
    {
        
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     // For mega boards
     const long InternalReferenceVoltage = 1115L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc reference
        // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
  
#else
     // For 168/328 boards
     const long InternalReferenceVoltage = 1056L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc external reference
        // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)         -Selects channel 14, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
       
#endif
     delay(50);  // Let mux settle a little to get a more stable A/D conversion
        // Start a conversion  
     ADCSRA |= _BV( ADSC );
        // Wait for it to complete
     while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
        // Scale the value
     int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value 
     return results;
 
    }

Am I on the right track?

I still need to study the sleep variation, but baby steps for now...

aisc:
I was trying to avoid the use of a voltage divider so as not to add to battery drain.

FWIW my main objective is to get a low battery alarm - say at 10% above min required operating voltage.

Are u saying without the voltage divider I won't get that level of accuracy?

http://jeelabs.org/2013/05/16/measuring-the-battery-without-draining-it/

@Wawa : Thanks for the link. It still uses a voltage divider albeit with a capacitor across the low value resistor to lower current draw.

If I can do it with no additional parts, that would be preferable. If I not mistaken, the current method I am pursuing uses no extra parts. Just waiting for Coding Badly to confirm.

[quote author=Coding Badly date=1434874149 link=msg=2285174]
If the Pro Mini is powered directly from batteries this is a better method for measuring the supply voltage...
http://forum.arduino.cc/index.php?topic=38119.0%5B/quote%5D
Full marks to whoever figured that out. Measuring a known voltage with an unknown voltage reference and then figuring out the reference voltage is very neat.

...R

Am I on the right track?

Yes.

Some things to bear in mind...

• The value is not linear. At the high end (5.5V) the granularity is 2.667 mV giving an accuracy of 5.335 mV for the ideal case. At the low end (~1.1V) the granularity is 0.108 mV giving an accuracy of 0.215 mV. Which works well for measuring battery voltage.

• From a design perspective it is impossible to read supply voltages below the internal bandgap voltage (~1.1V). However, if VCC is below about 1.6V the bandgap stops working correctly. Which should not a problem because the operating voltage minimum is 1.8V.

• Sleeping dramatically reduces the noise. Which can also be reduced by averaging multiple readings. For checking "are the batteries dead", reducing noise is unlikely to make a difference.

• Most of the code variations have mistakes. Use the @retrolefty version (or something functionally identical). There are a few, essentially cosmetic, things that could be done to improve it (like returning unsigned instead of int) but the code is correct and has been well reviewed.

• If absolutely accuracy is important you will have to determine the actual value for your internal bandgap. Each processor / batch is different; they all fall between 1.000 and 1.200. I have been storing the bandgap value for each processor in EEPROM. I store the one-byte value left over after multiplying by 1000 then subtracting 1000 (e.g. 1.123 is stored as 1.123 * 1000 - 1000 = 123).

If I not mistaken, the current method I am pursuing uses no extra parts. Just waiting for Coding Badly to confirm.

Confirmed. No extra external parts. The method works extremely well.

check out my blog
there is 2 posts about calculate voltage for alkaline batteries "very simple" with the code and the circuit :slight_smile: if you have any questions about the code just let me know

What means "directly" here, over 3.3V/5V and GND and not via Vin?

ProMini is usually marked Vcc (after the regulator) and RAW (before the regulator).
Apply 3.3V or 5V to the VCC pin.