-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Conversation
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>
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. |
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:
|
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:
all wakeup from timer expected output:
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:
dont know why it's not working for me.with your code,it will not wakeup when using lightsleep() wiring: |
There are some missunderstandings and some faulty use. As I originally wanted to preserve interface same as with regular esp32, I decided to ignore Then I tried again the example I posted above and it works to me. With Then I tried also lightsleep. It was working as well, with difference that At the end I made more deeper test:
The result is following:
This means that I have to add at least 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>
Anyway, I patched GPIO vs. EXT1 issue - 848805e:
So the result is now correct
|
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>
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. |
from the reference look like any gpio pin can be used as (lightsleep) wake up pin. |
have retest your latest changes but still got different result:
wiring : v3.3 -- R1 -- pin(4) 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 :
|
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. |
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 |
for the changes I made, ext0 does not works as intended. I agree that calling esp32.ext0 should throws exception. |
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 |
With ESP32-C3 there is different approach for setting wake-up pins. Signed-off-by: Ondrej Sienczak <ondrej.sienczak@gmail.com>
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. |
what do you mean?
all look good now:
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>
we should have resolved all issue regarding esp32-c3 wake-pin. waiting to be merged. :) |
ports/esp32/modmachine.c
Outdated
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); | ||
} | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
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 😉 |
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. |
@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. |
@ondiiik i just realized that lightsleep did not update wake_reason but deepsleep did.
i modified from your example code:
|
I'd be happy to see this merged and released. |
When will this merge please? |
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 |
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! |
I use the attached code as an extmod to let me set wakeup pins for the LIGHT sleep case on both esp32 and esp32S3:
It works for me, your milage may vary. |
i just tried on firmware 1.21.0, when wakeup from lightsleep, it cause 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. |
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.
|
@ondiiik I have a couple commits fixing the documentation, but the invitation for your repo expired. Can you re-invite me? |
updated micropython#9583 to current codebase( 1.22.0). Signed-off-by: Rinaldi Jandrinata <puppet13th@gmail.com>
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 |
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 import esp32 |
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.... |
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. |
Hi,
There is no other branch, but as it seems that it is very hard to get
features reviewed and merged, I am not going to be putting in the effort.
Someone else who needs this should take it, I decided to use a different
board that does support this.
…On Thu, Jun 27, 2024, 05:02 夜明 ***@***.***> wrote:
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.
—
Reply to this email directly, view it on GitHub
<#9583 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA6BYMGAKDJUDMALEMN4JNTZJNXCNAVCNFSM6AAAAAARCXRBICVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJSHEYTANZWGA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
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. |
updated micropython#9583 to current codebase( 1.22.0). Signed-off-by: Rinaldi Jandrinata <puppet13th@gmail.com>
updated micropython#9583 to current codebase( 1.22.0). Signed-off-by: Rinaldi Jandrinata <puppet13th@gmail.com>
Remove ESP_CONSOLE_UART from sdkconfig for 666 S3 Display panels
There was a problem hiding this 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.
Ah, I missed this PR has a newer alternative in #13333. I'll close this one for now, and review the newer one. |
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