diff --git a/app/modules/wifi.c b/app/modules/wifi.c index ddf573dd0d..8efb599f2c 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,222 @@ static int wifi_getphymode( lua_State* L ) return 1; } -//wifi.sleep() -static int wifi_sleep(lua_State* L) +/* Begin WiFi suspend functions*/ + +static int wifi_resume_cb_ref = LUA_NOREF; // Holds resume callback reference +static int wifi_suspend_cb_ref = LUA_NOREF; // Holds suspend callback reference +static uint8 wifi_resume_mode = 0; +static os_timer_t wifi_is_suspended_timer; // Timer structure for wifi_rf_closed_timer + +// C callback for bringing WiFi back from forced sleep +static void wifi_resume_cb(void) { - uint8 desired_sleep_state = 2; - sint8 wifi_fpm_do_sleep_return_value = 1; - if(lua_isnumber(L, 1)) + wifi_fpm_close(); // Disable force sleep API + SLEEP_DBG("\n\tWiFi resume\n"); + + // If resume_opmode is STATION_MODE, SOFTAP_MODE or STATIONAP_MODE + if (wifi_resume_mode > 0 && wifi_resume_mode < 4) { - if(luaL_checknumber(L, 1) == 0) - { - desired_sleep_state = 0; - } - else if(luaL_checknumber(L, 1) == 1) - { - desired_sleep_state = 1; - } + // If wifi_set_opmode_current is successful + if (wifi_set_opmode_current(wifi_resume_mode)) + { + // If opmode is STATION_MODE or STATIONAP_MODE + if (wifi_resume_mode == STATION_MODE || wifi_resume_mode == STATIONAP_MODE) + { + wifi_station_connect(); // Connect to currently configured Access Point + } + SLEEP_DBG("\n\tWiFi mode restored\n"); + wifi_resume_mode = 0; // reset variable to default value + } } - if (!FLAG_wifi_force_sleep_enabled && desired_sleep_state == 1 ) + + // If resume callback was defined + if (wifi_resume_cb_ref != LUA_NOREF) { - 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; - } + lua_State* L = lua_getstate(); // Get Lua main thread pointer + lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_resume_cb_ref); // Push resume callback onto the stack + lua_unref(L, wifi_resume_cb_ref); // Remove resume callback from registry + wifi_resume_cb_ref = LUA_NOREF; // Update variable since reference is no longer valid + lua_call(L, 0, 0); // Execute resume callback + } + return; +} +// This callback executes the suspend lua callback when Wifi is finally suspended +static void wifi_is_suspended_timer_cb(int arg) +{ + // If WiFi is suspended + if (fpm_rf_is_closed()) + { + os_timer_disarm(&wifi_is_suspended_timer); // Stop rf_closed_timer + SLEEP_DBG("\n\tWiFi is suspended\n"); + + // If suspend callback was defined + if (wifi_suspend_cb_ref != LUA_NOREF) + { + 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 + } } - else if(FLAG_wifi_force_sleep_enabled && desired_sleep_state == 0) +} + +// Lua: wifi.suspend([[duration], [Suspend_CB, [Resume_CB]], [Preserve_wifi_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(); + // If WiFi is suspended + if (fpm_rf_is_closed()) + { + lua_pushnumber(L, WIFI_SUSPENDED); + } + // If WiFi suspension is pending + else if (fpm_is_open()) + { + lua_pushnumber(L, WIFI_SUSPENSION_PENDING); + } + else + { + lua_pushnumber(L, WIFI_AWAKE); + } + return 1; // Return WiFi suspension state } - if (desired_sleep_state == 1 && FLAG_wifi_force_sleep_enabled == FALSE) + // If forced sleep api is enabled + if (fpm_is_open()) { - lua_pushnil(L); - lua_pushnumber(L, wifi_fpm_do_sleep_return_value); + // If WiFi is suspended + if (fpm_rf_is_closed()) + { + return luaL_error(L, "WiFi already suspended"); + } + else + { + return luaL_error(L, "WiFi suspension pending"); + } } - else + + uint8 current_wifi_mode = wifi_get_opmode(); // Get Current WiFi mode + uint32 suspend_duration = 0; // variable to hold suspend duration + uint8 stack_ptr = 1; // pointer to iterate through items on stack + + // If a suspend duration is provided + if (lua_isnumber(L, stack_ptr)) { - lua_pushnumber(L, FLAG_wifi_force_sleep_enabled); - lua_pushnil(L); + // Verify duration is 0 or 50000 - 268435454, if it isn't return error + luaL_argcheck(L, + (((luaL_checknumber(L, stack_ptr) > 49999) && (luaL_checknumber(L, stack_ptr) < FPM_SLEEP_MAX_TIME)) || + (luaL_checknumber(L, stack_ptr) == 0)), stack_ptr, "50000-268435454 us"); + + suspend_duration = luaL_checknumber(L, 1); // Get suspend duration + stack_ptr++; // Increment the stack pointer } - return 2; + + // If suspend duration is 0, then set it to FPM_SLEEP_MAX_TIME + if (suspend_duration == 0) + { + suspend_duration = FPM_SLEEP_MAX_TIME; + } + + // If a suspend callback was provided + if (lua_isfunction(L, stack_ptr)) + { + // If there is already a suspend callback reference + if (wifi_suspend_cb_ref != LUA_NOREF) + { + luaL_unref(L, LUA_REGISTRYINDEX, wifi_suspend_cb_ref); // Discard old callback + } + lua_pushvalue(L, stack_ptr); // Push suspend callback to the top of the stack + wifi_suspend_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); // Register suspend callback in LUA_REGISTRY and store it's reference + stack_ptr++; //Increment the stack pointer + } + + // If a resume callback was provided + if (lua_isfunction(L, stack_ptr)) + { + // If there is already a resume callback reference + if (wifi_resume_cb_ref != LUA_NOREF) + { + luaL_unref(L, LUA_REGISTRYINDEX, wifi_resume_cb_ref); // Discard old callback + } + lua_pushvalue(L, stack_ptr); // Push resume callback to the top of the stack + wifi_resume_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); // Register resume callback in LUA_REGISTRY and store it's reference + stack_ptr++; // Increment the stack pointer + } + + // If Preserve_wifi_mode parameter is TRUE and current WiFi mode is not NULL_MODE + if (lua_toboolean(L, stack_ptr) == 1 && current_wifi_mode != 0) + { + SLEEP_DBG("\n\tWiFi opmode will be preserved\n"); + wifi_resume_mode = 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 + if (wifi_set_opmode_current(NULL_MODE)) + { + wifi_fpm_open(); // Enable force sleep API + + // Verify forced sleep api is enabled + if (fpm_is_open()) + { + wifi_fpm_set_wakeup_cb(wifi_resume_cb); // Set resume C callback + sint8 return_value = wifi_fpm_do_sleep(suspend_duration); // Request WiFi suspension and store return value + + // If wifi_fpm_do_sleep success + if (return_value == 0) + { + SLEEP_DBG("\n\twifi_fpm_do_sleep success, starting rf_closed_timer\n"); + os_timer_disarm(&wifi_is_suspended_timer); // Disable timer if it is already running + os_timer_setfn(&wifi_is_suspended_timer,(os_timer_func_t *) wifi_is_suspended_timer_cb, NULL); //Set timer callback for wifi_rf_closed_timer + os_timer_arm(&wifi_is_suspended_timer, 1, 1); // Start rf_closed_timer + } + else // This should never happen + { + wifi_fpm_close(); + return luaL_error(L, "wifi_fpm_do_sleep returned %d", return_value); + } + } + } + return 0; } +// Lua: wifi.resume([Resume_CB]) +static int wifi_resume(lua_State* L) +{ + // If forced sleep api is not enabled, return error + if (!fpm_is_open()) + { + return luaL_error(L, "WIFi not suspended"); + } + + // If a resume callback was provided + if (lua_isfunction(L, 1)) + { + // If there is already a resume callback reference + if (wifi_resume_cb_ref != LUA_NOREF) + { + luaL_unref(L, LUA_REGISTRYINDEX, wifi_resume_cb_ref); // Discard old callback + } + lua_pushvalue(L, 1); //Push resume callback to the top of the stack + wifi_resume_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); // Register resume callback in LUA_REGISTRY and store it's reference + SLEEP_DBG("\n\tResume CB registered\n"); + } + wifi_fpm_do_wakeup(); // Wake up from sleep + wifi_resume_cb(); // Finish WiFi wakeup + return 0; +} + +/* End WiFi suspend functions*/ + // Lua: mac = wifi.xx.getmac() static int wifi_getmac( lua_State* L, uint8_t mode ) { @@ -895,6 +1046,7 @@ static int wifi_station_listap( lua_State* L ) { unregister_lua_cb(L, &wifi_scan_succeed); } + return 0; } // Lua: wifi.sta.gethostname() @@ -1267,7 +1419,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 ) }, diff --git a/app/modules/wifi_common.h b/app/modules/wifi_common.h index 758115fe49..213db7139d 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); @@ -35,12 +38,27 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref) } } -#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 +#if defined(WIFI_SLEEP_DEBUG) || defined(NODE_DEBUG) +#define SLEEP_DBG(...) c_printf(__VA_ARGS__) +#else +#define SLEEP_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/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index ec3aeed2c6..0663b018e5 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -61,6 +61,33 @@ 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: 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) + ## wifi.setmode() Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: @@ -204,6 +231,68 @@ 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_wifi_mode]])` + +#### Parameters +- `duration` Suspend duration in us. If a suspend duration of `0` is specified suspension will be indefinite (Range: 0 or 50000 - 268435454 us) () +- `suspend_cb` Callback to execute when WiFi is suspended. +- `resume_cb` Callback to execute when WiFi wakes from suspension. +- `preserve_wifi_mode` if set to true, previous wifi mode will be restored when waking up from suspension. (Station and StationAP modes will automatically reconnect to previously configured Access Point.) + +#### 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 +wifi.suspend(10*1000*1000) + +--Preserve WiFi mode and Suspend WiFi for 10 seconds w/ no callbacks +wifi.suspend(10000*1000, true) + +--Suspend WiFi for 10 seconds w/ suspend callback +wifi.suspend(10000*1000, function() print("WiFi suspended") end) + +--Suspend WiFi for 10 seconds w/ suspend and resume callbacks +wifi.suspend(10000*1000, function() print("WiFi suspended") end, function() print("WiFi resume") end) + +--Preserve wifi mode and suspend WiFi for 10 seconds w/ suspend and resume callbacks +wifi.suspend(10000*1000, function() print("WiFi suspended") end, function() print("WiFi resume") end, true) + +--Suspend WiFi indefinitely w/ suspend callback +wifi.suspend(function() print("WiFi suspended") end) + +--Suspend WiFi indefinitely w/ suspend and resume callbacks +wifi.suspend(function() print("WiFi suspended") end, function() print("WiFi resume") end) + +--Preserve wifi mode and suspend WiFi indefinitely +wifi.suspend(true) + +--Preserve wifi mode and suspend WiFi indefinitely w/ suspend callback +wifi.suspend(function() print("WiFi suspended") end, true) + +--Preserve wifi mode and suspend WiFi indefinitely w/ suspend and resume callbacks +wifi.suspend(function() print("WiFi suspended") end, function() print("WiFi resume") end, true) +``` + +#### See also +[`wifi.resume()`](#wifiresume) + # wifi.sta Module ## wifi.sta.autoconnect() diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 2fe4da29ec..dd072e6686 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 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_ */