Pico W not reading GPIO24

Hi, i’m trying to detect if my pico w is being powered by USB.

charging = Pin(24, Pin.IN)
charging.value()

charging .value() is always 0, I don’t understand!

It’s plugged into usb, has a lipo shim and pico display attached

Has anyone successfully detected usb power using micropython on pi pico w ?

Yep, puzzled me for a while though :)

On the Pico W, vbus sense is on one of the special internal function pins, not GP24 - try:

from machine import Pin

charging = Pin('WL_GPIO2', Pin.IN)
print (charging.value())

More details on these pins on page 8 of the Pico W datasheet: https://datasheets.raspberrypi.com/picow/pico-w-datasheet.pdf

fantastic!! thank you so much, I was searching everywhere for this!

So my script is working correctly now and picking up the charging status…brilliant

I’m also trying to read the voltage input when the pico w is not plugged in via usb.
In theory I should be able to use Pin29 however this shows a very weird voltage (0.04V should be 3-4v), however after some googling it appears that pulling pin25 high corrects this

Pin(25, mode=Pin.OUT, pull=Pin.PULL_DOWN).high()

That works, but now that I am using your fix for usb detection it has stopped working.

the complete script I am running should display the charging voltage and battery level, or a ‘charging!’ status when it is plugged in.

It’s basically the script you have put together here pimoroni-pico/battery_pico.py at main · pimoroni/pimoroni-pico · GitHub … but it doesn’t seem to work for a pico w

so currently the script I have is this

# This example shows how to read the voltage from a LiPo battery connected to a Raspberry Pi Pico via our Pico Lipo SHIM
# and uses this reading to calculate how much charge is left in the battery.
# It then displays the info on the screen of Pico Display.
# Remember to save this code as main.py on your Pico if you want it to run automatically!

from machine import ADC, Pin
import time

# change to DISPLAY_PICO_DISPLAY_2 for Pico Display 2.0
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY, rotate=0)

display.set_backlight(0.5)

Pin(25, mode=Pin.OUT, pull=Pin.PULL_DOWN).high()
Pin(29, Pin.IN)
vsys = ADC(29)# reads the system input voltage

charging = Pin('WL_GPIO2', Pin.IN)  # reading GP24 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.4                # the values could vary by battery size/manufacturer so you might need to adjust them

# Create some pen colours for drawing with
BLACK = display.create_pen(0, 0, 0)
GREY = display.create_pen(190, 190, 190)
GREEN = display.create_pen(0, 255, 0)
RED = display.create_pen(255, 0, 0)



while True:
    # convert the raw ADC read into a voltage, and then a percentage
    Pin(25, mode=Pin.OUT, pull=Pin.PULL_DOWN).high()
    voltage = vsys.read_u16() * conversion_factor
    percentage = 100 * ((voltage - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100.00

    # draw the battery outline
    display.set_pen(BLACK)
    display.clear()
    display.set_pen(GREY)
    display.rectangle(0, 0, 220, 135)
    display.rectangle(220, 40, 20, 55)
    display.set_pen(GREEN)
    display.rectangle(3, 3, 214, 129)

    # draw a green box for the battery level
    display.set_pen(GREEN)
    display.rectangle(5, 5, round(210 / 100 * percentage), 125)

    # add text
    display.set_pen(RED)
    if charging.value() == 1:         # if it's plugged into USB power...
        display.text("Charging!", 15, 55, 240, 4)
    else:                             # if not, display the battery stats
        display.text('{:.2f}'.format(voltage) + "v", 15, 10, 240, 5)
        display.text('{:.0f}%'.format(percentage), 15, 50, 240, 5)

    display.update()
    time.sleep(0.5)

I think you’re on the right lines - you don’t need to set the pin high in your while True: loop though, just doing that once at the beginning of your code where you set up the pins should be sufficient, like this:

# on a Pico W we need to pull GP25 high to be able to read vsys
# this is because this pin is shared with the wireless module's SPI interface

spi_output = Pin(25, Pin.OUT)
spi_output.value(True)

# set up the ADC that's connected to the system input voltage
vsys = ADC(29)

You also don’t need the Pin(29, Pin.IN) - we’re reading GP29 as an ADC pin, not as a standard IO pin.

I’ll try and and add a Pico W version of this example to add to Github when I get the chance - I’ve not used a Pico LiPo SHIM with a Pico W yet. They were unobtainium for a while after the Pico W launch!

very interesting…so I have updated the script now

If I start it with the usb plugged in, the charging works, including unplugging and plugging in, but the battery voltage is wrong
If I start it unplugged the battery voltage is correct for the first loop (0.5s) but is subsequently incorrect, and the charging detection also works…

my script is as follows

from machine import ADC, Pin
import time

# change to DISPLAY_PICO_DISPLAY_2 for Pico Display 2.0
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY, rotate=0)

display.set_backlight(0.5)

# on a Pico W we need to pull GP25 high to be able to read vsys
# this is because this pin is shared with the wireless module's SPI interface

spi_output = Pin(25, Pin.OUT)
spi_output.value(True)

vsys = ADC(29)# reads the system input voltage

charging = Pin('WL_GPIO2', Pin.IN)  # reading GP24 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.4                # the values could vary by battery size/manufacturer so you might need to adjust them

# Create some pen colours for drawing with
BLACK = display.create_pen(0, 0, 0)
GREY = display.create_pen(190, 190, 190)
GREEN = display.create_pen(0, 255, 0)
RED = display.create_pen(255, 0, 0)

while True:
    # 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

    # draw the battery outline
    display.set_pen(BLACK)
    display.clear()
    display.set_pen(GREY)
    display.rectangle(0, 0, 220, 135)
    display.rectangle(220, 40, 20, 55)
    display.set_pen(GREEN)
    display.rectangle(3, 3, 214, 129)

    # draw a green box for the battery level
    display.set_pen(GREEN)
    display.rectangle(5, 5, round(210 / 100 * percentage), 125)


    # add text
    display.set_pen(RED)
    if charging.value() == 1:         # if it's plugged into USB power...
        display.text("Charging!", 15, 55, 240, 4)
    else:
        # if not, display the battery stats
        display.text('{:.2f}'.format(voltage) + "v", 15, 10, 240, 5)
        display.text('{:.0f}%'.format(percentage), 15, 50, 240, 5)
        
    display.update()
    time.sleep(0.5)

Managed to find myself a Pico LiPo SHIM, woop!

Try this - uses a vsys reading function I found online that changes the pin state of GP29 before and after a read (and does some stuff with enabling/disabling network). Vsys reading and vbus sense both seem to be working OK - the voltage reading seems to jump around more than on a standard Pico though.

# This example shows how to read the voltage from a LiPo battery connected to a Raspberry Pi Pico via our Pico Lipo SHIM
# and uses this reading to calculate how much charge is left in the battery.
# It then displays the info on the screen of Pico Display.
# Remember to save this code as main.py on your Pico if you want it to run automatically!

from machine import ADC, Pin
import time
import network
# change to DISPLAY_PICO_DISPLAY_2 for Pico Display 2.0
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY


def get_vsys():
    # Pico W voltage read function by darconeous on reddit: 
    # https://www.reddit.com/r/raspberrypipico/comments/xalach/comment/ipigfzu/
    conversion_factor = 3 * 3.3 / 65535
    wlan = network.WLAN(network.STA_IF)
    wlan_active = wlan.active()

    try:
        # Don't use the WLAN chip for a moment.
        wlan.active(False)

        # Make sure pin 25 is high.
        Pin(25, mode=Pin.OUT, pull=Pin.PULL_DOWN).high()
        
        # Reconfigure pin 29 as an input.
        Pin(29, Pin.IN)
        
        vsys = ADC(29)
        return vsys.read_u16() * conversion_factor

    finally:
        # Restore the pin state and possibly reactivate WLAN
        Pin(29, Pin.ALT, pull=Pin.PULL_DOWN, alt=7)
        wlan.active(wlan_active)


display = PicoGraphics(display=DISPLAY_PICO_DISPLAY, rotate=0)

display.set_backlight(0.8)

charging = Pin('WL_GPIO2', Pin.IN)  # reading this pin tells us whether or not USB power is connected

full_battery = 4.2                  # these are our reference voltages for a full/empty battery, in volts
empty_battery = 2.8                 # the values could vary by battery size/manufacturer so you might need to adjust them

# Create some pen colours for drawing with
BLACK = display.create_pen(0, 0, 0)
GREY = display.create_pen(190, 190, 190)
GREEN = display.create_pen(0, 255, 0)
RED = display.create_pen(255, 0, 0)

while True:
    # convert the raw ADC read into a voltage, and then a percentage
    percentage = 100 * ((get_vsys() - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100.00
    
    # draw the battery outline
    display.set_pen(BLACK)
    display.clear()
    display.set_pen(GREY)
    display.rectangle(0, 0, 220, 135)
    display.rectangle(220, 40, 20, 55)
    display.set_pen(GREEN)
    display.rectangle(3, 3, 214, 129)

    # draw a green box for the battery level
    display.set_pen(GREEN)
    display.rectangle(5, 5, round(210 / 100 * percentage), 125)

    # add text
    display.set_pen(RED)
    if charging.value() == True:         # if it's plugged into USB power...
        display.text("Charging!", 15, 55, 240, 4)
    else:                             # if not, display the battery stats
        display.text('{:.2f}'.format(get_vsys()) + "v", 15, 10, 240, 5)
        display.text('{:.0f}%'.format(percentage), 15, 50, 240, 5)
    
    display.update()
    time.sleep(0.5)

weeeeeey congrats on finding a shim :-)

that works like a charm, thank you so much, absolutely fantastic

For anyone ending up here looking for how to read pins that are shared with the wifi chip like VSYS (GPIO29) and VBUS sense (WL_GPIO2), I recently opened an issue on github after failing to do this with the C/C++ SDK.
They pointed me to CYW43_THREAD_ENTER / EXIT macro’s which helped with an implementation that allows reading the VBUS/VSYS while using wifi.
Not sure if equivalent behavior is available via micropython but the discussion cleared up much of the not-so-well documented specific usage of these pins.

basic example here: pico-examples/pico_w/wifi_vsys at issue/324 · pspeybro/pico-examples · GitHub

2 Likes