Calculate battery percentage of alkaline batteries using the voltage

I'd like to calculate the approximate capacity that is left in an AA alkaline battery, given its voltage. (I'd be discharging it at like 1mA in case that matters.)

But I can only find images of graphs. Eg. |500x285

I can't find mathematical functions describing the curves.

I'm sure I'm not the fist one to stumble upon this issue.

So I'd like to ask if anyone of you has done this before or if there even is a library to do this kind of calculation.

Any help would be appreciated. :)

Calculaing remaining time is very difficult for all kinds of battories. Looking at my smart(?)phone I feel that the percentage drop much faster when close to zero than it falls from fully charged. Make a linear approximation from some 1.0 volt to some 1.40, 1.45 volt. It will show more than 100% in the beginning but You easily limit that displaying.

I can't find mathematical functions describing the curves.

It is not difficult to fit a function to most curves (i.e. the raw data points).

However, in this case, there is no point to fitting a curve, because the curve depends on the discharge current, battery type, age, brand, ambient temperature, etc.

People often use a simple table that is set up for each individual application, current age and type/brand of battery, then updated from time to time within the application, as the installed battery ages or when it is changed.

For a phone battery this would be much more difficult though because of capacity degradation, temperature and load.

For a new AA alkaline battery under a constant load and temperature this should be much easier to calculate.

I just found a website that can generate a mathematical function given a couple of coordinates. Unfortunately I have my doubts that the Arduino will be able to make use it this monster:

f(x) =

This is the data I have given it:

alkaline-table.png

The Arduino can "make use" of any properly defined function, provided you can fit the code and data into memory. The ridiculous example you posted would be no problem, but meaningful only for some particular battery type, age and set of conditions.

this should be much easier to calculate.

[u]What[/u] should be "easier to calculate"?

If you want to pursue this approach, you need to actually measure the discharge curve for the battery of your choice, under the appropriate conditions, and use it in any way that makes sense.

Measure the voltage and use Your experience and Your engineering knowledge.

I meant it should be much easier to calculate how much percent of the battery is left if all these mentioned factors were constant (temperature, load, battery type, inital capacity, age).

And as I said it doesn't have to be perfect. It just needs to be an approximation. A small function representing the graph I posted should be fine.

My worries weren't about the size of the function in memory of the Arduino. My worries were that the Arduino might not be able to spit out a result in its life time.

Please post the data you get by measuring the battery voltage as a function of time, under the conditions of your application. We will be very patient.

My worries were that the Arduino might not be able to spit out a result in its life time.

Try it and see. Evidently, you will be quite surprised.

Why overcomplicate things ?

The discharge is close enough to a straight line for practical purposes from say 100% capacity to about 5%.

Is it in practice really important to know the difference between 3% and 2% remaining capacity ?

Given the variations depending on battery manufacturer, temperature, discharge current, continous disscharge or pulse, I wish you luck in coming up with an accurate guesstimate ...............

I would suggest to read this:

https://learn.sparkfun.com/tutorials/measuring-internal-resistance-of-batteries/internal-resistance

Measuring internal resistance of the battery is the only way to more reliable estimate it's current capacity. As pointed in the article, for a common AA alkaline battery, it is between 0.1 Ω and 0.9 Ω internal resistance.

Thanks Felic, what a fascinating question!

As has been said

Measuring internal resistance of the battery is .. (a better way) .. to estimate it's current capacity.

However the internal resistance is LOW. Usually less than 1 ohm; so to measure it you need to draw a fair current from the battery. So a 100mA current would cause a change of only about 0.1V in battery voltage. Still enough to measure.

Also (I havent tested this) the battery doesnt respond immediately. It acts a bit like a capacitor; so you need to maintain the test current for a little while. Which of course drains the battery. And as the battery voltage is changing, you need to arrange a constant current, not just a resistive load.

So while it is a much more accurate way to test the charge remaining in the battery, its a bit like testing valves with a hammer. (if it breaks it was a good one)

As JRemington hinted, I'd suggest you use the battery curve to create a simple look-up table; "if its over 1.25V theres 50% left."

Actually, is this a question regarding testing battery capacity externally and one time only or constantly inside some device? This question is crucial for decision which method to use.

Measuring internal resistance does require few second for voltage to stabilize. With a low value power resistor, it will draw some significant current for that period of time depending on its value, however it will hardly discharge it. For instance, with 5 Ohm resistor, current draw is about 300mA for a few seconds. It will hardy drain it, as usually AA alkaline battery have capacity of between 1700 and 2850 mAh.

To constantly monitor inside a device, it is quite simple, as the drop is a straight line and in such manner proportional in range of 1.5 to 1.1V from the graph for an alkaline AA battery. Above (including) 1.5V is 100% full and under 1.1V is empty. The difference is 0.4V and the formula is quite simple:

Vm = Measured voltage d = Voltage difference p = Estimated percentage of capacity

d = Vm - 1.1V

0.4V : 100 = d : p -> p = (d / 0.4) * 100 -> p = 250 * d

Since none of device use just one AA battery, upper method is way unprecise or complicated to implement inside a device which have several batteries in order to measure each battery separately.

If measuring capacity of the battery outside the device, probably non of commercial testers use upper method, but measiring internal resistance of the alkaline battery. It is another story with batteries have different chemistry. For instance, good Li-Ion battery have almost constant internal resistance, no matter of it's capacity and thus internal resistance value is used to determinate the battery is for usage or recycling bin.

felic: For a phone battery this would be much more difficult though because of capacity degradation, temperature and load.

For a new AA alkaline battery under a constant load and temperature this should be much easier to calculate.

I just found a website that can generate a mathematical function given a couple of coordinates. Unfortunately I have my doubts that the Arduino will be able to make use it this monster:

f(x) = |500x10

This is the data I have given it:

To make the equation less complex, one would use fewer data points for the curve fit. As a minimum one would use two points to get a linear fit as suggested in post #8. Finally you want remaining charge as a function of voltage, so the X and Y columns in your data table need to be reversed.

Thusly, using [(1.4, 90.9), (1.15, 18)], that is entries 3 and 11 in the table, one gets the formula y = 291.6x - 317.34 from the "equation finder" web page. This approximation gives 0% remaining at a shade under 1.1 Volts and claims 120% at 1.5 Volts, but it's probably close enough for most applications. Generally one is most interested in getting the approximation right near the "almost dead" end.

As an aside, line 2 in your table probably shouldn't be there as the battery is not of any practical use long before it hits 0 volts and it will do bad things to any curve fit.

MrMark: This approximation gives 0% remaining at a shade under 1.1 Volts and claims 120% at 1.5 Volts, but it's probably close enough for most applications.

This is quite large mistake. And there is no need to globalize formula as the most of the curve is straight line. It is anyway approx. value and as well voltage above 1.5V and under 1.1V are quite irrelevant. Under 1.1V anyway battery will soon drop to 0V.

Anyway, you should split the calculation on three parts and use according specific formula for more precise estimation for specific part of the graph, even that have no much sense in this case.

As a sort of aside , if you put the data into a spreadsheet you can get it to calculate a curve fit function for you .As already said not really needed in this case , but I have used it in the past on various projects .

I want to keep it simple in terms of hardware and I want to be able to constantly check the battery level without draining it. The battery (two AA cells in series) I want to measure is the one powering the Arduino.

I think I’ll do it like this:

float convertAlkalineVoltageToCapacity(float x) {
    if (x > 1.4)
        return 0.0164835*x-0.0983516;
    else if (x < 1.1)
        return 0.121667*x;
    else
        return 1.030946 + 0.008599974*x - 0.000135847*x*x + 0.0000009444818*x*x*x;
}

I’m still having doubts if the Arduino could actually handle that. First of all I don’t know how computationally demanding this would be and secondly, I don’t think the floating precision would be sufficient.
(I’m planning on using a 3V Pro Mini.)

Any ideas?

Felic, you have made a common math error, called “extending the precision”.

What do you get if you divide 4 by 7?

0.57142857142857142857142857142857

NO. 4 and 7 are given to single figure accuracy. A more correct answer is 0.6

Your voltage readings can not have more than 0.25% accuracy and may be a LOT worse depending on the reference voltage you use.

Secondly, you are taking readings from a graph that shows the behaviour of a population, and applying it to an individual case. But you dont know how the population was distributed.

Start by looking again at the graph. Having done many battery discharge tests with my data logger I can state the following. (see also https://eu.industrial.panasonic.com/sites/default/pidseu/files/downloads/files/id_alkaline_1203_e.pdf)

1: the open-circuit voltage of a fresh cell is between 1.5 and 1.7V

2: on load the voltage drops to about 1.4 when it is about 10% discharged.

3: The voltage you measure will depend on the load current.

4: when the battery has only about 10% charge left its voltage will start to drop much more sharply.

5: EVERY battery will be different.

6: the graph is only representative of the discharge current used.

(Also, a useful arduino circuit will generally draw a lot more than “like 1mA” as you said initially)

So the simplest and most realistic approach is to ignore the first and last 10% and use interpolation between the 10% and 90% points.

at 10% the voltage is AROUND 1.4V (NOT 1.40000000000)
at 90% the voltage is AROUND 1.1V

Lets say your scaling factor is 0=0V and 1023 = 2.096V you your reading N is 700 for 1.4V and 540 for 1.08V (I’ve chosen that to make the math numbers easy: 700-540 = 160)

we do a straight line fit 700=90%, 540 = 10% to find the remaining percentage P

then:
if N > 701 let P = 100;
if N = (700 - 540) P = 90 - (700 - N) /2 ;
if N <539 let P = 0;

If you MUST have (unrealistic) values in the 0-10% && 90-100% ranges I’d probably use a switch case structure such as
Switch (N)
case >750 P=98;
case 740 … 749 P=96;


case 540 … 700 {P = 90 - (700 - N) / 2 ;} //use a right shift

case 530 …539 P=8;

etc.

And of course for 2 cells in series, you should really measure each seperately.

float convertAlkalineVoltageToCapacity(float v) {
  if (v >= 1.55)
    return 100.0; //static value
  else if (v <= 0)
    return 0.0; //static value
  else if (v > 1.4)
    return 60.60606*v + 6.060606; //linear regression
  else if (v < 1.1)
    return 8.3022*v; //linear regression
  else
    return 9412 - 23449*v + 19240*v*v - 5176*v*v*v; // cubic regression
}

I ended up with this which works great for me.

OK Felic but 1: you are still using nonsensical levels of precision, and 2: as your voltage reading is an integer it would make more sense to use integer arithmetic.

However if youre satisfied with the result thats fine.