Measure and estimate battery percentage

Posted on
• I made a quick hack to measure the battery for a project where I'm using the Espruino Pico running off of 3 AA batteries. It's an e-paper display that updates once a day, so it will run off of the batteries for a very long time, but I had no idea how long. It turns out measuring the battery charge isn't too difficult. Here is how I implemented it.

I can't connect the battery directly to the ADC, since the three AA batteries start off at about 4.5 volts, much higher than the 3.3v that is the maximum the ADC can measure. The ADC will report 1.0 for 3.3v and 0.0 for 0v, with 12 bits of resolution, so I needed to move the battery voltage into that range. A voltage divider is a simple circuit that can do that. The simplest voltage divider is just two equally large resistors in series, where the point between them will be half the voltage across both of them. `4.5 / 2 = 2.25` which the ADC will report as `2.25 / 3.3 = 0.6818` is the highest battery measurement, while `0.5` is the lowest (`3.3 / 2 / 3.3 = 0.5`). This gives a range of 0.1818, which is about 8 bits of ADC resolution. But instead of using two identical resistors I used one of 560k and one of 270k:

`````` o BAT_IN
|
[ ] 270k
|
|
[ ] 560k
|
o GND
``````

This puts the ADC at roughly 2/3 the battery voltage. Given that the max the ADC can measure is 3.3v then the maximum battery voltage that can be measured is 4.95v, but it's unlikely to go that high. I measured a fresh battery at 1.6v, so it might go as high as 4.8v, but that was under no load. 4.5v will give an ADC measurement of 0.9090 and 3.3v will give 0.666, so we have a range of 0.2424, a bit more than before but still about 8 bits of ADC resolution.

This setup drains about 6uA, so it does affect the battery life quite a bit given that the sleeping Espruino pulls 25uA. Higher resistor values could reduce the current drain.

The code for measuring the battery is very simple and not at all accurate. It assumes a linear discharge curve between 1.65v and 1.1v, which isn't correct for AA batteries. I'm planning to log the recorded values and plot them to produce a more accurate discharge curve, then I can make a better estimation function from that. But since this is a project that is designed to run for as long as possible on battery power it will take a few months to log this data.

``````// returns a number between 0 and 100
function getBattery(){
let avg = 0;
for(let i=0; i<10; i++){
}
return Math.round(avg);
}
``````

The code above makes 10 measurements and takes the average of them. I multiply out the value right away to delay a bit between measurements. This code does not clamp the values, so it might return values higher than 100% and if no battery is connected it will return -200.

• Hi - that's great - thanks for posting up!

As you say 6uA is not such a big deal, but if you did want to reduce it further, as far as I can tell the ADC pin on the Espruino Pico is 5v tolerant (even if it won't measure above 3.3v), so you could attach the GND part of the potential divider to a GPIO, and then let that GPIO float normally and pull it to GND when measuring.

Also putting a small capacitor between A5 and GND can help with ADC measurement accuracy if you need it (they get less accurate when they're measuring a high resistance).

• Hmm, connecting to a gpio was a good idea, didn't think of that. And about that small capacitor, by small you mean something around 1uF? Or 1nF?

• And about that small capacitor, by small you mean something around 1uF? Or 1nF?

I'd say 0.1uF is a pretty safe bet. 1nF might be a bit small, and I'm sure 1uF would be fine but it's actually quite large (it'd take a while to charge up after changing the GPIO).