I have over forty Ni-MH low self-discharge rechargeable batteries (mostly
Eneloops, and a few Vartas and GPs) and I have noticed that some of them tend
to not keep the charge very well. In order to find out which ones are worth
throwing away to be recycled, I needed a device that would measure their
capacity.
The principle is very simple: recharge the battery, discharge it through a
resistor with a known value, measure the current while it discharges, and
integrate the current over time. This will give you the battery's capacity.
A few things need to be taken into account: how do you know that a battery is
depleted and what discharge current should be used? From what I could gather
by reading a few Web forums, the battery is considered to be depleted when its
voltage goes below 1.0 V or 0.9 V (I settled on 1.0 V), and the current should
be such that the battery discharges in about five hours (for a 2000 mAh battery,
that would be about 400 mA). If the current is too high, the battery's voltage
while it is discharging will be very close to 1.0 V already at the beginning
of the process and the capacity measurement will probably be biased. I noticed
this in the first iteration of the device where I had a 1 Ω resistor and
a 1 A current, and decided to change for a 3.3 Ω resistor after reading
about the “discharge in 5 hours” rule.
The device
The prototype I built is very simple. The battery discharges through a
3.3 Ω ±1% 0.6 W resistor, and the voltage is measured using the
Arduino's ADC (more on that below). A MOSFET (STMicroelectronics VNP35NV04-E,
quite expensive but it turns fully On with only 2.5 VGS and its On
resistance is only 13 mΩ, which is below the tolerance of the main
resistor and the accuracy of what the Arduino can measure, so it can simply be
ignored) controls the discharge and cuts the current once the “depleted”
threshold has been reached to prevent damaging the battery. A 10 kΩ
resistor acts as a pull-down for the MOSFET's gate, and a 110 Ω limits
the gate current (whether it's really needed or not seems to depend whom you
ask on Web forums, but since I had some lying around I decided to use them).
There are two instances of the circuit on the prototype board, so I can test
two batteries in parallel.
Accuracy of the Arduino's ADC
The Arduino's ADC has a maximum precision of 10 bits, meaning that the value
read from e.g., A0 is translated to a value between 0 and 1023 included, by
comparing A0 to a reference voltage that by default is the Arduino's supply
voltage. It is however possible to get (reasonably) accurate voltage
measurement with the Arduino (see this Web post titled
Secret Arduino Voltmeter)
by measuring first the supply voltage accurately against the ATMega328's internal
band-gap reference. To do so, you need to first measure the band-gap
reference's correct value (it's about 1.1 V, it's very stable over time and
temperature, but it's exact value differs from one ATMega328 to the next).
The process therefore goes like this.
- With a trusted voltmeter, measure the Arduino's supply voltage
VCC as accurately as possible.
- Using the code snippet in the blog post, measure the band-gap's value
with the ADC; that gives you a number Nref. For better accuracy,
compute the arithmetic mean of 1000 measurements to get rid of the random
fluctuations in the measurement.
- Calculate Vref = Nref × Vcc / 1024 (and not
1023 as stated in the blog post). In my case,
Vref = 1.089 V ±0.001V.
- Hardcode the Vref you have found in your Arduino program.
Then when you want to actually measure a voltage V with the Arduino's ADC, do
as follows.
- Measure Nref with the ADC and compute the value of
Vcc = Vref × 1024 / Nref (again, take the
average of 1000 measurements).
- Measure N with the ADC and compute the value V = N × Vcc / 1024
(once again, average over 1000 measurements).
If my uncertainty calculations are correct, I get a 0.12% uncertainty on the
value of Vcc (compounding the uncertainty of the voltmeter and the
ADC), which is less than the 1% uncertainty on the discharge resistor value. I
tried to accurately measure the 3.3 Ω resistor value, but my amperemeter
is not accurate enough for currents above 200 mA, so that's the best I can
hope to achieve.
Take a look at the code
if you want to know the details of the implementation (especially computing in
millivolts in order to use only integers instead of floats).
What about those batteries, then?
I measured the capacity of 27 batteries so far: 2 GPs, 3 Vartas and
22 Eneloops (6 of generation 1, 7 of generation 2, 2 of generation 3 and 7 of
generation 4). All have a nominal capacity of 1900 mAh.
Both GPs have a capacity of 1 mAh, so they are useful only as paperweights.
The Vartas have capacities of 870, 450 and 250 mAh, so they are only
marginally more useful than the GPs.
One Eneloop is in really bad shape (200 mAh), but the others have capacities
between 1660 and 1900 mAh, with a mean of 1790 mAh.
This is a typical battery, in good shape (1890 mAh). The voltage quickly drops
to around 1.2 V and remains stable for a while before dropping again.
This one is definitely good for nothing (200 mAh). I remember accidentally
dropping batteries on the floor a couple of times in the past, maybe this one
suffered from it?
This one is a first generation eneloop, bought probably around 2011. It still
has a capacity of 1660 mAh, but the shape of the curve is different from the
typical one, maybe due to old age?