# Deep Sleep with CircuitPython

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/097/935/medium800/circuitpython_sleep-bed.jpg?1608150509)

If you'd like to maximize the battery life on a CircuitPython project, you need to be able to put your program to sleep when it's not doing something. For instance, you may want to read a temperature or fetch some data only every few minutes or hours. In between, your board can go to sleep and draw only a tiny amount of power from the battery.

If you're using a display that is visible even when powered off, such as the e-ink display on the Adafruit MagTag, then you can sleep between updates to the display.

This guide will talk about using the sleep and wake-up alarm capabilities that are available in CircuitPython.

# Deep Sleep with CircuitPython

## Alarms and Sleep

![](https://cdn-learn.adafruit.com/assets/assets/000/097/936/medium800/circuitpython_sleep-nightcap.jpg?1608150626)

## Terminology

We'll distinguish between _deep sleep_ and _light sleep_:

- If a program does a _deep sleep_, it first exits, and then the microcontroller goes to sleep, turning off as much as possible while still being able to wake up later. When the microcontroller wakes up, it will start your program ( **code.py** ) _from the beginning_.
- If a program does a _light sleep_, it still goes to sleep but continues running the program, resuming after the statement that did the light sleep. Power consumption will be minimized. However, on some boards, such as the ESP32-S2, light sleep does not save power compared with just using `time.sleep()`.

CircuitPython uses _alarms_ to wake up from sleeping. An alarm can be triggered based on a specified time being reached, or based on an external event, such as a pin changing state. The pin might be attached to a button, so you would be able to wake up on a button press.

## The `alarm` module

Alarms and sleep are available in the `alarm` module in CircuitPython ([latest documentation](https://docs.circuitpython.org/en/latest/shared-bindings/alarm/index.html)). You create one or more alarms, and then go into a light sleep or deep sleep while waiting for them.

![](https://cdn-learn.adafruit.com/assets/assets/000/097/991/medium800thumb/projects_magtag-blink-with-fade.jpg?1608232193)

## TimeAlarm Light Sleep

Here's a simple program that just blinks the status NeoPixel every 10 seconds, and does a light sleep in between, using a `TimeAlarm`. The video above demonstrates this program, eliding the 10-second sleeps.

```python
import alarm
import board
import digitalio
import neopixel
import time

# On MagTag, enable power to NeoPixels.
# Remove these two lines on boards without board.NEOPIXEL_POWER.
np_power = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
np_power.switch_to_output(value=False)

np = neopixel.NeoPixel(board.NEOPIXEL, 1)

while True:
    np[0] = (50, 50, 50)
    time.sleep(1)
    np[0] = (0, 0, 0)

    # Create a an alarm that will trigger 10 seconds from now.
    time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10)

    # Do a light sleep until the alarm wakes us.
    alarm.light_sleep_until_alarms(time_alarm)
    # Finished sleeping. Continue from here.
```

## `TimeAlarm` Deep Sleep

Here's a similar program, which does a deep sleep. The video above is still what you'd see. Remember that for deep sleep, the program exits, and restarts when woken up. So in this program there's no `while True:` loop.

```python
import alarm
import board
import digitalio
import neopixel
import time

# On MagTag, enable power to NeoPixels.
# Remove these two lines on boards without board.NEOPIXEL_POWER.
np_power = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
np_power.switch_to_output(value=False)

np = neopixel.NeoPixel(board.NEOPIXEL, 1)

np[0] = (50, 50, 50)
time.sleep(1)
np[0] = (0, 0, 0)

# Create a an alarm that will trigger 20 seconds from now.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 20)
# Exit the program, and then deep sleep until the alarm wakes us.
alarm.exit_and_deep_sleep_until_alarms(time_alarm)
# Does not return, so we never get here.
```

## What Woke Me Up?

When a program awakens from light sleep or deep sleep, it's because of an alarm. You can find out what kind of alarm woke up the program by looking at `alarm.wake_alarm`.

If the program **did not wake up from sleep** , then `alarm.wake_alarm` will be `None`.&nbsp;

If the program woke up from a **light sleep** , then `alarm.wake_alarm` will be one of the alarm objects passed to `alarm.light_sleep_until_alarms(...)`. The triggered alarm is also returned by that function, so you can get the alarm value directly. Here are two ways to get the triggered alarm:

## `PinAlarm` Deep Sleep

This example uses `PinAlarm` instead of `TimeAlarm`. It will deep sleep until one of the `D11`, `D12`, `D14`, or `D15` buttons on the MagTag is pressed. On MagTag boards, pressing a button connects a pin to ground, so we wait for a&nbsp;`False` value.

Setting `pull=True` in the `PinAlarm` constructor enables a pull-up or pull-down, to pull the pin to the level opposite that of&nbsp;`value`. In this case,&nbsp;`value`&nbsp;is set to&nbsp;`False`, so setting&nbsp;`pull`&nbsp;to&nbsp;`True`&nbsp;will enable a pull-up, to hold the pin high until pressing the button pulls it low.

(If `value` were set to True, setting `pull=True` would enable a pull-_down_.)

```python
import alarm
import board
import digitalio
import neopixel
import time

# On MagTag, enable power to NeoPixels.
# Remove these two lines on boards without board.NEOPIXEL_POWER.
np_power = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
np_power.switch_to_output(value=False)

np = neopixel.NeoPixel(board.NEOPIXEL, 1)

# Choose a color based on which button woke us up.
COLORS = {
    board.D11 : (50, 0, 0),  # red
    board.D12 : (0, 50, 0),  # green
    board.D14 : (0, 0, 50),  # blue
    board.D15 : (50, 50, 0), # yellow
    }

wake_alarm = alarm.wake_alarm

if isinstance(wake_alarm, alarm.pin.PinAlarm):
    np[0] = COLORS[wake_alarm.pin]
else:
    # If not a pin wakeup, display white.
    np[0] = (50, 50, 50)
time.sleep(1)
np[0] = (0, 0, 0)

# Since value=False, setting pull=True will enable a pull-up.
pin_alarm_d11 = alarm.pin.PinAlarm(pin=board.D11, value=False, pull=True)
pin_alarm_d12 = alarm.pin.PinAlarm(pin=board.D12, value=False, pull=True)
pin_alarm_d14 = alarm.pin.PinAlarm(pin=board.D14, value=False, pull=True)
pin_alarm_d15 = alarm.pin.PinAlarm(pin=board.D15, value=False, pull=True)

# Exit the program, and then deep sleep until an alarm wakes us.
alarm.exit_and_deep_sleep_until_alarms(pin_alarm_d11, pin_alarm_d12, pin_alarm_d14, pin_alarm_d15)

# Does not return, so we never get here.
```

### Release Pins if Needed

The MagTag and other boards have helper libraries that take control of button pins. If you would like to use a `PinAlarm` or `TouchAlarm`&nbsp;when using such a library, you must deinitalize the pins that were taken by the library.

For the `adafruit_magtag` library, call [`magtag.peripherals.deinit()`](https://docs.circuitpython.org/projects/magtag/en/latest/api.html#adafruit_magtag.peripherals.Peripherals.deinit) to release the pins so they can be used with an alarm.

## `TouchAlarm` Deep Sleep

This example is for the Metro ESP32-S2. The MagTag has no pins that can be used for touch. (`D10` could theoretically be used, but protection components are connected to it that prevent it being used for touch.)

It will sleep until pin `IO5` is touched, or 10 seconds has elapsed, whichever comes first. The on-board LED blinks for one second at the beginning of the program.

```python
import alarm
import board
import digitalio
import time

# Print out which alarm woke us up, if any.
print(alarm.wake_alarm)

led = digitalio.DigitalInOut(board.LED)
led.switch_to_output(value=True)
time.sleep(1)
led.value = False

# Create an alarm that will trigger 10 seconds from now.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10)
# Create an alarm that will trigger if pin IO5 is touched.
touch_alarm = alarm.touch.TouchAlarm(pin=board.IO5)

# Exit the program, and then deep sleep until one of the alarms wakes us.
alarm.exit_and_deep_sleep_until_alarms(time_alarm, touch_alarm)
# Does not return, so we never get here.
```

## Pretending to Sleep When Connected

When your board is connected to a host computer via USB, you don't want it to really do a light sleep or deep sleep, because that would break the USB connection and make it difficult to debug or edit your program. So when the board is connected, CircuitPython **simulates** light and deep sleep. This is often called "fake light sleep" or "fake deep sleep". If CircuitPython can reduce power consumption while pretending to sleep and still remain connected, it will do so, but you will not be saving nearly as much power as when you're not connected.

So if you're trying to measure how much power you're saving while sleeping, you really need to do your measurements from battery power (or a power supply) while unconnected.

```python
triggered_alarm = alarm.light_sleep_until_alarms(alarm1, alarm2)

# is the same as

alarm.light_sleep_until_alarms(alarm1, alarm2)
triggered_alarm = alarm.wake_alarm
```

If the program restarted after a deep sleep, then `alarm.wake_alarm` will be an alarm object of the same type as the original alarm, but it will not be exactly the same object. It's attributes may be different, or they may be incomplete in some way. For instance, for `TimeAlarm`, the .`monotonic_time` attribute may not contain the same value. But you can still do an `isinstance(alarm.wake_alarm, TimeAlarm)` to find out it was a `TimeAlarm` that woke up the program.

# Deep Sleep with CircuitPython

## Sleep Memory

![](https://cdn-learn.adafruit.com/assets/assets/000/097/937/medium800/circuitpython_sleep-nightcap-sleep.jpg?1608151222)

When a program goes into deep sleep, it exits and then sleeps. So all the information in its variables is lost. But the program might want to remember something for use when it restarts. It could write into a file onto the board **CIRCUITPY drive** , or it could write into internal flash using `microcontroller.nvm`. But flash has a limited lifetime, so it's better not to write it over and over. Instead, the program can write into a special part of memory (RAM) that is powered during deep sleep. In most microcontrollers, this kind of memory is called "backup RAM"; in CircuitPython, we call it `alarm.sleep_memory`. This memory requires very little power to maintain. If power is removed completely, then the memory is lost, but as long as USB power or a battery is connected, it will remember what is stored in it.

`alarm.sleep_memory` is just a byte array of a few thousand bytes. You can use it to store whatever you want, but you'll need to encode the data as bytes. You could use `struct.pack` and `struct.unpack`, or use JSON, or some other format that's convenient for you.

## MagTag Example

Here's a simple example of using sleep memory, without any encoding. Each time the program wakes up, it increments a count kept in one byte in `alarm.sleep_memory`. This program displays the current battery voltage and the count on the MagTag display.

The very first time the program runs, we want to initialize the count. We can tell if this is the first time the program has run by checking `alarm.wake_alarm`. If it is `None`, then we know we have not done a deep sleep yet.

The video below shows the program running, with the long 60 second-sleeps elided.

![](https://cdn-learn.adafruit.com/assets/assets/000/097/992/medium800thumb/projects_sleepmem.jpg?1608235060)

```python
import alarm
import microcontroller
import time
from adafruit_magtag.magtag import MagTag


magtag = MagTag()

magtag.add_text(
    text_scale=2,
    text_wrap=25,
    text_maxlen=300,
    text_position=(10, 10),
    text_anchor_point=(0, 0),
)

# Reset the count if we haven't slept yet.
if not alarm.wake_alarm:
    # Use byte 5 in sleep memory. This is just an example.
    alarm.sleep_memory[5] = 0

alarm.sleep_memory[5] = (alarm.sleep_memory[5] + 1) % 256

# Display the current battery voltage and the count.
magtag.set_text(
    "battery: {}V    count: {}".format(
        magtag.peripherals.battery, alarm.sleep_memory[5]
    )
)

magtag.refresh()

# Sleep for 60 seconds.
al = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60)
alarm.exit_and_deep_sleep_until_alarms(al)
# Does not return. Exits, and restarts after 60 seconds.
```

# Deep Sleep with CircuitPython

## Power Consumption

How much power would you actually save by using light sleep or deep sleep? It depends on which board you're using, what your program is doing when it's not sleeping, what is left turned on during light sleep, and how long you spend sleeping vs running (your program's _duty cycle_).

Note that if there are other devices on your board that can be left turned on, such as sensors or NeoPixels, a deep sleep will not automatically turn them off. Your program should shut down what it can if you want to minimize power usage. Sometimes these on-board devices are controlled by the microcontroller pins, and may be turned off as a consequence of the pins being turned off, but on some boards, the on-board devices are powered whenever the board is powered.

Given all those variables, let's look at power consumption using the sample programs in the [Alarms and Sleep](https://learn.adafruit.com/deep-sleep-with-circuitpython/alarms-and-sleep) section of this guide. If you recall, those programs just blink a NeoPixel for one second and then sleep for ten seconds.

We'll show a bunch of power-consumption graphs below. These screenshots were taken using the [Nordic Semiconductor Power Profiler Kit II](https://www.nordicsemi.com/Software-and-tools/Development-Tools/Power-Profiler-Kit-2) (PPK2) hardware and software. The PPK2 is quite inexpensive (\< $100) compared to the usual power meter, is easy to use, and works well.

## ESP32-S2 TimeAlarm Deep Sleep Power Consumption

Here's a graph showing the `TimeAlarm` deep sleep demo program, running on a MagTag ESP32-S2, sleeping every ten seconds. There's a big short spike when the program starts up after sleeping. The program blinks the NeoPixel, and then sleeps. We are supplying 3.7V to the board, which is a typical LiPo battery voltage.

![](https://cdn-learn.adafruit.com/assets/assets/000/097/914/medium800/circuitpython_deep-sleep-cycling.png?1608135836)

Let's zoom in on the power consumption while the program is actively running during one cycle. You can see it's using about 50mA. Note that the vertical axis scale has changed, because we're not including the big spike at the beginning of a run.

If this program were using WiFi, you'd see much higher power consumption, up to a few hundred mA during active WiFi use.

![](https://cdn-learn.adafruit.com/assets/assets/000/097/913/medium800/circuitpython_deep-sleep-running.png?1608135821)

Now let's look at the power consumption during deep sleep. In the graphs above, that's where the power consumption line looks very close to 0. Again, the vertical axis has expanded, so we can see microamps accurately. You can see the board is using a little under 230uA when it's sleeping. About 25-30uA of this is the actual ESP32-S2 module on the MagTag; the rest is board overhead, like the voltage regulator, and the (dim) power LED.

![](https://cdn-learn.adafruit.com/assets/assets/000/098/687/medium800/projects_deep-sleep-sleeping.png?1610813512)

## ESP32-S2 TimeAlarm Light Sleep Sample Power Consumption

For comparison, here's a graph of the `TimeAlarm` light sleep demo program, which also cycles every ten seconds. On the ESP32-S2, as mentioned, light sleep is no better than `time.sleep` in terms of power consumption. For one second before the NeoPixel is turned on (marker 1), power consumption is about 33mA. Turning on the NeoPixel raises the current drawn to about 75mA. Then the program sleeps (marker 2), but the consumption is still about 33mA.

So there's not much reason to use light sleep on the ESP32-S2 if you're trying to save power.

![](https://cdn-learn.adafruit.com/assets/assets/000/098/686/medium800/projects_light-sleep.png?1610812513)

## ESP32-S2 PinAlarm Deep Sleep Power Consumption

Now let's look at deep sleep power consumption when using a `PinAlarm` on the ESP32-S2 MagTag. Here are several cycles of sleeping. Each red arrow points to when the D11 button was pressed. The intervals are different lengths because the time between button pushes was not the same.

![](https://cdn-learn.adafruit.com/assets/assets/000/098/688/medium800/projects_pin-alarm-cycle.png?1610819067)

Here is the start of a single `PinAlarm` sleep cycle. Unfortunately, the circuitry that needs to stay on to detect pin changes takes a significant amount of current. Even when deep sleeping, the board is using about 1.65mA, much more than it would if we were just using `TimeAlarm`.

![](https://cdn-learn.adafruit.com/assets/assets/000/098/689/medium800/projects_pin-alarm-deep-sleep.png?1610819334)

## ESP32-S2 TouchAlarm Deep Sleep Power Consumption

ESP32-S2 `TouchAlarm` power consumption is similarly higher than you might like  It's about 2.6mA, even&nbsp; higher than `PinAlarm`, about 2.6mA. So again, if you really want to save your battery, use `TimeAlarm`.

## Sleep Power Summary

Here's a chart of deep sleep power consumption on ESP32-S2 when different kinds of alarms are used. For other chip families, see the other pages in this guide.

### ESP32-S2 (MagTag) Deep Sleep

- `TimeAlarm`: 230uA
- `PinAlarm`: 1.65mA
- `TouchAlarm`: 2.6mA

### ESP32-S2 Light Sleep

- Sleep current is the same as `time.sleep()`, so there's no advantage to using light sleep.

Warning: 

## Measure to be Sure

As we mentioned, for any particular program and board, there are many things that can affect its power consumption. If you want to be know how long your battery will last, and whether you're consuming power unnecessarily, it's really helpful to have a power meter.

### Don't Forget About Simulated Sleep

**Don't forget that when your board is connected to a host computer via USB, it does not actually sleep when sleep is requested** , because we don't want to break the USB serial and mass storage ( **CIRCUITPY** ) connections. So you need to test power consumption when not connected. The board can be powered from the battery connector or via the USB port (via a power pack or a wall adapter), but **it can't be actively connected to a host computer.**

# Deep Sleep with CircuitPython

## Other Chip Families

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!&nbsp;

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 the `PinAlarm` 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 (when `edge` is `True`) or when it is polled at that level (`edge` is `False`)
- `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 of `value`!

### 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.

```python
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.

```python
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
```

# Deep Sleep with CircuitPython

## STM32 Sleep

Info: 

To observe power consumption on the STM32F405 Feather Express, connect your power probe to the **VBAT** and **GND** pins.

Observing the Feather with our generic deepsleep test shows how much the power drops when sleeping, even with both alarms enabled. Note that the period of relatively high power consumption right before sleeping is the chip waiting to see if there's a viable USB connection, before deciding to enter "fake" or "real" deep sleep.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/719/medium800/projects_deeptest-without-SD.png?1623179006)

In contrast, STM32 uses a simple WFI (wit for interrupt) operation for light sleep, so it doesn't save very much power compared to regular operation:

![](https://cdn-learn.adafruit.com/assets/assets/000/102/721/medium800/projects_light_sleep.png?1623179241)

## STM32 Example

One of the benefits of the STM32F405 Feather Express is the built in SD card reader! This could be beneficial to a datalogging project, where you want to collect and store data from a sensor onto easily removable external storage. As an example, the following sketch will wake up every ten seconds to store the internal chip temperature to the SD card:

```python
import alarm
import board
import time
import microcontroller
import sdioio
import storage
import neopixel

np = neopixel.NeoPixel(board.NEOPIXEL, 1)
np[0] = (50, 0, 0)

# SD Card
sd = sdioio.SDCard(
    clock=board.SDIO_CLOCK,
    command=board.SDIO_COMMAND,
    data=board.SDIO_DATA,
    frequency=25000000)
vfs = storage.VfsFat(sd)
storage.mount(vfs, '/sd')

with open("/sd/log.txt", "a") as sdc:
    temperature = microcontroller.cpu.temperature
    print("Temperature:", temperature)
    sdc.write("{}\n".format(temperature))

## USB enumeration may take 4-5s per restart
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10)

np[0] = (0, 0, 0)

alarm.exit_and_deep_sleep_until_alarms(time_alarm)
```

This sketch uses deep sleep, so it saves quite a bit of power when not logging data. However, just having the SD card plugged in costs a little bit of power, taking consumption up from under a milliamp to around 3.7mA:

![](https://cdn-learn.adafruit.com/assets/assets/000/102/722/medium800/projects_SD_demo.png?1623179407)

# Deep Sleep with CircuitPython

## nRF Sleep

The nRf examples here use the Circuit Playground Bluefruit board. If you don't have a JST connector handy, the Circuit Playground Bluefruit can also be equivalently monitored with the **VCC** and **GND** clip pads.

The Circuit Playground Bluefruit is a good example of a board that suffers a bit in sleep performance because of parasitic components - since the board has many sensor ICs, the power does not drop very dramatically during deep sleep:

![](https://cdn-learn.adafruit.com/assets/assets/000/102/724/medium800/projects_deepsleep.png?1623184177)

Light sleep is similar, if a bit noisier:

![](https://cdn-learn.adafruit.com/assets/assets/000/102/725/medium800/projects_light_sleep.png?1623184209)

However, other boards may get better power performance depending on their PCB design.

## nRF Example

A core NRF feature that makes sense to pair with low power is BLE Advertising. By spacing out advertising with deep sleep in between, a device can periodically signal without draining its battery too quickly. The following sketch shows how this could be implemented in practice:

```python
import time
import adafruit_ble
from adafruit_ble_eddystone import uid, url
import alarm

np = neopixel.NeoPixel(board.NEOPIXEL, 1)

radio = adafruit_ble.BLERadio()
# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)
eddystone_url = url.EddystoneURL("https://adafru.it/discord")

while True:
    np[0] = (50, 0, 0)
    # Alternate between advertising our ID and our URL.
    radio.start_advertising(eddystone_uid)
    time.sleep(0.5)
    radio.stop_advertising()

    radio.start_advertising(eddystone_url)
    time.sleep(0.5)
    radio.stop_advertising()

    ## USB enumeration may take 4-5s per restart
    time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10)

    np[0] = (0, 0, 0)

    alarm.exit_and_deep_sleep_until_alarms(time_alarm)
```

You can learn more about BLE Advertising in the official [Learn Guide](https://learn.adafruit.com/circuitpython-ble-advertising-beacons?view=all).

 **Boot.py:**

# Deep Sleep with CircuitPython

## RP2040 Sleep

The Raspberry Pi Pico development board power consumption can be monitored by connecting to **VBUS** and **GND**.

The RP2040, conveniently, is the only currently supported chip that supports every kind of wakeup configuration across all sleep types. However, note that unlike most other chip families, it will actually lose a bit of deep sleep performance when using a `TimeAlarm`. This is because the RP2040 dormant mode actually doesn’t support the RTC at all without an external clock source, so its deep sleep is actually a very extreme version of light sleep, rather than a separate mode.

When avoiding `TimeAlarm`s, the Raspberry Pi Pico devboard can achieve under 2mA while in deep sleep:

![](https://cdn-learn.adafruit.com/assets/assets/000/102/729/medium800/projects_deepsleep-notime.png?1623188277)

When a Timealarm is added, the draw raises up to 7mA.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/730/medium800/projects_deepsleep-wtime.png?1623188443)

RP2040's Light sleep mode, while not near deep sleep levels of efficiency, is still a useful power reduction, dropping power consumption by over a third:

![](https://cdn-learn.adafruit.com/assets/assets/000/102/731/medium800/projects_lightsleep.png?1623188595)

## RP2040 Example

While it doesn’t have removable storage, the RP2040 can also be used for datalogging using the internal storage on the chip. The following sketch will gather the internal chip temperature and append it to a file in the internal storage, which can be accessed whenever the board is plugged in.&nbsp;

Since this sketch uses the `storage` module to access the internal filesystem, you'll need to also include a **boot.py** file, and will not be able to access the CPy drive while the datalogger is running. You can read more about the `storage` module in the [Guide](https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage).

**code.py:**

```auto
import alarm
import board
import time
import microcontroller
import storage

temperature = microcontroller.cpu.temperature
print("Temperature:", temperature)

try:
    with open("/log.txt", "a") as sdc:
        sdc.write("{}\n".format(temperature))
except OSError as e:
    print("Cannot write to fs, is GP4 grounded?")

## USB enumeration may take 4-5s per restart
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10)

alarm.exit_and_deep_sleep_until_alarms(time_alarm)
```

 **boot.py:**

```python
import board
import digitalio
import storage

switch = digitalio.DigitalInOut(board.GP4)

switch.direction = digitalio.Direction.INPUT
switch.pull = digitalio.Pull.UP

# If the switch pin is connected to ground CircuitPython can write to the drive
storage.remount("/", switch.value)
```

## Other Resources for RP2040

User **bablokb** on GitHub [has done extensive measurements](https://github.com/bablokb/pico-sleepcurrent) of RP2040 power consumption.&nbsp;


## Featured Products

### Adafruit MagTag - 2.9" Grayscale E-Ink WiFi Display

[Adafruit MagTag - 2.9" Grayscale E-Ink WiFi Display](https://www.adafruit.com/product/4800)
The Adafruit MagTag combines the ESP32-S2 wireless module and a 2.9" grayscale E-Ink display to make a low-power IoT display that can show data on its screen even when power is removed! The ESP32-S2 is great because it builds on the years of code and support for the ESP32 and also adds...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4800)
[Related Guides to the Product](https://learn.adafruit.com/products/4800/guides)
### Adafruit MagTag Starter Kit - ADABOX017 Essentials

[Adafruit MagTag Starter Kit - ADABOX017 Essentials](https://www.adafruit.com/product/4819)
The **Adafruit MagTag** combines the new ESP32-S2 wireless module and a 2.9" grayscale E-Ink display to make a low-power IoT display that can show data on its screen even when power is removed! The ESP32-S2 is great because it builds on the years of code and support for the...

In Stock
[Buy Now](https://www.adafruit.com/product/4819)
[Related Guides to the Product](https://learn.adafruit.com/products/4819/guides)
### Nordic nRF-PPK2 - Power Profiler Kit II

[Nordic nRF-PPK2 - Power Profiler Kit II](https://www.adafruit.com/product/5048)
The **Power Profiler Kit II** &nbsp;is a standalone unit, which can measure and optionally supply currents all the way from sub-uA and as high as 1A on all Nordic DKs, in addition to external hardware. [As we've featured on...](https://www.youtube.com/watch?v=60q8QCiYhjc)

In Stock
[Buy Now](https://www.adafruit.com/product/5048)
[Related Guides to the Product](https://learn.adafruit.com/products/5048/guides)

## Related Guides

- [Adafruit MagTag](https://learn.adafruit.com/adafruit-magtag.md)
- [MagTag Weekly Showtimes Event Notifier](https://learn.adafruit.com/magtag-weekly-event-showtimes-display.md)
- [Microsoft Azure Cost Monitor](https://learn.adafruit.com/azure-cost-monitor.md)
- [Creating MagTag Projects with CircuitPython](https://learn.adafruit.com/creating-magtag-projects-with-circuitpython.md)
- [SpaceX Next Launch Display with Adafruit MagTag](https://learn.adafruit.com/spacex-next-launch-display-with-adafruit-magtag.md)
- [MagTag Showerthoughts and Quotes](https://learn.adafruit.com/magtag-showerthoughts.md)
- [OSHWA Project Display With Adafruit MagTag](https://learn.adafruit.com/oshwa-project-display-with-adafruit-magtag.md)
- [MagTag Tides Viewer](https://learn.adafruit.com/magtag-tides-viewer.md)
- [CircuitPython Display_Text Library](https://learn.adafruit.com/circuitpython-display-text-library.md)
- [MagTag Cat Fed Clock](https://learn.adafruit.com/magtag-cat-feeder-clock.md)
- [Clue And MagTag Pep Talk Generator](https://learn.adafruit.com/clue-and-magtag-pep-talk-generator.md)
- [eInk Literature Quotes Clock for MagTag](https://learn.adafruit.com/eink-literary-quotes-clock-for-magtag.md)
- [CircuitPython Animated Holiday Wreath Lights](https://learn.adafruit.com/circuitpython-animated-holiday-wreath-lights.md)
- [No-Code MagTag Every Day Goal Tracker](https://learn.adafruit.com/no-code-magtag-every-day-goal-tracker.md)
- [Adafruit MagTag COVID Vaccination Percent Tracker](https://learn.adafruit.com/adafruit-magtag-covid-vaccination-percent-tracker.md)
- [MagTag Case](https://learn.adafruit.com/magtag-case.md)
