micropython icon indicating copy to clipboard operation
micropython copied to clipboard

docs: Add Power Management documentation.

Open microbit-carlos opened this issue 2 years ago • 2 comments

Proposal

The current proposal has been simplified to only have two sleep functions power.deep_sleep() and power.off() (as there is already microbit.sleep() we had to use different wording) and a function to configure the wake up sources power.wake_source().

Preview build of the docs: https://microbit-micropython--754.org.readthedocs.build/en/754/power.html

Things Still Under Consideration

  • The MakeCode implementation, which is a relatively thin wrapper around CODAL, had to offer the option to "request sleep" which goes to sleep when the current fiber yields, or to "request sleep and block the fiber until it goes to sleep". For MicroPython I think we could have only the second option, as the usage of fibers is limited
    • MakeCode example for reference: https://makecode.microbit.org/_EoqLgCDgr9Vj
  • CODAL also offers lowPowerEnable() and lowPowerIsEnabled() as barriers to disable sleep in important portions of code. With the current implementation I don't think is required to expose these to the MicroPython users, but if we find a use case that needs it we might have to reconsider.

Alternative Proposals

Wake up sources as deep_sleep() arguments

The reason the wake up sources have been separated to a different function call was allow setting the sources once during startup, so that programmes with multiple deep_sleep() calls didn't have to replicate these values around.

from microbit import *
import radio

power.wake_source(pins=(pin0, pin1), buttons=button_a)

@run_every(s=1)
def check_pin_every_second():
    if pin2.read_digital():
        power.deep_sleep()

radio.on()
while True:
    if button_b.is_pressed():
        power.deep_sleep()
    if radio.receive() == 'sleep':
        power.deep_sleep()

However, this is the kind of thing that could be resolved by the user creating a wrapper function:

from microbit import *
import radio

def go_to_sleep():
    power.deep_sleep(pins=(pin0, pin1), buttons=button_a)

@run_every(s=1)
def check_pin_every_second():
    if pin2.read_digital():
        go_to_sleep()

radio.on()
while True:
    if button_b.is_pressed():
        go_to_sleep()
    if radio.receive() == 'sleep':
        go_to_sleep()

One of the advantages of having the wake up sources as arguments is that it removes any ambiguity about the sources also being applicable to power.off(), as it only applies for deep_sleep().

At this point I'm leaning towards this simplification, unless somebody can think of a good reason to keep them separated.

Implementation details not shown in the docs

  • The pin classes might need to be updated to be able to set them as wake up sources? It's an implementation detail, but might be worth discussing if there are trade offs.
  • MakeCode extension implementation for reference: https://github.com/microbit-foundation/pxt-microbit-v2-power/blob/master/power.cpp
  • We are providing a way to detect the wake up source

microbit-carlos avatar Jun 20 '22 10:06 microbit-carlos

On reflection, the alternative listed in the PR description makes sense and I think it's a better starting point. So I've updated the docs to do that, and if based on the review/discussion/testing it turns out we need a separate function for the wake up sources then we can revert it.

microbit-carlos avatar Jun 28 '22 19:06 microbit-carlos

@dpgeorge this is ready to do an initial implementation (although technically is lower priority than the Sound Effects), mostly to see if there are any technical reasons that might affect the functions as they are defined here. The MakeCode implementation turned out a bit more complex than we anticipated, mostly to be able to deal with how all fibers go to sleep, but I think that should not affect MicroPython.

microbit-carlos avatar Jun 28 '22 19:06 microbit-carlos

As discussed elsewhere, my suggestion is that run_every=True does not end the deep_sleep() call, but rather wakes briefly to execute the run every and then resumes the deep sleep. The deep sleep only ends when the given timeout expires, or a wake_on event occurs. In particular, power.deep_sleep(run_every=True) will never return. (And maybe we can then make run_every=True the default.)

dpgeorge avatar Aug 31 '22 11:08 dpgeorge

This has been shipped in v2.1.0-beta.1 in the current form, so I'll merge the docs, and any changes to sleep will need a new PR here as well.

And the last comment has been captured in this issue:

  • https://github.com/microbit-foundation/micropython-microbit-v2/issues/118

microbit-carlos avatar Sep 05 '22 18:09 microbit-carlos