Detecting Pico-W LiPo Shim Battery Charge Level

I’ve got a couple of Pimoroni’s excellent LiPo SHIM for Pico. When soldered to a Pico they allow a LiPo battery to be connected, or USB power, and if the latter then the battery is charged. There’s also a power button, which is a bonus.

I wanted to programattically determine charge level, which it appears is done by reading voltage which diminishes as battery level reduces, and found a seemingly ready-made script here.

Except it didn’t seem to work, even when I removed all of the code relating to the display and just logged to a file using this library.

Firstly, it didn’t detect if the device was connected to USB power. And secondly, it didn’t read voltage at all.

More searching and this post on the Pimoroni forum pointed to a solution. Apparently the WiFi on the Pico-W interferes with the voltage reader so some tweaking to Pin inputs/outputs needs to be made.

I had to set the output of the Pin to “high” on every voltage read and haven’t looked too deeply into whether there’s a better solution, because this Micropython works for my purposes:

from machine import ADC, Pin
import time
import logging                # the values could vary by battery size/manufacturer so you might need to adjust them

while True:
    spi_output = Pin(25, Pin.OUT)
    vsys = ADC(29)                      # reads the system input voltage
    charging = Pin("WL_GPIO2", Pin.IN)          # GP24 on the PIco, WL_GPIO2 on the Pico-W, tells us whether or not USB power is connected
    conversion_factor = 3 * 3.3 / 65535

    full_battery = 4.2                  # these are our reference voltages for a full/empty battery, in volts
    empty_battery = 2.8 
    # convert the raw ADC read into a voltage, and then a percentage
    voltage = vsys.read_u16() * conversion_factor
    percentage = 100 * ((voltage - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100.00
    if charging.value() == 1:         # if it's plugged into USB power..."Charging!")
    else:                             # if not, display the battery stats'{:.2f}'.format(voltage) + "v")'{:.0f}%'.format(percentage))


I haven’t calibrated it to the battery I’m using to work out whether 4.2 and 2.8 is the correct min/max range, but this is a start.