Add wifi.suspend() and node.sleep() (#1231)

* Exposed forced sleep API and more
Added timer suspend functionality
* wifi.suspend
* wifi.resume
* node.sleep 
* tmr.suspend
* tmr.suspend_all
* tmr.resume
* tmr.resume_all
* Implement timer suspend functionality
* Fix for uart TX glitch
* Made some modifications to the error reporting
This commit is contained in:
dnc40085 2017-04-04 12:31:06 -07:00 committed by Marcel Stör
parent 176443c07f
commit 41a5736d78
22 changed files with 2029 additions and 87 deletions

View File

@ -45,7 +45,11 @@ SUBDIRS= \
http \ http \
fatfs \ fatfs \
esp-gdbstub \ esp-gdbstub \
websocket websocket \
swTimer \
misc \
pm \
endif # } PDIR endif # } PDIR
@ -90,11 +94,15 @@ COMPONENTS_eagle.app.v6 = \
dhtlib/libdhtlib.a \ dhtlib/libdhtlib.a \
tsl2561/tsl2561lib.a \ tsl2561/tsl2561lib.a \
http/libhttp.a \ http/libhttp.a \
pm/libpm.a \
websocket/libwebsocket.a \ websocket/libwebsocket.a \
esp-gdbstub/libgdbstub.a \ esp-gdbstub/libgdbstub.a \
net/libnodemcu_net.a \ net/libnodemcu_net.a \
mbedtls/libmbedtls.a \ mbedtls/libmbedtls.a \
modules/libmodules.a \ modules/libmodules.a \
swTimer/libswtimer.a \
misc/libmisc.a \
# Inspect the modules library and work out which modules need to be linked. # 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 # For each enabled module, a symbol name of the form XYZ_module_selected is

40
app/include/misc/dynarr.h Normal file
View File

@ -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__

View File

@ -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__

View File

@ -116,6 +116,10 @@ extern void luaL_assertfail(const char *file, int line, const char *message);
#define WIFI_SDK_EVENT_MONITOR_ENABLE #define WIFI_SDK_EVENT_MONITOR_ENABLE
#define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE #define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE
#define ENABLE_TIMER_SUSPEND
#define PMSLEEP_ENABLE
#define STRBUF_DEFAULT_INCREMENT 32 #define STRBUF_DEFAULT_INCREMENT 32
#endif /* __USER_CONFIG_H__ */ #endif /* __USER_CONFIG_H__ */

52
app/misc/Makefile Normal file
View File

@ -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

131
app/misc/dyn_arr.c Normal file
View File

@ -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;
}

View File

@ -55,6 +55,7 @@ INCLUDES += -I ../fatfs
INCLUDES += -I ../http INCLUDES += -I ../http
INCLUDES += -I ../sjson INCLUDES += -I ../sjson
INCLUDES += -I ../websocket INCLUDES += -I ../websocket
INCLUDES += -I ../pm
PDIR := ../$(PDIR) PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile sinclude $(PDIR)Makefile

View File

@ -74,19 +74,36 @@ static int node_deepsleep( lua_State* L )
return 0; return 0;
} }
// Lua: dsleep_set_options
// Combined to dsleep( us, option ) #ifdef PMSLEEP_ENABLE
// static int node_deepsleep_setoption( lua_State* L ) #include "pmSleep.h"
// {
// s32 option; int node_sleep_resume_cb_ref= LUA_NOREF;
// option = luaL_checkinteger( L, 1 ); void node_sleep_resume_cb(void)
// if ( option < 0 || option > 4) {
// return luaL_error( L, "wrong arg range" ); PMSLEEP_DBG("START");
// else pmSleep_execute_lua_cb(&node_sleep_resume_cb_ref);
// deep_sleep_set_option( option ); PMSLEEP_DBG("END");
// return 0; }
// }
// Lua: info() // 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 ) static int node_info( lua_State* L )
{ {
@ -570,6 +587,10 @@ static const LUA_REG_TYPE node_map[] =
{ {
{ LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) },
{ LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) },
#ifdef PMSLEEP_ENABLE
{ LSTRKEY( "sleep" ), LFUNCVAL( node_sleep ) },
PMSLEEP_INT_MAP,
#endif
{ LSTRKEY( "info" ), LFUNCVAL( node_info ) }, { LSTRKEY( "info" ), LFUNCVAL( node_info ) },
{ LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) }, { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) },
{ LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) }, { LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) },

View File

@ -53,6 +53,7 @@ tmr.softwd(int)
#include "platform.h" #include "platform.h"
#include "c_types.h" #include "c_types.h"
#include "user_interface.h" #include "user_interface.h"
#include "swTimer/swTimer.h"
#define TIMER_MODE_OFF 3 #define TIMER_MODE_OFF 3
#define TIMER_MODE_SINGLE 0 #define TIMER_MODE_SINGLE 0
@ -60,6 +61,7 @@ tmr.softwd(int)
#define TIMER_MODE_AUTO 1 #define TIMER_MODE_AUTO 1
#define TIMER_IDLE_FLAG (1<<7) #define TIMER_IDLE_FLAG (1<<7)
#define STRINGIFY_VAL(x) #x #define STRINGIFY_VAL(x) #x
#define STRINGIFY(x) STRINGIFY_VAL(x) #define STRINGIFY(x) STRINGIFY_VAL(x)
@ -172,14 +174,14 @@ static int tmr_register(lua_State* L){
lua_pushvalue(L, 4); lua_pushvalue(L, 4);
sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX); sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) 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 //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) if(tmr->lua_ref != LUA_NOREF && tmr->lua_ref != ref)
luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
tmr->lua_ref = ref; tmr->lua_ref = ref;
tmr->mode = mode|TIMER_IDLE_FLAG; tmr->mode = mode|TIMER_IDLE_FLAG;
tmr->interval = interval; tmr->interval = interval;
ets_timer_setfn(&tmr->os, alarm_timer_common, tmr); os_timer_setfn(&tmr->os, alarm_timer_common, tmr);
return 0; return 0;
} }
@ -197,7 +199,7 @@ static int tmr_start(lua_State* L){
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
}else{ }else{
tmr->mode &= ~TIMER_IDLE_FLAG; 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); lua_pushboolean(L, 1);
} }
return 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) //we return false if the timer is idle (of not registered)
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){ if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){
tmr->mode |= TIMER_IDLE_FLAG; tmr->mode |= TIMER_IDLE_FLAG;
ets_timer_disarm(&tmr->os); os_timer_disarm(&tmr->os);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
}else{ }else{
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
@ -229,6 +231,73 @@ static int tmr_stop(lua_State* L){
return 1; 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 ) // Lua: tmr.unregister( id / ref )
static int tmr_unregister(lua_State* L){ static int tmr_unregister(lua_State* L){
timer_t tmr = tmr_get(L, 1); 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) 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) if(tmr->lua_ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
tmr->lua_ref = LUA_NOREF; tmr->lua_ref = LUA_NOREF;
@ -256,8 +325,8 @@ static int tmr_interval(lua_State* L){
if(tmr->mode != TIMER_MODE_OFF){ if(tmr->mode != TIMER_MODE_OFF){
tmr->interval = interval; tmr->interval = interval;
if(!(tmr->mode&TIMER_IDLE_FLAG)){ if(!(tmr->mode&TIMER_IDLE_FLAG)){
ets_timer_disarm(&tmr->os); os_timer_disarm(&tmr->os);
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);
} }
} }
return 0; return 0;
@ -271,9 +340,15 @@ static int tmr_state(lua_State* L){
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
lua_pushboolean(L, (tmr->mode&TIMER_IDLE_FLAG)==0);
lua_pushinteger(L, tmr->mode&(~TIMER_IDLE_FLAG)); lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0);
return 2; 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 /*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->lua_ref = LUA_NOREF;
ud->self_ref = LUA_NOREF; ud->self_ref = LUA_NOREF;
ud->mode = TIMER_MODE_OFF; ud->mode = TIMER_MODE_OFF;
ets_timer_disarm(&ud->os); os_timer_disarm(&ud->os);
return 1; 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 // Module function map
static const LUA_REG_TYPE tmr_dyn_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( "unregister" ), LFUNCVAL( tmr_unregister ) },
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) }, { 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( "__gc" ), LFUNCVAL( tmr_unregister ) },
{ LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) }, { LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) },
{ LNILKEY, LNILVAL } { 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[] = { static const LUA_REG_TYPE tmr_map[] = {
{ LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) }, { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
{ LSTRKEY( "now" ), LFUNCVAL( tmr_now ) }, { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
@ -377,10 +482,19 @@ static const LUA_REG_TYPE tmr_map[] = {
{ LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) }, { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
{ LSTRKEY( "start" ), LFUNCVAL( tmr_start ) }, { 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( "unregister" ), LFUNCVAL( tmr_unregister ) },
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) }, { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) },
{ LSTRKEY( "create" ), LFUNCVAL( tmr_create ) }, { 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_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) },
{ LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) }, { LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) },
{ LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) }, { 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].lua_ref = LUA_NOREF;
alarm_timers[i].self_ref = LUA_REFNIL; alarm_timers[i].self_ref = LUA_REFNIL;
alarm_timers[i].mode = TIMER_MODE_OFF; 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); ets_timer_disarm(&alarm_timers[i].os);
} }
last_rtc_time=system_get_rtc_time(); // Right now is time 0 last_rtc_time=system_get_rtc_time(); // Right now is time 0
last_rtc_time_us=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_disarm(&rtc_timer);
ets_timer_setfn(&rtc_timer, rtc_callback, NULL); os_timer_setfn(&rtc_timer, rtc_callback, NULL);
ets_timer_arm_new(&rtc_timer, 1000, 1, 1); os_timer_arm(&rtc_timer, 1000, 1);
return 0; return 0;
} }

View File

@ -26,10 +26,6 @@ static uint8 getap_output_format=0;
#define INVALID_MAC_STR "MAC:FF:FF:FF:FF:FF:FF" #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 #ifdef WIFI_SMART_ENABLE
static void wifi_smart_succeed_cb(sc_status status, void *pdata){ static void wifi_smart_succeed_cb(sc_status status, void *pdata){
NODE_DBG("wifi_smart_succeed_cb is called.\n"); NODE_DBG("wifi_smart_succeed_cb is called.\n");
@ -302,65 +298,91 @@ static int wifi_getphymode( lua_State* L )
return 1; return 1;
} }
// Lua: wifi.sleep() #ifdef PMSLEEP_ENABLE
static int wifi_sleep(lua_State* L) /* 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; PMSLEEP_DBG("\n\tDBG: %s start\n", __func__);
sint8 wifi_fpm_do_sleep_return_value = 1; if (wifi_suspend_cb_ref != LUA_NOREF)
if(lua_isnumber(L, 1))
{ {
if(luaL_checknumber(L, 1) == 0) lua_State* L = lua_getstate(); // Get main Lua thread pointer
{ lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_suspend_cb_ref); // Push suspend callback onto stack
desired_sleep_state = 0; 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
else if(luaL_checknumber(L, 1) == 1) lua_call(L, 0, 0); // Execute suspend callback
{
desired_sleep_state = 1;
}
}
if (!FLAG_wifi_force_sleep_enabled && desired_sleep_state == 1)
{
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 else
{ {
wifi_fpm_close(); PMSLEEP_DBG("\n\tDBG: lua cb unavailable\n");
FLAG_wifi_force_sleep_enabled = FALSE;
} }
} PMSLEEP_DBG("\n\tDBG: %s end\n", __func__);
else if(FLAG_wifi_force_sleep_enabled && desired_sleep_state == 0) 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; // Return current WiFi suspension state
// wake up to use WiFi again lua_pushnumber(L, pmSleep_get_state());
wifi_fpm_do_wakeup(); return 1; // Return WiFi suspension state
wifi_fpm_close();
} }
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); pmSleep_parse_table_lua(L, 1, &cfg, &wifi_suspend_cb_ref, &wifi_resume_cb_ref);
lua_pushnumber(L, wifi_fpm_do_sleep_return_value);
} }
else else
{ return luaL_argerror(L, 1, "must be table");
lua_pushnumber(L, FLAG_wifi_force_sleep_enabled); cfg.resume_cb_ptr = &wifi_pmSleep_resume_CB;
lua_pushnil(L); cfg.suspend_cb_ptr = &wifi_pmSleep_suspend_CB;
} pmSleep_suspend(&cfg);
return 2; 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)
{
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
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() // Lua: wifi.nullmodesleep()
static int wifi_null_mode_auto_sleep(lua_State* L) 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( "getchannel" ), LFUNCVAL( wifi_getchannel ) },
{ LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) }, { LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) },
{ LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) }, { 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 ) }, { LSTRKEY( "nullmodesleep" ), LFUNCVAL( wifi_null_mode_auto_sleep ) },
#ifdef WIFI_SMART_ENABLE #ifdef WIFI_SMART_ENABLE
{ LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) },

View File

@ -52,6 +52,15 @@ void wifi_change_default_host_name(void);
#define EVENT_DBG(...) //c_printf(__VA_ARGS__) #define EVENT_DBG(...) //c_printf(__VA_ARGS__)
#endif #endif
enum wifi_suspension_state
{
WIFI_AWAKE = 0,
WIFI_SUSPENSION_PENDING = 1,
WIFI_SUSPENDED = 2
};
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE #ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
extern const LUA_REG_TYPE wifi_event_monitor_map[]; extern const LUA_REG_TYPE wifi_event_monitor_map[];
void wifi_eventmon_init(); void wifi_eventmon_init();

52
app/pm/Makefile Normal file
View File

@ -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

381
app/pm/pmSleep.c Normal file
View File

@ -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<<UART_TXFIFO_CNT_S)) == 0 &&
(READ_PERI_REG(UART_STATUS(1)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S)) == 0){
ets_timer_disarm(&null_mode_check_timer);
suspend_all_timers();
//Ensure UART 0/1 TX FIFO is clear
SET_PERI_REG_MASK(UART_CONF0(0), UART_TXFIFO_RST);//RESET FIFO
CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_TXFIFO_RST);
SET_PERI_REG_MASK(UART_CONF0(1), UART_TXFIFO_RST);//RESET FIFO
CLEAR_PERI_REG_MASK(UART_CONF0(1), UART_TXFIFO_RST);
wifi_fpm_do_sleep(current_config.sleep_duration);
return;
}
else{
return;
}
}
else{ //MODEM_SLEEP_T
sint8 retval_wifi_fpm_do_sleep = wifi_fpm_do_sleep(current_config.sleep_duration); // Request WiFi suspension and store return value
// If wifi_fpm_do_sleep success
if (retval_wifi_fpm_do_sleep == 0){
PMSLEEP_DBG("wifi_fpm_do_sleep success, starting wifi_suspend_test timer");
os_timer_disarm(&wifi_suspended_test_timer);
os_timer_setfn(&wifi_suspended_test_timer, (os_timer_func_t*)wifi_suspended_timer_cb, NULL);
os_timer_arm(&wifi_suspended_test_timer, 1, 1);
}
else{ // This should never happen. if it does, return the value for error reporting
wifi_fpm_close();
PMSLEEP_ERR("wifi_fpm_do_sleep returned %d", retval_wifi_fpm_do_sleep);
}
}
ets_timer_disarm(&null_mode_check_timer);
return;
}
}
//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
static void resume_cb(void){
PMSLEEP_DBG("START");
//TODO: Add support for extended light sleep duration
wifi_fpm_close(); // Disable force sleep API
PMSLEEP_DBG("WiFi resume");
resume_all_timers();
//this section restores null mode auto sleep setting
if(autosleep_setting_temp == 1) {
wifi_fpm_auto_sleep_set_in_null_mode(autosleep_setting_temp);
autosleep_setting_temp = 0;
}
//this section restores previous wifi mode
if (resume_opmode == STATION_MODE || resume_opmode == SOFTAP_MODE || resume_opmode == STATIONAP_MODE){
if (wifi_set_opmode_current(resume_opmode)){
if (resume_opmode == STATION_MODE || resume_opmode == STATIONAP_MODE){
wifi_station_connect(); // Connect to currently configured Access Point
}
PMSLEEP_DBG("WiFi mode restored");
resume_opmode = 0; // reset variable to default value
}
}
else{
wifi_set_opmode_current(NULL_MODE);
}
//execute the external resume callback
if (user_resume_cb != NULL){
PMSLEEP_DBG("calling user resume cb (%p)", user_resume_cb);
user_resume_cb();
user_resume_cb = NULL;
}
PMSLEEP_DBG("END");
return;
}
// This callback executes the suspended callback when Wifi suspension is in effect
static void wifi_suspended_timer_cb(int arg){
// check if wifi is suspended.
if (pmSleep_get_state() == PMSLEEP_SUSPENDED){
os_timer_disarm(&wifi_suspended_test_timer); // Stop rf_closed_timer
PMSLEEP_DBG("WiFi is suspended");
//execute the external suspended callback
if (user_suspend_cb != NULL){
PMSLEEP_DBG("calling user suspend cb (%p)", user_suspend_cb);
user_suspend_cb();
user_suspend_cb = NULL;
}
PMSLEEP_DBG("END");
}
}
/* EXTERNAL FUNCTIONS */
//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
}
}
//this function checks current wifi suspension state and returns the result
uint8 pmSleep_get_state(void){
if (fpm_rf_is_closed()) return PMSLEEP_SUSPENDED;
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){
lua_Integer Linteger_tmp = 0;
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);
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(&current_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

69
app/pm/pmSleep.h Normal file
View File

@ -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__

52
app/swTimer/Makefile Normal file
View File

@ -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

632
app/swTimer/swTimer.c Normal file
View File

@ -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

View File

@ -127,6 +127,11 @@ node.dsleep(1000000, 4)
node.dsleep(nil,4) node.dsleep(nil,4)
``` ```
#### See also
[`wifi.suspend()`](wifi.md#wifisuspend)
[`wifi.resume()`](wifi.md#wifiresume)
[`node.sleep()`](#nodesleep)
## node.flashid() ## node.flashid()
Returns the flash chip ID. Returns the flash chip ID.
@ -321,6 +326,71 @@ target CPU frequency (number)
node.setcpufreq(node.CPU80MHZ) 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() ## node.stripdebug()
Controls the amount of debug information kept during [`node.compile()`](#nodecompile), and allows removal of debug information from already compiled Lua code. Controls the amount of debug information kept during [`node.compile()`](#nodecompile), and allows removal of debug information from already compiled Lua code.

View File

@ -62,9 +62,11 @@ Functions supported in timer object:
- [`t:alarm()`](#tmralarm) - [`t:alarm()`](#tmralarm)
- [`t:interval()`](#tmrinterval) - [`t:interval()`](#tmrinterval)
- [`t:register()`](#tmrregister) - [`t:register()`](#tmrregister)
- [`t:resume()`](#tmrresume)
- [`t:start()`](#tmrstart) - [`t:start()`](#tmrstart)
- [`t:state()`](#tmrstate) - [`t:state()`](#tmrstate)
- [`t:stop()`](#tmrstop) - [`t:stop()`](#tmrstop)
- [`t:suspend()`](#tmrsuspend)
- [`t:unregister()`](#tmrunregister) - [`t:unregister()`](#tmrunregister)
#### Parameters #### Parameters
@ -182,6 +184,61 @@ mytimer:start()
- [`tmr.create()`](#tmrcreate) - [`tmr.create()`](#tmrcreate)
- [`tmr.alarm()`](#tmralarm) - [`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() ## tmr.softwd()
Provides a simple software watchdog, which needs to be re-armed or disabled before it expires, or the system will be restarted. 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.stop()`](#tmrstop)
- [`tmr.unregister()`](#tmrunregister) - [`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() ## tmr.time()
Returns the system uptime, in seconds. Limited to 31 bits, after that it wraps around back to zero. Returns the system uptime, in seconds. Limited to 31 bits, after that it wraps around back to zero.

View File

@ -74,6 +74,37 @@ The current physical mode as one of `wifi.PHYMODE_B`, `wifi.PHYMODE_G` or `wifi.
#### See also #### See also
[`wifi.setphymode()`](#wifisetphymode) [`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() ## wifi.setmode()
Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes:
@ -222,6 +253,61 @@ none
#### See also #### See also
[`wifi.startsmart()`](#wifistartsmart) [`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 Module
## wifi.sta.autoconnect() ## wifi.sta.autoconnect()
@ -1010,7 +1096,7 @@ The current state which can be one of the following:
## wifi.ap.config() ## 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 #### Syntax
`wifi.ap.config(cfg)` `wifi.ap.config(cfg)`

View File

@ -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); int ets_vsprintf (char *d, const char *s, va_list ap);
extern ETSTimer *timer_list;
#endif /* SDK_OVERRIDES_INCLUDE_ETS_SYS_H_ */ #endif /* SDK_OVERRIDES_INCLUDE_ETS_SYS_H_ */

View File

@ -16,4 +16,15 @@ void call_user_start(void);
#include_next "osapi.h" #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 #endif

View File

@ -15,4 +15,13 @@ enum ext_flash_size_map {
// Documented in section 4.5 of 9b-esp8266_low_power_solutions_en.pdf // Documented in section 4.5 of 9b-esp8266_low_power_solutions_en.pdf
void system_deep_sleep_instant(uint32 time_in_us); 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_ */ #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */