The Alarm module has also been implemented on the NRF, STM32, and RP2040 ports. Sleep modes always work a little differently between MCU families - some chips have better power savings, some have worse, some chips have big differences between light and deep sleep and others don’t, some may only be able to wake on specific kinds of pin transitions, etc etc. In addition, development boards themselves will have components that passively consume power - many dev boards that aren’t specifically designed for maintaining a long battery life may consume several mA of current even when the MCU is completely turned off!
We’ve done our best to make the CircuitPython API represent the light sleep and deep sleep states in as similar a way as possible across ports. This page describes the differences in setup and power performance you may see across some common dev boards, and includes some extra sketches that may be helpful for using sleep mode with family-specific strengths, like BLE on NRF52 boards, or SDIO on the STM32 family.
Generic Examples
These sketches are similar to those used on the MagTag earlier in this guide. However, they have some additional flexibility for boards that may only have an LED instead of a Neopixel, and they show some of the differences between chip families in terms of setup. As a recap on the terms used by the API:
-
edge
: this defines whether CircuitPython will wake up only when it sees the pin value change, versus anytime it sees the pin at the target value. This doesn’t always impact behavior that much, but it’s important for certain specific cases: for instance, a level interrupt for a “high” value will wake up immediately if thePinAlarm
is already high when it goes to sleep, but an edge interrupt will wait until the pin drops and then goes high again. Most chip families only support one of these options. -
value
: this is the logic level value of the pin that will cause CircuitPython to wake, either on a pin transition (whenedge
isTrue
) or when it is polled at that level (edge
isFalse
) -
pull
: sets whether the internal pullup/pulldown resistors are enabled or not. It does not set the direction - the pulls are automatically set to the opposite ofvalue
!
Light Sleep
On many ports, light sleep is equivalent to the "wait for interrupt" used in time.sleep()
, so it won't always save that much power.
import alarm import board import time import digitalio import neopixel ## WAKING PINS - uncomment appropriate pin per microcontroller wake_pin = board.X1 # STM32F4 Pyboard # wake_pin = board.GP0 # RP2040 Pico # wake_pin = board.A4 # NRF52840 Feather # wake_pin = board.IO5 # ESP32-S2 Saola ## LED - use on RP2040 Pico, STM32F4 Pyboard ## PinAlarms blink 1x, TimeAlarms 2x, Startup 3x led_pin = board.LED led = digitalio.DigitalInOut(led_pin) led.direction = digitalio.Direction.OUTPUT def blink(num_blinks): for i in range(num_blinks): led.value = True time.sleep(0.2) led.value = False time.sleep(0.2) def show_timealarm(): blink(2) def show_pinalarm(): blink(1) def show_noalarm(): blink(3) ## Comment out above if using Neopixel ## NEOPIXEL - use on Circuitplayground Bluefruit, ESP32-S2 Saola ## TimeAlarms are red, PinAlarms are blue, Default is white # np = neopixel.NeoPixel(board.NEOPIXEL, 1) # def show_timealarm(): # np[0] = (50, 0, 0) # time.sleep(1) # np[0] = (0, 0, 0) # def show_pinalarm(): # np[0] = (0, 0, 50) # time.sleep(1) # np[0] = (0, 0, 0) # def show_noalarm: # np[0] = (50, 50, 50) # time.sleep(1) # np[0] = (0, 0, 0) ## Comment out above if using LED ## PinAlarm only needs to be set once pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=True, pull=True) # STM32, RP2040 # pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=False, pull=True) # NRF, ESP32-S2, RP2040 while True: ## TimeAlarms must be reset each time you sleep, since they use monotonic time time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) ret_alarm = alarm.light_sleep_until_alarms(pin_alarm, time_alarm) print("Returned alarm vs global alarm:") # These should be the same print(ret_alarm) print(alarm.wake_alarm) if isinstance(ret_alarm, alarm.time.TimeAlarm): show_timealarm() elif isinstance(ret_alarm, alarm.pin.PinAlarm): show_pinalarm() else: show_noalarm()
Deep Sleep
Deep sleep is designed to put the board in the lowest possible power consuming state. If you need to do datalogging for a really long time, this is the best tool.
import alarm import board import time import digitalio import neopixel ## WAKING PINS - uncomment appropriate pin per microcontroller wake_pin = board.X1 # STM32F4 Pyboard # wake_pin = board.GP0 # RP2040 Pico # wake_pin = board.A4 # NRF52840 Feather # wake_pin = board.IO5 # ESP32-S2 Saola ## LED - use on RP2040 Pico, STM32F4 Pyboard ## PinAlarms blink 1x, TimeAlarms 2x, Startup 3x led_pin = board.LED led = digitalio.DigitalInOut(led_pin) led.direction = digitalio.Direction.OUTPUT def blink(num_blinks): for i in range(num_blinks): led.value = True time.sleep(0.2) led.value = False time.sleep(0.2) def show_timealarm(): blink(2) def show_pinalarm(): blink(1) def show_noalarm(): blink(3) ## Comment out above if using Neopixel ## NEOPIXEL - use on Circuitplayground Bluefruit, ESP32-S2 Saola ## TimeAlarms are red, PinAlarms are blue, Default is white # np = neopixel.NeoPixel(board.NEOPIXEL, 1) # def show_timealarm(): # np[0] = (50, 0, 0) # time.sleep(1) # np[0] = (0, 0, 0) # def show_pinalarm(): # np[0] = (0, 0, 50) # time.sleep(1) # np[0] = (0, 0, 0) # def show_noalarm: # np[0] = (50, 50, 50) # time.sleep(1) # np[0] = (0, 0, 0) ## Comment out above if using LED ## Show which alarm woke the chip print("Wake alarm:") print(alarm.wake_alarm) if isinstance(alarm.wake_alarm, alarm.time.TimeAlarm): show_timealarm() elif isinstance(alarm.wake_alarm, alarm.pin.PinAlarm): show_pinalarm() else: show_noalarm() ## USB enumeration may take 4-5s per restart time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) ## Deep sleep pin alarms may only accept a single configuration. pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=True, edge=True, pull=True) # STM32 must be this exact config # pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=False, pull=True) # NRF and ESP32S2 must use level, not edge # pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=True, pull=True) # RP2040 supports any config alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm) # alarm.exit_and_deep_sleep_until_alarms(pin_alarm) # Using TimeAlarm will reduce performance on the RP2040 # alarm.exit_and_deep_sleep_until_alarms(time_alarm) # Using PinAlarm will reduce performance on the ESP32-S2
Text editor powered by tinymce.