Detecting charge level of a LiPo Battery

I’ve received my Photons today and started a new project. A LiPo battery is connected to a Photon and and also a charging circuit. It expects 5V which I am supplying with a QI Charging receiver. That’s all great and works - I can run the photon off the lipo and also charge the lipo when the qi receiver is on the qi transmitter for charging.

To detect that the system is in charging mode, I put the 5V qi receiver input to the lipo charger through a 10K resistor and then connected it to D0, where I can simply check digitalRead(D0) for charging detection.

I am currently “breathing” - turning slowly on and off a neopixel ring to signal the charging. I know this is not exactly power-saving, so probably charging slower, but that’s OK.

I would like to detect the charge level of the LiPo to let the LEDs breathe faster or slower. Does anyone have a recommendation on how to do that? I assume that I can take the input voltage, connect to a resistor and then read the analog in value. But is there maybe an internal function already that let’s me check the input voltage on VIN?

Hi @hansamann

I suggest you do some research on battery fuel gauges since the voltage level of the battery does not give the full story for LiPo’s. Maxim and TI and several others make parts specifically for this application.


I second @bko’s advice. The key search term is “battery fuel gauge”.
You may have to think beyond simply bolting a fuel gauge onto the side of an existing charging solution.

1 Like

As @bko and @AndyW suggest, a fuel gauge is the way to go, it will give you an actual “level of charge”.

If like me you want something super quick just to give a Low Battery alarm you could use a voltage divider and read the voltage using a analog input. I wanted to measure the voltage of a 3S lipo pack and make sure its above 10.5v before running the motors. Have a read of this post

Its probably worth having a read to see how the resistors work to reduce the voltage to a safe level before connecting to an input, as your 10k resistor wont reduce the voltage but will just limit the current. although the pins on the photon are 5v tolerant there are some rules to prevent damage.
[1] FT = 5.0V tolerant pins. All pins except A3 and DAC are 5V tolerant (when not in analog mode). If used as a 5V input the pull-up/pull-down resistor must be disabled.

And a handy site for calculating resistor values for the divider… here

1 Like

Thx all for the replies. The battery level does not need to be very accurate, so I decided to go with what I roughly understand: a voltage divider.

rigth now I am using two 10k resistors across VIN/GND and read the input on A0. I am currently sending out events with the raw A0 readings and will later map the upper/lower levels to 0/100 using the map function.

Whats your lipo voltage?

So when I measure the voltage with a multimeter across the VIN/GND pins of the lipo, I get around 3.7+ for a fully charged one and it will go down to about 3.4 or less. Then the lipo circuit will shut off automatically to not damage the battery.

When I read the A0 I got values from around 2370 for fully charged to about 2100 for almost empty. I simply convert that:

   uint8_t batteryLevel()
    int raw = analogRead(A0);
    int lower = 2100; //raw A0 voltage reading
    int upper = 2370; //raw A0 voltage reading
    uint8_t percent =  map(raw, lower, upper, 0, 100);
    return percent;

I will run a few more cycles and adapt the lower/upper accordingly.

Many thx for the links to maxim fuel gauges, too. I am using a single cell lipo battert, 400maH. Even with a 12led neopixel that turns off and on in a 1s inerval, constant connection of the photon and events every minute or so it runs > 1 hour.

The fuel gauge I might play with for now is this

would that be suitable? If so does someone know how to best get started, e.g. is there a breakout board for this?

I believe that your percent can go over 100% and less than 0% by using the map() function. Probably won’t hurt anything, but just FYI.

May be a problem tho if percent is unsigned and it goes below 0.

Good observation:

uint8_t batteryLevel()
    int raw = analogRead(A0);
    const int lower = 2100; // low batt
    const int upper = 2370; // charged batt
    if (raw > upper) raw = upper;
    else if (raw < lower) raw = lower;
    uint8_t percent =  map(raw, lower, upper, 0, 100);
    return percent;

That looks good. But more than one way to skin a cat.
uint8_t percent = constrain(map(raw, lower, upper, 0, 100), 0, 100);
They should both do the trick. One with less lines of code, the other easier to read.
Thanks, Jack

Yeah sure we can whittle it down to one line for the ultimate in unreadability :smile: I would also prefer to constrain my inputs than the outputs to prevent wrapping. You also have to now be really careful in how all of these functions play with each other as far as input and output types go. More explicitly set types will be easier to debug if you have unexpected results down the road.

#define BATTERYLEVEL() (map(constrain(analogRead(A0), 2100, 2370), 2100, 2370, 0, 100))

Another solution for this, may be to just not declare percent (and the function ) as unsigned.

If a value can possibly/ever go below zero, it is not a good idea to declare it as unsigned.

I think you meant to say signed here. And unless you are testing for values below or above 0 percent, it won’t matter much. If you go to the extreme to test for errors post function call, then I believe this particular function is implemented incorrectly. You should trap your errors in the called function and keep the user from having to post process the output. If those errors were very specific though, like -1 for a battery value out of range low, or -2 for a battery value out of range high, then that would be more appropriate and a good use of a signed return value. You can code it however your heart pleases you :heart_eyes: but that’s my 2 cents.

We could, and someone probably will (and we are actually in progress of…), write a book about all of the different ways we could implement this batteryLevel() function :spark:

That’s great. Is this book just for LiPo, or other types of batteries ?
Will it cover how to charge/maintain the LiPo (3.8V vs 4.2V) ?

I may have misunderstood you. I thought you were writing a book, but looking back, I can see how I may have been confused on that point.

I guess I/we really don’t know how the OP intends to use the result of this function. I would generally use a “if less than”, or “if greater than”, but that’s just my thoughts. Maybe they just want to display the percentage. We don’t know.

You missed the “not” in that quote. Makes a difference.

True, and if not unsigned… then for those who know what’s correct it’s implied you should code it as signed… but better to explicitly say signed so those who are learning by reading our posts can learn :smile:

I am! We are!! :slight_smile: I’m trying to be subtle in saying we are kind of going on and on about this and are off topic now.


Thx all for the great comments. I of course discovered that I get negative and above-threshold values, so that is fixed.

I had a look at the maxim lipo chips:

The problem really is that they are tiny - is there a package available that I am not seeing and that I could prototype with easily?

You can get breakout boards like this one for the '043 part:

1 Like

I’m currently using an INA3221 board in my project. It provides 3 inputs and can monitor both current and voltage. I’m using it to measure the LIPO, solar panel and the load (in this case a Moteino w/several sensors) in an outdoor weather unit. Before that I used a INA219 board with only one input. Easy to code and very accurate.