Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

ports/zephyr: Add machine lightsleep. - #16864

#16864
Open
ThinkTransit wants to merge 4 commits into
micropython:mastermicropython/micropython:masterfrom
ThinkTransit:feature-zephyr-lightsleepThinkTransit/micropython:feature-zephyr-lightsleepCopy head branch name to clipboard
Open

ports/zephyr: Add machine lightsleep.#16864
ThinkTransit wants to merge 4 commits into
micropython:mastermicropython/micropython:masterfrom
ThinkTransit:feature-zephyr-lightsleepThinkTransit/micropython:feature-zephyr-lightsleepCopy head branch name to clipboard

Conversation

@ThinkTransit

@ThinkTransit ThinkTransit commented Mar 5, 2025

Copy link
Copy Markdown
Contributor

Summary

Machine lightsleep is currently not implemented in the zephyr port.

nrf socs have excellent low power capabilities however they do require some additional PM setup. In particular the uart module consumes ~1mA of power even when not being used. Powering down the uart modue can reduce sleep current to as low as 4uA (depending on hardware).

This PR adds support for lightsleep in the zephyr port. It disables the main uart and calls k_sleep() to put the micropython thread to sleep. This allows Zephyr power management to reduce power to minimum. The uart is made active when the thread is resumed.

lightsleep is included by default on all boards but can be disabled by setting:

CONFIG_PM=n
CONFIG_PM_DEVICE=n

Note: It is disabled on the beagleconnect_freedom as it does not seem to be supported.

Testing

Tested on nrf5340dk with power profile kit.

MicroPython b4cf82b2d-dirty on 2025-03-05; zephyr-nrf5340dk with nrf5340
Type "help()" for more information.
>>> import machine
>>> machine.lightsleep(1000)
>>> 

image
Current reduced from idle 1mA to 4uA during lightsleep.

Confirmed that ram is retained as this is lightsleep

MicroPython b4cf82b2d-dirty on 2025-03-05; zephyr-nrf5340dk with nrf5340
Type "help()" for more information.
>>> import machine
>>> my_var="123456789"
>>> machine.lightsleep(1000)
>>> my_var
'123456789'
>>> 

Firmware has been built for all board variants in the mp repo. All boards were sucsefully built except for:

qemu_cortex_m3 - Failed due to unrelated issue
beagleconnect_freedom - Does not seem to support PM so it has been explicitly disabled in prj.conf

CONFIG_PM=n
CONFIG_PM_DEVICE=n

Trade-offs and Alternatives

I don't believe there are any negative impacts of this change although I am not sure how to measure the increase in firmware size.

machine.deepsleep, machine.get_freq, machine.set_freq and machine.unique_id return "not implemented".

@ThinkTransit ThinkTransit changed the title ports/zephyr: Add machine lightsleep ports/zephyr: Add machine lightsleep. Mar 5, 2025
@ThinkTransit
ThinkTransit force-pushed the feature-zephyr-lightsleep branch 3 times, most recently from 09c18f1 to 9b0020b Compare March 5, 2025 03:43
Signed-off-by: Patrick Joy <patrick@thinktransit.com.au>
@ThinkTransit
ThinkTransit force-pushed the feature-zephyr-lightsleep branch from 625c8bf to 075fcdc Compare March 5, 2025 04:20
@ThinkTransit
ThinkTransit marked this pull request as ready for review March 5, 2025 04:27
@jonnor

jonnor commented Mar 12, 2025

Copy link
Copy Markdown
Contributor

This looks like a very useful addition to the Zephyr port. Thanks!

@jonnor
jonnor requested a review from dpgeorge March 12, 2025 22:29
@VynDragon

VynDragon commented Mar 13, 2025

Copy link
Copy Markdown
Contributor

I think, instead of grabbing the console device and putting it to sleep, it should either rely on zephyr's power policy management or implement pm_policy_next_state, or provide a API to chose what devices to turn on and off.

Otherwise this looks good.

@ThinkTransit

Copy link
Copy Markdown
Contributor Author

I think, instead of grabbing the console device and putting it to sleep, it should either rely on zephyr's power policy management or implement pm_policy_next_state, or provide a API to chose what devices to turn on and off.

Otherwise this looks good.

Thanks @VynDragon

I think the uart/console has to be manually shutdown on the nrf chips regardless of PM policy.

My understanding is that when CONFIG_SERIAL=y then serial logging is enabled for debugging which uses power, even in low power modes. We can't turn off CONFIG_SERIAL because it's needed for micropython repl.

There is some info on it here.

Power Optimisation

You can see also in the power management examples they shut down the console device manually before power off.

System off sample

I'm very new to zephyr so I could be wrong, what do you think?

Also looking at the example if might be worthwhile also implementing deepsleep with sys_poweroff();?

Signed-off-by: Patrick Joy <patrick@thinktransit.com.au>
@ThinkTransit

Copy link
Copy Markdown
Contributor Author

I have added machine.deepsleep() to this PR which completely powers down the system using sys_poweroff();

@VynDragon

VynDragon commented Mar 14, 2025

Copy link
Copy Markdown
Contributor

I am trying to write PM for the chips I want to support but I am nowhere near a expert on this subsystem, so take what I say with a grain of salt.

I think the uart/console has to be manually shutdown on the nrf chips regardless of PM policy.

Dont take the NRF sdk docs as exclusive reference, there are a lot of other chips :)
In theory, Power policy via residency (the default when the application doesnt implement one), guides device power via power states: https://docs.zephyrproject.org/latest/services/pm/device.html, put differently, this is a zephyr setting rather than something the application should manage in the first place, unless power management is left unconfigured. This then follows the system power management states in https://docs.zephyrproject.org/latest/services/pm/system.html.
When calling sleep, the susbsytem calculates how much time it has to shut down and come back up, and switches to the lowest possible power state listed in the DTS, the code for this is there: https://github.com/zephyrproject-rtos/zephyr/tree/main/subsys/pm/policy

You can see also in the power management examples they shut down the console device manually before power off.
System off sample

That is indeed a example of manual control of the power saving, but not of managing power states via policies. See https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/subsys/pm/latency for the default latency policy.

I am also not sure if poweroff should be deep sleep of if that should be PM_STATE_SOFT_OFF. I have implemented power off on my platform, and it conserves some ways to be powered back on, but in comparison to ESP32 deepsleep, it would match a higher power state with more ways to wake up.

@dpgeorge dpgeorge added this to the release-1.26.0 milestone May 2, 2025
Comment thread ports/zephyr/modmachine.c Outdated
Comment thread ports/zephyr/modmachine.c
@dpgeorge

dpgeorge commented Jul 8, 2025

Copy link
Copy Markdown
Member

I tested this on nucleo_wb55rg, and it breaks the UART REPL on that board: the UART responds only occasionally to input (output is fine though). I'm guessing it's because power management is now enabled and the UART is mostly asleep?

I also tested it on frdm_k64f and rpi_pico. machine.lightsleep(2000) waits 2 seconds but doesn't seem to change the power consumption at all on those boards, maybe they don't implement low power?

If we want to move forward with this, I suggest only enabling CONFIG_PM/CONFIG_PM_DEVICE on devices where it's known to work.

@ThinkTransit

Copy link
Copy Markdown
Contributor Author

I tested this on nucleo_wb55rg, and it breaks the UART REPL on that board: the UART responds only occasionally to input (output is fine though). I'm guessing it's because power management is now enabled and the UART is mostly asleep?

I also tested it on frdm_k64f and rpi_pico. machine.lightsleep(2000) waits 2 seconds but doesn't seem to change the power consumption at all on those boards, maybe they don't implement low power?

If we want to move forward with this, I suggest only enabling CONFIG_PM/CONFIG_PM_DEVICE on devices where it's known to work.

Hi Damien,

Apologies for the late reply.

I did some more research into this and it turns out that this high power usage on the UART is only related to nordic chips, something to do with the HFCLK being required when the uart is enabled.

Did enabling CONFIG_PM break the repl or was it calling lightsleep()?

Is there a mechanism where we could include the feature on just the nrf family? Or would if be better to add an nrf module with uart_sleep function?

@VynDragon

VynDragon commented Jul 14, 2025

Copy link
Copy Markdown
Contributor

Is there a mechanism where we could include the feature on just the nrf family? Or would if be better to add an nrf module with uart_sleep function?

if you make the config proper on zephyr side, UART will go to sleep automaticlaly when calling k_sleep or power off

@ThinkTransit

Copy link
Copy Markdown
Contributor Author

if you make the config proper on zephyr side, UART will go to sleep automaticlaly when calling k_sleep or power off

Not for the nordic chips unfortunately, it's a know limitation that requires the uart to be powered down manually at the discretion of the application.

https://devzone.nordicsemi.com/f/nordic-q-a/101872/how-to-make-zephyr-power-management-unit-work-with-nrf52

@VynDragon

VynDragon commented Jul 15, 2025

Copy link
Copy Markdown
Contributor

If it is not standard within zephyr even, why add it to micropython code?

Sounds like that's true for all UART on all platforms though, so I'd be wrong. Regardless there should be some way to disable that right? It seems @dpgeorge 's issue is exactly what's described as happeneing when you turn off uart :)

@RodLaird

Copy link
Copy Markdown

Seems this machine.lightsleep() accessible in Micropython 1.26 beta?. On a pico 2 at least I get a response from the function. However the time delay seems a bit random as / and seems to turn off the internal RTC. Cannot be totally sure as testing with Thonny so UART kicked into action by interaction. But when I set up some delays and print(time.time()) I get no change in the time. And I cannot set up predictable delay loops. I would just like to be able to sleep the machine and reboot it reliably after a relatively accurate time delay... Still a bridge too far it seems..

Comment thread docs/zephyr/quickref.rst
import machine

# power down the system
machine.depsleep()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo here. Should be deepsleep()

@bikeNomad

Copy link
Copy Markdown
Contributor

I just created PR #18197 that allows Pin IRQs to interrupt machine.lightsleep()

@ant9000

ant9000 commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

Did enabling CONFIG_PM break the repl or was it calling lightsleep()?

CONFIG_PM_DEVICE breaks the REPL. I have a different board (an Apollo3 from Ambiq), and tried to investigate this stuff directly at Zephyr level, starting with the shell sample and adding PM configuration. On my board, the working config is the following:

CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y

Skipping the last line makes the UART unresponsive to user input (it does print messages, though). It seems that there is nothing that marks the REPL (or the Zephyr shell) as making use of the console. Adding the following to main is enough to make the shell usable even without CONFIG_PM_DEVICE_RUNTIME:

    const struct device *dev;
    dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
    pm_device_busy_set(dev);

I think this line would solve the REPL issue also for MicroPython; but then, before going to sleep you would need to relinquish the console with a corresponding pm_device_busy_clear().

@ant9000

ant9000 commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

I can confirm that the solution above works also for MicroPython on the board I have. Here is my patch:

diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c
index 8d39935884..ef1c7590ae 100644
--- a/ports/zephyr/main.c
+++ b/ports/zephyr/main.c
@@ -89,6 +89,10 @@ static __noinit char heap[MICROPY_HEAP_SIZE];
 extern int mp_usbd_init(void);
 #endif // defined(CONFIG_USB_DEVICE_STACK_NEXT)
 
+#if CONFIG_PM_DEVICE
+#include <zephyr/pm/device.h>
+#endif
+
 void init_zephyr(void) {
     // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap
     // network addresses.
@@ -163,6 +167,14 @@ soft_reset:
     }
     #endif
 
+#if CONFIG_PM_DEVICE
+    const struct device *dev;
+    dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+    if (dev != NULL) {
+        pm_device_busy_set(dev);
+    }
+#endif
+
     for (;;) {
         if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
             if (pyexec_raw_repl() != 0) {

@dpgeorge dpgeorge removed this from the release-1.28.0 milestone Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.