Battery level monitoring when using step-up/down voltage regulator

I am using a Hobbytronics Ardulog RTC (based on Atmega 328), which will run on 3.3 V but is 5V tolerant, together with a sensor that requires 5V. To maximise battery longevity and cold weather performance (both critical to the application), I am using 4 x Lithium AA batteries, giving a nominal 6V (actually close to 7V) when fresh. I use a Pololu step-up/down 5V voltage regulator. This all works very well.

I would now like to log battery voltage, so that it is obvious when the batteries need changing. I have read a good deal on the Arduino forums, Nick Gammon's website, etc about monitoring battery voltage, but I can't quite see how to apply it in my case. (I'm having to learn basic electronics on the hoof according to need - but I guess that's the norm here.)

The regulator does a very good job of maintaining a constant voltage, so there's little point measuring that. The battery voltage upstream of the regulator will often exceed 5V, and presumably applying this to the pins of the Atmega 328 would risk damage. If I use a voltage divider to down-rate the measured voltage, that will presumably consume power, which I can't afford. I have considered tapping into the battery pack so as to measure the state of only one or two batteries, but there seems to be quite a lot of variation between individual cells, so I am not convinced that this is a great solution, although admittedly the logged data would reveal an interpretable trend.

Is there a simple electronic ruse that would help here? Thanks in advance for your thoughts.

use a simple voltage divider to scale the battery voltage with high value resistors to limit the current drain and use a micropower unity gain voltage follower (like MCP6L01) to have a low impedance output to connect to the ADC. but in most cases the ADC input impedance i large enough to connect the voltage divider to it.

be aware with this method, to use lithium batteries with their protection PCB. this protection PCB cut the output of the cell if the voltage drops under 2.5v (UCV).
It's complex and long to explain but in short, under a certain voltage limit the chemistry is modified and the cell can short itslef. (that was explained to me by Varta team when they came for polymer batteries fires in our products)

So the voltage divider current drain can be used if this protection PCB is connected to the cell.

without this Under Cell Voltage protection the continuous divider current drain will cause battery failure.

but the best way when your system is working with high and low temperature is to use a coulomb counter or a real battery gauge chip IC.
this is what I'm using in our industrial products because the battery volage vary over the temperature and measuring only the cell voltage is not sufficient to have a good battery low trip point.

Thanks so much for this advice from within the industry! I am home-building several dozen units for non-commercial research use. Adding either a battery gauge PCB or a coulomb counter (both new concepts for me) would probably add too much to the cost and size of the gadgets, but I will look into those options seriously.

I really appreciate the warning about using a simple voltage divider. But is it possible that you are confusing simple 1.5V Lithium AA cells (which I think are Lithium Iron Disulphide) with LiPo batteries, which I know to be much more ticklish?

Yes you're right. I was talking about 18650 near AA size lithium ion/polymer cells. :wink:
so for your technology the voltage divider should be sufficient but the voltage on such battery is constant like for lithium ion and the current is the best method for precision.
The batteries seems to already have their protective PTC inside
you can add a high side Pmos enabled only when you want to check the voltage to avoid continuous current drain :slight_smile:

You could sample the battery voltage periodically.
Connect the battery voltage divider only during sampling to the analogue pin.
e.g with a two transistor/mosfet level shifter.
Leo..

This is what i've said just before your post
My three last lines with Pmos :wink:

Yes, sorry.
Just saw that after posting.
Did not want to delete it.
Leo..

Thanks, both. I will certainly explore that idea.

I'm working with BQ34Z110 for the industrial power I'm currently designing.
check their application note you have the battery voltage sensing enabled by the chip to limit the current drain from the cells.
that can help you for the design :wink: (check the schematic around Q4 and Q5)
BQ34Z110 EVK

Forgive my naivety, but what is the advantage of the PMOS in this context? Is it that you control the gate by pulling it to ground?

the Pmos disconnect the high side of the voltage divider.
if you use a Nmos to disconnect the low side of the voltage divider you don't have anymore a voltage divider but only one resistor in serie with the cell vltage
if the battery gauge IC is powered by 5v supply voltage the current flows between the Vbat the resistor the internal chip clamp diode and the power supply pin of the gauge.
-> you don't stop the current flowing from the cell.

I get it. That's neat. Thanks so much for explaining.

you're welcome :slight_smile:

FYI,
You should be aware that the analogRead values are based on the Vcc voltage when the AREF is DEFAULT.
Consequently, the read value is Vcc/1023, which for Vcc = 5V , yields 4.88 mV/per count. When Vcc drops BELOW 5V then:
ie:
Let Vcc = 4.7V
AnalogRead = Vcc/1023=4.7V/1023=0.00459 (4.59 mV) /per count. Thus 4.7V=1023 , so if you don't know what Vcc, what good does it do to read 1023 counts if you have no idea what voltage it represents ?
You can read the voltage all you want but you will never know when it is below 5V because your Vcc is below 5V. If you think a count of 1023 represents 5V when it actually represents 4.7 V, you will never know when it is time to charge the battery. If , on the other hand, your Vcc NEVER drops below 5V, then your voltage divider scheme will work.

raschemmel, thanks for the advice. I am thinking that what I need to do is to compare voltage upstream and downstream of the step-up/down voltage regulator. That should be a valid indicator of battery status, shouldn't it?

The upstream voltage (battery pack) will be too high to connect directly to an input pin, hence the voltage divider is needed.

Will be grateful to know whether you think there is a fundamental flaw in that.

I am thinking that what I need to do is to compare voltage upstream and downstream of the step-up/down voltage regulator. That should be a valid indicator of battery status, shouldn't it?

The upstream voltage (battery pack) will be too high to connect directly to an input pin, hence the voltage divider is needed.

Will be grateful to know whether you think there is a fundamental flaw in that.

Upstream, downstream, what's the difference ? If the arduino Vcc falls below 5V, all your "upstream" readings will be false.

The fundamental flaw is that voltage divider formulas are based on a fixed input voltage and the fact that the uP Vcc is 5V. Without a voltage reference that does not vary, the readings will be inaccurate. Subtracting the fixed 3V reference from an unknown voltage still results in an unknown. If the battery voltage is 4.7V, it will still report 1023 counts
(the normal max for Vcc=5V) so the result will still look like 5V-3V= 2V when in fact it is 4.7V-3V=1.7V.
Since the voltage drops are based on current , which is also an unknown, even a difference amplifier will still yield inaccurate results. A precision 5V reference Precision 5V voltage reference can only work if it has an input voltage greater than 5V, so again the problem is the moment the input voltage drops below 5V. After that , nothing is reliable. The standard procedure for system design is to have a separate battery for the microprocessor circuitry that has a battery life which will always exceed the life of the battery powering the primary load (motor or whatever it is). In this scenario, at no time is the uP battery below 5V when the load battery drains to the point of needing charging. Sorry there is not a one battery solution. It is what it is...

The only way you can use one battery is if you program it to report LOW BATT when the battery terminal is lower than "x" , but the Vin >5.5V

This is why your power supply must work until your battery is near empty.
For example if you are using a two lithium batteries cells in serie.
The pack voltage will vary between 2.8v2=5.6v (empty) to 4.2v2=8.4v (full)

For such application you must ensure that your voltage regulator is a "low drop" one to ensure you have 5v at the output even if the input voltage go down to 5.6v when the battery pack is depleted.
Here you can use the 5v as reference if the LDO is a 1% voltage accuracy and
set the battery sensing voltage divider to 8.4/5= 1.7*
In that case you will get near 8.4v/1.7 = 4.82v (near full adc scale) when the batery is full and 5.6v/1.7 = 3.29v a the ADC input when the battery is empty.

So you must give a schematic to see if your system can work.

Another thing to keep in mind is to make a mean measure of the ADC value (over a second or more) because the battery voltage can vary when peak currents occurs. more when the battery is depleted or cold (high internal impedance).

