Optimize battery use for the Raspberry Pi Pico

Power Saver

© Lead Image © Julia Burlachenko, 123RF.com

© Lead Image © Julia Burlachenko, 123RF.com

Article from Issue 265/2022
Author(s):

The Raspberry Pi Pico's high-performance chip is trimmed for I/O and does not try to save power. However, a few tricks in battery mode can keep it running longer.

A large number of pins, variable voltage input, and good community support are what make the Raspberry Pi Pico a popular single-board computer (SBC) for newcomers and professionals alike. Just connect it to your PC over USB and get started, power supply included.

When you want the Pico to run autonomously, you will need to face the question of how long the battery supply will last. Like most microcontrollers, the Pi Pico requires comparatively little power, but it has limitations when it comes to operating continuously on battery operation, not least because it continues to consume power during sleep periods. A few tricks will help you reduce the power consumption in these phases to extend the battery runtime.

Whenever you read "Pico," it also applies to other microcontrollers, either because they directly support CircuitPython (a software implementation of the Python 3 programming language used here and targeted toward beginners), or because everything also works in the same way with C/C++.

There's an old adage: If you work, you get to eat. But microcontrollers are more like non-workers in their normal state. A remote sensor or control lies around nearly 100 percent of the time, then has to respond very quickly when a button is pressed. Even a sensor that records the current temperature every 10 minutes has an uptime of less than one percent. Therefore, it's a good idea for the Pico to save resources to the extent possible when it's doing nothing.

Measure Again

The measurement setup for the program examples shown here is simple. The power supply is routed by way of a measuring circuit that continuously monitors voltage and current. The Pico simulates regular work with its built-in LED, which it switches on for one second every 30 seconds. It does nothing in between. The source code for the sample programs is available from my GitHub project [1].

If you are familiar with microcontroller programming, you will be aware that time can elapse in several ways (Listing 1). One possibility is an empty loop that keeps the program busy for a while (lines 26-29), which is the Pico treading virtual water. As you would expect, power consumption is high (Figure 1) and is why I will be using this approach as the benchmark when comparing alternatives.

Listing 1

Empty Test Loop

01 import time
02 import board
03 import alarm
04 from digitalio import DigitalInOut, Direction, Pull
05
06 LED_TIME     = 1
07 INT_TIME     = 30 - LED_TIME
08 SPIN         = 0x1
09 SLEEP        = 0x2
10 LIGHT_SLEEP  = 0x3
11 DEEP_SLEEP   = 0x4
12 MODE         = SPIN
13
14 led           = DigitalInOut(board.LED)
15 led.direction = Direction.OUTPUT
16
17 # --- Simulate work
18 def work():
19   led.value = 1
20   time.sleep(LED_TIME)
21   led.value = 0
22
23 # --- Main loop
24 while True:
25   work()
26   if MODE == SPIN:
27     next = time.monotonic() + INT_TIME
28     while time.monotonic() < next:
29       continue
30   elif MODE == SLEEP:
31     time.sleep(INT_TIME)
32   elif MODE == LIGHT_SLEEP:
33     time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic()+INT_TIME)
34     alarm.light_sleep_until_alarms(time_alarm)
35   elif MODE == DEEP_SLEEP:
36     time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic()+INT_TIME) 40:
37     alarm.exit_and_deep_sleep_until_alarms(time_alarm)
Figure 1: Power consumption with the empty loop in Listing 1.

The basic consumption of the Pico in this active wait mode is 26mA. The LED pushes the consumption up by 5mA for a short time. Taken by itself, the basic requirement is still very low because even a Pi Zero uses 90mA in idle mode, which represents the lower limit of what headless operation can offer without digging deep into your bag of tricks.

Converting consumption into the expected battery life proves to be relatively easy. Popular lithium polymer (LiPo) batteries have a self-discharge rate of 10 percent within the first 24 hours, then about five percent per month. This value already includes the current draw by the integrated protection circuit. As soon as the voltage drops below 3V, the battery switches off. You can also assume the residual capacity to be 10 percent. Exact values for self-discharge and residual capacity need be determined experimentally.

Given these assumptions, the flashing Pico will run for 46 hours on a 1,500mAh battery, whereas the Pi Zero lasts a good 15 hours. Consequently, the use of a Raspberry Pi in combination with rechargeable batteries only makes sense in scenarios where long service lives are not important (e.g., robotic cars).

Sleep Modes

With the use of Python, you can send the Pico to sleep for X seconds with time.sleep(<X>) (Listing 1, lines 30 and 31). However, the technical implementation of time.sleep() on the Pico uses the Pico SDK's busy_wait() functions. In other words, the sleep function has no effect on power consumption; it just makes the Python code a bit easier to understand. The power consumption graph looks exactly like Figure 1 when this function is used. For other microcontrollers, on the other hand, the implementation can offer good savings with the light-sleep mode.

Besides time.sleep(), CircuitPython offers two special sleep modes: light sleep and deep sleep. In both of these modes, the CPU not only stops computing but also shuts down various function blocks of the SBC, saving electricity. Both sleep modes require a timer to wake up.

In light sleep mode, the program continues normally at the point after the sleep command, but on waking up from deep sleep, when the Pico restarts after the specified time, the current program context, including the variable values, is lost. The Pico's internal real-time clock (RTC) only retains its value during light sleep.

From the programming point of view, the two variants are not very complex, provided you import the alarm module. The timers created in lines 33 and 36 then use the alarm.light_sleep_until_alarms() (line 34) and alarm.exit_and_deep_sleep_until_alarms() (line 37) commands.

With these two modes, the idle power consumption drops to 17mA in light sleep mode and 6mA in deep sleep mode (Figure 2). The additional overhead when waking up from deep sleep can also be seen: It pushes the average consumption up to 10mA in the sample scenario. The battery will then last for five days. On the ESP32-S2, which also runs CircuitPython, the consumption in deep sleep drops to below 1mA and the average consumption to 2.5mA – with a correspondingly longer runtime.

Figure 2: Power consumption in light sleep mode (top) and deep sleep mode (bottom).

If you want to test the sleep modes yourself, please note that deep sleep does not work with an active USB connection (i.e., the serial console). This safety measure prevents buggy code from immediately sending the Pico into deep sleep without the user being able to intervene.

Also important is that the Pico does not turn off the 3.3V output. If a peripheral device is connected, it will continue to run. If you don't want that, you also need to turn off the devices connected to the output, if possible, before you send the Pico to sleep, or use a simple circuit to deactivate them.

Awake at the Touch of a Button

Timed wake-up is a useful solution to many cyclic application problems but is of little value for the remote control example cited above. Fortunately, besides alarm.time.TimeAlarm, you also have alarm.pin.PinAlarm, which causes the Pico to wake up after a pin voltage change (Listing 2).

Listing 2

alarm.pin.PinAlarm

pin_alarm = alarm.pin.PinAlarm(WAKE_PIN,value=False,edge=True,pull=True)
if MODE == LIGHT_SLEEP:
  alarm.light_sleep_until_alarms(pin_alarm)
elif MODE == DEEP_SLEEP:
  alarm.exit_and_deep_sleep_until_alarms(pin_alarm)

Although light sleep doesn't change anything in terms of power consumption, deep sleep is far more efficient with PinAlarm than with TimeAlarm; the current draw drops from 6mA to 1. You do not need to connect a pushbutton to the pin: Any component that pulls the pin to ground will work in the same way. One firm candidate for this is an external RTC like the DS3231 with its interrupt pin.

Another feature of the CircuitPython implementation proves to be very useful in this context: The sleep_until_ functions accept any number of alarms. If a device has several buttons, you don't need to use a dedicated wake-up button. The alarms for the sleep functions can theoretically be a mix of timer and pin alarms, but not every chip supports all variants.

Some controllers additionally wake up on a touch event. The Pico is not one of them, but the ESP32-S2 supports this feature. As with the timer alarm, the potential savings in sleep modes for the pin and touch alarms also depends on the processor.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Web Serial API

    Upgrade your computer with LEDs, buttons, or sensors to control a microcontroller board over USB from your web browser.

  • Light Painting

    In the photographic method of light painting, you expose a subject over an extended period of time while moving the light sources. With a little technical support from a Raspberry Pi Pico, you can achieve sophisticated results.

  • Bluetooth Communication

    We use a Raspberry Pi, a Pi Pico, and a smartphone to communicate over Bluetooth.

  • ESPHome

    With an ESP32 or Raspberry Pi Pico W microcontroller board, you can easily create your own home automation devices. Thanks to ESPHome, you don't even have to be a programmer.

  • Bluetooth LE

    Bluetooth Low Energy is ideal for networking battery-powered sensors. We show you how to use it on the Raspberry Pi.

comments powered by Disqus