Presto – Checking Battery Voltage During Operation

Hi all.

When using the Presto with a LiPo battery, four NiMH rechargeable batteries, or three alkaline batteries, it may be necessary to monitor voltage drops. This is particularly important during operations such as write access to an SD card.

In the Raspberry Pi Pico series (including W and 2W models), a voltage divider reduces VSYS to 1/3 of its value, allowing it to be monitored through GPIO29 (ADC3).

However, after reviewing the Presto circuit diagram (https://cdn.shopify.com/s/files/1/0174/1800/files/pico_presto_schematic.pdf?v=1734528559), it seems that no such voltage divider circuit exists, and all ADC ports are already in use.

Since the Presto is equipped with a JST-PH 2.0 connector for battery input, I would like to make use of it for battery-powered operation. However, the lack of a built-in way to monitor the battery voltage is disappointing.

I am considering an alternative solution: using external resistors to create a voltage divider circuit and connecting the divided voltage to the PIEZO-AUDIO port. This way, the port could serve both as audio output and for voltage monitoring.

That said, is there a better solution for monitoring battery voltage on the Presto? Any advice would be greatly appreciated.

There are I2C battery fuel gauges available and they are cheap and really simple to use. But they only support single cell LiPos.

Thank you for your comment.

For lithium-ion batteries, I agree that an I2C battery fuel gauge could be a good solution. However, for cases where NiMH or alkaline batteries are used, monitoring the voltage with a general-purpose I2C ADC might be more suitable.

For example, something like this could work:

That said, using it just for battery voltage monitoring does feel a bit excessive, doesn’t it? 😊

Oh yes, 16-Bit and 4 channels.

Do you know about this product: IO Expander Breakout? It is very flexible since it does not only provide additional ADCs, but also general purpose IOs. Could be something useful in addition to the monitoring. And is only half the price of the high-end ADS1115

This seems like the perfect device for the job!

Thank you for the information. I’ll definitely add it to my cart the next time I shop at Pimoroni.

I believe the Presto is still in its beta phase, so I’m hoping that future design updates might include features like an onboard IO Expander, a battery voltage monitoring system, or even repositioning the USB-C connector.

Thanks again for your helpful comment!

1 Like

I’m interested in this IO Expander Breakout. Does it need the Int pin to be controlled as Presto I2C only has SDA and SCL via the 3.3V Qw/ST socket?

I have used this in the past to monitor a battery pack on a small outdoor weather station ( Adafruit MAX17048 LiPoly / LiIon Fuel Gauge and Battery Monitor). It is available from Pimoroni.

1 Like

As far as I can tell: the interrupt-pin is optional.

Ordered one. I’ll report how I get on with it.

1 Like

I’ve got the IO Expander and setup was easy. The examples just need the SDA and SCL pin numbers changed.

Buttons, potentiometers and LEDs are straightforward but I’m having trouble with PWM.

See the topic here:
PRESTO with IO Expander - PWM and ADC advice - Support - Pimoroni Buccaneers

All sorted now. This board is easy to use and addresses the lack of available IO and ADC pins on the PRESTO. A very useful PRESTO add-on.

Here is a demo with Potentiometers, voltages, PWM, LEDs, simple graphics and basic digital IO. All very straight forward.

# IO Expander Demo on Presto
# Tony Goodhew 16th February 2025
# Requirements: IO expander on Qw/ST port
# Button to GND on Pin 2
# 10K potentiometer wiper on 10, + 3.3v and GND
# LED with 330 ohm resistor on pin 6

import time
from pimoroni_i2c import PimoroniI2C
from breakout_ioexpander import BreakoutIOExpander
from presto import Presto
from picovector import ANTIALIAS_FAST, PicoVector, Polygon, Transform
# Setup for the Presto display
presto = Presto(full_res=True)
display = presto.display

# Pico Vector setup section
vector = PicoVector(display)
vector.set_antialiasing(ANTIALIAS_FAST)
t = Transform()

vector.set_font("Roboto-Medium.af", 54)
vector.set_font_letter_spacing(100)
vector.set_font_word_spacing(100)
vector.set_transform(t)
vector.set_font_size(20)

# Setup IO Expander
PINS_PRESTO  = {"sda": 40, "scl": 41}
i2c = PimoroniI2C(**PINS_PRESTO)
ioe = BreakoutIOExpander(i2c, address=0x18)

def clean(): # Clear the screen to Black
    display.set_pen(BLACK)
    display.clear()

# Create some colours
BLUE = display.create_pen(20,0,255)
WHITE = display.create_pen(255, 255, 255)
RED = display.create_pen(255,0,0)
ORANGE = display.create_pen(245, 165, 4)
GREEN = display.create_pen(0,255,0)
PINK = display.create_pen(250, 125, 180)
CYAN = display.create_pen(0,255,255)
MAGENTA = display.create_pen(255,0,255)
BLACK = display.create_pen(0, 0, 0)
YELLOW = display.create_pen(255, 255, 0)

clean()
display.set_pen(RED)
vector.set_font_size(70)
vector.text("IO Expander",40,120)
display.set_pen(BLUE)
vector.set_font_size(20)
vector.text("Tony Goodhew, Leicester UK",120,450)
presto.update()
time.sleep(1)

#  10K Potentiometer - ADC input
ioe_adc_pin = 10
ioe.set_mode(ioe_adc_pin, BreakoutIOExpander.PIN_ADC)

for count in range(60):
    clean()
    display.set_pen(GREEN)
    vector.set_font_size(35)
    vector.text("Turn the Potentiometer",10,50)
    pot = ioe.input(ioe_adc_pin) // 4 # 10 bit resolution
    h = int(pot/2.5)
    display.set_pen(YELLOW)
    vector.set_font_size(40)
    vector.text("Pot Value: " + str(int(pot)),10,150)
    display.set_pen(ORANGE)
    display.rectangle(350,430-h,60,h)
    display.set_pen(BLUE)
    display.rectangle(335,431,90,4)
    voltage = round(ioe.input_as_voltage(ioe_adc_pin),2)
    vector.text("Volts: " + str(voltage),10,200)
    presto.update()
    time.sleep(0.2)

clean()
display.set_pen(BLUE)
vector.text("PWM on LED",10,50)
display.set_pen(YELLOW)
vector.text("Press button to start",10,150)
presto.update()

ioe_button_pin = 2

ioe.set_mode(ioe_button_pin, BreakoutIOExpander.PIN_IN_PU)

ioe_pwm_pin = 6
button_state = ioe.input(ioe_button_pin)
while button_state == 1:
    button_state = ioe.input(ioe_button_pin)

# Settings to produce a 50Hz output from the 24MHz clock.
# 24,000,000 Hz / 8 = 3,000,000 Hz
# 3,000,000 Hz / 60,000 Period = 50 Hz
divider = 8
period = 60000 

ioe.set_pwm_period(period)
ioe.set_pwm_control(divider)

ioe.set_mode(ioe_pwm_pin, BreakoutIOExpander.PIN_PWM)
clean()
display.set_pen(BLUE)
vector.text("PWM on LED",10,50)
display.set_pen(YELLOW)
presto.update()

time.sleep(0.3)

for count in range(2):
    # Brightening
    for duty_cycle in range(0, 65536,2048):
        clean()
        display.set_pen(BLUE)
#        display.text("PWM on LED",10,50,400,4)
        vector.text("PWM on LED",10,50)
        display.set_pen(YELLOW)
        vector.text("Duty cycle: "+ str(duty_cycle),50,200)
        ioe.output(ioe_pwm_pin, duty_cycle)
        presto.update()
    time.sleep(0.5)
    
    # Dimming
    for duty_cycle in range(65535, -1,-2048):
        clean()
        display.set_pen(BLUE)
        vector.text("PWM on LED",10,50)
        display.set_pen(YELLOW)
        vector.text("Duty cycle: "+ str(duty_cycle),50,200)
        ioe.output(ioe_pwm_pin, duty_cycle)
        presto.update()
    
    clean()
    display.set_pen(BLUE)
    vector.text("PWM on LED",10,50)
    ioe.output(ioe_pwm_pin, 0) # LED off
    display.set_pen(YELLOW)
    vector.text("Duty cycle: "+ str(0),50,200)
    presto.update()
    time.sleep(0.6)
    
ioe_LED_pin = 6 # LED now digital on/off

ioe.set_mode(ioe_LED_pin, BreakoutIOExpander.PIN_OUT)

clean()
display.set_pen(GREEN)
vector.text("Basic LED Flashing",10,50)
presto.update()
for i in range(10):
    ioe.output(ioe_LED_pin, 1)
    time.sleep(0.2)
    ioe.output(ioe_LED_pin, 0)
    time.sleep(0.2)

clean()
display.set_pen(GREEN)
vector.text("Button Control of LED",10,50)
display.set_pen(YELLOW)
vector.text("Press button ON/OFF",10,150)
presto.update()
ioe.output(ioe_LED_pin, 0) # LED OFF
future = time.time() + 8 # 8 seconds
while time.time() < future:
    button_state = not(ioe.input(ioe_button_pin)) # True if pressed
    ioe.output(ioe_LED_pin, button_state)
    colour = RED
    if button_state == 0:
        colour = BLACK
    display.set_pen(colour)
    display.circle(240,300,50)
    presto.update()
    
ioe.output(ioe_LED_pin, 0) # LED OFF    

clean()
presto.update()

I’ve updated the code above to use a vector font and inverted the button press action so that the LED lights up when the button is pressed - more logical?

Where do you put your sensor stick? A bit of Blu Tack holds it neatly on the front with light sensor pointing into the room.

Have fun!

1 Like

My package from Pimoroni has arrived! It includes the Picade Max, an additional Presto, and various other small parts. The I/O Expander you recommended is also in there.

I’ll give it a try - thanks again!

Some order! You will be busy. Enjoy.

The IO Expander, Qw/STpad and Rotary Encoder all use the same communication chip (Nuvoton MS51 microcontroller) and there are a couple of interrupt instructions. Has anyone worked out how to use them?

From: pimoroni-pico/libraries/breakout_encoder/breakout_encoder.cpp at main · pimoroni/pimoroni-pico · GitHub

  bool BreakoutEncoder::get_interrupt_flag() {
    return ioe.get_interrupt_flag();
  }

  void BreakoutEncoder::clear_interrupt_flag() {
    ioe.clear_interrupt_flag();
  }

An Micropython example would be useful.

Interrupts are complicated and you have to make sure you don’t mess up things. Have you read the basic documentation? See Writing interrupt handlers — MicroPython latest documentation

What you basically do is set some flag in your ISR (interrupt service routine) and in your main loop you poll this flag and take actions. So interrupts are not that useful in a MicroPython context, because you could basically poll the state of the device directly. But sometime it helps to use ISRs, especially if you have many things to poll.

Yes I can do interrupts on native Pico pins.

This is slightly different with the interrupt coming from an I2C device via a FLAG, possibly setting one of the unused digital pins to HIGH???

Having had a further think I now see how it works and will have a go at using the method in a future project.