Skip to content

modmachine: Implemented GPIO wake-up for ESP32-C3. #9583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from

Conversation

ondiiik
Copy link

@ondiiik ondiiik commented Oct 11, 2022

As ESP32-C3 uses GPIO wake-up instead of EXT0/1 wake-up, several small changes in code was required. Interface stays the same as for generic ESP32 but EXT0 is ignored and EXT1 is used as wake-up source.

Signed-off-by: Ondrej Sienczak ondrej.sienczak@gmail.com

As ESP32-C3 uses GPIO wake-up instead of EXT0/1 wake-up,
several small changes in code was required. Interface stays
the same as for generic ESP32 but EXT0 is ignored and EXT1
is used as wake-up source.

Signed-off-by: Ondrej Sienczak <ondrej.sienczak@gmail.com>
@puppet13th
Copy link

puppet13th commented Oct 12, 2022

i just submitted #9578 about esp32-c3 wake pin not working.hopefully this commit will get merged soon.btw any updates on which pin can be used for wakeup? currently only pin 0,2,4 are useable.
according to this useable wakeup pin 0 to pin 5.

@sienczak
Copy link

sienczak commented Oct 12, 2022

i just submitted #9578 about esp32-c3 wake pin not working.hopefully this commit will get merged soon.btw any updates on which pin can be used for wakeup? currently only pin 0,2,4 are useable. according to this useable wakeup pin 0 to pin 5.

Exactly. Only RTC pins are possible to be used as wake-up sources. I have implemented check if all pins in mask are capable to be set as wake-up sources, otherwise exception is raised when you attempt to sleep.

@puppet13th
Copy link

i just submitted #9578 about esp32-c3 wake pin not working.hopefully this commit will get merged soon.btw any updates on which pin can be used for wakeup? currently only pin 0,2,4 are useable. according to this useable wakeup pin 0 to pin 5.

Exactly. Only RTC pins are possible to be used as wake-up sources. I have implemented check if all pins in mask are capable to be set as wake-up sources, otherwise exception is raised when you attempt to sleep.

Care to share which one have you implemented about the wake pins?

@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

i just submitted #9578 about esp32-c3 wake pin not working.hopefully this commit will get merged soon.btw any updates on which pin can be used for wakeup? currently only pin 0,2,4 are useable. according to this useable wakeup pin 0 to pin 5.

Exactly. Only RTC pins are possible to be used as wake-up sources. I have implemented check if all pins in mask are capable to be set as wake-up sources, otherwise exception is raised when you attempt to sleep.

Care to share which one have you implemented about the wake pins?

Any of them. You can use any pin to set it as wake-up source. Pin is assigned to wake-up sources bitmap. When you attempt to sleep, micropython checks if all of this pins are supported as valid wake-up sources and go to deep/light sleep. If any of this pins is not supported, then deep/light sleep raises value error with message "invalid wake-up pin". Check if pin is supported or not is done by espidf function. This means that if we have some other ESP-like chip in future capable of GPIO wake-up, we can reuse this piece of code without any change.

@puppet13th
Copy link

Any of them. You can use any pin to set it as wake-up source. Pin is assigned to wake-up sources bitmap. When you attempt to sleep, micropython checks if all of this pins are supported as valid wake-up sources and go to deep/light sleep. If any of this pins is not supported, then deep/light sleep raises value error with message "invalid wake-up pin". Check if pin is supported or not is done by espidf function. This means that if we have some other ESP-like chip in future capable of GPIO wake-up, we can reuse this piece of code without any change.

thanks, that nice.will try later and report back.currently no c3 board at hand.

@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

Any of them. You can use any pin to set it as wake-up source. Pin is assigned to wake-up sources bitmap. When you attempt to sleep, micropython checks if all of this pins are supported as valid wake-up sources and go to deep/light sleep. If any of this pins is not supported, then deep/light sleep raises value error with message "invalid wake-up pin". Check if pin is supported or not is done by espidf function. This means that if we have some other ESP-like chip in future capable of GPIO wake-up, we can reuse this piece of code without any change.

thanks, that nice.will try later and report back.currently no c3 board at hand.

I tried following code snippet. It is just for one pin, but it can be easily extended for more. ESP32-C3 went to deep sleep and has been woken-up when 3,3V has been connected to IO2:

MicroPython ab317a0d6-dirty on 2022-10-12; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> import esp32
>>> from machine import Pin
>>> from machine import deepsleep
>>> wake1 = Pin(4, mode=Pin.IN)
>>> esp32.wake_on_ext1(pins=(wake1,), level=esp32.WAKEUP_ANY_HIGH)
>>> deepsleep()
ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd6100,len:0xe3c
load:0x403ce000,len:0x6f4
load:0x403d0000,len:0x28ec
entry 0x403ce000
MicroPython ab317a0d6-dirty on 2022-10-12; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> 

@puppet13th
Copy link

puppet13th commented Oct 12, 2022

example code:

import machine
import esp32
from time import ticks_ms

def wake_msg():
    reset_cause = machine.reset_cause()
    if reset_cause is machine.HARD_RESET:
        reset_cause = 'HARD_RESET'
    elif reset_cause is machine.PWRON_RESET:
        reset_cause = 'PWRON_RESET'
    elif reset_cause is machine.WDT_RESET:
        reset_cause = 'WDT_RESET'
    elif reset_cause is machine.DEEPSLEEP_RESET:
        reset_cause = 'DEEPSLEEP_RESET'
    elif reset_cause is machine.SOFT_RESET:
        reset_cause = 'SOFT_RESET'
        
    wake_reason = machine.wake_reason()
    if wake_reason is machine.PIN_WAKE:
        wake_reason = 'PIN_WAKE'
    elif wake_reason is machine.EXT0_WAKE:
        wake_reason = 'EXT0_WAKE'
    elif wake_reason is machine.EXT1_WAKE:
        wake_reason = 'EXT1_WAKE'
    elif wake_reason is machine.TIMER_WAKE:
        wake_reason = 'TIMER_WAKE'
    elif wake_reason is machine.TOUCHPAD_WAKE:
        wake_reason = 'TOUCHPAD_WAKE'
    elif wake_reason is machine.ULP_WAKE:
        wake_reason = 'ULP_WAKE'
    return reset_cause,wake_reason

pin_list = [ 0, 1 ,2, 3, 4, 5 ] #C3

for num in pin_list:
    try:
        wake_pin = machine.Pin(num,machine.Pin.IN)
        esp32.wake_on_ext0(pin = wake_pin, level = esp32.WAKEUP_ANY_HIGH)
    except Exception as exc:
        print(num,exc)
    else:
        ticks = ticks_ms()
        before_sleep = wake_pin()
        machine.lightsleep(5000)
        ticks = ticks_ms() - ticks
        print(num,before_sleep,wake_pin(),wake_msg(),ticks)

for num in pin_list:
    try:
        wake_pin = machine.Pin(num,machine.Pin.IN)
        esp32.wake_on_ext1(pins = (wake_pin,), level = esp32.WAKEUP_ANY_HIGH)
    except Exception as exc:
        print(num,exc)
    else:
        ticks = ticks_ms()
        before_sleep = wake_pin()
        machine.lightsleep(5000)
        ticks = ticks_ms() - ticks
        print(num,before_sleep,wake_pin(),wake_msg(),ticks)

code output:

MicroPython ab317a0d6 on 2022-10-12; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
0 1 1 ('SOFT_RESET', 'TIMER_WAKE') 5000
1 invalid pin
2 1 1 ('SOFT_RESET', 'TIMER_WAKE') 5000
3 invalid pin
4 1 1 ('SOFT_RESET', 'TIMER_WAKE') 5000
5 invalid pin
0 1 1 ('SOFT_RESET', 'TIMER_WAKE') 5000
1 invalid pin
2 1 1 ('SOFT_RESET', 'TIMER_WAKE') 5000
3 invalid pin
4 1 1 ('SOFT_RESET', 'TIMER_WAKE') 5000
5 invalid pin

all wakeup from timer

expected output:

MicroPython v1.19.1-528-gb8982ec5f on 2022-10-09; LOLIN S3 with ESP32S3
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
0 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
1 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
2 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
3 1 1 ('SOFT_RESET', 'PIN_WAKE') 2
4 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
5 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
0 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
1 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
2 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
3 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
4 1 1 ('SOFT_RESET', 'PIN_WAKE') 1
5 1 1 ('SOFT_RESET', 'PIN_WAKE') 1

with your code:

import esp32
from machine import Pin
from machine import deepsleep
wake1 = Pin(4, mode=Pin.IN)
esp32.wake_on_ext1(pins=(wake1,), level=esp32.WAKEUP_ANY_HIGH)
deepsleep()

output:

ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x5 (DSLEEP),boot:0xf (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd6100,len:0xe3c
load:0x403ce000,len:0x6f4
load:0x403d0000,len:0x28d8
entry 0x403ce000
('DEEPSLEEP_RESET', 'TIMER_WAKE') <<-- TIMER_WAKE???
MicroPython ab317a0d6 on 2022-10-12; ESP32C3 module with ESP32C3
Type "help()" for more information.

dont know why it's not working for me.with your code,it will not wakeup when using lightsleep()

wiring:
pin(0,1,2,3,4,5) <-> R1 <-> v3.3

@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

There are some missunderstandings and some faulty use. As I originally wanted to preserve interface same as with regular esp32, I decided to ignore ext0 and use ext1 as gpio wake-up mask. This means that ext0 is ignored right now so this part of test will never pass.

Then I tried again the example I posted above and it works to me. With deepsleep the reset_cause showed me DEEPSLEEP_RESET. However as wake reason is returned number 7 which does not match to any of supported wake-up reasons. The reason of this behavior is probably that gpio weak-up uses different reason ID then ext1 reason ID. However processor woken-up from deep sleep without any problems.

Then I tried also lightsleep. It was working as well, with difference that reset_cause showed me SOFT_RESET.

At the end I made more deeper test:

import esp32
import machine
from time import sleep_ms

rc2str = {getattr(machine, i): i for i in ('PWRON_RESET',
                                           'HARD_RESET',
                                           'WDT_RESET',
                                           'DEEPSLEEP_RESET',
                                           'SOFT_RESET')}

ws2str = {getattr(machine, i): i for i in ('EXT0_WAKE',
                                           'EXT1_WAKE',
                                           'TIMER_WAKE',
                                           'TOUCHPAD_WAKE',
                                           'ULP_WAKE')}
ws2str[7] = '(hidden ESP_SLEEP_WAKEUP_GPIO)'

wake1 = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_DOWN)
esp32.wake_on_ext1(pins=(wake1,), level=esp32.WAKEUP_ANY_HIGH)

print('Zzzzz ...')
sleep_ms(100)

machine.lightsleep(10000)

rc = machine.reset_cause()
ws = machine.wake_reason()

print('Woken-up',
      rc2str.get(rc, str(rc)),
      ws2str.get(ws, str(ws)))

The result is following:

>>> %Run -c $EDITOR_CONTENT
Zzzzz ...
Woken-up SOFT_RESET (hidden ESP_SLEEP_WAKEUP_GPIO)
>>> 

This means that I have to add at least WAKEUP_GPIO representation into module machine. In this case another question comes up - shall we change API to prevent from confusions in future?

Anyway, the basics are working to me fine, however I'll do similar test like you as here is still not full explanation of your behavior.

Wake-up reason for pin wake-up is not EXT1 but GPIO on ESP32-C3.

Signed-off-by: Ondrej Sienczak <ondrej.sienczak@gmail.com>
@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

Anyway, I patched GPIO vs. EXT1 issue - 848805e:

import esp32
import machine
from time import sleep_ms

rc2str = {getattr(machine, i): i for i in ('PWRON_RESET',
                                           'HARD_RESET',
                                           'WDT_RESET',
                                           'DEEPSLEEP_RESET',
                                           'SOFT_RESET')}

ws2str = {getattr(machine, i): i for i in ('EXT0_WAKE',
                                           'EXT1_WAKE',
                                           'TIMER_WAKE',
                                           'TOUCHPAD_WAKE',
                                           'ULP_WAKE')}

wake1 = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_DOWN)
esp32.wake_on_ext1(pins=(wake1,), level=esp32.WAKEUP_ANY_HIGH)

print('Zzzzz ...')
sleep_ms(100)

machine.lightsleep(10000)

rc = machine.reset_cause()
ws = machine.wake_reason()

print('Woken-up',
      rc2str.get(rc, str(rc)),
      ws2str.get(ws, str(ws)))

So the result is now correct

>>> %Run -c $EDITOR_CONTENT
Zzzzz ...
Woken-up SOFT_RESET EXT1_WAKE
>>> 

@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

I also found the reason why you got "invalid pin". There is embedded check in pins assignment which does not fit on ESP32C3. I'll fix it.

There have been incorrect test of used EXT1 (GPIO) wake-up capable
(RTC) pins for ESP32-C3.

Signed-off-by: Ondrej Sienczak <ondrej.sienczak@gmail.com>
@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

I made second patch (7411293) related to EXT1 (GPIO) wake-up capable pins. Finaly it seems that there is no need to change original ESP32 inteface.

@puppet13th - could you please test with your code again please?

@puppet13th
Copy link

I made second patch (7411293) related to EXT1 (GPIO) wake-up capable pins. Finaly it seems that there is no need to change original ESP32 inteface.

@puppet13th - could you please test with your code again please?

will retest with latest changes.

@puppet13th
Copy link

from the reference look like any gpio pin can be used as (lightsleep) wake up pin.

@puppet13th
Copy link

have retest your latest changes but still got different result:

MicroPython ab317a0d6 on 2022-10-12; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
Zzzzz ...
Woken-up SOFT_RESET TIMER_WAKE

wiring : v3.3 -- R1 -- pin(4)
board : ai-thinker esp32-c3s

i mean time i made changes at modmachine.c:

    #if CONFIG_IDF_TARGET_ESP32C3

    if (machine_rtc_config.ext0_pin != -1) {
        gpio_sleep_set_direction((gpio_num_t)machine_rtc_config.ext0_pin, GPIO_MODE_INPUT);
        esp_deep_sleep_enable_gpio_wakeup(
            machine_rtc_config.ext0_pin,
            machine_rtc_config.ext0_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        gpio_wakeup_enable(machine_rtc_config.ext0_pin,
            machine_rtc_config.ext0_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        esp_sleep_enable_gpio_wakeup();
    }

    if (machine_rtc_config.ext1_pins != -1) {
        gpio_sleep_set_direction((gpio_num_t)machine_rtc_config.ext1_pins, GPIO_MODE_INPUT);
        esp_deep_sleep_enable_gpio_wakeup(
            machine_rtc_config.ext1_pins,
            machine_rtc_config.ext1_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        gpio_wakeup_enable(machine_rtc_config.ext1_pins,
            machine_rtc_config.ext1_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        esp_sleep_enable_gpio_wakeup();
    }

    #else

code output :

MicroPython v1.19.1-528-gb8982ec5f-dirty on 2022-10-12; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
Zzzzz ...
Woken-up SOFT_RESET 0

@sienczak
Copy link

sienczak commented Oct 12, 2022

I'm using ESP-C3-12F.

Suggested changes can be missleading as there is no EXT0 and EXT1, but only GPIO wake-up. Then I'm not sure if it is good idea to mix them as someone may use different setupt for EXT0 and EXT1. I would rather see that EXT0 throws exception - something like "unsupported" and keep EXT1 as wake-up sources in similar logic as with regular ESP32.

Regarding use of -1, I have to check the code if -1 is only meant as initial value or if it is used in general to disable feature.

@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

have retest your latest changes but still got different result:

i mean time i made changes at modmachine.c:

    #if CONFIG_IDF_TARGET_ESP32C3

    if (machine_rtc_config.ext0_pin != -1) {
        gpio_sleep_set_direction((gpio_num_t)machine_rtc_config.ext0_pin, GPIO_MODE_INPUT);
        esp_deep_sleep_enable_gpio_wakeup(
            machine_rtc_config.ext0_pin,
            machine_rtc_config.ext0_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        gpio_wakeup_enable(machine_rtc_config.ext0_pin,
            machine_rtc_config.ext0_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        esp_sleep_enable_gpio_wakeup();
    }

    if (machine_rtc_config.ext1_pins != -1) {
        gpio_sleep_set_direction((gpio_num_t)machine_rtc_config.ext1_pins, GPIO_MODE_INPUT);
        esp_deep_sleep_enable_gpio_wakeup(
            machine_rtc_config.ext1_pins,
            machine_rtc_config.ext1_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        gpio_wakeup_enable(machine_rtc_config.ext1_pins,
            machine_rtc_config.ext1_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
        esp_sleep_enable_gpio_wakeup();
    }

    #else

I'm afraid that this changes are not good idea from the reason I mentioned above (mixing of EXT0 and EXT1). We also shall not compare ext1_pins with -1 as it is defined as unsigned type and it is used as bit array.

@puppet13th
Copy link

I'm using ESP-C3-12F.

Suggested changes can be missleading as there is no EXT0 and EXT1, but only GPIO wake-up. Then I'm not sure if it is good idea to mix them as someone may use different setupt for EXT0 and EXT1. I would rather see that EXT0 throws exception - something like "unsupported" and keep EXT1 as wake-up sources in similar logic as with regular ESP32.

Regarding use of -1, I have to check the code if -1 is only meant as initial value or if it is used in general to disable feature.

for the changes I made, ext0 does not works as intended. I agree that calling esp32.ext0 should throws exception.

@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

from the reference look like any gpio pin can be used as (lightsleep) wake up pin.

It seems to be true. There is used different approach for deep sleep and light sleep. The question is how to handle it directly. Pin checking in time of setting of EXT1 is not sufficient in this case as it may differ based on usage. So I'll have to move it into machine_sleep_helper based on sleep type.

With ESP32-C3 there is different approach for setting wake-up pins.

Signed-off-by: Ondrej Sienczak <ondrej.sienczak@gmail.com>
@ondiiik
Copy link
Author

ondiiik commented Oct 12, 2022

I have modified approach for setting pins for the case of deep and light sleep - 4c7c2b0

@puppet13th - I found that all works perfectly only when I set all the stuff just once after reset. Problem seems to be somewhere inside espidf so I have no way to work around. The suggested strategy for light sleep is to set up wake-up pins just once at the beginning and not to modify them later. I tested it and it worked fine for me. In the case of deep sleep this is not an issue as it always leeds to processor reset.

@puppet13th
Copy link

puppet13th commented Oct 13, 2022

what do you mean?

I found that all works perfectly only when I set all the stuff just once after reset.

all look good now:

  1. lightsleep works on any gpio pin
  2. deepsleep works on rtc gpio pin
  3. when calling ext0 mpy throws exception

however calling deepsleep on non rtc_gpio does not throw error. review

@ondiiik
Copy link
Author

ondiiik commented Oct 13, 2022

what do you mean?

I found that all works perfectly only when I set all the stuff just once after reset.

all look good now:

  1. lightsleep works on any gpio pin
  2. deepsleep works on rtc gpio pin
  3. when calling ext0 mpy throws exception

however calling deepsleep on non rtc_gpio does not throw error. review

I agree, it shall rather throw an exception then silently proceed non-functional code. I'll add this check to code as well.

The light sleep on ESP32-C3 is allowed on all IO pins, however deepsleep
on RTC pins only. This patch raises exception when deepsleep is invoked
and non-RTC pins are used as wake-up sources.

Signed-off-by: Ondrej Sienczak <ondrej.sienczak@gmail.com>
@puppet13th
Copy link

puppet13th commented Oct 14, 2022

I agree, it shall rather throw an exception then silently proceed non-functional code. I'll add this check to code as well.

MicroPython 45f80abd6 on 2022-10-14; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
Zzzzz ...
Woken-up SOFT_RESET TIMER_WAKE
Traceback (most recent call last):
  File "<stdin>", line 32, in <module>
ValueError: wake-up pin not supported

we should have resolved all issue regarding esp32-c3 wake-pin. waiting to be merged. :)

Comment on lines 140 to 149
for (int i = 0; i < GPIO_NUM_MAX; ++i) {
uint64_t bm = 1ULL << i;

if (machine_rtc_config.ext1_pins & bm) {
if (!esp_sleep_is_valid_wakeup_gpio((gpio_num_t)i)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid wake-up port"));
}
gpio_sleep_set_direction((gpio_num_t)i, GPIO_MODE_INPUT);
}
}

Choose a reason for hiding this comment

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

i think this code should belong to precheck when defining esp32.wake_ext1.the one that throw exception "invalid pin" reference

Copy link
Author

Choose a reason for hiding this comment

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

The problem here is that this deffinition may differ for use of light and deep sleep (as you stated above that for light sleep there can be used also non-RTC pins). If we would like to have pre-check, then we have to modify interface and define e.g. esp32.wake_gpio_lightsleep and esp32.wake_gpio_deepsleep instead of one shared esp32.wake_ext1. Or add optional argument to esp32.deepsleep/esp32.lightsleep, who knows 😉. But this is more question for someone responsible for esp module architecture.

machine_rtc_config.ext1_level ? ESP_GPIO_WAKEUP_GPIO_HIGH : ESP_GPIO_WAKEUP_GPIO_LOW);
esp_sleep_enable_gpio_wakeup();

if (MACHINE_WAKE_DEEPSLEEP == wake_type) {

Choose a reason for hiding this comment

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

maybe we should use esp_sleep_is_valid_wakeup_gpio to check if pin used for deepsleep is rtc gpio pin(wake from deepsleep can only triggered from rtc gpio)

Copy link
Author

Choose a reason for hiding this comment

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

I checked ESPIDF code and esp_deep_sleep_enable_gpio_wakeup does this check internally as one of first operation.

However there are also some other operation which may also return failure and there is no diostinguish between failure type (it unfortunatelly returns always the same error code). This means that reason of failure can be caused also by other failure then use of incorrect wake-up GPIO and then the message about incorrect pin in exception can be missleading.

@ondiiik
Copy link
Author

ondiiik commented Jun 12, 2023

It shall be working in principle, but there were some suggestions for modification of code and fixing the documentation from you.

I don't know exactly the merge procedure, what needs to be done to merge this code changes, however I would expect that at least documentation shall be fixed 😉

@meirarmon
Copy link

I can try making the documentation fixes, and see what needs to be done. Do you want me to push to your branch, or should I open a new PR? It seems a shame to open a new PR and lose this thread.

@ondiiik
Copy link
Author

ondiiik commented Jun 12, 2023

@meirarmon Thanks! Feel free to push my branch and change anything necessary. I just set you as an collaborator, so you should be able to do changes if necessary.

@puppet13th
Copy link

@ondiiik i just realized that lightsleep did not update wake_reason but deepsleep did.

Woken-up PWRON_RESET 0 <<<=== fresh poweron
Zzzzz ...
Woken-up PWRON_RESET TIMER_WAKE <<<=== wakeup from timer
1001
Zzzzz ...
Woken-up PWRON_RESET TIMER_WAKE <<<=== wakeup from pin( lightsleep )
102
>>> machine.deepsleep( 1000 )
ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x5 (DSLEEP),boot:0xd (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd6100,len:0xe3c
load:0x403ce000,len:0x6f4
load:0x403d0000,len:0x28d8
entry 0x403ce000
MicroPython 45f80abd6 on 2022-11-23; ESP32C3 module with ESP32C3
Type "help()" for more information.
>>> %run -c $EDITOR_CONTENT
Woken-up DEEPSLEEP_RESET (hidden ESP_SLEEP_WAKEUP_GPIO) <<<=== wakeup from pin( deepsleep updates wake_reason )
Zzzzz ...
Woken-up DEEPSLEEP_RESET TIMER_WAKE
1001
Zzzzz ...
Woken-up DEEPSLEEP_RESET TIMER_WAKE
102

i modified from your example code:

import esp32
import machine
from time import sleep_ms, ticks_ms

rc2str = {getattr(machine, i): i for i in ('PWRON_RESET',
                                           'HARD_RESET',
                                           'WDT_RESET',
                                           'DEEPSLEEP_RESET',
                                           'SOFT_RESET')}

ws2str = {getattr(machine, i): i for i in ('EXT0_WAKE',
                                           'EXT1_WAKE',
                                           'TIMER_WAKE',
                                           'TOUCHPAD_WAKE',
                                           'ULP_WAKE')}
ws2str[7] = '(hidden ESP_SLEEP_WAKEUP_GPIO)'

def msg():
    rc = machine.reset_cause()
    ws = machine.wake_reason()

    print('Woken-up',
          rc2str.get(rc, str(rc)),
          ws2str.get(ws, str(ws)))

msg()

print('Zzzzz ...')
sleep_ms(100)

ticks = ticks_ms()
machine.lightsleep(1000)
msg()
print( ticks_ms() - ticks )

wake1 = machine.Pin(1)
esp32.wake_on_ext1(pins=(wake1,), level=esp32.WAKEUP_ALL_LOW)

ticks = ticks_ms()
print('Zzzzz ...')
sleep_ms(100)

machine.lightsleep(1000)
msg()
print( ticks_ms() - ticks )

@timshac
Copy link

timshac commented Jul 10, 2023

I'd be happy to see this merged and released.

@vencax
Copy link

vencax commented Aug 29, 2023

When will this merge please?

@brianwyld
Copy link

maybe coming to this discussion a bit late, but I think there is a fundamental difference between the way I would expect the 'wakeup' from a gpio in lightsleep and deepsleep cases.

In deepsleep, the system is restarted, so we can only check the wake reason and get a gpio pin list - we don't have any of the previous context about Pin objects, callbacks etc.

In lightsleep, the system is essentially just 'suspended' (for power consumption reasons), so when the lightsleep returns we want to execute the 'wake reason' (timer or gpio) as though nothing has happened - the timer callback gets called, or the gpio irq callback gets called. No need in the python code to check wake reasons, pin lists etc - the micropython system should just call my callbacks as though no sleeping had happened....

My python code may indeed have to decide to use the wake_on_ext1() or wake_on_gpio() cases specific for an ESP32 (so these should be in the esp32 module), but for 'lightsleep()' it doesn't need to call any esp32 specific code - the internals of machine module should deal with it!

btw this is also IMHO how the Timer alarms should be working - if I lightsleep(), then any running Timer.Alarm should be taken into account to set the actual duration of the sleep, so that the timer callback happens at the 'right' wall-clock time...

my 2c

@vencax
Copy link

vencax commented Sep 6, 2023

My python code may indeed have to decide to use the wake_on_ext1() or wake_on_gpio()

That's now not true, wake from deepsleep is not working now. Its waiting for this PR to merge. Am I right?

@perdix1975
Copy link

My python code may indeed have to decide to use the wake_on_ext1() or wake_on_gpio()

That's now not true, wake from deepsleep is not working now. Its waiting for this PR to merge. Am I right?

I can't wake it up with gpio either!

@brianwyld
Copy link

I use the attached code as an extmod to let me set wakeup pins for the LIGHT sleep case on both esp32 and esp32S3:

STATIC mp_obj_t esp32ext_wake_on_gpios(mp_obj_t list_obj) {
    if (!mp_obj_is_type(list_obj, &mp_type_list)) {
        mp_raise_TypeError("list expected");
    }
    mp_obj_list_t* list = MP_OBJ_TO_PTR(list_obj);
    // Check all params before setting up any hw to avoid leaving it in strange state
    for (uint32_t i=0; i<list->len; i++) {
        if (!mp_obj_is_type(list->items[i], &mp_type_tuple)) {
            mp_raise_TypeError("list of tuples expected");
        }
        mp_obj_tuple_t* tuple = MP_OBJ_TO_PTR(list->items[i]);
        if (tuple->len!=2) {
            mp_raise_TypeError("list of 2-tuples expected");
        }
        gpio_num_t pin_id = machine_pin_get_id(tuple->items[0]);
        if (pin_id<0) {
            mp_raise_ValueError("unknown pin");
        }
        uint32_t level = mp_obj_get_int(tuple->items[1]);
        if ((level!=ESP_LIGHTSLEEP_WAKEUP_LOW) && (level!=ESP_LIGHTSLEEP_WAKEUP_HIGH)) {
            mp_raise_ValueError("wrong level (0 or 1 expected)");
        }
    }
    // Disable all previous gpio enables? Not required apparently, they are reset at each sleep

    // Enable the ones in the list
    for (uint32_t i=0; i<list->len; i++) {
        mp_obj_tuple_t* tuple = MP_OBJ_TO_PTR(list->items[i]);
        gpio_num_t pin_id = machine_pin_get_id(tuple->items[0]);
        gpio_int_type_t level = mp_obj_get_int(tuple->items[1]);
        gpio_wakeup_enable(pin_id, level+GPIO_INTR_LOW_LEVEL);
        gpio_wakeup_source_flags_set(pin_id);
    }
    esp_sleep_enable_gpio_wakeup();
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32ext_wake_on_gpios_obj, esp32ext_wake_on_gpios);

It works for me, your milage may vary.

@puppet13th
Copy link

puppet13th commented Nov 15, 2023

I use the attached code as an extmod to let me set wakeup pins for the LIGHT sleep case on both esp32 and esp32S3:

STATIC mp_obj_t esp32ext_wake_on_gpios(mp_obj_t list_obj) {
    if (!mp_obj_is_type(list_obj, &mp_type_list)) {
        mp_raise_TypeError("list expected");
    }
    mp_obj_list_t* list = MP_OBJ_TO_PTR(list_obj);
    // Check all params before setting up any hw to avoid leaving it in strange state
    for (uint32_t i=0; i<list->len; i++) {
        if (!mp_obj_is_type(list->items[i], &mp_type_tuple)) {
            mp_raise_TypeError("list of tuples expected");
        }
        mp_obj_tuple_t* tuple = MP_OBJ_TO_PTR(list->items[i]);
        if (tuple->len!=2) {
            mp_raise_TypeError("list of 2-tuples expected");
        }
        gpio_num_t pin_id = machine_pin_get_id(tuple->items[0]);
        if (pin_id<0) {
            mp_raise_ValueError("unknown pin");
        }
        uint32_t level = mp_obj_get_int(tuple->items[1]);
        if ((level!=ESP_LIGHTSLEEP_WAKEUP_LOW) && (level!=ESP_LIGHTSLEEP_WAKEUP_HIGH)) {
            mp_raise_ValueError("wrong level (0 or 1 expected)");
        }
    }
    // Disable all previous gpio enables? Not required apparently, they are reset at each sleep

    // Enable the ones in the list
    for (uint32_t i=0; i<list->len; i++) {
        mp_obj_tuple_t* tuple = MP_OBJ_TO_PTR(list->items[i]);
        gpio_num_t pin_id = machine_pin_get_id(tuple->items[0]);
        gpio_int_type_t level = mp_obj_get_int(tuple->items[1]);
        gpio_wakeup_enable(pin_id, level+GPIO_INTR_LOW_LEVEL);
        gpio_wakeup_source_flags_set(pin_id);
    }
    esp_sleep_enable_gpio_wakeup();
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32ext_wake_on_gpios_obj, esp32ext_wake_on_gpios);

It works for me, your milage may vary.

i just tried on firmware 1.21.0, when wakeup from lightsleep, it cause Guru Meditation Error: Core 0 panic'ed (Load access fault). Exception was unhandled.
i works on firmware 1.20.0 though.
i have tried to rebuild with idf version 5.0.2/5.0.4/5.1 and have same issue.

edit: in current code i use irq to trigger lightsleep on the same pin. i have remove pin irq and it does not crashed now. look like it crashed when having irq on the same pin.

@puppet13th
Copy link

After further testing, look like when same pin used for wake_on_ext1 and Pin.irq, it will cause glitch and error.

example code:

from machine import Pin, lightsleep, wake_reason
import esp32
from time import sleep_ms, ticks_ms

standby_pin = 9 # boot button default high
wake_pin = Pin( standby_pin, Pin.IN )
esp32.wake_on_ext1( [ wake_pin ], esp32.WAKEUP_ALL_LOW )

def standby_irq( pin ):
    pass

pin_wake = 0
timer_wake = 0
count = 0
while True:
    count += 1
    print( count, end='\r' )
    sleep_ms( 100 )
    if count % 100 == 0:
        print( '\ngoing to sleep soon!' )
        sleep_ms( 200 )
        ticks = ticks_ms()
        lightsleep( 10_000 )
        print( 'wakeup from sleep!', ticks_ms() - ticks, wake_reason() )
        
        if wake_reason() == 4:
            timer_wake += 1
            wake_pin.irq( standby_irq, Pin.IRQ_FALLING ) # enable IRQ
            print( 'wake by timer! wake pin irq enabled!' )
        elif wake_reason() == 7:
            print( 'wake by pin!' )
            pin_wake += 1
            if pin_wake > 3:
                wake_pin.irq( None ) # disable IRQ
                print( 'wake pin irq disabled!' )

when no irq was set on the pin, holding the pin to low to trigger wake up does not have glitch.
after irq was enabled, holding pin to low to trigger wake up will cause it to stuck, and will resume after the pin is high.
if pin irq set to None, will cause Guru Meditation Error when trigger pin wakeup.

wakeup from sleep! 2186 7
Guru Meditation Error: Core  0 panic'ed (Load access fault). Exception was unhandled.

Core  0 register dump:
MEPC    : 0x4038227c  RA      : 0x42017d5e  SP      : 0x3fca8620  GP      : 0x3fc97e00  
TP      : 0x3fc6d4c0  T0      : 0x4005890e  T1      : 0x40391b44  T2      : 0x0cfc2f70  
S0/FP   : 0x00000000  S1      : 0x00000001  A0      : 0x00000000  A1      : 0x00000001  
A2      : 0x00000000  A3      : 0x3fca864c  A4      : 0x3fca8808  A5      : 0x00000000  
A6      : 0x60000000  A7      : 0x00000000  S2      : 0x00000000  S3      : 0x3fca864c  
S4      : 0x0000001c  S5      : 0x0000016f  S6      : 0x3fca8960  S7      : 0x00000000  
S8      : 0x00000030  S9      : 0x3c131000  S10     : 0x3c131000  S11     : 0x3c131000  
T3      : 0x00000001  T4      : 0x00022f5d  T5      : 0x00001000  T6      : 0x00000823  
MSTATUS : 0x00001881  MTVEC   : 0x40380001  MCAUSE  : 0x00000005  MTVAL   : 0x00000000  
MHARTID : 0x00000000  

Stack memory:
3fca8620: 0x3fca8960 0x0000016f 0x3fc9afd0 0x3fcac670 0x3c131df0 0x00000000 0x3fc9afd0 0x42017e40
3fca8640: 0x3c13306c 0x00000011 0x00020000 0x3c131df0 0x400391e6 0x00000001 0x3fc9afd0 0x42018cd0
3fca8660: 0xf7ffbcd5 0x08006a3b 0x00000000 0x3c131df0 0x3fcac575 0x00000001 0x3fca8808 0x00000006
3fca8680: 0x42018cc6 0x3fc9afd0 0x00000000 0x3c131df0 0x3fcac670 0x0000001c 0x0000016f 0x3fca8960
3fca86a0: 0x00000000 0x00000030 0x3c131000 0x3c131000 0x3c131000 0x3fca8660 0x60000000 0x00000002
3fca86c0: 0x3c165ba1 0x3c1301a4 0x00000002 0x420062c6 0x3c165ba1 0x0000000a 0x3c165ba1 0x42029f44
3fca86e0: 0x3fca8960 0x0000016f 0x3c165ba0 0x00000000 0x00000000 0x3c165ba0 0x00000001 0x4204c650
3fca8700: 0x3c131000 0x3c131000 0x00000001 0x4201966c 0x3fca8960 0x0000016f 0x3c165ba0 0x00000000
3fca8720: 0x3fca8960 0x0000016f 0x3c165ba0 0x3c13cef8 0x00000003 0x00000003 0x3fca8984 0x42019a5a
3fca8740: 0x3fca8960 0x0000016f 0x3700000f 0x00000000 0x00000003 0x00000002 0x3fca8980 0x42044614
3fca8760: 0x00000002 0x3fc97998 0x00000016 0x3c14b778 0x42019a4e 0x00000001 0x00000001 0x3c14b778
3fca8780: 0x3fca8960 0x0000016f 0x00000079 0x42006fd8 0x3c131000 0x3c131000 0x00000030 0x4200f7d2
3fca87a0: 0x3fca8960 0x00000007 0x00000000 0x3fca8978 0x3fca8960 0x0000016f 0x3fc9afd0 0x42018f06
3fca87c0: 0x3fcac893 0x3fca8970 0x3fcac876 0x40382b6e 0x00000008 0x3fca8984 0x3fca8960 0x3c14abc8
3fca87e0: 0x3c138274 0x3fca8980 0x3fc9afd0 0x00000000 0x3fca8940 0x00000016 0x3fca8968 0x3fca8978
3fca8800: 0x0000001f 0x3fcaca2c 0x3fca89e8 0x00000003 0x40382928 0x3fca89c0 0x3fc9b02c 0x00000000
3fca8820: 0x00000000 0x00000000 0x00000004 0x3fca8960 0x00000000 0x00000030 0x3c131000 0x3c131000
3fca8840: 0x3c131000 0x3fca87d0 0x3fca8940 0x3fcaca3c 0x00000003 0x3fcaca64 0x3fcaca44 0x00000006
3fca8860: 0x3fca8940 0x3fcaca64 0x00000005 0x4203d63c 0x00000003 0x3fcaca64 0x3fcaca7c 0x00000004
3fca8880: 0x3fca8940 0x3fcaca94 0x00000004 0x4203d6d8 0x00000000 0x3fcac51c 0x0000011a 0x4204b1ee
3fca88a0: 0x00000002 0x00000001 0x0000011a 0x4203d2f6 0x3fca8940 0x3fcaca94 0x00000001 0x00000000
3fca88c0: 0x3c131000 0x3c131000 0x00000001 0x00000000 0x00000008 0x000000fc 0x3fca89e0 0x42041000
3fca88e0: 0x3fcac1f0 0x00000001 0x3fca8968 0x42042a19 0x3fca8940 0x3fcacadc 0x3fcac1a0 0x42041754
3fca8900: 0x00000000 0x0000001d 0x3fca89e0 0x00000000 0x00000008 0x00000004 0x00000010 0x00000004
3fca8920: 0x3fcac1a0 0x3fcab900 0x3fcac1f0 0x3c131000 0x3c131000 0x3c131000 0x00000030 0x00000000
3fca8940: 0x3fca8960 0x00000004 0x00000000 0x00000000 0x00000000 0x3fc9b02c 0x3fca89c0 0x4200f722
3fca8960: 0x3fcabf20 0x3fcac875 0x3fca8970 0x00000004 0x3fc9b02c 0x0000000e 0x00000009 0x00001115
3fca8980: 0x0000000f 0x00000004 0x3fca89c0 0x4200f676 0x00000041 0x00000005 0x00000080 0x3c13b000
3fca89a0: 0x00000041 0x00000005 0x3c133000 0x00000004 0x00000003 0x00000002 0x3fcabf20 0x4202a0c6
3fca89c0: 0x3fca96a0 0x00000041 0x3fca8b44 0x00000001 0x3fca8b3c 0x42029f86 0x4202a1d4 0x00000003
3fca89e0: 0x3fcacaa8 0x3fcac9a0 0x00000000 0x00000000 0x4202a042 0x00000001 0x00000002 0x00000003
3fca8a00: 0x00000004 0x3c133000 0x00000005 0x00000041 0x3c13b000 0x00000080 0x3c131000 0x3c131000

@meirarmon
Copy link

@ondiiik I have a couple commits fixing the documentation, but the invitation for your repo expired. Can you re-invite me?

puppet13th added a commit to puppet13th/micropython that referenced this pull request Jan 3, 2024
updated micropython#9583 to current codebase( 1.22.0).
Signed-off-by: Rinaldi Jandrinata <puppet13th@gmail.com>
@m-cas
Copy link

m-cas commented Jan 3, 2024

if someone could let me know when this changes will make it into a nightly build (including the macro fix to avoid 'invalid pin'), I will test it immediately on my xiao esp32c3. My use case requires two pins to work as wake-up input from deepsleep() and all the test code discussed in this VERY insightful thread always tested a single pin, so I guess my testing would be of some value as well :D

@alexphobby
Copy link

Help, I cannot get to wakeup from deepsleep. Pin4 is low, i want to wake when it is high.

Using latest MicroPython v1.22.1 on 2024-01-05; ESP32C3 module with ESP32C3
Also, my machine is disconnecting drom serial on sleep. How do you manage to get serial data after reset?

import esp32
from machine import Pin
from machine import deepsleep
wake1 = Pin(4, mode=Pin.IN)
esp32.wake_on_ext1(pins=(wake1,), level=esp32.WAKEUP_ANY_HIGH)
deepsleep()

@m-cas
Copy link

m-cas commented Feb 2, 2024

the current version of micropython doesn't work with C3 (waking it up with pins). If you look at the other branches linked in the text above you'll find a downloadable unofficial version that work (I am using it for my projects). We need to wait until these branches get incorporated into main to be able to use the normal distribution....

@Yeming-SAMA
Copy link

As of the "ESP32_GENERIC_C3-20240602-v1.23.0" version, it seems that it still cannot be used normally (deepsleep). If there is a usable branch, please quote it, thanks.

@meirarmon
Copy link

meirarmon commented Jun 27, 2024 via email

@m-cas
Copy link

m-cas commented Jun 27, 2024

the code is all here: #13333, what I did was to download the latest code, setup the build environment on my mac and do the "merge" myself on the 2 files needed.
I have the binaries I need for my project and I am an happy camper. It seems that the time available for the maintainers of the repo for merging this not much ...

puppet13th added a commit to puppet13th/micropython that referenced this pull request Aug 1, 2024
updated micropython#9583 to current codebase( 1.22.0).
Signed-off-by: Rinaldi Jandrinata <puppet13th@gmail.com>
puppet13th added a commit to puppet13th/micropython that referenced this pull request Aug 1, 2024
updated micropython#9583 to current codebase( 1.22.0).
Signed-off-by: Rinaldi Jandrinata <puppet13th@gmail.com>
RetiredWizard pushed a commit to RetiredWizard/micropython that referenced this pull request Sep 18, 2024
Remove ESP_CONSOLE_UART from sdkconfig for 666 S3 Display panels
@projectgus projectgus self-requested a review January 6, 2025 23:31
Copy link
Contributor

@projectgus projectgus left a comment

Choose a reason for hiding this comment

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

@ondiiik Sorry no maintainer got back to you when you originally submitted this. Are you still interested in working on it?

The approach looks pretty good, and I think it should clean up even more if rebased as we've added more guards based on SoC capability macros rather than chip type. That should make it pretty minimal to have this work on C6 as well.

One thing I am wondering is whether it'd be cleaner to add this support via the machine.Pin.irq(wake=) argument (see `esp32/machine_pin.c). This is the standard non-port-specific way to do wakeup, and it's actually a good fit for the C3 because there isn't the weird distinction between EXT0 and EXT1. Let me know what you think.

@projectgus
Copy link
Contributor

projectgus commented Jan 17, 2025

Ah, I missed this PR has a newer alternative in #13333. I'll close this one for now, and review the newer one.

@projectgus projectgus closed this Jan 17, 2025
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.