From 57fd447e0b4be29b474b514a861f5b09631259fb Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Sun, 10 Apr 2016 03:55:38 -0700 Subject: [PATCH] Exposed Forced sleep API Added functions `wifi.suspend()`, `wifi.resume()` and `node.sleep()` --- app/Makefile | 6 +- app/include/user_config.h | 3 + app/modules/Makefile | 1 + app/modules/node.c | 46 +++- app/modules/wifi.c | 131 +++++---- app/modules/wifi_common.h | 14 +- app/pm/Makefile | 52 ++++ app/pm/pmSleep.c | 356 +++++++++++++++++++++++++ app/pm/pmSleep.h | 63 +++++ docs/en/modules/node.md | 61 +++++ docs/en/modules/wifi.md | 87 +++++- sdk-overrides/include/user_interface.h | 7 + 12 files changed, 759 insertions(+), 68 deletions(-) create mode 100644 app/pm/Makefile create mode 100644 app/pm/pmSleep.c create mode 100644 app/pm/pmSleep.h diff --git a/app/Makefile b/app/Makefile index 1051145172..9fd041cd2b 100644 --- a/app/Makefile +++ b/app/Makefile @@ -41,8 +41,9 @@ SUBDIRS= \ crypto \ dhtlib \ tsl2561 \ - net \ - http + net \ + http \ + pm endif # } PDIR @@ -87,6 +88,7 @@ COMPONENTS_eagle.app.v6 = \ dhtlib/libdhtlib.a \ tsl2561/tsl2561lib.a \ http/libhttp.a \ + pm/libpm.a \ net/libnodemcu_net.a \ modules/libmodules.a \ diff --git a/app/include/user_config.h b/app/include/user_config.h index 27efbc9a7f..b5fb3883f5 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -118,6 +118,9 @@ extern void luaL_assertfail(const char *file, int line, const char *message); #define WIFI_SDK_EVENT_MONITOR_ENABLE #define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE +#define PMSLEEP_ENABLE + + #define STRBUF_DEFAULT_INCREMENT 32 #endif /* __USER_CONFIG_H__ */ diff --git a/app/modules/Makefile b/app/modules/Makefile index ef6d6dae06..2bea9f6d7f 100644 --- a/app/modules/Makefile +++ b/app/modules/Makefile @@ -53,6 +53,7 @@ INCLUDES += -I ../smart INCLUDES += -I ../cjson INCLUDES += -I ../dhtlib INCLUDES += -I ../http +INCLUDES += -I ../pm PDIR := ../$(PDIR) sinclude $(PDIR)Makefile diff --git a/app/modules/node.c b/app/modules/node.c index 39b0feea4d..8c05252157 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -66,19 +66,35 @@ static int node_deepsleep( lua_State* L ) return 0; } -// Lua: dsleep_set_options -// Combined to dsleep( us, option ) -// static int node_deepsleep_setoption( lua_State* L ) -// { -// s32 option; -// option = luaL_checkinteger( L, 1 ); -// if ( option < 0 || option > 4) -// return luaL_error( L, "wrong arg range" ); -// else -// deep_sleep_set_option( option ); -// return 0; -// } -// Lua: info() + +#ifdef PMSLEEP_ENABLE +#include "pmSleep.h" + +int node_sleep_resume_cb_ref= LUA_NOREF; +void node_sleep_resume_cb(void) +{ + PMSLEEP_DBG("\n\tDBG: %s START\n", __func__); + pmSleep_execute_lua_cb(&node_sleep_resume_cb_ref); +} + +// Lua: node.sleep(table) +static int node_sleep( lua_State* L ) +{ + pmSleep_INIT_CFG(cfg); + cfg.sleep_mode=LIGHT_SLEEP_T; + if(lua_istable(L, 1)) + { + pmSleep_parse_table_lua(L, 1, &cfg, NULL, &node_sleep_resume_cb_ref); + } + else + { + return luaL_argerror(L, 1, "must be table"); + } + cfg.resume_cb_ptr=&node_sleep_resume_cb; + pmSleep_suspend(&cfg); + return 0; +} +#endif //PMSLEEP_ENABLE static int node_info( lua_State* L ) { @@ -638,6 +654,10 @@ static const LUA_REG_TYPE node_map[] = { { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, +#ifdef PMSLEEP_ENABLE + { LSTRKEY( "sleep" ), LFUNCVAL( node_sleep ) }, + PMSLEEP_INT_MAP, +#endif { LSTRKEY( "info" ), LFUNCVAL( node_info ) }, { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) }, { LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) }, diff --git a/app/modules/wifi.c b/app/modules/wifi.c index af9beeb4b6..b1cf005e6e 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -24,10 +24,6 @@ static uint8 getap_output_format=0; #define INVALID_MAC_STR "MAC:FF:FF:FF:FF:FF:FF" -//wifi.sleep variables -#define FPM_SLEEP_MAX_TIME 0xFFFFFFF -static bool FLAG_wifi_force_sleep_enabled=0; - #ifdef WIFI_SMART_ENABLE static void wifi_smart_succeed_cb(sc_status status, void *pdata){ NODE_DBG("wifi_smart_succeed_cb is called.\n"); @@ -311,67 +307,97 @@ static int wifi_getphymode( lua_State* L ) return 1; } -//wifi.sleep() -static int wifi_sleep(lua_State* L) +#ifdef PMSLEEP_ENABLE +/* Begin WiFi suspend functions*/ +#include "pmSleep.h" + +static int wifi_resume_cb_ref = LUA_NOREF; // Holds resume callback reference +static int wifi_suspend_cb_ref = LUA_NOREF; // Holds suspend callback reference + +void wifi_pmSleep_suspend_CB(void) { - uint8 desired_sleep_state = 2; - sint8 wifi_fpm_do_sleep_return_value = 1; - if(lua_isnumber(L, 1)) + PMSLEEP_DBG("\n\tDBG: %s start\n", __func__); + if (wifi_suspend_cb_ref != LUA_NOREF) { - if(luaL_checknumber(L, 1) == 0) - { - desired_sleep_state = 0; - } - else if(luaL_checknumber(L, 1) == 1) - { - desired_sleep_state = 1; - } + lua_State* L = lua_getstate(); // Get main Lua thread pointer + lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_suspend_cb_ref); // Push suspend callback onto stack + lua_unref(L, wifi_suspend_cb_ref); // remove suspend callback from LUA_REGISTRY + wifi_suspend_cb_ref = LUA_NOREF; // Update variable since reference is no longer valid + lua_call(L, 0, 0); // Execute suspend callback } - if (!FLAG_wifi_force_sleep_enabled && desired_sleep_state == 1 ) + else { - uint8 wifi_current_opmode = wifi_get_opmode(); - if (wifi_current_opmode == 1 || wifi_current_opmode == 3 ) - { - wifi_station_disconnect(); - } - // set WiFi mode to null mode - wifi_set_opmode(NULL_MODE); - // set force sleep type - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - wifi_fpm_do_sleep_return_value = wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME); - if (wifi_fpm_do_sleep_return_value == 0) - { - FLAG_wifi_force_sleep_enabled = TRUE; - } - else - { - wifi_fpm_close(); - FLAG_wifi_force_sleep_enabled = FALSE; - } - + PMSLEEP_DBG("\n\tDBG: lua cb unavailable\n"); } - else if(FLAG_wifi_force_sleep_enabled && desired_sleep_state == 0) + PMSLEEP_DBG("\n\tDBG: %s end\n", __func__); + return; +} + +void wifi_pmSleep_resume_CB(void) +{ + PMSLEEP_DBG("\n\tDBG: %s start\n", __func__); + // If resume callback was defined + pmSleep_execute_lua_cb(&wifi_resume_cb_ref); + PMSLEEP_DBG("\n\tDBG: %s end\n", __func__); + return; +} + +// Lua: wifi.suspend({duration, suspend_cb, resume_cb, preserve_mode}) +static int wifi_suspend(lua_State* L) +{ + // If no parameters were provided + if (lua_isnone(L, 1)) { - FLAG_wifi_force_sleep_enabled = FALSE; - // wake up to use WiFi again - wifi_fpm_do_wakeup(); - wifi_fpm_close(); + // Return current WiFi suspension state + lua_pushnumber(L, pmSleep_get_state()); + return 1; // Return WiFi suspension state } - if (desired_sleep_state == 1 && FLAG_wifi_force_sleep_enabled == FALSE) + pmSleep_INIT_CFG(cfg); + cfg.sleep_mode=MODEM_SLEEP_T; + if(lua_istable(L, 1)) { - lua_pushnil(L); - lua_pushnumber(L, wifi_fpm_do_sleep_return_value); + pmSleep_parse_table_lua(L, 1, &cfg, &wifi_suspend_cb_ref, &wifi_resume_cb_ref); } else + return luaL_argerror(L, 1, "must be table"); + cfg.resume_cb_ptr = &wifi_pmSleep_resume_CB; + cfg.suspend_cb_ptr = &wifi_pmSleep_suspend_CB; + pmSleep_suspend(&cfg); + return 0; +} +// Lua: wifi.resume([Resume_CB]) +static int wifi_resume(lua_State* L) +{ + PMSLEEP_DBG("\n\tDBG: %s start\n", __func__); + uint8 fpm_state=pmSleep_get_state(); +// If forced sleep api is not enabled, return error + if (fpm_state==0) { - lua_pushnumber(L, FLAG_wifi_force_sleep_enabled); - lua_pushnil(L); + return luaL_error(L, "WIFi not suspended"); } - return 2; + + // If a resume callback was provided + if (lua_isfunction(L, 1)) + { + // If there is already a resume callback reference + lua_pushvalue(L, 1); //Push resume callback to the top of the stack + register_lua_cb(L, &wifi_resume_cb_ref); +// if (wifi_resume_cb_ref != LUA_NOREF) +// { +// luaL_unref(L, LUA_REGISTRYINDEX, wifi_resume_cb_ref); // Discard old callback +// } +// wifi_resume_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); // Register resume callback in LUA_REGISTRY and store it's reference + PMSLEEP_DBG("\n\tDBG: Resume CB registered\n"); + } + pmSleep_resume(NULL); + PMSLEEP_DBG("\n\tDBG: %s end\n", __func__); + return 0; } +/* End WiFi suspend functions*/ +#endif + // Lua: mac = wifi.xx.getmac() static int wifi_getmac( lua_State* L, uint8_t mode ) { @@ -895,6 +921,7 @@ static int wifi_station_listap( lua_State* L ) { unregister_lua_cb(L, &wifi_scan_succeed); } + return 0; } // Lua: wifi.sta.gethostname() @@ -1267,7 +1294,8 @@ static const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) }, { LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) }, { LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) }, - { LSTRKEY( "sleep" ), LFUNCVAL( wifi_sleep ) }, + { LSTRKEY( "suspend" ), LFUNCVAL( wifi_suspend ) }, + { LSTRKEY( "resume" ), LFUNCVAL( wifi_resume ) }, #ifdef WIFI_SMART_ENABLE { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, { LSTRKEY( "stopsmart" ), LFUNCVAL( wifi_exit_smart ) }, @@ -1349,6 +1377,7 @@ void wifi_change_default_host_name(void) int luaopen_wifi( lua_State *L ) { +// wifi_fpm_auto_sleep_set_in_null_mode(1); #if defined(WIFI_SDK_EVENT_MONITOR_ENABLE) wifi_eventmon_init(); #endif diff --git a/app/modules/wifi_common.h b/app/modules/wifi_common.h index 633fd3a4ac..a546679abb 100644 --- a/app/modules/wifi_common.h +++ b/app/modules/wifi_common.h @@ -13,6 +13,9 @@ #include "c_stdio.h" #include "task/task.h" +//#define WIFI_SLEEP_DEBUG +//#define WIFI_EVENTMON_DEBUG + void wifi_add_sprintf_field(lua_State* L, char* name, char* string, ...); void wifi_add_int_field(lua_State* L, char* name, lua_Integer integer); @@ -37,12 +40,21 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref) void wifi_change_default_host_name(void); -#ifdef NODE_DEBUG +#if defined(WIFI_EVENTMON_DEBUG) || defined(NODE_DEBUG) #define EVENT_DBG(...) c_printf(__VA_ARGS__) #else #define EVENT_DBG(...) //c_printf(__VA_ARGS__) #endif +enum wifi_suspension_state +{ + WIFI_AWAKE = 0, + WIFI_SUSPENSION_PENDING = 1, + WIFI_SUSPENDED = 2 +}; + + + #ifdef WIFI_SDK_EVENT_MONITOR_ENABLE extern const LUA_REG_TYPE wifi_event_monitor_map[]; void wifi_eventmon_init(); diff --git a/app/pm/Makefile b/app/pm/Makefile new file mode 100644 index 0000000000..8143bb2afa --- /dev/null +++ b/app/pm/Makefile @@ -0,0 +1,52 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = libpm.a +endif + +STD_CFLAGS=-std=gnu11 -Wimplicit + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ./include +INCLUDES += -I ../include +INCLUDES += -I ../../include +INCLUDES += -I ../lua +INCLUDES += -I ../platform +INCLUDES += -I ../libc + +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/pm/pmSleep.c b/app/pm/pmSleep.c new file mode 100644 index 0000000000..2dee7225d8 --- /dev/null +++ b/app/pm/pmSleep.c @@ -0,0 +1,356 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEERWARE LICENSE" (Revision 42): + * dnc40085 wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +#include "pmSleep.h" + +#define STRINGIFY_VAL(x) #x +#define STRINGIFY(x) STRINGIFY_VAL(x) + +//holds duration error string +const char *PMSLEEP_DURATION_ERR_STR="duration: 0 or "STRINGIFY(PMSLEEP_SLEEP_MIN_TIME)"-"STRINGIFY(PMSLEEP_SLEEP_MAX_TIME)" us"; + +void (*pmSleep_user_suspend_cb)(void)=NULL; // holds pointer to external C callback +void (*pmSleep_user_resume_cb)(void)=NULL; // holds pointer to external C callback +uint8 pmSleep_resume_opmode = 0; // variable to hold WiFi opmode across suspension. +os_timer_t pmSleep_wifi_is_suspended_timer; // Timer structure for wifi_rf_closed_timer + +//function to register a lua callback function in the LUA_REGISTRYINDEX for later execution +static inline void register_lua_cb(lua_State* L,int* cb_ref) +{ + int ref=luaL_ref(L, LUA_REGISTRYINDEX); + if( *cb_ref != LUA_NOREF) + { + luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref); + } + *cb_ref = ref; +} + +// C callback for bringing WiFi back from forced sleep +void pmSleep_resume_cb(void) +{ + PMSLEEP_DBG("\n\tDBG: %s START\n", __func__); + wifi_fpm_close(); // Disable force sleep API + PMSLEEP_DBG("\n\t\tDBG: WiFi resume\n"); + + //this section restores previous wifi mode if mode was preserved + + // If resume_opmode is STATION_MODE, SOFTAP_MODE or STATIONAP_MODE + if (pmSleep_resume_opmode > 0 && pmSleep_resume_opmode < 4) + { + // If wifi_set_opmode_current is successful +//TODO: When Espressif fixes the bug (introduced with sdk 1.5.4) that causes error "fpm 737", replace "wifi_set_opmode()" with "wifi_set_opmode_current()" + if (wifi_set_opmode(pmSleep_resume_opmode)) + { + // If opmode is STATION_MODE or STATIONAP_MODE + if (pmSleep_resume_opmode == STATION_MODE || pmSleep_resume_opmode == STATIONAP_MODE) + { + wifi_station_connect(); // Connect to currently configured Access Point + } + PMSLEEP_DBG("\n\t\tDBG: WiFi mode restored\n"); + pmSleep_resume_opmode = 0; // reset variable to default value + } + } + + //this section executes the firmware developer's C callback + if (pmSleep_user_resume_cb!=NULL) + { + PMSLEEP_DBG("\n\t\tDBG: calling user resume cb (%p)\n", pmSleep_user_resume_cb); + pmSleep_user_resume_cb(); + pmSleep_user_resume_cb=NULL; + } + + PMSLEEP_DBG("\n\tDBG: %s END\n", __func__); + return; +} + +// This callback executes the suspend lua callback when Wifi suspension is in effect +void pmSleep_wifi_suspended_timer_cb(int arg) +{ + // If WiFi is suspended + if (pmSleep_get_state()==PMSLEEP_SUSPENDED) + { + PMSLEEP_DBG("\n\tDBG: %s START\n", __func__); + os_timer_disarm(&pmSleep_wifi_is_suspended_timer); // Stop rf_closed_timer + PMSLEEP_DBG("\n\t\tDBG: WiFi is suspended\n"); + + // If suspend callback was defined + if (pmSleep_user_suspend_cb!=NULL) + { + PMSLEEP_DBG("\n\t\tDBG: calling user suspend cb (%p)\n", pmSleep_user_suspend_cb); + pmSleep_user_suspend_cb(); + pmSleep_user_suspend_cb=NULL; + } + PMSLEEP_DBG("\n\tDBG: %s END\n", __func__); + } +} + +//This function resumes ESP from MODEM_SLEEP +void pmSleep_resume(void (*resume_cb_ptr)(void)) +{ + PMSLEEP_DBG("\n\tDBG: %s START\n", __func__); + uint8 fpm_state=pmSleep_get_state(); + + if(fpm_state>0) + { + if(resume_cb_ptr!=NULL) + { + pmSleep_user_resume_cb=resume_cb_ptr; + } + wifi_fpm_do_wakeup(); // Wake up from sleep + pmSleep_resume_cb(); // Finish WiFi wakeup + } + + PMSLEEP_DBG("\n\tDBG: %s END\n", __func__); + return; +} + +//this function puts the ESP8266 into MODEM_SLEEP or LIGHT_SLEEP +void pmSleep_suspend(pmSleep_param_t *cfg) +{ + PMSLEEP_DBG("\n\tDBG: %s START\n", __func__); + + uint8 current_wifi_mode = wifi_get_opmode(); // Get Current WiFi mode + lua_State *L = lua_getstate(); + + pmSleep_user_resume_cb=cfg->resume_cb_ptr; //pointer to hold address of user_cb + pmSleep_user_suspend_cb=cfg->suspend_cb_ptr; //pointer to hold address of user_cb + + // If Preserve_wifi_mode parameter is TRUE and current WiFi mode is not NULL_MODE + if (cfg->preserve_opmode && current_wifi_mode != 0) + { + pmSleep_resume_opmode = current_wifi_mode; + } + + + if (current_wifi_mode == STATION_MODE || current_wifi_mode == STATIONAP_MODE) + { + wifi_station_disconnect(); // Disconnect from Access Point + } + + // If wifi_set_opmode_current is successful +//TODO: When Espressif fixes the bug (introduced with sdk 1.5.4) that causes error "fpm 737", replace "wifi_set_opmode()" with "wifi_set_opmode_current()" + if (wifi_set_opmode(NULL_MODE)) + { + PMSLEEP_DBG("\n\t\tDBG: sleep_mode is %s\n", cfg->sleep_mode==MODEM_SLEEP_T ? "MODEM_SLEEP_T" : "LIGHT_SLEEP_T"); + //if cfg.sleep_mode is LIGHT_SLEEP_T and provided GPIO is valid then configure wake-up pin + wifi_fpm_set_sleep_type(cfg->sleep_mode); + + wifi_fpm_open(); // Enable force sleep API + + // Verify forced sleep api is enabled + if (fpm_is_open()) + { + if (cfg->sleep_mode == LIGHT_SLEEP_T) + { + PMSLEEP_DBG("\n\tcfg->wake_pin=%d\n", cfg->wake_gpio); + if(cfg->wake_gpio != 255 && 1&& (cfg->wake_gpio >= 0)) + { + PMSLEEP_DBG("\n\t\tDBG: Wake-up pin is %d\n", cfg->wake_gpio); + gpio_pin_wakeup_enable(GPIO_ID_PIN(cfg->wake_gpio), cfg->int_type); + } + else if(cfg->sleep_duration==FPM_SLEEP_MAX_TIME && cfg->wake_gpio==255) + { + wifi_fpm_close(); + PMSLEEP_DBG("\n\t\tDBG: No wake-up pin defined\n"); + } + } + + wifi_fpm_set_wakeup_cb(pmSleep_resume_cb); // Set resume C callback + + if(cfg->sleep_mode==LIGHT_SLEEP_T) + { + PMSLEEP_DBG("\n\twifi_fpm_do_sleep(cfg->sleep_duration)\n"); + wifi_fpm_do_sleep(cfg->sleep_duration); + return; + } + else + { + PMSLEEP_DBG("\n\twifi_fpm_do_sleep(cfg->sleep_duration)\n"); + sint8 retval_wifi_fpm_do_sleep = wifi_fpm_do_sleep(cfg->sleep_duration); // Request WiFi suspension and store return value + + // If wifi_fpm_do_sleep success + if (retval_wifi_fpm_do_sleep == 0) + { + PMSLEEP_DBG("\n\t\tDBG: wifi_fpm_do_sleep success, starting rf_closed timer\n"); + os_timer_disarm(&pmSleep_wifi_is_suspended_timer); // Disable timer if it is already running + os_timer_setfn(&pmSleep_wifi_is_suspended_timer,(os_timer_func_t *) pmSleep_wifi_suspended_timer_cb, NULL); //Set timer callback for wifi_rf_closed_timer + os_timer_arm(&pmSleep_wifi_is_suspended_timer, 1, 1); // Start rf_closed_timer + } + else // This should never happen + { + wifi_fpm_close(); + c_printf("\nDBG(%s): wifi_fpm_do_sleep returned %d\n", __func__, retval_wifi_fpm_do_sleep); + } + } + } + } + PMSLEEP_DBG("\n\tDBG: %s END\n", __func__); + return; +} + +//this function checks current wifi suspension state and returns the result +uint8 pmSleep_get_state(void) +{ + if (fpm_rf_is_closed()) + { + return PMSLEEP_SUSPENDED; + } + // If WiFi suspension is pending + else if (fpm_is_open()) + { + return PMSLEEP_SUSPENSION_PENDING; + } + else + { + return PMSLEEP_AWAKE; + } +} + +//this function parses the lua configuration table provided by the application developer +int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg, int *suspend_lua_cb_ref, int *resume_lua_cb_ref) +{ + uint8 current_pmSleep_state=pmSleep_get_state(); + + if (current_pmSleep_state != PMSLEEP_AWAKE) + { + // If WiFi is suspended + if (current_pmSleep_state == PMSLEEP_SUSPENDED) + { + return luaL_error(L, "WiFi currently suspended"); + } + else + { + return luaL_error(L, "WiFi suspension pending"); + } + } + + lua_Integer Linteger_tmp=0; // variable to hold lua_integer for verification + + if(cfg->sleep_mode==MODEM_SLEEP_T) //WiFi suspend + { + lua_getfield(L, table_idx, "duration"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isnumber(L, -1) ) + { + lua_Integer Linteger=luaL_checkinteger(L, -1); + luaL_argcheck(L, + (((Linteger >= PMSLEEP_SLEEP_MIN_TIME) && (Linteger <= PMSLEEP_SLEEP_MAX_TIME)) || + (Linteger == 0)), table_idx, PMSLEEP_DURATION_ERR_STR); + cfg->sleep_duration = (uint32)Linteger; // Get suspend duration + } + else + return luaL_argerror( L, table_idx, "duration: must be number" ); + } + else + { + return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR ); + } + lua_pop(L, 1); + + lua_getfield(L, table_idx, "suspend_cb"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isfunction(L, -1) ) + { + lua_pushvalue(L, -1); // Push resume callback to the top of the stack + register_lua_cb(L, suspend_lua_cb_ref); + } + else + return luaL_argerror( L, table_idx, "suspend_cb: must be function" ); + } + lua_pop(L, 1); + } + else //CPU suspend + { + lua_getfield(L, table_idx, "wake_gpio"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isnumber(L, -1) ) + { + Linteger_tmp=lua_tointeger(L, -1); + + luaL_argcheck(L, platform_gpio_exists(Linteger_tmp), 1, "wake_gpio: invalid gpio"); + cfg->wake_gpio=Linteger_tmp; + } + else + return luaL_argerror( L, table_idx, "wake_gpio: must be number" ); + } + else + { + return luaL_argerror( L, table_idx, "wake_gpio: must specify gpio" ); + } + lua_pop(L, 1); + + lua_getfield(L, table_idx, "int_type"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isnumber(L, -1) ) + { + Linteger_tmp=lua_tointeger(L, -1); + + luaL_argcheck(L, (Linteger_tmp==GPIO_PIN_INTR_ANYEDGE || Linteger_tmp==GPIO_PIN_INTR_HILEVEL + || Linteger_tmp==GPIO_PIN_INTR_LOLEVEL || Linteger_tmp==GPIO_PIN_INTR_NEGEDGE + || Linteger_tmp==GPIO_PIN_INTR_POSEDGE ), 1, "int_type: invalid interrupt type"); + cfg->int_type=Linteger_tmp; + } + else + return luaL_argerror( L, table_idx, "int_type: must be number" ); + } + else + { + cfg->int_type=GPIO_PIN_INTR_LOLEVEL; + } + lua_pop(L, 1); + } + + lua_getfield(L, table_idx, "resume_cb"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isfunction(L, -1) ) + { + lua_pushvalue(L, -1); // Push resume callback to the top of the stack + register_lua_cb(L, resume_lua_cb_ref); + } + else + return luaL_argerror( L, table_idx, "resume_cb: must be function" ); + } + lua_pop(L, 1); + + lua_getfield(L, table_idx, "preserve_mode"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isboolean(L, -1) ) + { + cfg->preserve_opmode=lua_toboolean(L, -1); + } + else + return luaL_argerror( L, table_idx, "preseve_mode: must be boolean" ); + } + else + { + cfg->preserve_opmode=true; + } + lua_pop(L, 1); + + if (cfg->sleep_duration == 0) + { + cfg->sleep_duration = FPM_SLEEP_MAX_TIME; + } + + return 0; +} + +//this function executes the application developer's Lua callback +void pmSleep_execute_lua_cb(int* cb_ref) +{ + if (*cb_ref != LUA_NOREF) + { + lua_State* L = lua_getstate(); // Get Lua main thread pointer + lua_rawgeti(L, LUA_REGISTRYINDEX, *cb_ref); // Push resume callback onto the stack + lua_unref(L, *cb_ref); // Remove resume callback from registry + *cb_ref = LUA_NOREF; // Update variable since reference is no longer valid + lua_call(L, 0, 0); // Execute resume callback + } + +} diff --git a/app/pm/pmSleep.h b/app/pm/pmSleep.h new file mode 100644 index 0000000000..2fd086007a --- /dev/null +++ b/app/pm/pmSleep.h @@ -0,0 +1,63 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEERWARE LICENSE" (Revision 42): + * dnc40085 wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +#ifndef __FPM_SLEEP_H__ +#define __FPM_SLEEP_H__ +#include "user_interface.h" +#include "c_types.h" +#include "lauxlib.h" +#include "gpio.h" +#include "platform.h" + +//#define PMSLEEP_DEBUG + +#define PMSLEEP_SLEEP_MIN_TIME 50000 +#define PMSLEEP_SLEEP_MAX_TIME 268435454 //FPM_MAX_SLEEP_TIME-1 +#define pmSleep_INIT_CFG(X) pmSleep_param_t X = {.sleep_duration=0, .wake_gpio=255, \ + .preserve_opmode=TRUE, .suspend_cb_ptr=NULL, .resume_cb_ptr=NULL} + +#define PMSLEEP_INT_MAP \ + { LSTRKEY( "INT_BOTH" ), LNUMVAL( GPIO_PIN_INTR_ANYEDGE ) }, \ + { LSTRKEY( "INT_UP" ), LNUMVAL( GPIO_PIN_INTR_POSEDGE ) }, \ + { LSTRKEY( "INT_DOWN" ), LNUMVAL( GPIO_PIN_INTR_NEGEDGE ) }, \ + { LSTRKEY( "INT_HIGH" ), LNUMVAL( GPIO_PIN_INTR_HILEVEL ) }, \ + { LSTRKEY( "INT_LOW" ), LNUMVAL( GPIO_PIN_INTR_LOLEVEL ) } + + +#if defined(PMSLEEP_DEBUG) || defined(NODE_DEBUG) + #define PMSLEEP_DBG(...) c_printf(__VA_ARGS__) +#else + #define PMSLEEP_DBG(...) //c_printf(__VA_ARGS__) +#endif + +typedef struct pmSleep_param{ + uint32 sleep_duration; + uint8 sleep_mode; + uint8 wake_gpio; + uint8 int_type; + bool preserve_opmode; + void (*suspend_cb_ptr)(void); + void (*resume_cb_ptr)(void); +}pmSleep_param_t; //structure to hold pmSleep configuration + + +enum PMSLEEP_STATE{ + PMSLEEP_AWAKE = 0, + PMSLEEP_SUSPENSION_PENDING = 1, + PMSLEEP_SUSPENDED = 2 +}; + +uint8 pmSleep_get_state(void); +void pmSleep_resume(void (*resume_cb_ptr)(void)); +void pmSleep_suspend(pmSleep_param_t *param); +void pmSleep_execute_lua_cb(int* cb_ref); +int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg, int *suspend_lua_cb_ref, int *resume_lua_cb_ref); + + +#endif // __FPM_SLEEP_H__ diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index 7989d7082a..13f23694b9 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -123,6 +123,11 @@ node.dsleep(1000000, 4) node.dsleep(nil,4) ``` +#### See also +[`wifi.suspend()`](wifi.md#wifisuspend) +[`wifi.resume()`](wifi.md#wifiresume) +[`node.sleep()`](#nodesleep) + ## node.flashid() Returns the flash chip ID. @@ -350,6 +355,62 @@ target CPU frequency (number) node.setcpufreq(node.CPU80MHZ) ``` + +## node.sleep() + +Put NodeMCU in light sleep mode to reduce current consumption. +!!! note "Note:" + + NodeMCU can not enter light sleep mode if wifi is suspended! + + +#### Syntax +`node.sleep({wake_gpio[,int_type, resume_cb, preserve_mode]})` + +#### Parameters +- `wake_gpio` 0-15, gpio to attach interrupt to. Note that gpio16 does not support interrupts. +- `int_type` type of interrupt that you would like to wake on. (Optional, Default: `node.INT_LOW`) + - valid interrupt modes: + - `node.INT_UP` Rising edge + - `node.INT_DOWN` Falling edge + - `node.INT_BOTH` Both edges + - `node.INT_LOW` Low level + - `node.INT_HIGH` High level +- `resume_cb` Callback to execute when WiFi wakes from suspension. (Optional) +- `preserve_mode` preserve current WiFi mode through node sleep. (Optional, Default: true) + - If true, Station and StationAP modes will automatically reconnect to previously configured Access Point when NodeMCU resumes. + - If false, discard WiFi mode and leave NodeMCU in `wifi.NULL_MODE`. WiFi mode will be restored to original mode on restart. + +#### Returns +- `nil` + +#### Example + +```lua + +--Put NodeMCU in light sleep mode with resume callback + cfg={} + cfg.wake_gpio=0 + cfg.resume_cb=function() print("WiFi resume") end + + node.sleep(cfg) + +--Put NodeMCU in light sleep mode with interrupt triggering on both edges, resume callback and discard WiFi mode + cfg={} + cfg.wake_gpio=0 + cfg.int_type=node.INT_BOTH + cfg.resume_cb=function() print("WiFi resume") end + cfg.preserve_mode=false + + node.sleep(cfg) + +``` + +#### See also +[`wifi.suspend()`](wifi.md#wifisuspend) +[`wifi.resume()`](wifi.md#wifiresume) +[`node.dsleep()`](#nodedsleep) + ## node.stripdebug() Controls the amount of debug information kept during [`node.compile()`](#nodecompile), and allows removal of debug information from already compiled Lua code. diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index cb705d5413..824fae9728 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -56,6 +56,37 @@ The current physical mode as one of `wifi.PHYMODE_B`, `wifi.PHYMODE_G` or `wifi. #### See also [`wifi.setphymode()`](#wifisetphymode) +## wifi.resume() + +Wake up WiFi from suspended state or cancel pending wifi suspension + +#### Syntax +`wifi.resume([resume_cb])` + +#### Parameters +- `resume_cb` Callback to execute when WiFi wakes from suspension. + !!! note "Note:" + + Any previously provided callbacks will be replaced! + +#### Returns +`nil` + +#### Example + +```lua +--Resume wifi from timed or indefinite sleep +wifi.resume() + +--Resume wifi from timed or indefinite sleep w/ resume callback +wifi.resume(function() print("WiFi resume") end) +``` + +#### See also +[`wifi.suspend()`](#wifisuspend) +[`node.sleep()`](node.md#nodesleep) +[`node.dsleep()`](node.md#nodedsleep) + ## wifi.setmode() Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: @@ -201,6 +232,60 @@ none #### See also [`wifi.startsmart()`](#wifistartsmart) +## wifi.suspend() + +Suspend Wifi to reduce current consumption. +This function is also useful for preventing WiFi stack related crashes when executing functions or tasks that take longer than ~500ms + +#### Syntax +`wifi.suspend({duration[, suspend_cb, resume_cb, preserve_mode]})` + +#### Parameters +- `duration` Suspend duration in microseconds(μs). If a suspend duration of `0` is specified, suspension will be indefinite (Range: 0 or 50000 - 268435454 μs) +- `suspend_cb` Callback to execute when WiFi is suspended. (Optional) +- `resume_cb` Callback to execute when WiFi wakes from suspension. (Optional) +- `preserve_mode` preserve current WiFi mode through node sleep. (Optional, Default: true) + - If true, Station and StationAP modes will automatically reconnect to previously configured Access Point when NodeMCU resumes. + - If false, discard WiFi mode and leave NodeMCU in [`wifi.NULL_MODE`](#wifigetmode). WiFi mode will be restored to original mode on restart. + +#### Returns +- `suspend_state` if no parameters are provided, current WiFi suspension state will be returned + - States: + - `0` WiFi is awake. + - `1` WiFi suspension is pending. (Waiting for idle task) + - `2` WiFi is suspended. + + +#### Example + +```lua +--get current wifi suspension state +print(wifi.suspend()) + +--Suspend WiFi for 10 seconds with suspend/resume callbacks + cfg={} + cfg.duration=10*1000*1000 + cfg.resume_cb=function() print("WiFi resume") end + cfg.suspend_cb=function() print("WiFi suspended") end + + wifi.suspend(cfg) + +--Suspend WiFi for 10 seconds with suspend/resume callbacks and discard WiFi mode + cfg={} + cfg.duration=10*1000*1000 + cfg.resume_cb=function() print("WiFi resume") end + cfg.suspend_cb=function() print("WiFfi suspended") end + cfg.preserve_mode=false + + wifi.suspend(cfg) + +``` + +#### See also +[`wifi.resume()`](#wifiresume) +[`node.sleep()`](node.md#nodesleep) +[`node.dsleep()`](node.md#nodedsleep) + # wifi.sta Module ## wifi.sta.autoconnect() @@ -741,7 +826,7 @@ number: 0~5 ## wifi.ap.config() -Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE-9997C3. +Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE_9997C3. NOTE: SoftAP Configuration will be retained until changed even if device is turned off. diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 2fe4da29ec..5284c056a0 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -5,4 +5,11 @@ bool wifi_softap_deauth(uint8 mac[6]); +//force sleep API +#define FPM_SLEEP_MAX_TIME 268435455 //0xFFFFFFF +void wifi_fpm_set_wakeup_cb(void (*fpm_wakeup_cb_func)(void)); +bool fpm_is_open(void); +bool fpm_rf_is_closed(void); + + #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */