diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e8afae655..452c50014f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `lists:keysort/2` - Added `lists:merge/2,3` +- [ESP32] Added support to light sleep (`esp:light_sleep/0`) and to GPIO and timer wakeup from +light sleep. ### Fixed diff --git a/libs/eavmlib/src/esp.erl b/libs/eavmlib/src/esp.erl index f9d8ac1879..37d48ee611 100644 --- a/libs/eavmlib/src/esp.erl +++ b/libs/eavmlib/src/esp.erl @@ -38,6 +38,10 @@ sleep_enable_ulp_wakeup/0, deep_sleep/0, deep_sleep/1, + light_sleep/0, + gpio_wakeup_enable/2, + sleep_enable_gpio_wakeup/0, + sleep_enable_timer_wakeup/1, mount/4, umount/1, nvs_fetch_binary/2, @@ -272,6 +276,47 @@ sleep_enable_ulp_wakeup() -> deep_sleep() -> erlang:nif_error(undefined). +%%----------------------------------------------------------------------------- +%% @doc Put the esp32 into light sleep. +%% This function returns ok on success after the sleep. +%% Program is NOT restarted and wake up reason can be inspected to determine how +%% the esp32 was woken up. +%% @end +%%----------------------------------------------------------------------------- +-spec light_sleep() -> ok | error. +light_sleep() -> + erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @doc Configure given GPIO as light sleep wakeup pin +%% @param GPIONum the GPIO number +%% @param logic level, either low or high that triggers the wakeup condition +%% @returns `ok | error' +%% @end +%%----------------------------------------------------------------------------- +-spec gpio_wakeup_enable(GPIONum :: non_neg_integer(), Level :: low | high) -> ok | error. +gpio_wakeup_enable(_GPIONum, _Level) -> + erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @doc Enable waking up from light sleep using a configured GPIO +%% @returns `ok | error' +%% @end +%%----------------------------------------------------------------------------- +-spec sleep_enable_gpio_wakeup() -> ok | error. +sleep_enable_gpio_wakeup() -> + erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @doc Enable waking up from light sleep using a timer +%% @param time before wakeup in microseconds +%% @returns `ok | error' +%% @end +%%----------------------------------------------------------------------------- +-spec sleep_enable_timer_wakeup(SleepUS :: non_neg_integer()) -> ok | error. +sleep_enable_timer_wakeup(_SleepUS) -> + erlang:nif_error(undefined). + %%----------------------------------------------------------------------------- %% @param SleepMS time to deep sleep in milliseconds %% @doc Put the esp32 into deep sleep. diff --git a/src/platforms/esp32/components/avm_sys/CMakeLists.txt b/src/platforms/esp32/components/avm_sys/CMakeLists.txt index a44a0cd410..ed0a89dc46 100644 --- a/src/platforms/esp32/components/avm_sys/CMakeLists.txt +++ b/src/platforms/esp32/components/avm_sys/CMakeLists.txt @@ -45,7 +45,7 @@ idf_component_register( SRCS ${AVM_SYS_COMPONENT_SRCS} INCLUDE_DIRS "include" REQUIRES "spi_flash" "soc" "newlib" "pthread" "vfs" "mbedtls" ${ADDITIONAL_COMPONENTS} - PRIV_REQUIRES "libatomvm" "esp_timer" ${ADDITIONAL_PRIV_REQUIRES} + PRIV_REQUIRES "libatomvm" "driver" "esp_timer" ${ADDITIONAL_PRIV_REQUIRES} ) target_compile_features(${COMPONENT_LIB} INTERFACE c_std_11) diff --git a/src/platforms/esp32/components/avm_sys/platform_nifs.c b/src/platforms/esp32/components/avm_sys/platform_nifs.c index 7c3fe6210e..e76a77dc98 100644 --- a/src/platforms/esp32/components/avm_sys/platform_nifs.c +++ b/src/platforms/esp32/components/avm_sys/platform_nifs.c @@ -38,12 +38,16 @@ #include #include #include + +// driver/gpio.h is required for wakeup from light sleep +#include #include #include #include #include #include #include + #include // introduced starting with 4.4 @@ -340,6 +344,17 @@ static term nif_esp_deep_sleep(Context *ctx, int argc, term argv[]) return OK_ATOM; } +static term nif_esp_light_sleep(Context *ctx, int argc, term argv[]) +{ + UNUSED(ctx); + UNUSED(argc); + UNUSED(argv); + + esp_err_t ret = esp_light_sleep_start(); + + return (ret == ESP_OK) ? OK_ATOM : ERROR_ATOM; +} + #if SOC_PM_SUPPORT_EXT_WAKEUP || SOC_PM_SUPPORT_EXT0_WAKEUP static const char *const sleep_wakeup_ext0_atom = "\x11" "sleep_wakeup_ext0"; #endif @@ -513,6 +528,47 @@ static term nif_esp_deep_sleep_enable_gpio_wakeup(Context *ctx, int argc, term a } #endif +static term nif_esp_gpio_wakeup_enable(Context *ctx, int argc, term argv[]) +{ + UNUSED(ctx); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_integer); + if ((argv[1] != LOW_ATOM) && (argv[1] != HIGH_ATOM)) { + RAISE_ERROR(BADARG_ATOM); + } + + avm_int_t gpio = term_to_int(argv[0]); + gpio_int_type_t int_type = (argv[1] == LOW_ATOM) ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL; + + esp_err_t ret = gpio_wakeup_enable(gpio, int_type); + + return (ret == ESP_OK) ? OK_ATOM : ERROR_ATOM; +} + +static term nif_esp_sleep_enable_gpio_wakeup(Context *ctx, int argc, term argv[]) +{ + UNUSED(ctx); + UNUSED(argc); + UNUSED(argv); + + esp_err_t ret = esp_sleep_enable_gpio_wakeup(); + + return (ret == ESP_OK) ? OK_ATOM : ERROR_ATOM; +} + +static term nif_esp_sleep_enable_timer_wakeup(Context *ctx, int argc, term argv[]) +{ + UNUSED(ctx); + UNUSED(argc); + + VALIDATE_VALUE(argv[0], term_is_any_integer); + avm_int64_t us = term_maybe_unbox_int64(argv[0]); + + esp_err_t ret = esp_sleep_enable_timer_wakeup(us); + + return (ret == ESP_OK) ? OK_ATOM : ERROR_ATOM; +} #if SOC_ULP_SUPPORTED @@ -842,6 +898,11 @@ static const struct Nif esp_deep_sleep_nif = .base.type = NIFFunctionType, .nif_ptr = nif_esp_deep_sleep }; +static const struct Nif esp_light_sleep_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_esp_light_sleep +}; static const struct Nif esp_sleep_get_wakeup_cause_nif = { .base.type = NIFFunctionType, @@ -880,6 +941,21 @@ static const struct Nif esp_deep_sleep_enable_gpio_wakeup_nif = .nif_ptr = nif_esp_deep_sleep_enable_gpio_wakeup }; #endif +static const struct Nif esp_gpio_wakeup_enable_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_esp_gpio_wakeup_enable +}; +static const struct Nif esp_sleep_enable_gpio_wakeup_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_esp_sleep_enable_gpio_wakeup +}; +static const struct Nif esp_sleep_enable_timer_wakeup_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_esp_sleep_enable_timer_wakeup +}; #if SOC_ULP_SUPPORTED static const struct Nif esp_sleep_ulp_wakeup_nif = { @@ -981,6 +1057,10 @@ const struct Nif *platform_nifs_get_nif(const char *nifname) TRACE("Resolved platform nif %s ...\n", nifname); return &esp_deep_sleep_nif; } + if (strcmp("esp:light_sleep/0", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &esp_light_sleep_nif; + } if (strcmp("esp:sleep_get_wakeup_cause/0", nifname) == 0) { TRACE("Resolved platform nif %s ...\n", nifname); return &esp_sleep_get_wakeup_cause_nif; @@ -1013,6 +1093,18 @@ const struct Nif *platform_nifs_get_nif(const char *nifname) return &esp_deep_sleep_enable_gpio_wakeup_nif; } #endif + if (strcmp("esp:gpio_wakeup_enable/2", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &esp_gpio_wakeup_enable_nif; + } + if (strcmp("esp:sleep_enable_gpio_wakeup/0", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &esp_sleep_enable_gpio_wakeup_nif; + } + if (strcmp("esp:sleep_enable_timer_wakeup/1", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &esp_sleep_enable_timer_wakeup_nif; + } #if SOC_ULP_SUPPORTED if (strcmp("esp:sleep_ulp_wakeup/0", nifname) == 0) { TRACE("Resolved platform nif %s ...\n", nifname);