Introduction: Measure Li-ion Cell Capacity With an Arduino

Picture of Measure Li-ion Cell Capacity With an Arduino

I like to use lithium ion cells because they hold a lot more power than "regular" rechargeable cells, and they can be harvested for cheap or free from discarded laptop batteries. They are super handy for flashlights and all kinds of projects. The problem is that, when getting li-ion cells from different sources, you might want to know the capacity of the cells you harvest, especially if you will be using them in series. This project aims to do just that: measure the capacity of a cell using a real load until the cell goes dead.

This instructable was originally published in 2014, but has since been rebooted as I wanted to test another batch of batteries. I re-assembled the circuit, but I added a few features, namely an LCD screen, as well as a cell resistance check.

Before I go on, I'm going to distinguish between “battery” and “cell.” A cell is a single unit of fancy stuff that uses chemicals to store electrical energy. A battery is a collection of cells. So, a D, C, AA, AAA, and an 18650 are all cells. A car battery, laptop battery, or 9 volt are all batteries, because they contain multiple cells.

Step 1: Theory of Operation

Picture of Theory of Operation

This whole step is theory, so if you just want to get it running, skip on to the circuit.

The capacity of a cell is usually given in milliamp hours (mAh). Milliamps are units of current, and hours are units of time, and when we multiply them, we get charge. I*t=Q. So really the capacity of cells is given in Q, the amount of “usable charge” they have in them, or, really, the number of electrons that cell can push through itself in a single discharge cycle.

If we had a constant current, we could just run that current until the cell gets below the minimum voltage, then multiply the current times time, and we would have a rough approximation of capacity (Q=I*T). This is a solid and simple technique, but it will only give rough values, but not a reliably accurate value. This issue comes in when we realize that the current is not likely to stay constant, but rather start off high, then with dropping cell voltage, decrease drastically.

The solution to this conundrum is to use calculus. If we integrate current over time, we can find the exact capacity (Q= ∫ I dt), because if we plot a graph of current vs. time, the area under the curve would represent capacity (milliamps times hours gives milliamp hours). Problem is, I didn't really want to do “proper” integration, so I figured I'd take a shortcut; I used a thing called a Riemann Sum to perform a simple stand in for integration.

In short, we can look at a plot and break it up into a bunch of thin rectangles, calculate the area of each rectangle (width times height), then add all the small areas together. In terms of capacity, we use short time intervals (rectangle width) and multiply each of those with the current (rectangle height) to find a small amount of capacity expended in that time interval. We can just sum up all of the small capacities to find the large capacity.

In the Riemann sum picture (with all the vertical lines), sometimes the q is a little big, and sometimes it is a little small. The idea of a Riemann sum is that the positive errors cancel out the negative errors, and it all washes out to be pretty close.

This Riemann sum is performed until the cell voltage gets below a threshold, then it is stopped, cause that's all the usable charge the cell has (without draining the cell beyond recommend safe limits).

Now as a side note, if we were interested in the total energy contained in the cell, we would need to look at the electrical power expended in the test (which is not the same thing as the charge expended). We could definitely measure this using this same apparatus, by multiplying the voltage by the small q and adding the resulting small e (interval energy expenditure) to a running total E, total cell power. I decided not to do this in the code for this project, but it would not be difficult to implement if you're interested. Otherwise, you can get a rough estimate of the cell energy capacity by taking the mAh rating and multiplying it with the nominal cell voltage, 3.7. (for example, if we had a 1000mAh cell, multiply it with 3.7v to get 3700mWh)

Step 2: Cell Resistance

Picture of Cell Resistance

The main reason for two loads is to be able to measure the cell resistance.

Assuming that the cell is a linear voltage source, all I need to measure the cell resistance is two data points: voltage and current for a given load, then voltage and current for a different load (more or less current). I can then look at how the voltage changed for a given change in current, and the quotient of those two changes gives me the resistance. (remember, one ohm is one volt per amp).

Ohms = -ΔV / ΔI

The method used to measure the cell resistance is to gather many data points, after putting the cell under a random load state out of the four possible states (on on/off off/on off/off on).

After collecting many data points of voltage and current, the program calculates and records the resistance using every data point against every other data point for which neither the voltages nor the currents match. It's sort of like the math handshake problem.

Based on my brief testing, this gives resistance values that can vary by ~10% in the same battery. So it's not great, but it will give you a rough idea.

Step 3: The Circuit

Picture of The Circuit

The pictures in this step show how to assemble the cell tester. The text discusses the theory in design of the circuit. You can find a fritzing file at the bottom of this page.

If you have read this instructable before, you will recall that you used to need to have your arduino connected to a computer in order to read the data from the test. This was definitely not ideal, so when I rebuilt this tester, I added an LCD and questioned why I hadn't done that the first time around. You can buy a 16x2 character LCD on ebay for about $5 with shipping included.

The actual test circuit contains two loads and two mosfet switches, to facilitate measuring the cell resistance. If you aren't interested in the cell resistance, you can certainly build the circuit with just one mosfet and load (the code won't mind).

This test circuit uses an N-channel mosfet to switch the current. The best source I have found for large power mosfets is computer power supplies (easy to find: they're on the big heat sinks). Before running this test, read the datasheet for your mosfets and make sure that they can handle a minimum of about 1A, unless you use a lower current load. Also, make sure that the "gate-source threshold voltage" is less than 5v. This is the voltage that the mosfet requires to turn on, and we can only give it 5v using the arduino. Lastly, you may want to put the mosfets on a heat sink.

The method we use to measure the current is to use a shunt resistor. We measure the voltage at both sides of the resistor in order to calculate the current. Ohm's law says V=I*R, or I=V/R (the current through the resistor is the voltage difference across it divided by the value of the resistor).

The value of the shunt resistor should be anywhere from 4 ohms down, but for a ~1A load, a good rule of thumb is that the power rating of the shunt resistor should be roughly four times the current, or about a 4w resistor at the smallest. This is in order to keep the resistor cool. As Domints has pointed out in the comments, if the resistor gets very hot, its value changes and your capacity readings are no longer accurate.

The load that I used in this circuit is a car brake light bulb (I am using the two filaments as the two loads). Between that and the shunt resistor, about one amp gets pulled during the test, and that's just about right for what I want to do. The Peukert effect discusses the effect of different current loads on a cell's capacity.

Be careful: connection A and B need to go to analog inputs. If you connect them to a digital pin, you will short the current lithium ion cell straight through your arduino. Bad times.

The schematic and breadboard layout can be found here, in the fritzing file (fzz).

Step 4: The Code

Picture of The Code

Download the code here.

The code in this project goes through 10 possible states, always starting after a reset at state 0, in which the cell voltage is measured and, depending on the voltage, the program goes to one of three possible next states.

State 1 occurs when the voltage is below 1v, and the program assumes that the battery holder is empty. It prompts for a new battery then reset. once the cell voltage goes over 1v, the program goes to state 2.

State 2 occurs when the tester started with no cell but now has a cell installed. The program displays the cell voltage and prompts for a reset.

State 3 occurs when the program started with a cell voltage between 1 and the minimum voltage to begin a test, in other words, a weak cell. The cell voltage is displayed along with an advisement of a weak battery.

State 4 occurs when the program began and the cell voltage was high enough to begin a test. In this state, the loads are turned on until the voltage gets below the threshold to check the resistance.

State 5 is the state in which the resistance is checked. The current and voltage are measured, then oneof the loads is turned off and back on after recording the two voltage/current values. The resistance is calculated from those two values with different load currents.

State 6 occurs right after state 5 finishes checking the resistance, and continues running the capacity check until the cell voltage falls below the minimum cell voltage.

State 7 occurs when the capacity check is complete and the cell is still in place. It displays the test results and the time that the test took.

State 8 occurs when the battery is removed from the tester after a test run. The capacity and resistance are still displayed, but not the time.

State 9 occurs when a new cell has been inserted. The capacity is still shown until a reset.

So, how should you edit this code to make it work for your specific circuit? The only things you should need to change are the pin numbers and the resistor value. They are in the following constants:

#define SHUNTRES 0.62
#define VIN A1
#define SHUNT A0
#define GATE1 0
#define GATE2 1

Something that a few of you have commented about is an inaccurate reference voltage. What if your 5v pin measures 4.6 volts? There is also a constant to take care of that. Measure the 5v reference pin with a multimeter, then put that votlage in the constant below:

#define VOLTREF 4.6

If you are powering the arduino from a computer's USB port, the voltage should be right at 5v (and there's no need to check it, unless you're the suspicious type). If you are using a power supply into your Arduino's voltage regulator (barrel plug), the you might need to check it, but it should be consistent. If you're using a USB charger to power it, you should check the voltage of every different one you'll use, cause they can vary a lot.

Step 5: Share Your Results!

So, this project is all well and good, but let's say you just want to know the capacity of your cell without building the thing. Well, I have started a list of batteries, identifiable by color and model number, and you can view it here.

If you have built a capacity meter and would like to contribute to this list, please message me and I'll add you as an editor.

Step 6: Put It Together and Measure

Picture of Put It Together and Measure

Ok, so once you build your circuit, throw your code on the arduino and you should be good to go!

The cells should be fully charged before you start a test, so you can know what their full capacity is. Connect a cell, and to start the test, either push your arduino reset button, or power cycle the arduino.

Once the test is complete, the screen will display "done" along with the time, cell resistance, and capacity.

I made a graph of voltage vs time for a test cycle, and it has some pretty interesting features. The beginning shows a sharp drop in voltage, but it levels off to a pretty much linear voltage decline, until it gets down to about 3300 mV. At that point, it starts to drop off steeper and steeper. This shows why discharging a li-ion cell below 2.9v really won't get you much more usable power. The battery starts strong and stays strong... then it's done.

This test apparatus can be used on different cells than just li-ion. You need to adjust the voltage constants of course, and you need to make sure you don't feed more than 5v to the analog pin, as that's the maximum it can read. A voltage divider would be one possible way to measure the voltage of higher voltage applications (for example, a 12v lead acid battery).

So far I have observed something about my cells: they seem to mostly be manufactured to be roughly 1000 or 2000 mAH. I had one get up to 3400 mAH, which is pretty impressive for just a little 18650. Also, most of the cells that come with really cheap LED flashlights tend to have horrible capacity. But that's no surprise.


msims13 (author)2017-05-15

For anyone having compile error with the hour() or any of the time related functions or libraries, go to this link and download the .zip file, then in Arduino, go to Sketch, Include Library, Add .zip file and chose the zip file you downloaded. Make sure the top of the sketch has the #inclulde <TimeLib.h> line as well as #include <time.h> and you should be good to go.

msims13 (author)2017-05-14

I'm curious about the 10k "pulldown resistor" ... if both MOSFETS are OFF, wont the battery continue to discharge through that pulldown resistor? What is the point of having it in the circuit in the first place?

millingby (author)2017-03-05

Would you be able to modify this to work with four cells on one LCD for capacity? I have MANY cells and would love to be able to test four at one time and just have the capacity displayed. I love this stuff, but I am not great at arduino code tbh. I would ideally like to be able to use the Nokia LCD panels as they like $2 a piece. I think four is a good number as four slot 18650 holders are cheap enough, but the screen and the arduino are the largest cost. Using one screen and one arduino would make this super cheap and useful. Would you be willing to redo this project with a four battery capacity for us? Thanks in advance, I really hope that you do this, I think many would benifit from it. Great job! Thanks again for your contribution.

thecheese429 (author)millingby2017-03-08

Hey MillingBy,

I started thinking about expanding this project to test 4 batteries, and realized that the Uno and Nano have few enough pins to constrain the project a bit. Then I found someone who made something very similar to my tester, except he wanted to test up to SIXTEEN cells at once. So rather than reinvent the wheel, I'd recommend you check out his project here:

Wrrr 10-G (author)2016-12-13

Hey Cheese, very nice 'structable, and I built one, works like a charm.

However, with an el cheapo Nano I got a 'reference' outgoing voltage of the 'duino board of 4.73V instead of a nice exact 5V, resulting in possibly the same problem Nityashish ran into: a starting voltage of the lithium cell that is way too high (and wrong). Have you got a workaround for us? Like telling the program to somehow add the 'missing' 0.27V to voltraw or something similar? Regards, W.

thecheese429 (author)Wrrr 10-G2016-12-13

You raise a good point there, and I hadn't considered compensating for it before. I just updated the code with a new constant to compensate.

Wrrr 10-G (author)thecheese4292016-12-14

Well BigCheese, this is great service you are providing here. Again, it works and now I can tweak the voltage shown spot on. Thanks a bunch!
Now all I need to do is find a better high power resistor, since the one I am using now is a beefy 10W/1 Ohm, but it's resistance all over the place heating up. Ah well.. Going cheap should have it's repercussions.

nityashish (author)2016-09-08

hey sir... i have a problem..... my 18650 cell voltage was 4.25....but arduino was showing 4.71... volts..... and it is discharging at the rate of 0.20 amp which is taking more time to discharge.... plz help me sir

thecheese429 (author)nityashish2016-09-10

Hi, it sounds like you might have some problems with the circuit, or with the constants in the code. I double check your circuit and make sure it is all connected correctly, and has good solder connections. Also, make sure to change the RES to the value of your shunt resistor.

If you are using a smaller light bulb, it is possible that the bulb simply won't pull as much current as a larger one. If you need higher current, go for a larger bulb or multiple bulbs per mosfet, each mosfet having them connected in parallel.

nityashish (author)thecheese4292016-09-11

Sir, I have checked my circuit 20 times i soldered exactly the same and I have solved the problem of current drawn from the batteries. I don't know what's wrong with the voltage displaying on LCD.

I have charge My 18650 upto 4.25 volts but Ardunio shows 4.65 sometimes 4.78 volts. what can i do sir??

thecheese429 (author)nityashish2016-12-13

Hi Nityashish, it's been a while since your comment, but someone else's comment suggested a fix to accomodate an inaccurate voltage reference; the code has been updated on github and I'd recommend trying it out.

As far as the low current draw, the most likely causes I can think of are: 1) a shunt resistor that's too large of resistance (should be below 1 ohm ideally), 2) too small a load (you may need a more powerful light bulb, such as a headlight bulb), or 3) your MOSFET isn't turning on all the way (you might need one with a lower gate-drain voltage and/or lower gate-source resistance).

thecheese429 (author)nityashish2016-09-12

I wonder if the 5v reference to the Arduino is lower than it should be - how are you powering the arduino?

coltoncavig (author)2016-11-01

Hi, thanks so much for this great tutorial! It's both useful and fun. I'm currently in the process of testing some different configurations/batteries, and wondering how you made the voltage x time graph. I would like to be able to make graphs of the test data including voltage, time, and current. Could you offer me any advice on recording the data from the test so that I could use it to plot? Thanks!

thecheese429 (author)coltoncavig2016-11-01

Hey Colton, thanks for the encouragement! The way I gathered that data was through the serial port with the arduino plugged into a computer. I had to add a serial communication bit to the code. Something like the following is the rough idea:

Serial.println("time (ms), current);
int cyclesSinceReport = 0;

void loop(){
  cyclesSinceReport = 0;

That will get the data out of the arduino and onto the serial monitor. If you're on linux, the easiest way to grab the data is "cat /dev/tty0 > /home/user/log.csv". If you're not on linux, you'll just have to log the serial data using another program, maybe putty. Once you have the CSV, it is trivial to graph it with GNUplot or excel.

Of course, you could record whatever data you want, like voltage vs capacity, or voltage vs current through the test. You can grab all the data at once just by adding more commas and more print statements with the data you're interested in.

Also, know that whenever the arduino accepts a serial connection, it does a reset, so you won't need to push any buttons.

domints (author)2014-04-06

Awesome project, simple, but useful. Of course it's not very accurate, but still good enough :D I'll surely make it, because I have tons of li-ions lying around :D But I'll try to use other microcontroller - ATtiny85, because it has got differential ADC input, so it can measure current and subtract all at once, giving more accurate results. And if the voltage difference is very low, it can aplify result 20x, to get even more accuracy ;)

thecheese429 (author)domints2014-04-07

Thanks! I was pretty happy how simple it turned out to be. I have to ask, though, what makes you say it's not very accurate? You're making me wonder if I missed something big in my theory...

domints (author)thecheese4292014-04-07

I'm not saying it's not accurate, but it could be more accurate - I'ts because you are making two separate measurements and then some calculation, so it introduces some delay (of course very small), and when current changes (because of voltage drop), first measurement shows voltage from the battery, and second measurement gives you voltage from shunt, but few miliseconds later, so it may be lower than when you have made first measurement, so calculated current might be slightly higher than real. Of course for big cells (like those from laptops) discharge rate is so low, that difference could be lower than 1 LSB, so just unmeasurable, but for small cells, it could be higher, introducing error.
That's why differential ADC is better - it measures just straight difference between pins, so this problem is going away.

And second thing is, when you are using very low resistor with high resistance light bulb (for example 0.5 Ohm resistor and 20 Ohm light bulb), voltage drop on that resistor can be very low. And there is where built-in amplifier comes in, because when you have reference voltage of 5V (like in Arduino), and 10 bit ADC, resolution is about 0.005V / LSB.
But when voltage drop on that resistor is lower than 0.25V, turning on internal amplifier gives you resolution of 0.00025V / LSB, what is very impressive value, allowing you to measure current in very accurate way.

But of course your way is very good, simple and accurate enough just to simply measure if cells are quite dead or maybe alive and doing some other things ;)
And please, don't be mad at me - I'm not trying to warn you, because what you've done is great. I'm just telling it might be better.
And of course even my calculations may go into trash bin, when someone buys some cheap, not accurate resistor, and allow it to heat-up, because this resistor may destroy all the calculations, when resistances doesn't match these in formulas ;)

thecheese429 (author)domints2016-08-18

Hi Domints, those are some good insights that never occurred to me! Did you ever build and code this device with your modifications? I would love to see your code if you did. I believe that the atmega328p (what I am using) has a comparator, and I wonder if I could make similar use of it.

tkanjo (author)2016-07-30


I made it... it isnt pretty but it works.. but i have an issue
i have 6 pack Li-ion battery in parallel and i put it on testing but strange things happen... capacity is quite high and when the code calculate elapsed time after 545 minutes it put a negative sign to next number 544, 545, -546, -545, -544
Anyone know how to solve this?

i did change the code, but i only added a i2c 16x2 lcd display, nothing more


thecheese429 (author)tkanjo2016-08-02

Hi, that sounds to me like a possible int rollover. If you want to run these tests over such a long period of time, you will probably need to change the time variable types to long or double.

The alternative would be to increase the load on the battery in question so that it will be discharged in a more reasonable time, perhaps with a bank of light bulbs in parallel and a much smaller shunt resistor.

tkanjo (author)thecheese4292016-08-08

Thanks for reply... but i have another issue..
downloaded your code and tried to compile it.. but i get this error what ever i tried to do:
'now' was not declared in this scope


ok i managed to get it working i changed the include file

#include <Time.h>


#include <TimeLib.h>

and it compiled without any issue... i noticed that my error happened when i updated the Arduino IDE software... so if anyone have the same issue my workaround will be helpfull

DIYPro0505 (author)2016-05-01

How can I find out how many amps my bulb pulls to measure the capacity of 18650's?

thecheese429 (author)DIYPro05052016-08-02

Hi, the shunt resistor in series with the bulb is used to measure that. Ohm's law says that the voltage across a resistor is equal to the value of the resistor multiplied with the current through it. Since we can measure the voltage across the resistor, all we have to do is divide that voltage by the value of the resistor to find the current through it.

SimoneR2 (author)2016-04-13

you should search for the datasheet of the components you found. I've made a little search for you and the result is that none of the component you found is suitable for the project. The first two components you found are schottky rectifier (similar to a diode) STPS20S100CT. And L7912 it's a negative voltage regulator (like the 78xx series but with negative voltage), in your case is a -12V regulator.
You might consider to buy one (to be large it will cost ~2€) or use a relay or transistor (like 2n3055) instead of a mosfet.

DIYPro0505 (author)SimoneR22016-04-14

Once again thank you for your response! Next question, I have a 10W 5Ohm resistor and a bulb with the resistance of 1 Ohm if Iput them in series can i use it or no?

SimoneR2 (author)DIYPro05052016-04-16

In italian i would say "sni": yes and/or no. I think you missed one important information: what is the battery voltage? From this you can easily calculate if the power for every single resistor is enough. I think you should change the code (but again i didn't read it carefully so i can't tell you where).
I have a question for you: do you know something about electronics? (just to understand how to help you as better as i can). ;)

DIYPro0505 (author)SimoneR22016-04-13

Thank you for yur response, I have another quick question: where else can I find the mosfets or the components for this project.

SimoneR2 (author)DIYPro05052016-04-14

It's very difficult to say. Onestly I didn't read the project with special care because i'm working on a similar one for agm batteries and i was here only to compare my capacity algorithm (programmed on a pic) and the creator's one.
Maybe in the power supply there could be the shunt resistor (sometimes is only a piece of wire) and i think that you could also find mosfet but withouth a picture i can't tell you where.
You may find N-channel mosfet in lot of other things, they're used as DC switch, to control motors and in some audio amplifier.

P.s.: i just saw your other comments, in my opinion you could go out and buy one (IRF540 works well and it's very common) instead of asking free samples.

NathanJ12 (author)2015-08-05

how can i redesign the circuit with a 11,1V battery? what shoud i change?