diff --git a/app/Makefile b/app/Makefile index 578b0087e1..e18139a9b7 100644 --- a/app/Makefile +++ b/app/Makefile @@ -45,7 +45,11 @@ SUBDIRS= \ http \ fatfs \ esp-gdbstub \ - websocket + websocket \ + swTimer \ + misc \ + pm \ + endif # } PDIR @@ -90,11 +94,15 @@ COMPONENTS_eagle.app.v6 = \ dhtlib/libdhtlib.a \ tsl2561/tsl2561lib.a \ http/libhttp.a \ + pm/libpm.a \ websocket/libwebsocket.a \ esp-gdbstub/libgdbstub.a \ net/libnodemcu_net.a \ mbedtls/libmbedtls.a \ modules/libmodules.a \ + swTimer/libswtimer.a \ + misc/libmisc.a \ + # Inspect the modules library and work out which modules need to be linked. # For each enabled module, a symbol name of the form XYZ_module_selected is diff --git a/app/include/misc/dynarr.h b/app/include/misc/dynarr.h new file mode 100644 index 0000000000..4eb1b1309d --- /dev/null +++ b/app/include/misc/dynarr.h @@ -0,0 +1,40 @@ +#ifndef __DYNARR_H__ +#define __DYNARR_H__ +#include "user_interface.h" +#include "c_stdio.h" +#include "c_stdlib.h" + +//#define DYNARR_DEBUG +//#define DYNARR_ERROR + + +typedef struct _dynarr{ + void* data_ptr; + size_t used; + size_t array_size; + size_t data_size; +} dynarr_t; + +bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size); +bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add); +bool dynarr_remove(dynarr_t* array_ptr, void* element_ptr); +bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size); +bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_ptr); +bool dynarr_free(dynarr_t* array_ptr); + + +#if 0 || defined(DYNARR_DEBUG) || defined(NODE_DEBUG) +#define DYNARR_DBG(fmt, ...) c_printf("\n DYNARR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__) +#else +#define DYNARR_DBG(...) + +#endif + +#if 0 || defined(DYNARR_ERROR) || defined(NODE_ERROR) +#define DYNARR_ERR(fmt, ...) c_printf("\n DYNARR: "fmt"\n", ##__VA_ARGS__) +#else +#define DYNARR_ERR(...) + +#endif + +#endif // __DYNARR_H__ diff --git a/app/include/swTimer/swTimer.h b/app/include/swTimer/swTimer.h new file mode 100644 index 0000000000..abbd374566 --- /dev/null +++ b/app/include/swTimer/swTimer.h @@ -0,0 +1,53 @@ +#ifndef __SW_TIMER_H__ +#define __SW_TIMER_H__ +#include "user_interface.h" +//#define SWTMR_DEBUG +#define USE_SWTMR_ERROR_STRINGS + +#if defined(DEVELOP_VERSION) +#define SWTMR_DEBUG +#endif + +#if defined(SWTMR_DEBUG) + #define SWTMR_DBG(fmt, ...) dbg_printf("\tSWTIMER(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__) +#else + #define SWTMR_DBG(...) +#endif + +#if defined(NODE_ERROR) + #define SWTMR_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "SWTIMER:", ##__VA_ARGS__) +#else + #define SWTMR_ERR(...) +#endif + +enum SWTMR_STATUS{ + SWTMR_FAIL = 0, + SWTMR_OK = 1, + + SWTMR_MALLOC_FAIL = 10, + SWTMR_TIMER_NOT_ARMED, +// SWTMR_NULL_PTR, + + SWTMR_REGISTRY_NO_REGISTERED_TIMERS, + +// SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED, +// SWTMR_SUSPEND_ARRAY_ADD_FAILED, +// SWTMR_SUSPEND_ARRAY_REMOVE_FAILED, + SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED, + SWTMR_SUSPEND_TIMER_ALREADY_REARMED, + SWTMR_SUSPEND_NO_SUSPENDED_TIMERS, + SWTMR_SUSPEND_TIMER_NOT_SUSPENDED, + +}; + +/* Global Function Declarations */ +void swtmr_register(void* timer_ptr); +void swtmr_unregister(void* timer_ptr); +int swtmr_suspend(os_timer_t* timer_ptr); +int swtmr_resume(os_timer_t* timer_ptr); +void swtmr_print_registry(void); +void swtmr_print_suspended(void); +void swtmr_print_timer_list(void); +const char* swtmr_errorcode2str(int error_value); +bool swtmr_suspended_test(os_timer_t* timer_ptr); +#endif // __SW_TIMER_H__ diff --git a/app/include/user_config.h b/app/include/user_config.h index c786e90955..03745f931d 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -116,6 +116,10 @@ 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 ENABLE_TIMER_SUSPEND +#define PMSLEEP_ENABLE + + #define STRBUF_DEFAULT_INCREMENT 32 #endif /* __USER_CONFIG_H__ */ diff --git a/app/misc/Makefile b/app/misc/Makefile new file mode 100644 index 0000000000..3a758ddefe --- /dev/null +++ b/app/misc/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 = libmisc.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/misc/dyn_arr.c b/app/misc/dyn_arr.c new file mode 100644 index 0000000000..b6ebb8c991 --- /dev/null +++ b/app/misc/dyn_arr.c @@ -0,0 +1,131 @@ +#include "misc/dynarr.h" + +#define ARRAY_PTR_CHECK if(array_ptr == NULL || array_ptr->data_ptr == NULL){\ + /**/DYNARR_DBG("array not initialized");\ + return false; \ + } + +bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size){ + if(array_ptr == NULL || data_size == 0 || array_size == 0){ + /**/DYNARR_DBG("Invalid parameter: array_ptr(%p) data_size(%u) array_size(%u)", array_ptr, data_size, array_size); + return false; + } + if(array_ptr->data_ptr != NULL ){ + /**/DYNARR_DBG("Array already initialized: array_ptr->data_ptr=%p", array_ptr->data_ptr); + return false; + } + /**/DYNARR_DBG("Array parameters:\n\t\t\tarray_size(%u)\n\t\t\tdata_size(%u)\n\t\t\ttotal size(bytes):%u", array_size, data_size, (array_size * data_size)); + + void* temp_array = c_zalloc(array_size * data_size); + if(temp_array == NULL){ + /**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (array_size * data_size), system_get_free_heap_size()); + return false; + } + + array_ptr->data_ptr = temp_array; + array_ptr->array_size = array_size; + array_ptr->data_size = data_size; + array_ptr->used = 0; + + return true; +} + +bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add){ + ARRAY_PTR_CHECK; + + if(elements_to_add <= 0){ + /**/DYNARR_DBG("Invalid qty: elements_to_add=%u", elements_to_add); + return false; + } + + size_t new_array_size = array_ptr->array_size + elements_to_add; + + /**/DYNARR_DBG("old size=%u\tnew size=%u\tmem used=%u", + array_ptr->array_size, new_array_size, (new_array_size * array_ptr->data_size)); + + void* temp_array_p = c_realloc(array_ptr->data_ptr, new_array_size * array_ptr->data_size); + if(temp_array_p == NULL){ + /**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (new_array_size * array_ptr->data_size), system_get_free_heap_size()); + return false; + } + + array_ptr->data_ptr = temp_array_p; + + size_t prev_size = array_ptr->array_size; + + array_ptr->array_size = new_array_size; + + //set memory to 0 for newly added array elements + memset((uint8*) array_ptr->data_ptr + (prev_size * array_ptr->data_size), 0, (elements_to_add * array_ptr->data_size)); + + /**/DYNARR_DBG("Array successfully resized"); + return true; +} + +bool dynarr_remove(dynarr_t* array_ptr, void* element_to_remove){ + ARRAY_PTR_CHECK; + + uint8* element_ptr = element_to_remove; + uint8* data_ptr = array_ptr->data_ptr; + + if(dynarr_boundaryCheck(array_ptr, element_to_remove) == FALSE){ + return false; + } + + //overwrite element to be removed by shifting all elements to the left + memmove(element_ptr, element_ptr + array_ptr->data_size, (array_ptr->array_size - 1) * array_ptr->data_size - (element_ptr - data_ptr)); + + //clear newly freed element + memset(data_ptr + ((array_ptr->array_size-1) * array_ptr->data_size), 0, array_ptr->data_size); + + //decrement array used since we removed an element + array_ptr->used--; + /**/DYNARR_DBG("element(%p) removed from array", element_ptr); + return true; +} + +bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size){ + ARRAY_PTR_CHECK; + + if(data_size != array_ptr->data_size){ + /**/DYNARR_DBG("Invalid data size: data_size(%u) != arr->data_size(%u)", data_size, array_ptr->data_size); + return false; + } + + if(array_ptr->array_size == array_ptr->used){ + if(!dynarr_resize(array_ptr, (array_ptr->array_size/2))){ + return false; + } + } + memcpy(((uint8*)array_ptr->data_ptr + (array_ptr->used * array_ptr->data_size)), data_ptr, array_ptr->data_size); + + array_ptr->used++; + return true; +} + +bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_to_check){ + ARRAY_PTR_CHECK; + + uint8* data_ptr = array_ptr->data_ptr; + uint8* element_ptr = element_to_check; + + if(element_ptr < data_ptr || ((element_ptr - data_ptr) / array_ptr->data_size) > array_ptr->array_size - 1){ + /**/DYNARR_DBG("element_ptr(%p) out of bounds: first element ptr:%p last element ptr:%p", + element_ptr, data_ptr, data_ptr + ((array_ptr->array_size - 1) * array_ptr->data_size)); + return false; + } + return true; +} + +bool dynarr_free(dynarr_t* array_ptr){ + ARRAY_PTR_CHECK; + + c_free(array_ptr->data_ptr); + + array_ptr->data_ptr=NULL; + array_ptr->array_size = array_ptr->used = 0; + + /**/DYNARR_DBG("array freed"); + return true; +} + diff --git a/app/modules/Makefile b/app/modules/Makefile index 87d49b7cb6..4c1f348920 100644 --- a/app/modules/Makefile +++ b/app/modules/Makefile @@ -55,6 +55,7 @@ INCLUDES += -I ../fatfs INCLUDES += -I ../http INCLUDES += -I ../sjson INCLUDES += -I ../websocket +INCLUDES += -I ../pm PDIR := ../$(PDIR) sinclude $(PDIR)Makefile diff --git a/app/modules/node.c b/app/modules/node.c index 2a34111f6a..1bfcc12b5e 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -74,19 +74,36 @@ 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("START"); + pmSleep_execute_lua_cb(&node_sleep_resume_cb_ref); + PMSLEEP_DBG("END"); +} + +// 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 ) { @@ -570,6 +587,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/tmr.c b/app/modules/tmr.c index b85ec80fc3..76ee6d62e7 100755 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -53,12 +53,14 @@ tmr.softwd(int) #include "platform.h" #include "c_types.h" #include "user_interface.h" +#include "swTimer/swTimer.h" #define TIMER_MODE_OFF 3 #define TIMER_MODE_SINGLE 0 #define TIMER_MODE_SEMI 2 #define TIMER_MODE_AUTO 1 -#define TIMER_IDLE_FLAG (1<<7) +#define TIMER_IDLE_FLAG (1<<7) + #define STRINGIFY_VAL(x) #x #define STRINGIFY(x) STRINGIFY_VAL(x) @@ -172,14 +174,14 @@ static int tmr_register(lua_State* L){ lua_pushvalue(L, 4); sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX); if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) - ets_timer_disarm(&tmr->os); + os_timer_disarm(&tmr->os); //there was a bug in this part, the second part of the following condition was missing if(tmr->lua_ref != LUA_NOREF && tmr->lua_ref != ref) luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); tmr->lua_ref = ref; tmr->mode = mode|TIMER_IDLE_FLAG; tmr->interval = interval; - ets_timer_setfn(&tmr->os, alarm_timer_common, tmr); + os_timer_setfn(&tmr->os, alarm_timer_common, tmr); return 0; } @@ -197,7 +199,7 @@ static int tmr_start(lua_State* L){ lua_pushboolean(L, 0); }else{ tmr->mode &= ~TIMER_IDLE_FLAG; - ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1); + os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO); lua_pushboolean(L, 1); } return 1; @@ -221,7 +223,7 @@ static int tmr_stop(lua_State* L){ //we return false if the timer is idle (of not registered) if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){ tmr->mode |= TIMER_IDLE_FLAG; - ets_timer_disarm(&tmr->os); + os_timer_disarm(&tmr->os); lua_pushboolean(L, 1); }else{ lua_pushboolean(L, 0); @@ -229,6 +231,73 @@ static int tmr_stop(lua_State* L){ return 1; } +#ifdef ENABLE_TIMER_SUSPEND + +static int tmr_suspend(lua_State* L){ + timer_t tmr = tmr_get(L, 1); + + if((tmr->mode & TIMER_IDLE_FLAG) == 1){ + return luaL_error(L, "timer not armed"); + } + + int retval = swtmr_suspend(&tmr->os); + + if(retval != SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + + return 1; +} + +static int tmr_resume(lua_State* L){ + timer_t tmr = tmr_get(L, 1); + + if(swtmr_suspended_test(&tmr->os) == FALSE){ + return luaL_error(L, "timer not suspended"); + } + + int retval = swtmr_resume(&tmr->os); + + if(retval != SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + return 1; +} + +static int tmr_suspend_all (lua_State *L) +{ + sint32 retval = swtmr_suspend(NULL); + // lua_pushnumber(L, swtmr_suspend(NULL)); + if(retval!=SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + return 1; +} + +static int tmr_resume_all (lua_State *L) +{ + sint32 retval = swtmr_resume(NULL); + if(retval!=SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + return 1; +} + + +#endif + // Lua: tmr.unregister( id / ref ) static int tmr_unregister(lua_State* L){ timer_t tmr = tmr_get(L, 1); @@ -239,7 +308,7 @@ static int tmr_unregister(lua_State* L){ } if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) - ets_timer_disarm(&tmr->os); + os_timer_disarm(&tmr->os); if(tmr->lua_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); tmr->lua_ref = LUA_NOREF; @@ -256,8 +325,8 @@ static int tmr_interval(lua_State* L){ if(tmr->mode != TIMER_MODE_OFF){ tmr->interval = interval; if(!(tmr->mode&TIMER_IDLE_FLAG)){ - ets_timer_disarm(&tmr->os); - ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1); + os_timer_disarm(&tmr->os); + os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO); } } return 0; @@ -271,9 +340,15 @@ static int tmr_state(lua_State* L){ lua_pushnil(L); return 1; } - lua_pushboolean(L, (tmr->mode&TIMER_IDLE_FLAG)==0); - lua_pushinteger(L, tmr->mode&(~TIMER_IDLE_FLAG)); - return 2; + + lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0); + lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG)); +#ifdef ENABLE_TIMER_SUSPEND + lua_pushboolean(L, swtmr_suspended_test(&tmr->os)); +#else + lua_pushnil(L); +#endif + return 3; } /*I left the led comments 'couse I don't know @@ -348,10 +423,27 @@ static int tmr_create( lua_State *L ) { ud->lua_ref = LUA_NOREF; ud->self_ref = LUA_NOREF; ud->mode = TIMER_MODE_OFF; - ets_timer_disarm(&ud->os); + os_timer_disarm(&ud->os); return 1; } + +#if defined(SWTMR_DEBUG) +static void tmr_printRegistry(lua_State* L){ + swtmr_print_registry(); +} + +static void tmr_printSuspended(lua_State* L){ + swtmr_print_suspended(); +} + +static void tmr_printTimerlist(lua_State* L){ + swtmr_print_timer_list(); +} + + +#endif + // Module function map static const LUA_REG_TYPE tmr_dyn_map[] = { @@ -362,11 +454,24 @@ static const LUA_REG_TYPE tmr_dyn_map[] = { { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) }, +#ifdef ENABLE_TIMER_SUSPEND + { LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) }, + { LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) }, +#endif { LSTRKEY( "__gc" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) }, { LNILKEY, LNILVAL } }; +#if defined(SWTMR_DEBUG) +static const LUA_REG_TYPE tmr_dbg_map[] = { + { LSTRKEY( "printRegistry" ), LFUNCVAL( tmr_printRegistry ) }, + { LSTRKEY( "printSuspended" ), LFUNCVAL( tmr_printSuspended ) }, + { LSTRKEY( "printTimerlist" ), LFUNCVAL( tmr_printTimerlist ) }, + { LNILKEY, LNILVAL } +}; +#endif + static const LUA_REG_TYPE tmr_map[] = { { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) }, { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) }, @@ -376,11 +481,20 @@ static const LUA_REG_TYPE tmr_map[] = { { LSTRKEY( "register" ), LFUNCVAL( tmr_register ) }, { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) }, { LSTRKEY( "start" ), LFUNCVAL( tmr_start ) }, - { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, + { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, +#ifdef ENABLE_TIMER_SUSPEND + { LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) }, + { LSTRKEY( "suspend_all" ), LFUNCVAL( tmr_suspend_all ) }, + { LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) }, + { LSTRKEY( "resume_all" ), LFUNCVAL( tmr_resume_all ) }, +#endif { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) }, { LSTRKEY( "create" ), LFUNCVAL( tmr_create ) }, +#if defined(SWTMR_DEBUG) + { LSTRKEY( "debug" ), LROVAL( tmr_dbg_map ) }, +#endif { LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) }, { LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) }, { LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) }, @@ -396,14 +510,16 @@ int luaopen_tmr( lua_State *L ){ alarm_timers[i].lua_ref = LUA_NOREF; alarm_timers[i].self_ref = LUA_REFNIL; alarm_timers[i].mode = TIMER_MODE_OFF; + //improve boot speed by using ets_timer_disarm instead of os_timer_disarm to avoid timer registry maintenance call. ets_timer_disarm(&alarm_timers[i].os); } last_rtc_time=system_get_rtc_time(); // Right now is time 0 last_rtc_time_us=0; + //improve boot speed by using ets_timer_disarm instead of os_timer_disarm to avoid timer registry maintenance call. ets_timer_disarm(&rtc_timer); - ets_timer_setfn(&rtc_timer, rtc_callback, NULL); - ets_timer_arm_new(&rtc_timer, 1000, 1, 1); + os_timer_setfn(&rtc_timer, rtc_callback, NULL); + os_timer_arm(&rtc_timer, 1000, 1); return 0; } diff --git a/app/modules/wifi.c b/app/modules/wifi.c index 4415e84dc5..fb4bc464b6 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -26,10 +26,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"); @@ -302,66 +298,92 @@ static int wifi_getphymode( lua_State* L ) return 1; } -// Lua: 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); + 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: wifi.nullmodesleep() static int wifi_null_mode_auto_sleep(lua_State* L) { @@ -1659,7 +1681,10 @@ 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 ) }, +#ifdef PMSLEEP_ENABLE + { LSTRKEY( "suspend" ), LFUNCVAL( wifi_suspend ) }, + { LSTRKEY( "resume" ), LFUNCVAL( wifi_resume ) }, +#endif { LSTRKEY( "nullmodesleep" ), LFUNCVAL( wifi_null_mode_auto_sleep ) }, #ifdef WIFI_SMART_ENABLE { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, diff --git a/app/modules/wifi_common.h b/app/modules/wifi_common.h index 65b7a7bd0f..df8a77a74b 100644 --- a/app/modules/wifi_common.h +++ b/app/modules/wifi_common.h @@ -52,6 +52,15 @@ void wifi_change_default_host_name(void); #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..ad8c75b918 --- /dev/null +++ b/app/pm/pmSleep.c @@ -0,0 +1,381 @@ +#include "pmSleep.h" +#ifdef PMSLEEP_ENABLE +#define STRINGIFY_VAL(x) #x +#define STRINGIFY(x) STRINGIFY_VAL(x) + +//holds duration error string +//uint32 PMSLEEP_SLEEP_MAX_TIME=FPM_SLEEP_MAX_TIME-1; +const char *PMSLEEP_DURATION_ERR_STR="duration: 0 or "STRINGIFY(PMSLEEP_SLEEP_MIN_TIME)"-"STRINGIFY(PMSLEEP_SLEEP_MAX_TIME)" us"; + + +/* INTERNAL VARIABLES */ +static void (*user_suspend_cb)(void) = NULL; +static void (*user_resume_cb)(void) = NULL; +static uint8 resume_opmode = 0; +static os_timer_t wifi_suspended_test_timer; +static uint8 autosleep_setting_temp = 0; +static os_timer_t null_mode_check_timer; +static pmSleep_param_t current_config; + + +/* INTERNAL FUNCTION DECLARATIONS */ +static void suspend_all_timers(void); +static void null_mode_check_timer_cb(void* arg); +static void resume_all_timers(void); +static inline void register_lua_cb(lua_State* L,int* cb_ref); +static void resume_cb(void); +static void wifi_suspended_timer_cb(int arg); + +/* INTERNAL FUNCTIONS */ + +#include "swTimer/swTimer.h" +static void suspend_all_timers(void){ +#ifdef ENABLE_TIMER_SUSPEND + swtmr_suspend(NULL); +#endif + return; +} + +static void resume_all_timers(void){ +#ifdef ENABLE_TIMER_SUSPEND + swtmr_resume(NULL); +#endif + return; +} + +static void null_mode_check_timer_cb(void* arg){ + if (wifi_get_opmode() == NULL_MODE){ + //check if uart 0 tx buffer is empty and uart 1 tx buffer is empty + if(current_config.sleep_mode == LIGHT_SLEEP_T){ + if((READ_PERI_REG(UART_STATUS(0)) & (UART_TXFIFO_CNT<= 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); + + if( cfg->sleep_mode == MODEM_SLEEP_T ){ //WiFi suspend + 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 if (cfg->sleep_mode == LIGHT_SLEEP_T){ //CPU suspend +#ifdef ENABLE_TIMER_SUSPEND + lua_getfield(L, table_idx, "wake_pin"); + 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) && Linteger_tmp > 0), table_idx, "wake_pin: Invalid interrupt pin"); + cfg->wake_pin = Linteger_tmp; + } + else{ + return luaL_argerror( L, table_idx, "wake_pin: must be number" ); + } + } + else if(cfg->sleep_duration == 0){ + return luaL_argerror( L, table_idx, "wake_pin: must specify pin if sleep duration is indefinite" ); + } + 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); +#endif + } + else{ + return luaL_error(L, "FPM Sleep mode not available"); + } + + 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 sleep duration is zero, set indefinite sleep duration + if( cfg->sleep_duration == 0 ){ + cfg->sleep_duration = FPM_SLEEP_MAX_TIME; + } + + return 0; +} + +//This function resumes ESP from MODEM_SLEEP +void pmSleep_resume(void (*resume_cb_ptr)(void)){ + PMSLEEP_DBG("START"); + uint8 fpm_state = pmSleep_get_state(); + + if(fpm_state>0){ + if(resume_cb_ptr != NULL){ + user_resume_cb = resume_cb_ptr; + } + wifi_fpm_do_wakeup(); // Wake up from sleep + resume_cb(); // Finish WiFi wakeup + } + + PMSLEEP_DBG("END"); + return; +} + +//this function puts the ESP8266 into MODEM_SLEEP or LIGHT_SLEEP +void pmSleep_suspend(pmSleep_param_t *cfg){ + PMSLEEP_DBG("START"); + + lua_State* L = lua_getstate(); +#ifndef ENABLE_TIMER_SUSPEND + if(cfg->sleep_mode == LIGHT_SLEEP_T){ + luaL_error(L, "timer suspend API is disabled, light sleep unavailable"); + return; + } +#endif + uint8 current_wifi_mode = wifi_get_opmode(); // Get Current WiFi mode + + user_resume_cb = cfg->resume_cb_ptr; //pointer to hold address of user_cb + 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){ + resume_opmode = current_wifi_mode; + } + + + if (current_wifi_mode == STATION_MODE || current_wifi_mode == STATIONAP_MODE){ + wifi_station_disconnect(); // Disconnect from Access Point + } + + //the null mode sleep functionality interferes with the forced sleep API and must be disabled + if(get_fpm_auto_sleep_flag() == 1){ + autosleep_setting_temp = 1; + wifi_fpm_auto_sleep_set_in_null_mode(0); + } + + // If wifi_set_opmode_current is successful + if (wifi_set_opmode_current(NULL_MODE)){ + PMSLEEP_DBG("sleep_mode is %s", cfg->sleep_mode == MODEM_SLEEP_T ? "MODEM_SLEEP_T" : "LIGHT_SLEEP_T"); + + wifi_fpm_set_sleep_type(cfg->sleep_mode); + + wifi_fpm_open(); // Enable force sleep API + + if (cfg->sleep_mode == LIGHT_SLEEP_T){ +#ifdef ENABLE_TIMER_SUSPEND + if(platform_gpio_exists(cfg->wake_pin) && cfg->wake_pin > 0){ + PMSLEEP_DBG("Wake-up pin is %d\t interrupt type is %d", cfg->wake_pin, cfg->int_type); + + if((cfg->int_type != GPIO_PIN_INTR_ANYEDGE && cfg->int_type != GPIO_PIN_INTR_HILEVEL + && cfg->int_type != GPIO_PIN_INTR_LOLEVEL && cfg->int_type != GPIO_PIN_INTR_NEGEDGE + && cfg->int_type != GPIO_PIN_INTR_POSEDGE )){ + wifi_fpm_close(); + PMSLEEP_DBG("Invalid interrupt type"); + return; + } + + GPIO_DIS_OUTPUT(pin_num[cfg->wake_pin]); + PIN_FUNC_SELECT(pin_mux[cfg->wake_pin], pin_func[cfg->wake_pin]); + wifi_enable_gpio_wakeup(pin_num[cfg->wake_pin], cfg->int_type); + } + else if(cfg->sleep_duration == FPM_SLEEP_MAX_TIME && cfg->wake_pin == 255){ + wifi_fpm_close(); + PMSLEEP_DBG("No wake-up pin defined"); + return; + } +#endif + + } + + wifi_fpm_set_wakeup_cb(resume_cb); // Set resume C callback + + c_memcpy(¤t_config, cfg, sizeof(pmSleep_param_t)); + PMSLEEP_DBG("sleep duration is %d", current_config.sleep_duration); + + //this timer intentionally bypasses the swtimer timer registration process + ets_timer_disarm(&null_mode_check_timer); + ets_timer_setfn(&null_mode_check_timer, null_mode_check_timer_cb, false); + ets_timer_arm_new(&null_mode_check_timer, 1, 1, 1); + } + else{ + PMSLEEP_ERR("opmode change fail"); + } + PMSLEEP_DBG("END"); + return; +} + +#endif diff --git a/app/pm/pmSleep.h b/app/pm/pmSleep.h new file mode 100644 index 0000000000..a3c6ff34ad --- /dev/null +++ b/app/pm/pmSleep.h @@ -0,0 +1,69 @@ +#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" +#include "task/task.h" +#include "c_string.h" + +#if defined(DEVELOP_VERSION) +#define PMSLEEP_DEBUG +#endif + +#if defined(PMSLEEP_DEBUG) + #define PMSLEEP_DBG(fmt, ...) dbg_printf("\tPMSLEEP(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__) +#else + #define PMSLEEP_DBG(...) //c_printf(__VA_ARGS__) +#endif + +#if defined(NODE_ERROR) + #define PMSLEEP_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "PMSLEEP:", ##__VA_ARGS__) +#else + #define PMSLEEP_ERR(...) +#endif + + + + + +#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_pin=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 ) } + + + +typedef struct pmSleep_param{ + uint32 sleep_duration; + uint8 sleep_mode; + uint8 wake_pin; + 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/app/swTimer/Makefile b/app/swTimer/Makefile new file mode 100644 index 0000000000..eaa132c911 --- /dev/null +++ b/app/swTimer/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 = libswtimer.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/swTimer/swTimer.c b/app/swTimer/swTimer.c new file mode 100644 index 0000000000..9c86c4aa12 --- /dev/null +++ b/app/swTimer/swTimer.c @@ -0,0 +1,632 @@ +/* swTimer.c SDK timer suspend API + * + * SDK software timer API info: + * + * The SDK software timer uses a linked list called `os_timer_t* timer_list` to keep track of + * all currently armed timers. + * + * The SDK software timer API executes in a task. The priority of this task in relation to the + * application level tasks is unknown (at time of writing). + * + * + * To determine when a timer's callback should be executed, the respective timer's `timer_expire` + * variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is + * less than the current FRC2 count, the timer's callback is fired. + * + * The timers in this list are organized in an ascending order starting with the timer + * with the lowest timer_expire. + * + * When a timer expires that has a timer_period greater than 0, timer_expire is changed to + * current FRC2 + timer_period, then the timer is inserted back in to the list in the correct position. + * + * when using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond. + * + * + * TIMER SUSPEND API: + * + * Timer registry: + * void sw_timer_register(void* timer_ptr); + * - Adds timers to the timer registry by adding it to a queue that is later + * processed by timer_register_task that performs the registry maintenance + * + * void sw_timer_unregister(void* timer_ptr); + * - Removes timers from the timer registry by adding it to a queue that is later + * processed by timer_unregister_task that performs the registry maintenance + * + * + * int sw_timer_suspend(os_timer_t* timer_ptr); + * - Suspend a single active timer or suspend all active timers. + * - if no timer pointer is provided, timer_ptr == NULL, then all currently active timers will be suspended. + * + * int sw_timer_resume(os_timer_t* timer_ptr); + * - Resume a single suspended timer or resume all suspended timers. + * - if no timer pointer is provided, timer_ptr == NULL, then all currently suspended timers will be resumed. + * + * + * + */ +#include "swTimer/swTimer.h" +#include "c_stdio.h" +#include "misc/dynarr.h" +#include "task/task.h" + +#ifdef ENABLE_TIMER_SUSPEND + +/* Settings */ +#define TIMER_REGISTRY_INITIAL_SIZE 10 +#ifdef USE_SWTMR_ERROR_STRINGS +static const char* SWTMR_ERROR_STRINGS[]={ + [SWTMR_MALLOC_FAIL] = "Out of memory!", + [SWTMR_TIMER_NOT_ARMED] = "Timer is not armed", +// [SWTMR_NULL_PTR] = "A NULL pointer was passed to timer suspend api", + [SWTMR_REGISTRY_NO_REGISTERED_TIMERS] = "No timers in registry", +// [SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED] = "Suspend array init fail", +// [SWTMR_SUSPEND_ARRAY_ADD_FAILED] = "Unable to add suspended timer to array", +// [SWTMR_SUSPEND_ARRAY_REMOVE_FAILED] = "Unable to remove suspended timer from array", + [SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED] = "Already suspended", + [SWTMR_SUSPEND_TIMER_ALREADY_REARMED] = "Already been re-armed", + [SWTMR_SUSPEND_NO_SUSPENDED_TIMERS] = "No suspended timers", + [SWTMR_SUSPEND_TIMER_NOT_SUSPENDED] = "Not suspended", + +}; +#endif + +/* Private Function Declarations */ +static inline bool timer_armed_check(os_timer_t* timer_ptr); +static inline int timer_do_suspend(os_timer_t* timer_ptr); +static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr); +static void timer_register_task(task_param_t param, uint8 priority); +static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr); +static inline void timer_registry_remove_unarmed(void); +static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr); +static void timer_unregister_task(task_param_t param, uint8 priority); + +/* Private Variable Definitions */ + static task_handle_t timer_reg_task_id = false; + static task_handle_t timer_unreg_task_id = false; + + static dynarr_t timer_registry = {0}; + static dynarr_t suspended_timers = {0}; + + typedef struct registry_queue{ + struct registry_queue* next; + os_timer_t* timer_ptr; + }registry_queue_t; + + static registry_queue_t* register_queue = NULL; + static registry_queue_t* unregister_queue = NULL; + +/* Private Function Definitions */ + +//NOTE: Interrupts are temporarily blocked during the execution of this function +static inline bool timer_armed_check(os_timer_t* timer_ptr){ + bool retval = FALSE; + + // we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though. + ETS_INTR_LOCK(); + + os_timer_t* timer_list_ptr = timer_list; //get head node pointer of timer_list + + //if present find timer_ptr in timer_list rand return result + while(timer_list_ptr != NULL){ + if(timer_list_ptr == timer_ptr){ + retval = TRUE; + break; + } + timer_list_ptr = timer_list_ptr->timer_next; + } + + //we are done with timer_list, it is now safe to unlock interrupts + ETS_INTR_UNLOCK(); + + //return value + return retval; +} + +static inline int timer_do_suspend(os_timer_t* timer_ptr){ + if(timer_ptr == NULL){ + SWTMR_ERR("timer_ptr is invalid"); + return SWTMR_FAIL; + } + + volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS); + + if(timer_armed_check(timer_ptr) == FALSE){ + return SWTMR_TIMER_NOT_ARMED; + } + + os_timer_t** suspended_timer_ptr = timer_suspended_check(timer_ptr); + + uint32 expire_temp = 0; + uint32 period_temp = timer_ptr->timer_period; + + if(timer_ptr->timer_expire < frc2_count){ + expire_temp = 5; // 16 us in ticks (1 tick = ~3.2 us) (arbitrarily chosen value) + } + else{ + expire_temp = timer_ptr->timer_expire - frc2_count; + } + + ets_timer_disarm(timer_ptr); + timer_unregister_task((task_param_t)timer_ptr, false); + + timer_ptr->timer_expire = expire_temp; + timer_ptr->timer_period = period_temp; + + if(suspended_timers.data_ptr == NULL){ + if(!dynarr_init(&suspended_timers, 10, sizeof(os_timer_t*))){ + SWTMR_ERR("Suspend array init fail"); + return SWTMR_FAIL; + } + } + + if(suspended_timer_ptr == NULL){ +// return SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED; + if(!dynarr_add(&suspended_timers, &timer_ptr, sizeof(timer_ptr))){ + SWTMR_ERR("Unable to add suspended timer to array"); + return SWTMR_FAIL; + } + } + + return SWTMR_OK; +} + +//NOTE: Interrupts are temporarily blocked during the execution of this function +static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr){ + if(suspended_timer_ptr == NULL){ + SWTMR_ERR("suspended_timer_ptr is invalid"); + return SWTMR_FAIL; + } + + os_timer_t* timer_list_ptr = NULL; + os_timer_t* resume_timer_ptr = *suspended_timer_ptr; + volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS); + + //verify timer has not been rearmed + if(timer_armed_check(resume_timer_ptr) == TRUE){ + SWTMR_DBG("Timer(%p) already rearmed, removing from array", resume_timer_ptr); + if(!dynarr_remove(&suspended_timers, suspended_timer_ptr)){ + SWTMR_ERR("Failed to remove timer from suspend array"); + return SWTMR_FAIL; + } + return SWTMR_OK; + } + + //Prepare timer for resume + resume_timer_ptr->timer_expire += frc2_count; + + timer_register_task((task_param_t)resume_timer_ptr, false); + SWTMR_DBG("Removing timer(%p) from suspend array", resume_timer_ptr); + + //This section performs the actual resume of the suspended timer + + // we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though. + ETS_INTR_LOCK(); + + timer_list_ptr = timer_list; + + while(timer_list_ptr != NULL){ + if(resume_timer_ptr->timer_expire > timer_list_ptr->timer_expire){ + if(timer_list_ptr->timer_next != NULL){ + if(resume_timer_ptr->timer_expire < timer_list_ptr->timer_next->timer_expire){ + resume_timer_ptr->timer_next = timer_list_ptr->timer_next; + timer_list_ptr->timer_next = resume_timer_ptr; + break; + } + else{ + //next timer in timer_list + } + } + else{ + timer_list_ptr->timer_next = resume_timer_ptr; + resume_timer_ptr->timer_next = NULL; + break; + } + } + else if(timer_list_ptr == timer_list){ + resume_timer_ptr->timer_next=timer_list_ptr; + timer_list = timer_list_ptr = resume_timer_ptr; + break; + } + + timer_list_ptr = timer_list_ptr->timer_next; + } + + //we no longer need to block interrupts + ETS_INTR_UNLOCK(); + + return SWTMR_OK; +} + +static void timer_register_task(task_param_t param, uint8 priority){ + if(timer_registry.data_ptr==NULL){ + if(!dynarr_init(&timer_registry, TIMER_REGISTRY_INITIAL_SIZE, sizeof(os_timer_t*))){ + SWTMR_ERR("timer registry init Fail!"); + return; + } + } + + os_timer_t* timer_ptr = NULL; + + //if a timer pointer is provided, override normal queue processing behavior + if(param != 0){ + timer_ptr = (os_timer_t*)param; + } + else{ + //process an item in the register queue + if(register_queue == NULL){ + /**/SWTMR_ERR("ERROR: REGISTER QUEUE EMPTY"); + return; + } + + registry_queue_t* queue_temp = register_queue; + register_queue = register_queue->next; + + timer_ptr = queue_temp->timer_ptr; + + c_free(queue_temp); + + if(register_queue != NULL){ + SWTMR_DBG("register_queue not empty, posting task"); + task_post_low(timer_reg_task_id, false); + } + } + + os_timer_t** suspended_tmr_ptr = timer_suspended_check(timer_ptr); + if(suspended_tmr_ptr != NULL){ + if(!dynarr_remove(&suspended_timers, suspended_tmr_ptr)){ + SWTMR_ERR("failed to remove %p from suspend registry", suspended_tmr_ptr); + } + SWTMR_DBG("removed timer from suspended timers"); + } + + if(timer_registry_check(timer_ptr) != NULL){ + /**/SWTMR_DBG("timer(%p) found in registry, returning", timer_ptr); + return; + } + + if(!dynarr_add(&timer_registry, &timer_ptr, sizeof(timer_ptr))){ + /**/SWTMR_ERR("Registry append failed"); + return; + } + + return; +} + +static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr){ + if(timer_registry.data_ptr == NULL){ + return NULL; + } + if(timer_registry.used > 0){ + + os_timer_t** timer_registry_array = timer_registry.data_ptr; + + for(uint32 i=0; i < timer_registry.used; i++){ + if(timer_registry_array[i] == timer_ptr){ + /**/SWTMR_DBG("timer(%p) is registered", timer_registry_array[i]); + return &timer_registry_array[i]; + } + } + } + + return NULL; +} + +static inline void timer_registry_remove_unarmed(void){ + if(timer_registry.data_ptr == NULL){ + return; + } + if(timer_registry.used > 0){ + os_timer_t** timer_registry_array = timer_registry.data_ptr; + for(uint32 i=0; i < timer_registry.used; i++){ + if(timer_armed_check(timer_registry_array[i]) == FALSE){ + timer_unregister_task((task_param_t)timer_registry_array[i], false); + } + } + } +} + + +static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr){ + if(suspended_timers.data_ptr == NULL){ + return NULL; + } + if(suspended_timers.used > 0){ + + os_timer_t** suspended_timer_array = suspended_timers.data_ptr; + + for(uint32 i=0; i < suspended_timers.used; i++){ + if(suspended_timer_array[i] == timer_ptr){ + return &suspended_timer_array[i]; + } + } + } + + return NULL; +} + +static void timer_unregister_task(task_param_t param, uint8 priority){ + if(timer_registry.data_ptr == NULL){ + return; + } + os_timer_t* timer_ptr = NULL; + + if(param != false){ + timer_ptr = (os_timer_t*)param; + } + else{ + + if(unregister_queue == NULL) { + SWTMR_ERR("ERROR register queue empty"); + return; + } + registry_queue_t* queue_temp = unregister_queue; + timer_ptr = queue_temp->timer_ptr; + unregister_queue = unregister_queue->next; + c_free(queue_temp); + if(unregister_queue != NULL){ + SWTMR_DBG("unregister_queue not empty, posting task"); + task_post_low(timer_unreg_task_id, false); + } + } + if(timer_armed_check(timer_ptr) == TRUE){ + SWTMR_DBG("%p still armed, can't remove from registry", timer_ptr); + return; + } + + + os_timer_t** registry_ptr = timer_registry_check(timer_ptr); + if(registry_ptr != NULL){ + if(!dynarr_remove(&timer_registry, registry_ptr)){ + + /**/SWTMR_ERR("Failed to remove timer from registry"); + /**/SWTMR_DBG("registry_ptr = %p", registry_ptr); + return; + } + } + else{ + //timer not in registry + } + return; +} + +/* Global Function Definitions */ + +#if defined(SWTMR_DEBUG) + +void swtmr_print_registry(void){ + volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS); + uint32 time_till_fire = 0; + uint32 time = system_get_time(); + + timer_registry_remove_unarmed(); + time = system_get_time()-time; + + /**/SWTMR_DBG("registry_remove_unarmed_timers() took %u us", time); + + os_timer_t** timer_array = timer_registry.data_ptr; + + c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n FRC2 COUNT %u\n", + timer_registry.used, timer_registry.array_size, timer_registry.array_size * timer_registry.data_size, frc2_count); + c_printf("\n Registered timer array contents:\n"); + c_printf(" %-5s %-10s %-10s %-13s %-10s %-10s %-10s\n", "idx", "ptr", "expire", "period(tick)", "period(ms)", "fire(tick)", "fire(ms)"); + + for(uint32 i=0; i < timer_registry.used; i++){ + time_till_fire = (timer_array[i]->timer_expire - frc2_count); + c_printf(" %-5d %-10p %-10d %-13d %-10d %-10d %-10d\n", i, timer_array[i], timer_array[i]->timer_expire, timer_array[i]->timer_period, (uint32)(timer_array[i]->timer_period/312.5), time_till_fire, (uint32)(time_till_fire/312.5)); + + } + + return; +} + +void swtmr_print_suspended(void){ + os_timer_t** susp_timer_array = suspended_timers.data_ptr; + + c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n", + suspended_timers.used, suspended_timers.array_size, suspended_timers.array_size * suspended_timers.data_size); + c_printf("\n Suspended timer array contents:\n"); + c_printf(" %-5s %-10s %-15s %-15s %-14s %-10s\n", "idx", "ptr", "time left(tick)", "time left(ms)", "period(tick)", "period(ms)"); + + for(uint32 i=0; i < suspended_timers.used; i++){ + c_printf(" %-5d %-10p %-15d %-15d %-14d %-10d\n", i, susp_timer_array[i], susp_timer_array[i]->timer_expire, (uint32)(susp_timer_array[i]->timer_expire/312.5), susp_timer_array[i]->timer_period, (uint32)(susp_timer_array[i]->timer_period/312.5)); + + } + return; +} + +void swtmr_print_timer_list(void){ + volatile uint32 frc2_count=RTC_REG_READ(FRC2_COUNT_ADDRESS); + os_timer_t* timer_list_ptr=NULL; + uint32 time_till_fire=0; + c_printf("\n\tcurrent FRC2 count:%u\n", frc2_count); + c_printf(" timer_list contents:\n"); + c_printf(" %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", "ptr", "expire", "period", "func", "arg", "fire(tick)", "fire(ms)"); + + ETS_INTR_LOCK(); + + timer_list_ptr=timer_list; + + while(timer_list_ptr != NULL){ + time_till_fire=(timer_list_ptr->timer_expire - frc2_count) / 312.5; + + c_printf(" %-10p %-10u %-10u %-10p %-10p %-10u %-10u\n", + timer_list_ptr, (uint32)(timer_list_ptr->timer_expire), + (uint32)(timer_list_ptr->timer_period ), timer_list_ptr->timer_func, + timer_list_ptr->timer_arg, (timer_list_ptr->timer_expire - frc2_count), time_till_fire); + + timer_list_ptr=timer_list_ptr->timer_next; + } + ETS_INTR_UNLOCK(); + c_printf(" NOTE: some timers in the above list belong to the SDK and can not be suspended\n"); + return; +} + +#endif + +int swtmr_suspend(os_timer_t* timer_ptr){ + int return_value = SWTMR_OK; + + if(timer_ptr != NULL){ + // Timer pointer was provided, suspending specified timer + + return_value = timer_do_suspend(timer_ptr); + + if(return_value != SWTMR_OK){ + return return_value; + } + } + else{ + //timer pointer not found, suspending all timers + + if(timer_registry.data_ptr == NULL){ + return SWTMR_REGISTRY_NO_REGISTERED_TIMERS; + } + + timer_registry_remove_unarmed(); + + os_timer_t** tmr_reg_arr = timer_registry.data_ptr; + os_timer_t* temp_ptr = tmr_reg_arr[0]; + + while(temp_ptr != NULL){ + return_value = timer_do_suspend(temp_ptr); + + if(return_value != SWTMR_OK){ + return return_value; + } + + temp_ptr = tmr_reg_arr[0]; + } + } + return return_value; +} + +int swtmr_resume(os_timer_t* timer_ptr){ + + if(suspended_timers.data_ptr == NULL){ + return SWTMR_SUSPEND_NO_SUSPENDED_TIMERS; + } + + os_timer_t** suspended_tmr_array = suspended_timers.data_ptr; + os_timer_t** suspended_timer_ptr = NULL; + int retval=SWTMR_OK; + + if(timer_ptr != NULL){ + suspended_timer_ptr = timer_suspended_check(timer_ptr); + if(suspended_timer_ptr == NULL){ + //timer not suspended + return SWTMR_SUSPEND_TIMER_NOT_SUSPENDED; + } + + retval = timer_do_resume_single(suspended_timer_ptr); + if(retval != SWTMR_OK){ + return retval; + } + } + else{ + suspended_timer_ptr = &suspended_tmr_array[0]; + + while(suspended_timers.used > 0){ + retval = timer_do_resume_single(suspended_timer_ptr); + if(retval != SWTMR_OK){ + SWTMR_ERR("Unable to continue resuming timers, error(%u)", retval); + return retval; + } + suspended_timer_ptr = &suspended_tmr_array[0]; + } + } + return SWTMR_OK; +} + +void swtmr_register(void* timer_ptr){ + if(timer_ptr == NULL){ + SWTMR_DBG("error: timer_ptr is NULL"); + return; + } + + registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t)); + + if(queue_temp == NULL){ + SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size()); + return; + } + queue_temp->timer_ptr = timer_ptr; + + if(register_queue == NULL){ + register_queue = queue_temp; + + if(timer_reg_task_id == false) timer_reg_task_id = task_get_id(timer_register_task); + task_post_low(timer_reg_task_id, false); + SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr); + } + else{ + registry_queue_t* register_queue_tail = register_queue; + + while(register_queue_tail->next != NULL){ + register_queue_tail = register_queue_tail->next; + } + + register_queue_tail->next = queue_temp; + SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr); + } + + return; +} + +void swtmr_unregister(void* timer_ptr){ + if(timer_ptr == NULL){ + SWTMR_DBG("error: timer_ptr is NULL"); + return; + } + + registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t)); + + if(queue_temp == NULL){ + SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size()); + return; + } + queue_temp->timer_ptr=timer_ptr; + + if(unregister_queue == NULL){ + unregister_queue = queue_temp; + if(timer_unreg_task_id==false) timer_unreg_task_id=task_get_id(timer_unregister_task); + task_post_low(timer_unreg_task_id, false); + SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr); + } + else{ + registry_queue_t* unregister_queue_tail=unregister_queue; + while(unregister_queue_tail->next != NULL){ + unregister_queue_tail=unregister_queue_tail->next; + } + unregister_queue_tail->next = queue_temp; +// SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr); + } + + return; +} + +const char* swtmr_errorcode2str(int error_value){ +#ifdef USE_SWTMR_ERROR_STRINGS + if(SWTMR_ERROR_STRINGS[error_value] == NULL){ + SWTMR_ERR("error string %d not found", error_value); + return NULL; + } + else{ + return SWTMR_ERROR_STRINGS[error_value]; + } +#else + SWTMR_ERR("error(%u)", error_value); + return "ERROR! for more info, use debug build"; +#endif + +} + +bool swtmr_suspended_test(os_timer_t* timer_ptr){ + os_timer_t** test_var = timer_suspended_check(timer_ptr); + if(test_var == NULL){ + return false; + } + return true; +} + +#endif diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index a957936a0d..683f571748 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -127,6 +127,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. @@ -321,6 +326,71 @@ target CPU frequency (number) node.setcpufreq(node.CPU80MHZ) ``` + +## node.sleep() + +Put NodeMCU in light sleep mode to reduce current consumption. + +* NodeMCU can not enter light sleep mode if wifi is suspended. +* All active timers will be suspended and then resumed when NodeMCU wakes from sleep. +* Any previously suspended timers will be resumed when NodeMCU wakes from sleep. + +#### Syntax +`node.sleep({wake_gpio[, duration, int_type, resume_cb, preserve_mode]})` + +#### Parameters +- `duration` Sleep duration in microseconds(μs). If a sleep duration of `0` is specified, suspension will be indefinite (Range: 0 or 50000 - 268435454 μs (0:4:28.000454)) +- `wake_pin` 1-12, pin to attach wake interrupt to. Note that pin 0(GPIO 16) does not support interrupts. + - If sleep duration is indefinite, `wake_pin` must be specified + - Please refer to the [`GPIO module`](gpio.md) for more info on the pin map. +- `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 indefinitely with resume callback and wake interrupt + cfg={} + cfg.wake_pin=3 + cfg.resume_cb=function() print("WiFi resume") end + + node.sleep(cfg) + +--Put NodeMCU in light sleep mode with interrupt, resume callback and discard WiFi mode + cfg={} + cfg.wake_pin=3 --GPIO0 + cfg.resume_cb=function() print("WiFi resume") end + cfg.preserve_mode=false + + node.sleep(cfg) + +--Put NodeMCU in light sleep mode for 10 seconds with resume callback + cfg={} + cfg.duration=10*1000*1000 + cfg.resume_cb=function() print("WiFi resume") end + + 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/tmr.md b/docs/en/modules/tmr.md index 919682d2f9..8c815f9f3a 100644 --- a/docs/en/modules/tmr.md +++ b/docs/en/modules/tmr.md @@ -62,9 +62,11 @@ Functions supported in timer object: - [`t:alarm()`](#tmralarm) - [`t:interval()`](#tmrinterval) - [`t:register()`](#tmrregister) +- [`t:resume()`](#tmrresume) - [`t:start()`](#tmrstart) - [`t:state()`](#tmrstate) - [`t:stop()`](#tmrstop) +- [`t:suspend()`](#tmrsuspend) - [`t:unregister()`](#tmrunregister) #### Parameters @@ -182,6 +184,61 @@ mytimer:start() - [`tmr.create()`](#tmrcreate) - [`tmr.alarm()`](#tmralarm) +## tmr.resume() + +Resume an individual timer. + +Resumes a timer that has previously been suspended with either `tmr.suspend` or `tmr.suspend_all` + +#### Syntax +`tmr.resume(id/ref)` + +#### Parameters +`id/ref` timer id (0-6) or object, obsolete for OO API (→ [`tmr.create()`](#tmrcreate)) + +#### Returns +`true` if timer was resumed successfully + +#### Example +```lua +--resume timer mytimer +mytimer:resume() + +--alternate metod +tmr.resume(mytimer) + +``` +#### See also +[`tmr.suspend()`](#tmrsuspend) +[`tmr.suspend_all()`](#tmrsuspendall) +[`tmr.resume_all()`](#tmrresumeall) + +## tmr.resume_all() + +Resume all timers. + +Resumes all timers including those previously been suspended with either `tmr.suspend` or `tmr.suspend_all` + +#### Syntax +`tmr.resume_all()` + +#### Parameters +none + +#### Returns +`true` if timers were resumed successfully + +#### Example +```lua +--resume all previously suspended timers +tmr.resume_all() + +``` +#### See also +[`tmr.suspend()`](#tmrsuspend) +[`tmr.suspend_all()`](#tmrsuspendall) +[`tmr.resume()`](#tmrresume) + ## tmr.softwd() Provides a simple software watchdog, which needs to be re-armed or disabled before it expires, or the system will be restarted. @@ -279,6 +336,67 @@ if not mytimer:stop() then print("timer not stopped, not registered?") end - [`tmr.stop()`](#tmrstop) - [`tmr.unregister()`](#tmrunregister) +## tmr.suspend() + +Suspend an armed timer. + +* Timers can be suspended at any time after they are armed. +* If a timer is rearmed with `tmr.start` or `tmr.alarm` any matching suspended timers will be discarded. + +#### Syntax +`tmr.suspend(id/ref)` + +#### Parameters +`id/ref` timer id (0-6) or object, obsolete for OO API (→ [`tmr.create()`](#tmrcreate)) + +#### Returns +`true` if timer was resumed successfully + +#### Example +```lua +--suspend timer mytimer +mytimer:suspend() + +--alternate metod +tmr.suspend(mytimer) + +``` +#### See also +[`tmr.suspend_all()`](#tmrsuspendall) +[`tmr.resume()`](#tmrresume) +[`tmr.resume_all()`](#tmrresumeall) + + +## tmr.suspend_all() + +Suspend all currently armed timers. + +!!! Warning + This function suspends ALL active timers, including any active timers started by the NodeMCU subsystem or other modules. this may cause parts of your program to stop functioning properly. + USE THIS FUNCTION AT YOUR OWN RISK! + + +#### Syntax +`tmr.suspend_all()` + +#### Parameters +none + +#### Returns +`true` if timers were suspended successfully + +#### Example +```lua +--suspend timer mytimer +tmr.suspend_all() + +``` +#### See also +[`tmr.suspendl()`](#tmrsuspend) +[`tmr.resume()`](#tmrresume) +[`tmr.resume_all()`](#tmrresumeall) + + ## tmr.time() Returns the system uptime, in seconds. Limited to 31 bits, after that it wraps around back to zero. diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 3cca640ebf..458f8b6534 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -74,6 +74,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: @@ -221,6 +252,61 @@ 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 (0:4:28.000454)) +- `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() @@ -1006,7 +1092,7 @@ The current state which can be one of the following: ## 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. #### Syntax `wifi.ap.config(cfg)` diff --git a/sdk-overrides/include/ets_sys.h b/sdk-overrides/include/ets_sys.h index b92b036b3e..eb4cf80d29 100644 --- a/sdk-overrides/include/ets_sys.h +++ b/sdk-overrides/include/ets_sys.h @@ -9,4 +9,6 @@ int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (pri int ets_vsprintf (char *d, const char *s, va_list ap); +extern ETSTimer *timer_list; + #endif /* SDK_OVERRIDES_INCLUDE_ETS_SYS_H_ */ diff --git a/sdk-overrides/include/osapi.h b/sdk-overrides/include/osapi.h index 6e8c56e3b2..46b797f2ba 100644 --- a/sdk-overrides/include/osapi.h +++ b/sdk-overrides/include/osapi.h @@ -16,4 +16,15 @@ void call_user_start(void); #include_next "osapi.h" +#ifdef ENABLE_TIMER_SUSPEND +extern void swtmr_register(void* timer_ptr); +#undef os_timer_arm +#define os_timer_arm(timer_ptr, duration, mode) do{swtmr_register(timer_ptr); \ + ets_timer_arm_new(timer_ptr, duration, mode, 1);}while(0); + +extern void swtmr_unregister(void* timer_ptr); +#undef os_timer_disarm +#define os_timer_disarm(timer_ptr) do{swtmr_unregister(timer_ptr); ets_timer_disarm(timer_ptr);}while(0); +#endif + #endif diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 246b02b91b..54dff6b8e5 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -15,4 +15,13 @@ enum ext_flash_size_map { // Documented in section 4.5 of 9b-esp8266_low_power_solutions_en.pdf void system_deep_sleep_instant(uint32 time_in_us); +//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); +uint8 get_fpm_auto_sleep_flag(void); + + + #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */