Lithium-Ion Battery Monitor Library for Arduino

A simple library to monitor battery voltage in small Arduino projects

Similar projects worth following
A simple library for monitoring battery voltage in Arduino projects. Utilizes the 1.1V internal reference of the ATmega328 to accurately monitor battery voltage and current.

The Arduino library can be downloaded from the github page here:

This project utilizes the ATmega328's internal 1.1V bandgap reference to allow for accurate measurements of external voltages. From these measurements the library is able to measure the external voltage, allowing users to choose what to do in undervoltage/overvoltage/overcurrent cases within their projects. External components are kept to an absolute minimum, requiring only the battery and a shunt resistor (which isn't needed if you don't care about current draw). It is worth noting that as the project stands right now for Arduino boards there is no way to prevent current from the USB programmer from going into the battery and causing problems. As a result, be sure to have the battery disconnected whenever programming. See the github page for some simple circuit guidelines for this library. This library is also capable of monitoring the battery while the Arduino is powered by a boost converter for those who need 5V instead of whatever the battery happens to be at

For anyone curious about what's happening behind the scenes the battery voltage is calculated by: 

where A terms represent the ADC value returned by reading the pin. 

The current is found using the equation: 

For anyone interested the full derivations are available in the logs of this project. 

This project is still ongoing, and awaiting parts for thorough testing. 

  • Progress and some actual testing

    Matthew James Bellafaire08/23/2019 at 00:26 0 comments

    This project has moved pretty fast overall, not sure how long I expected this project to take. Either way, I have been waiting on some OLED I2C displays to actually test this library properly and they finally arrived (I plugging into the arduino and using the serial monitor interferes with the results). I also tested the library using a boost converter circuit powered from the same battery that's being monitored so basically this is just an all around project update with some actual test footage. So lets talk about the code first, which doesn't change between either of the tests shown in this log: 

    #include <BatteryMonitor.h>
    #include <Wire.h>
    #include <Adafruit_GFX.h>
    #include <Adafruit_SSD1306.h>
    #define SCREEN_WIDTH 128 // OLED display width, in pixels
    #define SCREEN_HEIGHT 32 // OLED display height, in pixels
    Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
    #define batVoltagePin A0 //connect directly to positive terminal of the battery
    #define batCurrentPin A1 //connect to the other side of a shunt resistor going from the positive battery terminal to the rest of your circuit
    BatteryMonitor bat = BatteryMonitor(batVoltagePin, batCurrentPin);
    void loop() {
      display.setCursor(0, 0);
      display.println("Battery Monitor Test");
      display.print("Vbat = ");
    display.print("Ibat = ");
    display.print("Vop = ");

    I cut out the setup(), but it is important to note that the resistance for these tests was set to 1.42 ohms.... because that's the best I could cobble together from my salvaged parts box. So a better test will come when some proper .05 and .1 ohm resistors arrive from digikey. All this sketch really does is use the library functions getCurrentBatteryVoltage(), getBatteryCurrent(), and getCurrentOperatingVoltage() then print the return values to the display. 

    note the sketch prints battery input as Vbat, Battery Current as Ibat, and the operating voltage of the Arduino as Vop. 

    onto the simple battery monitor which was the original goal of this project it works! using the same schematic as is shown in the project image: 

    and the results of the test are shown in this video: 

    The voltage of the battery and the operating voltage appear to be reading pretty accurately but the current is somewhat off. I suspect this is due to the sense resistor I'm using, however, I currently don't own a multimeter that can measure resistances that low with any real accuracy. I measured 1.42 ohms by just doing the math after putting it across the power supply set to 1V and reading the current (its a 5w resistor it could take it). 

    Now onto the good stuff, the library can now officially monitor a battery while being powered by a boost converter. For this the schematic gets a little bit more messy but really its not that bad. I used some generic boost converter module that was laying around so there's really just a stand in for the boost converter in the schematic below: 

    Its worth noting that the AREF pin of the Arduino should always be connected to the highest voltage in your circuit provided that it's not outside the operating range of the board itself. Either way here's a nice little video of the library operating with the same program as above: 

    I was pretty sure that a boost converter could be used like this with the library but until now it hadn't been tried, either way all the codes there to allow someone to easily monitor battery voltage for whatever project they're doing. Even if you don't want to use your Arduino as a BMS it might be helpful in some cases to have a good estimate of the battery power remaining. Either...

    Read more »

  • Derivations for the voltage and current measurements (For anyone interested)

    Matthew James Bellafaire08/20/2019 at 02:24 0 comments

    Chances are this has been worked out before, I don't know, I didn't really look. But here is the process that was used to determine how to calculate the battery voltage and current. (its like circuits 101 all over again). 


    • Vbg = Voltage of internal bandgap reference on the AVR = 1.1V
    • Aref = ADC value returned by reading Vbg 
    • Vbat = Voltage of battery
    • Abat = ADC value returned by reading Vbat 
    • Vsense = Voltage of the negative side of the current sense resistor
    • Asense = ADC value returned by reading the negative side of the current sense resistor
    • Rsense = the value of the resistor used to sense current
    • Iread  = current read by the micro-controller

    First assume the ratio of the 1.1V reference to the battery voltage is equal to the ADC reading of the reference to the ADC reading of the battery voltage. (this is mostly true)

    Solve for Vbat and substitute 1.1V for Vbg since its constant. 

    And that's about it for the voltage calculation, onto current. First we find the voltage at the sense resistor using the same assumption as before that the ratio of the voltage is equal to the ratio of the ADC readings: 

    Solving for Vsense and substituting 1.1V for Vbg

    Using Ohm's law the current can be found by dividing the change in voltage by the resistance: 

    Now substituting in the above equations and simplifying yields Iread

    That's everything, since everything's now in terms of ADC values its fairly straight forward to simply get the values in software and calculate both the voltage of and current draw from the battery. All the units check out and a few sample calculations I ran seem to show that these equations work well enough (accuracy of the ADC throws current readings off by a few percentage points). If I overlooked something please let me know. 

  • massive update and improvements to the library

    Matthew James Bellafaire08/20/2019 at 01:30 0 comments

    Most of these changes will be applied to the description of the project. Just wanted to give credit to who pointed out that there was a much better way to get the battery voltage using an AVR. 

View all 3 project logs

Enjoy this project?



Gerben wrote 08/20/2019 at 13:55 point

Make sure the voltage across the shunt resistor will not exceed 0.5V or the voltage at A1 will exceed the maximum voltage of Vcc+0.5V.

You could add a high value resistor between the battery and A1. Then if the voltage drop acros the shunt gets to high, the clamping diodes of the A1 pin will keep the voltage at A1 in the safe range. The resistor will prevent to much current going through the clamping diodes.

Otherwise (most of) the current will bypass the shunt resistor and go though the clamping diode. I've actually done that a few times, not realizing that Vcc wasn't actually attached, and the whole project was powered from the ISP pins (MOSI, SCK), though the clamping diodes. So I found out the are quite robust little diodes (-;

  Are you sure? yes | no

Matthew James Bellafaire wrote 08/21/2019 at 01:43 point

yeah, that'd ruin your day, I kind of intended for the library to be used with a shunt resistor around 0.05 ohms where the voltage drop wouldn't be able to damage the AVR. Either way I added a note to the github page just to clear up resistor selection.

  Are you sure? yes | no

Gerben wrote 08/18/2019 at 19:13 point

You know, the AVR can measure it's internal 1.1V reference, in relation the AVCC. By measuring the 1.1V reference (which is constant), you get some ADC value that you can use to calculate the AVCC voltage.

(EDIT repost, as I accidentally deleted my comment.)

  Are you sure? yes | no

Matthew James Bellafaire wrote 08/18/2019 at 20:12 point

For some reason it won't let me reply to your most recent comment.

I have a pretty good idea generally of what's available on the AVR and how I can use it. I was having a conversation with one of my friends who was talking about a battery management system he was working on and how difficult it was for him to find parts to do undervoltage/overvoltage. That was really the inspiration for this I remembered that I could set the reference to 1.1v so I ran with that. 

It doesn't measure the constant voltage it measures the battery through the voltage divider then compares that with the 1.1V as a reference. Everything works well enough on it but it's not the best it could be at the moment. 

  Are you sure? yes | no


[this comment has been deleted]

Matthew James Bellafaire wrote 08/18/2019 at 18:35 point

I didn't catch that when I originally was looking through the datasheet to make this. Reading the 1.1V reference directly would allow the user to still read values from the ADC on a normal scale. Thanks for pointing that out, this simplifies everything massively and it's exactly the solution I wish I saw when writing this library originally. probably going to do some rewriting pretty soon. 

  Are you sure? yes | no

Gerben wrote 08/18/2019 at 19:13 point

Sorry about that ;-)

Looks like most of the library would stay the same, though.

It is kind of weird, measuring a constant voltage. When I first saw the method, it had me puzzled. I don't think I would have come up with it by just reading the datasheet.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates