Battery Gauge missing Badger2040(W) & some noob questsions

Hi all at Pimoroni!

Uhmmmm… I need a little help with the battery gauge, which was present in old badger_os.py and now all changed a bit with new versions around.

Where these ADC-objects removed from the Badger 2040 AND the 2040W lately?

  • ADC(badger2040.PIN_BATTERY)
  • ADC(badger2040.PIN_1V2_REF)
  • Pin(badger2040.PIN_VREF_POWER)

Is that correct?
Will they be added soon again?

There had been some issues with the readout on the Badger2040 and moved around pins on the 2040W (seen here)? Is that the reason, there is no working battery gauge for the Badger2040?

I am using badger2040-v0.0.4-micropython.uf2 on a Badger2040 and (and the correspondant “W-” variant on a Badger2040W).

This example for a battery-gauge that won’t work work, I found here.

How can I detect for which firmware-version the examples are being written?
I have to admit, I am not very familiar also with both (Micro)Python nor GitHub, so please explain it to a noob, thanks!

The Hitchhiker

A PICO W requires a different procedure to measure battery (vsys) voltage from how it was done on a PICO (RP2040). Pins got swapped around when the WIFI was added.
What worked on a Badger, won’t work on a Badger (Pico W onboard) 2040 W.

This is code I ran on a Pico W with a Display Pack attached.

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)
1 Like

Thanks for sharing your trasury with us!
Aye, that’ss more or less I figured out, too.The current state as I see it is:
badger_os v0.0.3 and badger_os v0.0.4 seem have lost the "PIN"s to measure Vbat on both badger2040 and badger2040W, hasn’t it?

Since I’m having the e-ink display I will have to modify your code to test it. Just for me as a beginner:
You wrote yourself a function to read out the voltage on a pico W. Would this code work on a badger2040 with badger_os v0-0-3/v0-0-4 installed?

Thanks for all the fish!

The Hitchhiker

I don’t own a Badger, so no (easy) way to see what’s going on with its image.

On my Tufty I press the B button to swap the Date Time display in my code with Battery State. I just display the Voltage and estimated % life left, no graphics. Release the button and it switches back to date time. It uses the old Pico code.

What I posted above was a test setup using spare bits & bytes. Pico W ,Pico Display Pack and a Pico Omnibus. It was just to have some verified working code to tinker with for any Pico W onboard devices I might have. I’m used to using Pico Graphics so I did a GUI version. Actually just modified Pimoroni code posted on the forum / github.
The from machine import ADC, Pin I would think, will work with any of the Pimoroni custom uf2 images? As far as I know, thats all you need to make the rest of it work.

The wiring should all be there, your just measuring the voltage on vsys. The complication is the ADC used to do it. On the Pico W that pin / ADC is also used for the WIFI. It ends up being, turn Wifi off, measure battery, turn Wifi back on.

The PICO W has the same pinout as a Pico. It’s what pin “on the RP2040” that goes where that complicates things.
Raspberry Pi Pico GPIO Pinout

I guessed its a Tuffy, by the color settings, aye.

Aha, so the badger_os-Image is “a volatile thingy”?
Or do I still mix the pico and the badger_os code?

ADC exists in the custom uf2, too. Let’s see, if I get this working. I will first try it out on the badger2040 and see, if this works.

Thanks for “jumpstart” in this topic!

The Hitchhiker

I use a lot of color LCD’s in my projects. The SPI Breakouts and the built ins when it has one. I like having a Green is good, Yellow not so good, Red is bad, at a glance indication. ;)

A lot of the Pimoroni uf2’s, as far as I know, have commonality in what’s included in them.

There likely isn’t any e-ink in the basic Pico or Pico W uf2, but they will i2c, SPI, ADC, Uart etc, and the Pimoroni Button function etc. How they get called up may vary a bit though? The Badger uf2 likely has all of the above rolled into it. Pins / pads brought out on the PCB will give you a hint at what’s there for tinkering with. Looking at the function reference will help too.

badger2040/docs/reference.md at main · pimoroni/badger2040 (github.com)

Tech Support can likely give you some detailed info.
Contact Us for Raspberry Pi Technical Support - Pimoroni

1 Like

Yeah, my first try somehow works… I think I solved the battery bar for the Badger2040 (non-W) today.

On a Badger2040 with 2x CR2032 button-cells backup battery the voltage sinks below 5V when drawing 25mA from those button cells, but that’s ok.
For redrawing a “laticed e-Ink-screen” once in a while or switching a to a picture and back to the badge, thats fine. Thank you for that code! And this link!

Wouldn’t do that on a Badger2040 with a picoW/ RP2040 aboard, which has the WiFi drawing a bit more, I guess. The BadgerW2040 will get its “keelhauling” tomorrow… I have to decide to attach a Lipo, but I don’t lnow yet, how to attach the “Lipo-Amigo”-Shim, so that it charges automatically the Lipo, when the Pico is connected via micro-USB (another story for tomorrow…).

The code:

I have hidden it in the launcher.py and sometimes the vref of 3.3 bounces a bit and the values are not accurate, I can live with that. The battery bar is similar like the mem-bar in an own function :

def draw_battery_usage(x):

    # Pico W voltage read function by darconeous on reddit: 
    # https://www.reddit.com/r/raspberrypipico/comments/xalach/comment/ipigfzu/
    # in reference of https://pico.pinout.xyz/ and https://picow.pinout.xyz/
    # the pins and ports are transfered, to make it work on a badger2040 non-W
         
    # these are our reference voltages for a full/empty battery, in volts 
    # the values could vary by battery size/manufacturer so you might need to adjust them
    # full/empty-pairs for some batteries:
    # lipo: 4.2 / 2.8
    # 2xAA/2xAAA alkaline: 3.2 / 2.4
    # 2xCR20332: 6.2 / 4.0
    # 1xCR2032:  3.2 / 2.1
    full_battery = 6.0
    empty_battery = 4.0
    
    conversion_factor = 60 * 3.3 / 65535

    # reading pin24 on a pico and 'WL_GPIO2' on a picoW tells us whether or not USB power is connected (VBUS Sense)
    charging = Pin(24, Pin.IN)  
        
    # Reconfigure pin 29 as an input. (Read VSYS/3 through resistor divider and FET Q1)
    Pin(29, Pin.IN)
    
    vsys = ADC(29)
    val_vsys = vsys.read_u16() * conversion_factor

    # convert the raw ADC read into a voltage, and then a percentage
    b_level = 100 * ( ( val_vsys - empty_battery) / (full_battery - empty_battery) )
    if b_level > 100:
        b_level = 100.00

    display.set_pen(15)
    display.image(
        bytearray(
            (
                0b110011,
                0b001100,
                0b011110,
                0b011110,
                0b010010,
                0b010010,
                0b010010,
                0b011110,
                0b000001,
            )
        ),
        6,
        9,
        x,
        3,
    )
    # assemble horizontal bar-graph beginning at position x+8 (bc. width of 6px the battery symbol)
    # outer white box
    display.rectangle(x + 8, 3, 80, 10)
    display.set_pen(0)
    # inner black box
    display.rectangle(x + 9, 4, 78, 8)
    # white bar according to percentage
    display.set_pen(15)
    #print(f"b_level: {b_level}")

    # if it's not plugged into USB power...
    if charging.value() == False:
        # if charging is false, display the battery status:
        # bar starts at coordinates x+10,5 and 6px high max length 76px (accordingly to percentage)
        display.rectangle(x + 10, 5, int(76 / 100.0 * b_level), 6) 
        display.text("{:.2f}%".format(b_level), x + 91, 4, WIDTH, 1.0)
    else:
        # fake full power on USB when "charging" is true
        display.rectangle(x + 10, 5, int(76 / 100.0 * 100 ), 6) 
        display.text("USB", x + 91, 4, WIDTH, 1.0)

Arrrr, I hacked it for my purpose. It’s a gift for a friend…
Ahoi!

The Hitchhiker


A lot, and I mean a lot, of my code is modified borrowed code.
Why reinvent the wheel? ;)

Don’t forget to tweak the full_battery = and the empty_battery = values for the batteries your using. If you don’t the % left will be off.
What I posted above is for a lipo battery. On my tufty I have 3 AAA for 4.5V.
The spec for VSYS: is 1.8 to 5.5V. Above 5.5 could cause damage. Nothing bad happens below 1.8, other than the RP2040 will glitch or just stop working.

1 Like

Well, it didn’t run on a badger2040(non-w), so I had to bend the rims to make that wheel going, or did I miss something? I mean, this was the starting point, that nothing worked for that OLD Badger w/o WiFi anymore and only code flying around for the 2040W. Maybe I didn’t pointed out this enough. My bad?

Would have love to just copy and paste it. 🫣

So >5.5V does harm? Only for the 2040W valid?
The 2040 does not have a stamped „max 5.5V on it. On the other hand: the voltage breaks in on a CR2040 with 25mA… so this settles in some kind „automatically“.

You probably saw the remarks I did in the code for the voltages, didn’t you? Yeah, the calibration is a bit „off“ too for the 2040(non-W). I had to tinker around a bit and I‘m sure, it’s not accurate, if you take another cell type. I crosschecked with a 4.2V almost fully charged LiPo to reference the voltage against the LiPo-boundaries given in the code.

I could cite vogon poetry in the code comments instead, if all fails?

Don’t let the fish bite your toes!

The Hitchhiker

The code you use for a Pico (to read vsys) won’t work on a Pico W, and vice versa. Near as I can tell anyway.

I would have thought, that any code already out there for the original Badger should still work?
Assuming your flashing the Badger 2040 with the non W image?

pimoroni-badger2040-v0.0.4-micropython-with-badger-os.uf2
or
pimoroni-badger2040-v0.0.4-micropython.uf2

VSYS is the main system input voltage, which can vary in the allowed range 1.8V to 5.5V, and is used by the on-board SMPS to generate the 3.3V for the RP2040 and its GPIO.

Raspberry Pi Pico Datasheet

Kudos for the Pinouts with this magic “advanced” check-box for the Pico and PicoW, @gadgetoid!
Buy Gadgetoid a Coffee

1 Like

Phil also has one for the Raspberry Pi.
Raspberry Pi GPIO Pinout

1 Like

I see his name eveywhere now in the net bound to Raspi/MicroPython topics!
^^

Me again!

Dang, I don’t get this code working on a 2040W… o.O
Is the Badger2040W different from the Tuffy???
puzzled

see what I did and what I get:

charging = Pin('WL_GPIO2', Pin.IN)
(...)
MPY: soft reboot
Traceback (most recent call last):
  File "<stdin>", line 294, in <module>
  File "<stdin>", line 269, in button
  File "<stdin>", line 225, in render
  File "<stdin>", line 127, in draw_battery_usage
ValueError: unknown named pin "WL_GPIO2"

The idea was, to write one function for the launcher.py for the Badger2040/Badger2040W and have a “battery bar”, similar to the “memory bar”.
Who stole the ‘WL_GPIO2’???
Please bring it back! (Or call me stupid and tell me, what I did possibly wrong…)
o.O

The Hitchhiker

the whole code

ef draw_battery_usage(x):

# Pico W voltage read function by darconeous on reddit: 
#def draw_battery_usage(x):

# Pico W voltage read function by darconeous on reddit: 
# https://www.reddit.com/r/raspberrypipico/comments/xalach/comment/ipigfzu/
# in reference of https://pico.pinout.xyz/ and https://picow.pinout.xyz/
# the pins and ports are transfered, to make it work on a badger2040 non-W
     
# these are our reference voltages for a full/empty battery, in volts 
# the values could vary by battery size/manufacturer so you might need to adjust them
# full/empty-pairs for some batteries:
# lipo: 4.2 / 2.8
# 2xAA/2xAAA alkaline: 3.2 / 2.4
# 2xCR20332: 6.0 / 4.0
# 1xCR2440:  3.0 / 2.0 experimental! 
full_battery = 4.2 # lowered to 5.0V for CR2032, since pico draws 25mA
empty_battery = 2.8 # lowered to 3.5V for CR2032, since pico draws 25mA

con_fac_2040w = 3 * 3.3 / 2**16  # for 2040W internal 3.3V as referece
con_fac_2040 = 60 * 3.3 / 2**16  # for 2040 its all a bit different... 

if badger2040.is_wireless() == True:
    
    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, pull=None)

        val_vsys = ADC(29).read_u16() * con_fac_2040w
                
    finally:
        # reading 'WL_GPIO2' on a picoW tells us whether or not USB power is connected (VBUS Sense)
        charging = Pin('WL_GPIO2', Pin.IN)
        # Why the heck this is all over the forums and no one has a problem with it???
        # since 'WL_GPIO2' seems not to work anymore on a 2040W

        # Restore the pin state and possibly reactivate WLAN
        Pin(25, Pin.OUT, value=0, pull=Pin.PULL_DOWN)
        Pin(29, Pin.ALT, pull=Pin.PULL_DOWN, alt=7)
        wlan.active(wlan_active)
       
else:
    # reading pin24 on a pico tells us whether or not USB power is connected (VBUS Sense)
    charging = Pin(24, Pin.IN)  

    # Configure pin 29 as an input. (Read VSYS/3 through resistor divider and FET Q1)
    Pin(25, mode=Pin.OUT, pull=Pin.PULL_DOWN).high()
    Pin(29, Pin.IN, pull=None)
    val_vsys = ADC(29).read_u16() * con_fac_2040

    
# convert the val_sys (raw ADC read) into a voltage, and then a percentage
b_level = 100 * ( ( val_vsys - empty_battery) / (full_battery - empty_battery) )
if b_level > 100:
    b_level = 100.00

display.set_pen(15)
display.image(
    bytearray(
        (
            0b110011,
            0b001100,
            0b011110,
            0b011110,
            0b010010,
            0b010010,
            0b010010,
            0b011110,
            0b000001,
        )
    ),
    6,
    9,
    x,
    3,
)
# assemble horizontal bar-graph beginning at position x+8 (bc. width of 6px the battery symbol)
# outer white box
display.rectangle(x + 8, 3, 80, 10)
display.set_pen(0)
# inner black box
display.rectangle(x + 9, 4, 78, 8)
# white bar according to percentage
display.set_pen(15)
#print(f"b_level: {b_level}")

# reading 'WL_GPIO2' on a picoW tells us whether or not USB power is connected (VBUS Sense)

# if it's not plugged into USB power...
if charging.value() == False:
    # if charging is false, display the battery status:
    # bar starts at coordinates x+10,5 and 6px high max length 76px (accordingly to percentage)
    display.rectangle(x + 10, 5, int(76 / 100.0 * b_level), 6) 
    display.text("{:.2f}%".format(b_level), x + 91, 4, WIDTH, 1.0)
else:
    # fake full power on USB when "charging" is true
    display.rectangle(x + 10, 5, int(76 / 100.0 * 100 ), 6) 
    display.text("USB", x + 91, 4, WIDTH, 1.0)

Where did you get that reference from?

EDIT: Never mind, I just saw your other post, in that other thread.
I don’t have an answer right now. Will have a look see though.

Second EDIT: I see that reference in my code, now that I have looked over what I posted again. Some time today I’m going to put something together and rerun it to see what happens, and if I get any errors with my hardware. I don’t have a Badger, or any e-ink display though.

1 Like

Thanks for caring!
It‘s not urgent, the battery sensing works somehow…
Ok, for button cells a tad better than „oops, they are empty“) due to 25mA drawing of the pico brings the cell voltage really low.

But I‘m happy to get a notice, somehow, in case the WL_GPIOx work again. Then I replace the „stub“ I put in this place for the real code.

I‘m just „growing into this all“ by doing and it’s great, how much feedback in a really short time some people here give. I throw a coin for you, if you tell me where… (Ko-FI, patreon…)

The following works (as near as I can tell anyway) on my Pico W Explorer.
It has a battery connector on the backside, and a Pico W onboard.
I flashed it with the Pimoroni 1.21.0 Pico W uf2 file.
It’s displaying the battery V and %, and shows “USB POWER” when powered via USB.

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_W_EXPLORER


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_W_EXPLORER, 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("USB POWER", 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)
1 Like

@gadgetoid Phil Howard works for Pimoroni. He writes a lot of this code, installers, custom uf2 files etc.
@hel might be able to help sort this out.

I’m thinking your issue is something to do with the Badger W uf2?

1 Like

Yes, definitely. The Badger2040W uses a Pico W (which includes both the RP2040 and a WLAN-chip), whereas the Tufty/Badger-without-W use a bare RP2040. So WL_GPIO2 is not available on the Tufty (nor on the Badger without W), it is a pin on the WLAN-chip. Just turn the BadgerW, Badger and Tufty around and compare the chips.

If you want to have a single program for multiple devices, you have to add an abstraction layer that does all the translation of the hardware-differences.

1 Like

Just get things sorted: the Badger2040 works fine. VBUS ist detected via Pin 24.

It’s the Badger2040W, which has a pico-W aboard, that doesn’t recognize the pin WL_GPIO anymore.

Maybe this wasn’t clear enough pointed out by me.

As you see in the code, I wrote a function to separate both. The odd thing is, another pico W loaded with its micropython fw does perfectly recognize the pin WL_GPIO. Just the Badger2040W with the current v0.0.0.4 Badger fw won’t work.

So maybe the problem is in the Badger-fw v0.0.4?
How can I verify this?

The (H)itchhiker