For such application you must ensure that your voltage regulator is a "low drop" one to ensure you have 5v at the output even if the input voltage go down to 5.6v when the battery pack is depleted.

=

The only way you can use one battery is if you program it to report LOW BATT when the battery terminal is lower than "x" , but the Vin >5.5V

you can use a step up or "buck-boost" regulator to 5v with one cell too. Like MAX1674 serie to supply the power to the microcontroller system. (I've allready successfully used this system for a one lithium cell powered handheld bluetooth barcode reader)
in that case the battery cell voltage can be directly used at the ADC input with only a serie resistor.
This is why a drawing is helpfull
building power supply from batteries is not so easy and an example is better to understand. like about the low dropout regulator use etc.

raschemmel:
The fundamental flaw is that voltage divider formulas are based on a fixed input voltage and the fact that the uP Vcc is 5V. Without a voltage reference that does not vary, the readings will be inaccurate.

Fortunately the Atmega 328 has an internal 1.1V voltage reference that can be used to determine the true battery voltage. The internal reference is inaccurate out of the box but is easy to calibrate with a DMM. Then what you can do is measure the apparent battery voltage with a voltage divider and one of the analog pins and correct for it by measuring Vcc using the internal reference and mulitplying the measured apparent battery voltage by Vcc/5.0.

Here's an example sketch; the key is those three lines in loop() and the readVcc() function:

#include <SPI.h>
#include <SSD1306_text.h>

#define OLED_POWER_PIN         2
#define OLED_DC                5
#define OLED_CS                6
#define OLED_RST               9
#define OUTPUT_PORT(pin)      (pin < 8 ? PORTD : (pin < 14 ? PORTB : PORTC))
#define PIN_MASK(pin)         (pin < 8 ? 1<<pin : (pin < 14 ? 1<<pin-8 : 1<<pin-14))
#define OLED_POWER_ON          OUTPUT_PORT(OLED_POWER_PIN) |= PIN_MASK(OLED_POWER_PIN)

#define BATTERY_LEVEL_PIN     A5 // analog input for battery with voltage divider

#define BATTERY_VOLTAGE_FACTOR  10.05   // Calibrated voltage divider factor
#define INTERNAL_REF_FACTOR 4892 // What it reads when Vcc is actually 5000 mV

SSD1306_text display(OLED_DC, OLED_RST, OLED_CS);

void setup() {
  Serial.begin(115200);
  Serial.println("\nBattery voltage display\n");
  
  pinMode(OLED_POWER_PIN, OUTPUT);
  OLED_POWER_ON;
  delay(1);
  display.init();
  display.clear();
  display.setTextSize(2);
}

void loop() {
  float vccVoltage;
  float apparentBatteryVoltage;
  float correctedBatteryVoltage;
  
  vccVoltage = 5.0*float(readVcc())/float(INTERNAL_REF_FACTOR);
  apparentBatteryVoltage = float(BATTERY_VOLTAGE_FACTOR*analogRead(BATTERY_LEVEL_PIN))/1024.0;
  correctedBatteryVoltage = apparentBatteryVoltage*vccVoltage/5.0;

  Serial.print("Vcc: ");
  Serial.print(vccVoltage, 2);
  Serial.print(", rawBatteryVolts: ");
  Serial.print(apparentBatteryVoltage, 2);
  Serial.print(", corrected: ");
  Serial.print(correctedBatteryVoltage, 2);
  Serial.println();
  
  display.setCursor(0);
  display.print(vccVoltage, 2);
  display.print(" Vcc");
  display.setCursor(3);
  display.print(apparentBatteryVoltage, 2);
  display.print(" raw");
  display.setCursor(6);
  display.print(correctedBatteryVoltage, 2);
  display.print(" adj");
  
  delay(500);
}

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  
 
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring
 
  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both
 
  long result = (high<<8) | low;
 
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

(edit)
Attached is a graph showing Vcc, measured battery voltage and corrected battery voltage for an Uno with display, GPS, and SD card running (for about 4 minutes) on an essentially dead 9V battery rescued from the recycle bin.