Skip to content
This repository was archived by the owner on Sep 6, 2023. It is now read-only.

is deepsleep() anywhere near to being implemented ? #32

Closed
pacmac opened this issue Feb 28, 2017 · 46 comments
Closed

is deepsleep() anywhere near to being implemented ? #32

pacmac opened this issue Feb 28, 2017 · 46 comments

Comments

@pacmac
Copy link

pacmac commented Feb 28, 2017

I know this is a big one, but it's really the critical thing when using battery power.

Is this on the near horizon ??

@nickzoic
Copy link
Collaborator

Wrapping the esp-idf deep sleep functions isn't a big problem, but it's more a matter of what should it do when it wakes: call a callback function perhaps?

@pacmac
Copy link
Author

pacmac commented Feb 28, 2017

A callback would be ideal, as the 8266 effectively performs a reset when it wakes up which means that you loose all your values.

@diginfo
Copy link

diginfo commented Feb 28, 2017

Not having deep sleep really limits the application now to be a powered device as even with a 2700mah battery, the device would only last just over a day on battery power.

@MrSurly
Copy link
Contributor

MrSurly commented Mar 22, 2017

@nickzoic, Why wouldn't it be implemented the same way as the 8266 wakeup (i.e. w/ a reset)? Then the code could check the wake reason. Would make MP scripts backwards compatible with 8266.

@nickzoic
Copy link
Collaborator

nickzoic commented Mar 22, 2017 via email

@MrSurly
Copy link
Contributor

MrSurly commented Mar 22, 2017 via email

@nickzoic
Copy link
Collaborator

I think the hope is to implement exactly the same as the ESP8266 API, but the underlying SDK functions are quite different so yeah, it's tricky to work out how that'd work. One option, I guess, would be to implement a compatible rtc module which supports the rtc.irq function and then have the deepsleep function understand that.

It does seem like an odd way to do it, I agree ... my original thoughts were much like your proposal above. Perhaps this is something Damien could weigh in on since he'll have to approve the PR anyway!

@dpgeorge
Copy link
Member

If there are good ideas for an alternative way to do deepsleep and waking then we can consider them and see if it's possible to change the esp8266 as well, so that everything is the same.

machine.deepsleep(timeMs = None, ext0_pin = None, ext0_level = None, ext1_pins = 0, ext1_level = None, touchPad = False)

I think this is too complex and won't generalise well to other MCUs. Some MCUs don't have a touch pad, and some would have other events that can wake it up (eg network traffic). That is why the current design has the peripherals configure their own wake-up behaviour, and deepsleep() just goes to sleep.

@MrSurly
Copy link
Contributor

MrSurly commented Mar 24, 2017

@dpgeorge:

I think this is too complex and won't generalise well to other MCUs.

Hmm, agreed.

That is why the current design has the peripherals configure their own wake-up behaviour, and deepsleep() just goes to sleep.

Proposed:

Backwards compatible

import machine

rtc = machine.RTC() # Need to implement this first!

rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP) # syntactic sugar to match, probably a NOP

rtc.alarm(rtc.ALARM0, 10000) # calls esp_deep_sleep_enable_timer_wakeup() in IDF

machine.deepsleep() # calls esp_deep_sleep_start() in IDF

Touch

Set or return wake on touch, wake = True or False to set, defaults to None to return. Always returns the value, even if just set.

rtc.wake_on_touch(wake = None) 

EXT0 pin

Set or return waking on a single external pin; pin = integer to set, special value of RTC.NO_PIN for pin to disable, or 'None' to return. level = 0 or 1.

rtc.wake_on_external_pin(pin = None, level = none) 

EXT1 pins

Set or return waking on a set of pins; pins = integer (bitmask to match pin #2) to set, 'None' to return. level = RTC.ALL_LOW or RTC.ANY_HIGH. Special value of RTC.NO_PIN for pin to disable.

rtc.wake_on_external_pin(pins = None, level = none) 

@MrSurly
Copy link
Contributor

MrSurly commented Mar 27, 2017

@dpgeorge

If there are good ideas for an alternative way to do deepsleep and waking then we can consider them and see if it's possible to change the esp8266 as well, so that everything is the same.

Would it be best to have a high-level "deep sleep" μPy interface (perhaps time.deepsleep(milliseconds)) that "just works", yet keep the machine.RTC interface as being machine-specific?

That way users who "just want to save power for a set period of time" can do so easily, across platforms, but users who want to use different features of their particular hardware (e.g. touch wake on ESP32) can do so.

Keeping the machine.RTC API as-is has the advantage of not breaking any existing ESP8266 code that uses the existing idioms, on an actual ESP8266, though it won't work on an ESP32 -- but that doesn't matter, since it doesn't work on the ESP32 now.

In any case, I'm going to move forward on making deep sleep work on the ESP32, and worry about the user-facing API later.

@MrSurly
Copy link
Contributor

MrSurly commented Mar 27, 2017

@pacmac
I have something working if you want to give it a try. Not thoroughly tested, but seems to work OK. No support for waking on anything except for an amount of time. If you set the time to 0 (or don't set it), it will sleep forever.

@MrSurly
Copy link
Contributor

MrSurly commented Mar 28, 2017

@dpgeorge

Wake on touch is 99% implemented, but since it requires and updated ESP-IDF, the actual call to enable touch wake is commented out in C, and if you try to set a touch wake, you get a RuntimeError.

The rest is working (see below); I'm awaiting your comments on the Python API (see above).

EXT0 wake:

import machine as m
p = m.Pin(4)
r = m.RTC()
p.init(p.IN, p.PULL_UP)
r.wake_on_ext0(pin = p, level = 0)
m.deepsleep()

EXT1 wake:

import machine as m
pins = (m.Pin(2), m.Pin(4))
for p in pins: 
    p.init(m.Pin.IN, m.Pin.PULL_UP)
r = m.RTC()
r.wake_on_ext1(pins = pins, level = r.WAKEUP_ALL_LOW)
m.deepsleep()

Timer wake:

import machine as m
r = m.RTC()
r.irq(trigger=r.ALARM0, wake=m.DEEPSLEEP)
r.alarm(r.ALARM0, 3000)
m.deepsleep()

@MrSurly
Copy link
Contributor

MrSurly commented Apr 3, 2017

@dpgeorge @nickzoic
Bump!

Only waiting on your API comments (and potential changes) before submitting a PR for this one.

@pacmac
Copy link
Author

pacmac commented Apr 4, 2017

Sorrie, I have not had chance to test this, I had to drop my focus on the ESP32 due to other projects on 8266 which I originally was hoping to convert to ESP32.

I will try it out in the next couple of days.

@MrSurly
Copy link
Contributor

MrSurly commented Apr 4, 2017 via email

@dpgeorge
Copy link
Member

dpgeorge commented Apr 4, 2017

@MrSurly for the pin wake up, the API was originally designed to allow something like:

import machine
p = machine.Pin(0, machine.Pin.IN)
p.irq(trigger=p.IRQ_RISING, wake=machine.DEEPSLEEP)
machine.deepsleep()

That's how it works on WiPy 1.0. That also mirrors how RTC can be used to wake after a specified time.

Would it be best to have a high-level "deep sleep" μPy interface (perhaps time.deepsleep(milliseconds)) that "just works", yet keep the machine.RTC interface as being machine-specific?

That way users who "just want to save power for a set period of time" can do so easily, across platforms, but users who want to use different features of their particular hardware (e.g. touch wake on ESP32) can do so.

Yes, perhaps it is a good idea to allow to pass a time to the deepsleep() function. I think @pfalcon had this idea long ago but I argued against it back then.

@nickzoic
Copy link
Collaborator

nickzoic commented Apr 4, 2017 via email

@igrr
Copy link

igrr commented Apr 12, 2017

Folks, just want to let you know that another kind of sleep (light sleep) will land into the IDF soon-ish. In the most basic case of light sleep, CPUs are halted and memory is preserved. All wakeup sources supported for deep sleep are also supported for light sleep. You may want to take this into consideration when designing the API.
In IDF, we will have to rename a bunch of esp_deep_sleep_ functions into esp_sleep_ when this is implemented.

@MrSurly
Copy link
Contributor

MrSurly commented Apr 12, 2017

@igrr On a wake from light sleep, does the CPU reset, or does it resume where it left off? Something else?

@igrr
Copy link

igrr commented Apr 12, 2017

By default, it continues from where it stopped. Resetting is also possible, if required.

@dpgeorge
Copy link
Member

@igrr thanks for the info! Light sleep will definitely be useful.

@MrSurly
Copy link
Contributor

MrSurly commented Apr 13, 2017 via email

@MrSurly
Copy link
Contributor

MrSurly commented May 10, 2017

@dpgeorge , @nickzoic

Any further thoughts? The API as it is now matches other implementations, but doesn't make much sense for the ESP32.

@nickzoic
Copy link
Collaborator

nickzoic commented May 11, 2017 via email

@MrSurly
Copy link
Contributor

MrSurly commented May 11, 2017

This is what I'm submitting, for now; not implementing wake-on-touch just yet, need to dig deeper.

EXT0 wake:

import machine as m
p = m.Pin(4)
r = m.RTC()
p.init(p.IN, p.PULL_UP)
r.wake_on_ext0(pin = p, level = 0)
m.deepsleep()

EXT1 wake:

import machine as m
pins = (m.Pin(2), m.Pin(4))
for p in pins: 
    p.init(m.Pin.IN, m.Pin.PULL_UP)
r = m.RTC()
r.wake_on_ext1(pins = pins, level = r.WAKEUP_ALL_LOW)
m.deepsleep()

Timer wake:

import machine as m
m.deepsleep(sleep_ms = 3000)

@MrSurly
Copy link
Contributor

MrSurly commented May 11, 2017

PR #85

@pacmac
Copy link
Author

pacmac commented Jun 12, 2017

Been a long time since the PR, is this going to happen ??

I have a few 8266 projects that are just waiting to convert to esp32, but deepsleep() is pretty much essential.

@dpgeorge dpgeorge mentioned this issue Sep 12, 2017
@MrSurly
Copy link
Contributor

MrSurly commented Sep 12, 2017

@igrr Any word on light sleep?

@igrr
Copy link

igrr commented Sep 12, 2017

Light sleep available is in ESP-IDF master: http://esp-idf.readthedocs.io/en/latest/api-reference/system/sleep_modes.html. Let me know if you see any issues with that.

Note that we still plan to optimize current consumption a bit, since the present value (1.1mA) is higher than the one advertised in the datasheet (0.8mA).

@manningt
Copy link

manningt commented Sep 30, 2017

Thanks for the deep sleep implementation.

Do you get the following during the restart after a deepsleep?

  • flash read err, 1000
  • TG0WDT_SYS_RESET

According to the ESP-IDF docs, a WDT causes the ESP to initialize RTC memory.
The TGOWDT/deepsleep issue was on the forum: https://esp32.com/viewtopic.php?t=1568

Here is my deepsleep trace:

>>> machine.deepsleep(3000)
ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
Falling back to built-in command interpreter.
OK
>ets Jun  8 2016 00:22:57

rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0010,len:4
load:0x3fff0014,len:4260
load:0x40078000,len:0
load:0x40078000,len:10648
entry 0x4007a56c
␛[0;33mW (48) boot: PRO CPU has been reset by WDT.␛[0m
␛[0;33mW (48) boot: WDT reset info: PRO CPU PC=0x68514429␛[0m
␛[0;33mW (48) boot: WDT reset info: APP CPU PC=0x4c694dae␛[0m
␛[0;32mI (1711) cpu_start: Pro cpu up.␛[0m
␛[0;32mI (1712) cpu_start: Single core mode␛[0m
␛[0;32mI (1714) heap_init: Initializing. RAM available for dynamic allocation:␛[0m
␛[0;32mI (1750) heap_init: At 3FFAE2A0 len 00001D60 (7 KiB): DRAM␛[0m
␛[0;32mI (1806) heap_init: At 3FFD4190 len 0000BE70 (47 KiB): DRAM␛[0m
␛[0;32mI (1864) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM␛[0m
␛[0;32mI (1923) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM␛[0m
␛[0;32mI (1983) heap_init: At 4008F764 len 0001089C (66 KiB): IRAM␛[0m
␛[0;32mI (2041) cpu_start: Pro cpu start user code␛[0m
␛[0;32mI (2201) cpu_start: Starting scheduler on PRO CPU.␛[0m
OSError: [Errno 2] ENOENT
MicroPython v1.9.2-272-g0d183d7f-dirty on 2017-09-30; ESP32 module with ESP32
Type "help()" for more information.
>>>

@MrSurly
Copy link
Contributor

MrSurly commented Sep 30, 2017

Sounds similar to #110

@manningt
Copy link

manningt commented Oct 2, 2017

The fix for the flash read err on the deep sleep wakeup is to change CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY from 0 to 2000 in sdkconfig.h
Fixing the flash read err eliminates the TG0WDT_SYS_RESET.

@MrSurly
Copy link
Contributor

MrSurly commented Oct 5, 2017

@manningt Thanks for this. When I get around to fixing this up, I'll use this.

@choies1
Copy link

choies1 commented Oct 8, 2017

I am using ESP32 board(ESP-WROOM-32). I downloaded the recent binary image(esp32-20171008-v1.9.2-276-ga9517c04.bin) for ESP32 boards (https://micropython.org/download#esp32).

I could run codes for GPIO, SPI, and Wifi. However, some methods ( machine.RTC(), utime.localtime()) do not work as follows:

>>> import machine
>>> machine.RTC()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'RTC'
>>>
>>> machine.deepsleep(sleep_ms = 3000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'deepsleep'
>>> 
>>> import utime
>>> utime.localtime()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'localtime'
>>>

Please let me know how I can work these methods.

@MrSurly
Copy link
Contributor

MrSurly commented Oct 9, 2017

However, some methods ( machine.RTC(), utime.localtime()) do not work as follows:

machine.RTC() and machine.deepsleep() are part of a PR that has not been merged into the ESP32 port.

utime.localtime is not implemented in the ESP32 port, though it probably should be.

@nickzoic
Copy link
Collaborator

nickzoic commented Oct 9, 2017 via email

@manningt
Copy link

manningt commented Oct 9, 2017

I added the RTC memory implementation to machine_rtc.c, as per the attached file.
The RTC memory is preserved through deepsleep. Not sure if this could be added to the pull.
machine_rtc.c.zip

@MrSurly
Copy link
Contributor

MrSurly commented Oct 9, 2017

@manningt

Thanks for this; I'll have time to look at it next week, probably.

@MrSurly
Copy link
Contributor

MrSurly commented Nov 6, 2017

@manningt

Thank you very much for your contributions. The memory bit works flawlessly, and the bit WRT CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY was critical.

Merging this now; added your name/email to the RTC module copyright, if that's okay:

* Copyright (c) 2017 "Tom Manning" <tom@manningetal.com>

@MrSurly
Copy link
Contributor

MrSurly commented Nov 6, 2017

@manningt Any luck on getting light sleep to work? It "works" in that it will sleep for the time specified, but then I get a screenfull of gibberish on the serial, then a WDT reset. Memory is not preserved.

@manningt
Copy link

manningt commented Nov 6, 2017

@MrSurly Glad to contribute. I needed both deep-sleep and RTC memory for my application, i.e. selfish motivation. Thanks for adding me on the copyright. I did not test light-sleep, although it looks like may not be ready yet according to this ESP32 forum post.

@MrSurly
Copy link
Contributor

MrSurly commented Nov 6, 2017

Glad to contribute. I needed both deep-sleep and RTC memory for my application, i.e. selfish motivation

As is most of what I'm doing =)

although it looks like may not be ready yet according to this ESP32 forum post.

Wow, those last two posts are nearly 2 years apart.

@robert-hh
Copy link
Contributor

robert-hh commented Nov 6, 2017

You looked at the "joined" dates. Happens to me all the time too.

@MrSurly
Copy link
Contributor

MrSurly commented Nov 6, 2017

@robert-hh I sure did =)

@yomasa
Copy link

yomasa commented Dec 1, 2017

RTC?

@nickzoic
Copy link
Collaborator

This has moved to micropython/micropython#3531 , closing it here.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants