Refactor timer suspend portion of node.sleep (pmsleep) (#2287)

* pmsleep refactor
* Shortened swtmr disabled message 
* Added swtimer debug module option to user_modules.h.
* Added comments to user_config.h.
* Fixed error in documentation for node.sleep()
* remove blank sntp.c that got added in during rebase onto dev(6218b92)
* Added #ifdefs around SWTIMER_REG_CB to prevent inclusion of disabled
code
This commit is contained in:
dnc40085 2018-04-13 12:41:14 -07:00 committed by Marcel Stör
parent 4c3d501b58
commit 96e5c026a8
36 changed files with 772 additions and 1245 deletions

View File

@ -46,8 +46,6 @@ SUBDIRS= \
fatfs \
esp-gdbstub \
websocket \
swTimer \
misc \
pm \
sjson \
sqlite3 \
@ -102,8 +100,6 @@ COMPONENTS_eagle.app.v6 = \
net/libnodemcu_net.a \
mbedtls/libmbedtls.a \
modules/libmodules.a \
swTimer/libswtimer.a \
misc/libmisc.a \
sjson/libsjson.a \
sqlite3/libsqlite3.a \

View File

@ -1,6 +1,7 @@
#include "node.h"
#include "coap_timer.h"
#include "os_type.h"
#include "pm/swtimer.h"
static os_timer_t coap_timer;
static coap_tick_t basetime = 0;
@ -48,6 +49,8 @@ void coap_timer_tick(void *arg){
void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t){
os_timer_disarm(&coap_timer);
os_timer_setfn(&coap_timer, (os_timer_func_t *)coap_timer_tick, queue);
SWTIMER_REG_CB(coap_timer_tick, SWTIMER_RESUME);
//coap_timer_tick processes a queue, my guess is that it is ok to resume the timer from where it left off
os_timer_arm(&coap_timer, t, 0); // no repeat
}

View File

@ -14,6 +14,7 @@
#include "mem.h"
#include "gpio.h"
#include "user_interface.h"
#include "pm/swtimer.h"
#include "driver/key.h"
@ -148,6 +149,8 @@ key_intr_handler(void *arg)
// 5s, restart & enter softap mode
os_timer_disarm(&keys->single_key[i]->key_5s);
os_timer_setfn(&keys->single_key[i]->key_5s, (os_timer_func_t *)key_5s_cb, keys->single_key[i]);
SWTIMER_REG_CB(key_5s_cb, SWTIMER_DROP);
// key_5s_cb checks the state of a gpio. After resume, gpio state would be invalid
os_timer_arm(&keys->single_key[i]->key_5s, 5000, 0);
keys->single_key[i]->key_level = 0;
gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_POSEDGE);
@ -155,6 +158,8 @@ key_intr_handler(void *arg)
// 50ms, check if this is a real key up
os_timer_disarm(&keys->single_key[i]->key_50ms);
os_timer_setfn(&keys->single_key[i]->key_50ms, (os_timer_func_t *)key_50ms_cb, keys->single_key[i]);
SWTIMER_REG_CB(key_50ms_cb, SWTIMER_DROP);
// key_50ms_cb checks the state of a gpio. After resume, gpio state would be invalid
os_timer_arm(&keys->single_key[i]->key_50ms, 50, 0);
}
}

View File

@ -527,6 +527,7 @@ void spi_slave_init(uint8 spi_no)
#ifdef SPI_SLAVE_DEBUG
#include "pm/swtimer.h"
/******************************************************************************
* FunctionName : hspi_master_readwrite_repeat
* Description : SPI master test function for reading and writing esp8266 slave buffer,
@ -545,6 +546,8 @@ void hspi_master_readwrite_repeat(void)
temp++;
spi_byte_write_espslave(SPI_HSPI,temp);
os_timer_setfn(&timer2, (os_timer_func_t *)hspi_master_readwrite_repeat, NULL);
SWTIMER_REGISTER_CB_PTR(hspi_master_readwrite_repeat, SWTIMER_RESUME);
//hspi_master_readwrite_repeat timer will be resumed on wake up, maybe data will still be in buffer?
os_timer_arm(&timer2, 500, 0);
}
#endif

View File

@ -323,11 +323,14 @@ uart_autobaud_timeout(void *timer_arg)
uart_div_modify(uart_no, divisor);
}
}
#include "pm/swtimer.h"
static void
uart_init_autobaud(uint32_t uart_no)
{
os_timer_setfn(&autobaud_timer, uart_autobaud_timeout, (void *) uart_no);
SWTIMER_REG_CB(uart_autobaud_timeout, SWTIMER_DROP);
//if autobaud hasn't done it's thing by the time light sleep triggered, it probably isn't going to happen.
os_timer_arm(&autobaud_timer, 100, TRUE);
}

View File

@ -20,6 +20,7 @@
#include "limits.h"
#include "httpclient.h"
#include "stdlib.h"
#include "pm/swtimer.h"
#define REDIRECTION_FOLLOW_MAX 20
@ -525,6 +526,8 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_
/* Set connection timeout timer */
os_timer_disarm( &(req->timeout_timer) );
os_timer_setfn( &(req->timeout_timer), (os_timer_func_t *) http_timeout_callback, conn );
SWTIMER_REG_CB(http_timeout_callback, SWTIMER_IMMEDIATE);
//http_timeout_callback frees memory used by this function and timer cannot be dropped
os_timer_arm( &(req->timeout_timer), req->timeout, false );
#ifdef CLIENT_SSL_ENABLE

View File

@ -1,40 +0,0 @@
#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__

30
app/include/pm/swtimer.h Normal file
View File

@ -0,0 +1,30 @@
/*
* swtimer.h
*
* Created on: Aug 4, 2017
* Author: anonymous
*/
#ifndef APP_INCLUDE_PM_SWTIMER_H_
#define APP_INCLUDE_PM_SWTIMER_H_
void swtmr_cb_register(void* timer_cb_ptr, uint8 suspend_policy);
#define SWTIMER_RESUME 0 //save remaining time
#define SWTIMER_RESTART 1 //use timer_period as remaining time
#define SWTIMER_IMMEDIATE 2 //fire timer immediately after resume
#define SWTIMER_DROP 3 //disarm timer, do not resume
#if defined(TIMER_SUSPEND_ENABLE)
#define SWTIMER_REG_CB(cb_ptr, suspend_policy) do{ \
static bool cb_ptr##_registered_flag;\
if(!cb_ptr##_registered_flag){ \
cb_ptr##_registered_flag = true; \
swtmr_cb_register(cb_ptr, suspend_policy);\
} \
}while(0);
#else
#define SWTIMER_REG_CB(...)
#endif
#endif /* APP_INCLUDE_PM_SWTIMER_H_ */

View File

@ -689,11 +689,15 @@ static inline void rtc_time_switch_to_system_clock(void)
static inline void rtc_time_tmrfn(void* arg);
#include "pm/swtimer.h"
static void rtc_time_install_timer(void)
{
static ETSTimer tmr;
os_timer_setfn(&tmr,rtc_time_tmrfn,NULL);
SWTIMER_REG_CB(rtc_time_tmrfn, SWTIMER_RESUME);
//I believe the function rtc_time_tmrfn compensates for drift in the clock and updates rtc time accordingly, This timer should probably be resumed
os_timer_arm(&tmr,10000,1);
}

View File

@ -1,53 +0,0 @@
#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,8 +116,8 @@ 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 PMSLEEP_ENABLE // Enable wifi.suspend() and node.sleep() (NOTE: node.sleep() is dependent on TIMER_SUSPEND_ENABLE)
//#define TIMER_SUSPEND_ENABLE //Required by node.sleep()
#define STRBUF_DEFAULT_INCREMENT 32

View File

@ -86,5 +86,9 @@
//#define LUA_USE_MODULES_WS2812_EFFECTS
//#define LUA_USE_MODULES_XPT2046
//debug modules
//#define LUA_USE_MODULES_SWTMR_DBG //SWTMR timer suspend Debug functions
#endif /* LUA_CROSS_COMPILER */
#endif /* __USER_MODULES_H__ */

View File

@ -1039,6 +1039,7 @@ mdns_reg(struct mdns_info *info) {
os_timer_disarm(&mdns_timer);
}
}
#include "pm/swtimer.h"
/**
* Initialize the resolver: set up the UDP pcb and configure the default server
@ -1129,6 +1130,8 @@ mdns_init(struct mdns_info *info) {
os_timer_disarm(&mdns_timer);
os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info);
SWTIMER_REG_CB(mdns_reg, SWTIMER_RESTART);
//going on the above comment, it's probably a good idea to let mdns_reg run it's course. not sure if the 1 second timing is important, so lets restart it to be safe.
os_timer_arm(&mdns_timer, 1000, 1);
}
}

View File

@ -1,52 +0,0 @@
#############################################################
# 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

View File

@ -1,131 +0,0 @@
#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

@ -190,7 +190,7 @@ static void cron_handle_tmr() {
struct rtc_timeval tv;
rtctime_gettimeofday(&tv);
if (tv.tv_sec == 0) { // Wait for RTC time
ets_timer_arm_new(&cron_timer, 1000, 0, 1);
os_timer_arm(&cron_timer, 1000, 0);
return;
}
time_t t = tv.tv_sec;
@ -202,7 +202,7 @@ static void cron_handle_tmr() {
diff += 60000;
gmtime_r(&t, &tm);
}
ets_timer_arm_new(&cron_timer, diff, 0, 1);
os_timer_arm(&cron_timer, diff, 0);
cron_handle_time(tm.tm_mon + 1, tm.tm_mday, tm.tm_wday, tm.tm_hour, tm.tm_min);
}
@ -220,11 +220,15 @@ static const LUA_REG_TYPE cron_map[] = {
{ LSTRKEY( "reset" ), LFUNCVAL( lcron_reset ) },
{ LNILKEY, LNILVAL }
};
#include "pm/swtimer.h"
int luaopen_cron( lua_State *L ) {
ets_timer_disarm(&cron_timer);
ets_timer_setfn(&cron_timer, cron_handle_tmr, 0);
ets_timer_arm_new(&cron_timer, 1000, 0, 1);
os_timer_disarm(&cron_timer);
os_timer_setfn(&cron_timer, cron_handle_tmr, 0);
SWTIMER_REG_CB(cron_handle_tmr, SWTIMER_RESTART);
//cron_handle_tmr determines when to execute a scheduled cron job
//My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer.
os_timer_arm(&cron_timer, 1000, 0);
luaL_rometatable(L, "cron.entry", (void *)cronent_map);
return 0;
}

View File

@ -134,6 +134,8 @@ static int ds18b20_lua_setting(lua_State *L) {
return 0;
}
#include "pm/swtimer.h"
// Reads sensor values from all devices
// Lua: ds18b20.read(function(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) print(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) end, ROM[, FAMILY])
static int ds18b20_lua_read(lua_State *L) {
@ -173,6 +175,9 @@ static int ds18b20_lua_read(lua_State *L) {
onewire_write(ds18b20_bus_pin, DS18B20_ROM_SKIP, 0);
onewire_write(ds18b20_bus_pin, DS18B20_FUNC_CONVERT, 1);
os_timer_setfn(&ds18b20_timer, (os_timer_func_t *)ds18b20_lua_readoutdone, NULL);
SWTIMER_REG_CB(ds18b20_lua_readoutdone, SWTIMER_DROP);
//The function ds18b20_lua_readoutdone reads the temperature from the sensor(s) after a set amount of time depending on temperature resolution
//MY guess: If this timer manages to get suspended before it fires and the temperature data is time sensitive then resulting data would be invalid and should be discarded
switch (ds18b20_device_res) {
case (9):

View File

@ -212,12 +212,15 @@ static void enduser_setup_connected_callback()
}
}
#include "pm/swtimer.h"
static void enduser_setup_check_station_start(void)
{
ENDUSER_SETUP_DEBUG("enduser_setup_check_station_start");
os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL);
SWTIMER_REG_CB(enduser_setup_check_station, SWTIMER_RESUME);
//The function enduser_setup_check_station checks for a successful connection to the configured AP
//My guess: I'm not sure about whether or not user feedback is given via the web interface, but I don't see a problem with letting this timer resume.
os_timer_arm(&(state->check_station_timer), 3*1000, TRUE);
}
@ -317,6 +320,9 @@ static void enduser_setup_check_station(void *p)
if (!manual)
{
os_timer_setfn(&(state->shutdown_timer), enduser_setup_stop_callback, NULL);
SWTIMER_REG_CB(enduser_setup_stop_callback, SWTIMER_RESUME);
//The function enduser_setup_stop_callback frees services and resources used by enduser setup.
//My guess: Since it would lead to a memory leak, it's probably best to resume this timer.
os_timer_arm(&(state->shutdown_timer), 10*1000, FALSE);
}
}

View File

@ -981,6 +981,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
return espconn_status;
}
#include "pm/swtimer.h"
// Lua: mqtt:connect( host, port, secure, auto_reconnect, function(client), function(client, connect_return_code) )
static int mqtt_socket_connect( lua_State* L )
{
@ -1114,6 +1115,9 @@ static int mqtt_socket_connect( lua_State* L )
os_timer_disarm(&mud->mqttTimer);
os_timer_setfn(&mud->mqttTimer, (os_timer_func_t *)mqtt_socket_timer, mud);
SWTIMER_REG_CB(mqtt_socket_timer, SWTIMER_RESUME);
//I assume that mqtt_socket_timer connects to the mqtt server, but I'm not really sure what impact light_sleep will have on it.
//My guess: If in doubt, resume the timer
// timer started in socket_connect()
if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0))

View File

@ -76,7 +76,7 @@ static int node_deepsleep( lua_State* L )
#ifdef PMSLEEP_ENABLE
#include "pmSleep.h"
#include "pm/pmSleep.h"
int node_sleep_resume_cb_ref= LUA_NOREF;
void node_sleep_resume_cb(void)
@ -89,6 +89,7 @@ void node_sleep_resume_cb(void)
// Lua: node.sleep(table)
static int node_sleep( lua_State* L )
{
#ifdef TIMER_SUSPEND_ENABLE
pmSleep_INIT_CFG(cfg);
cfg.sleep_mode=LIGHT_SLEEP_T;
@ -101,6 +102,10 @@ static int node_sleep( lua_State* L )
cfg.resume_cb_ptr = &node_sleep_resume_cb;
pmSleep_suspend(&cfg);
#else
c_printf("\n The option \"timer_suspend_enable\" in \"app/include/user_config.h\" was disabled during FW build!\n");
return luaL_error(L, "light sleep is unavailable");
#endif
return 0;
}
#endif //PMSLEEP_ENABLE

View File

@ -129,6 +129,8 @@ int platform_rotary_exists( unsigned int id )
return (id < ROTARY_CHANNEL_COUNT);
}
#include "pm/swtimer.h"
// Lua: setup(id, phase_a, phase_b [, press])
static int lrotary_setup( lua_State* L )
{
@ -155,6 +157,11 @@ static int lrotary_setup( lua_State* L )
d->id = id;
os_timer_setfn(&d->timer, lrotary_timer_done, (void *) d);
SWTIMER_REG_CB(lrotary_timer_done, SWTIMER_RESUME);
//lrotary_timer_done checks time elapsed since last event
//My guess: Since proper functionality relies on some variables to be reset via timer callback and state would be invalid anyway.
//It is probably best to resume this timer so it can reset it's state variables
int i;
for (i = 0; i < CALLBACK_COUNT; i++) {

View File

@ -319,6 +319,7 @@ static void sntp_handle_result(lua_State *L) {
}
}
#include "pm/swtimer.h"
static void sntp_dosend ()
{
@ -326,6 +327,9 @@ static void sntp_dosend ()
if (state->server_pos < 0) {
os_timer_disarm(&state->timer);
os_timer_setfn(&state->timer, on_timeout, NULL);
SWTIMER_REG_CB(on_timeout, SWTIMER_RESUME);
//The function on_timeout calls this function(sntp_dosend) again to handle time sync timeout.
//My guess: Since the WiFi connection is restored after waking from light sleep, it would be possible to contact the SNTP server, So why not let it
state->server_pos = 0;
} else {
++state->server_pos;
@ -708,6 +712,9 @@ static char *set_repeat_mode(lua_State *L, bool enable)
lua_rawgeti(L, LUA_REGISTRYINDEX, state->list_ref);
repeat->list_ref = luaL_ref(L, LUA_REGISTRYINDEX);
os_timer_setfn(&repeat->timer, on_long_timeout, NULL);
SWTIMER_REG_CB(on_long_timeout, SWTIMER_RESUME);
//The function on_long_timeout returns errors to the developer
//My guess: Error reporting is a good thing, resume the timer.
os_timer_arm(&repeat->timer, 1000 * 1000, 1);
} else {
if (repeat) {

View File

@ -53,7 +53,7 @@ tmr.softwd(int)
#include "platform.h"
#include "c_types.h"
#include "user_interface.h"
#include "swTimer/swTimer.h"
#include "pm/swtimer.h"
#define TIMER_MODE_OFF 3
#define TIMER_MODE_SINGLE 0
@ -231,68 +231,23 @@ static int tmr_stop(lua_State* L){
return 1;
}
#ifdef ENABLE_TIMER_SUSPEND
#ifdef TIMER_SUSPEND_ENABLE
#define TMR_SUSPEND_REMOVED_MSG "This feature has been removed, we apologize for any inconvenience this may have caused."
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;
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
}
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;
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
}
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_suspend_all (lua_State *L){
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
}
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;
static int tmr_resume_all (lua_State *L){
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
}
@ -343,12 +298,7 @@ static int tmr_state(lua_State* L){
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;
return 2;
}
/*I left the led comments 'couse I don't know
@ -454,7 +404,7 @@ 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
#ifdef TIMER_SUSPEND_ENABLE
{ LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) },
{ LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) },
#endif
@ -463,15 +413,6 @@ static const LUA_REG_TYPE tmr_dyn_map[] = {
{ LNILKEY, LNILVAL }
};
#if defined(ENABLE_TIMER_SUSPEND) && 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 ) },
@ -482,7 +423,7 @@ static const LUA_REG_TYPE tmr_map[] = {
{ LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
{ LSTRKEY( "start" ), LFUNCVAL( tmr_start ) },
{ LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
#ifdef ENABLE_TIMER_SUSPEND
#ifdef TIMER_SUSPEND_ENABLE
{ LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) },
{ LSTRKEY( "suspend_all" ), LFUNCVAL( tmr_suspend_all ) },
{ LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) },
@ -492,15 +433,13 @@ static const LUA_REG_TYPE tmr_map[] = {
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) },
{ LSTRKEY( "create" ), LFUNCVAL( tmr_create ) },
#if defined(ENABLE_TIMER_SUSPEND) && 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 ) },
{ LNILKEY, LNILVAL }
};
#include "pm/swtimer.h"
int luaopen_tmr( lua_State *L ){
int i;
@ -510,16 +449,23 @@ 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);
os_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);
os_timer_disarm(&rtc_timer);
os_timer_setfn(&rtc_timer, rtc_callback, NULL);
os_timer_arm(&rtc_timer, 1000, 1);
SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME);
//The function rtc_callback calls the a function that calibrates the SoftRTC for drift in the esp8266's clock.
//My guess: after the duration of light_sleep there's bound to be some drift in the clock, so a calibration is due.
SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
//The function alarm_timer_common handles timers created by the developer via tmr.create().
//No reason not to resume the timers, so resume em'.
return 0;
}

View File

@ -429,7 +429,7 @@ static int wifi_setmaxtxpower( lua_State* L )
#ifdef PMSLEEP_ENABLE
/* Begin WiFi suspend functions*/
#include "pmSleep.h"
#include <pm/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
@ -511,6 +511,19 @@ static int wifi_resume(lua_State* L)
}
/* End WiFi suspend functions*/
#else
static char *susp_note_str = "\n The option \"pmsleep_enable\" in \"app/include/user_config.h\" was disabled during FW build!\n";
static char *susp_unavailable_str = "wifi.suspend is unavailable";
static int wifi_suspend(lua_State* L){
c_sprintf("%s", susp_note_str);
return luaL_error(L, susp_unavailable_str);
}
static int wifi_resume(lua_State* L){
c_sprintf("%s", susp_note_str);
return luaL_error(L, susp_unavailable_str);
}
#endif
// Lua: wifi.nullmodesleep()
@ -963,7 +976,7 @@ static int wifi_station_config( lua_State* L )
lua_State* L_temp = NULL;
lua_getfield(L, 1, "connected_cb");
lua_getfield(L, 1, "connect_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
@ -976,12 +989,12 @@ static int wifi_station_config( lua_State* L )
}
else
{
return luaL_argerror(L, 1, "connected_cb:not function");
return luaL_argerror(L, 1, "connect_cb:not function");
}
}
lua_pop(L, 1);
lua_getfield(L, 1, "disconnected_cb");
lua_getfield(L, 1, "disconnect_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
@ -994,7 +1007,7 @@ static int wifi_station_config( lua_State* L )
}
else
{
return luaL_argerror(L, 1, "disconnected_cb:not function");
return luaL_argerror(L, 1, "disconnect_cb:not function");
}
}
lua_pop(L, 1);
@ -1910,10 +1923,8 @@ static const LUA_REG_TYPE wifi_map[] = {
{ LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) },
{ LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) },
{ LSTRKEY( "setmaxtxpower" ), LFUNCVAL( wifi_setmaxtxpower ) },
#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 ) },

View File

@ -8,7 +8,7 @@
#include "user_interface.h"
#include "driver/uart.h"
#include "osapi.h"
#include "swTimer/swTimer.h"
#include "pm/swtimer.h"
#include "ws2812.h"
#include "color_utils.h"

View File

@ -1056,6 +1056,7 @@ mdns_dup_info(const struct nodemcu_mdns_info *info) {
return result;
}
#include "pm/swtimer.h"
/**
* Initialize the resolver: set up the UDP pcb and configure the default server
* (NEW IP).
@ -1130,6 +1131,9 @@ nodemcu_mdns_init(struct nodemcu_mdns_info *info) {
//MDNS_DBG("About to start timer\n");
os_timer_disarm(&mdns_timer);
os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info);
SWTIMER_REG_CB(mdns_reg, SWTIMER_RESUME);
//the function mdns_reg registers the mdns device on the network
//My guess: Since wifi connection is restored after waking from light_sleep, the related timer would have no problem resuming it's normal function.
os_timer_arm(&mdns_timer, 1000 * 280, 1);
/* kick off the first one right away */
mdns_reg_handler_restart();

View File

@ -1,8 +1,10 @@
#include "pmSleep.h"
#include <pm/pmSleep.h>
#ifdef PMSLEEP_ENABLE
#define STRINGIFY_VAL(x) #x
#define STRINGIFY(x) STRINGIFY_VAL(x)
//TODO: figure out why timed light_sleep doesn't work
//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";
@ -28,17 +30,18 @@ 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);
#ifdef TIMER_SUSPEND_ENABLE
extern void swtmr_suspend_timers();
swtmr_suspend_timers();
#endif
return;
}
static void resume_all_timers(void){
#ifdef ENABLE_TIMER_SUSPEND
swtmr_resume(NULL);
#ifdef TIMER_SUSPEND_ENABLE
extern void swtmr_resume_timers();
swtmr_resume_timers();
#endif
return;
}
@ -49,7 +52,7 @@ static void null_mode_check_timer_cb(void* arg){
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);
os_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
@ -71,6 +74,8 @@ static void null_mode_check_timer_cb(void* arg){
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);
//The callback wifi_suspended_timer_cb detects when the esp8266 has successfully entered modem_sleep and executes the developer's suspend_cb.
//Since this timer is only used in modem_sleep and will never be active outside of modem_sleep, it is unnecessary to register the cb with SWTIMER_REG_CB.
os_timer_arm(&wifi_suspended_test_timer, 1, 1);
}
else{ // This should never happen. if it does, return the value for error reporting
@ -78,7 +83,7 @@ static void null_mode_check_timer_cb(void* arg){
PMSLEEP_ERR("wifi_fpm_do_sleep returned %d", retval_wifi_fpm_do_sleep);
}
}
ets_timer_disarm(&null_mode_check_timer);
os_timer_disarm(&null_mode_check_timer);
return;
}
}
@ -173,24 +178,24 @@ uint8 pmSleep_get_state(void){
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
if( cfg->sleep_mode == MODEM_SLEEP_T ){ //WiFi suspend
lua_getfield(L, table_idx, "duration");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isnumber(L, -1) ){
lua_Integer Linteger=luaL_checkinteger(L, -1);
luaL_argcheck(L,(((Linteger >= PMSLEEP_SLEEP_MIN_TIME) && (Linteger <= PMSLEEP_SLEEP_MAX_TIME)) ||
(Linteger == 0)), table_idx, PMSLEEP_DURATION_ERR_STR);
cfg->sleep_duration = (uint32)Linteger; // Get suspend duration
}
else{
return luaL_argerror( L, table_idx, "duration: must be number" );
}
}
else{
return luaL_argerror( L, table_idx, "duration: must be number" );
return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR );
}
}
else{
return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR );
}
lua_pop(L, 1);
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) ){
@ -204,7 +209,7 @@ int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg,
lua_pop(L, 1);
}
else if (cfg->sleep_mode == LIGHT_SLEEP_T){ //CPU suspend
#ifdef ENABLE_TIMER_SUSPEND
#ifdef TIMER_SUSPEND_ENABLE
lua_getfield(L, table_idx, "wake_pin");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isnumber(L, -1) ){
@ -216,9 +221,11 @@ int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg,
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" );
}
else{
return luaL_argerror( L, table_idx, "wake_pin: must specify pin" );
// 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");
@ -300,7 +307,7 @@ void pmSleep_suspend(pmSleep_param_t *cfg){
PMSLEEP_DBG("START");
lua_State* L = lua_getstate();
#ifndef ENABLE_TIMER_SUSPEND
#ifndef TIMER_SUSPEND_ENABLE
if(cfg->sleep_mode == LIGHT_SLEEP_T){
luaL_error(L, "timer suspend API is disabled, light sleep unavailable");
return;
@ -336,7 +343,7 @@ void pmSleep_suspend(pmSleep_param_t *cfg){
wifi_fpm_open(); // Enable force sleep API
if (cfg->sleep_mode == LIGHT_SLEEP_T){
#ifdef ENABLE_TIMER_SUSPEND
#ifdef TIMER_SUSPEND_ENABLE
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);
@ -366,10 +373,11 @@ void pmSleep_suspend(pmSleep_param_t *cfg){
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);
os_timer_disarm(&null_mode_check_timer);
os_timer_setfn(&null_mode_check_timer, null_mode_check_timer_cb, false);
//The function null_mode_check_timer_cb checks that the esp8266 has successfully changed the opmode to NULL_MODE prior to entering LIGHT_SLEEP
//This callback doesn't need to be registered with SWTIMER_REG_CB since the timer will have terminated before entering LIGHT_SLEEP
os_timer_arm(&null_mode_check_timer, 1, 1);
}
else{
PMSLEEP_ERR("opmode change fail");

545
app/pm/swtimer.c Normal file
View File

@ -0,0 +1,545 @@
/* 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 INFO:
*
* Timer suspension is achieved by first finding any non-SDK timers by comparing the timer function callback pointer
* of each timer in "timer_list" to a list of registered timer callback pointers stored in the Lua registry.
* If a timer with a corresponding registered callback pointer is found, the timer's timer_expire field is is compared
* to the current FRC2 count and the difference is saved along with the other timer parameters to temporary variables.
* The timer is then disarmed and the parameters are copied back, the timer pointer is then
* added to a separate linked list of which the head pointer is stored as a lightuserdata in the lua registry.
*
* Resuming the timers is achieved by first retrieving the lightuserdata holding the suspended timer list head pointer.
* Then, starting with the beginning of the list the current FRC2 count is added back to the timer's timer_expire, then
* the timer is manually added back to "timer_list" in an ascending order.
* Once there are no more suspended timers, the function returns
*
*
*/#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "user_interface.h"
#include "user_modules.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "ctype.h"
#include "c_types.h"
//#define SWTMR_DEBUG
#if !defined(SWTMR_DBG) && defined(LUA_USE_MODULES_SWTMR_DBG)
#define SWTMR_DEBUG
#endif
//this section specifies which lua registry to use. LUA_GLOBALSINDEX or LUA_REGISTRYINDEX
#ifdef SWTMR_DEBUG
#define SWTMR_DBG(fmt, ...) c_printf("\n SWTMR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__)
#define L_REGISTRY LUA_GLOBALSINDEX
#define CB_LIST_STR "timer_cb_ptrs"
#define SUSP_LIST_STR "suspended_tmr_LL_head"
#else
#define SWTMR_DBG(...)
#define L_REGISTRY LUA_REGISTRYINDEX
#define CB_LIST_STR "cb"
#define SUSP_LIST_STR "st"
#endif
typedef struct tmr_cb_queue{
os_timer_func_t *tmr_cb_ptr;
uint8 suspend_policy;
struct tmr_cb_queue * next;
}tmr_cb_queue_t;
typedef struct cb_registry_item{
os_timer_func_t *tmr_cb_ptr;
uint8 suspend_policy;
}cb_registry_item_t;
/* Internal variables */
static tmr_cb_queue_t* register_queue = NULL;
static task_handle_t cb_register_task_id = NULL; //variable to hold task id for task handler(process_cb_register_queue)
/* Function declarations */
//void swtmr_cb_register(void* timer_cb_ptr, uint8 resume_policy);
static void add_to_reg_queue(void* timer_cb_ptr, uint8 suspend_policy);
static void process_cb_register_queue(task_param_t param, uint8 priority);
#ifdef SWTMR_DEBUG
#define push_swtmr_registry_key(L) lua_pushstring(L, "SWTMR_registry_key")
#else
#define push_swtmr_registry_key(L) lua_pushlightuserdata(L, &register_queue);
#endif
#include <pm/swtimer.h>
void swtmr_suspend_timers(){
lua_State* L = lua_getstate();
//get swtimer table
push_swtmr_registry_key(L);
lua_rawget(L, L_REGISTRY);
//get cb_list table
lua_pushstring(L, CB_LIST_STR);
lua_rawget(L, -2);
//check for existence of the swtimer table and the cb_list table, return if not found
if(!lua_istable(L, -2) || !lua_istable(L, -1)){
// not necessarily an error maybe there are legitimately no timers to suspend
lua_pop(L, 2);
return;
}
os_timer_t* suspended_timer_list_head = NULL;
os_timer_t* suspended_timer_list_tail = NULL;
//get suspended_timer_list table
lua_pushstring(L, SUSP_LIST_STR);
lua_rawget(L, -3);
//if suspended_timer_list exists, find tail of list
if(lua_isuserdata(L, -1)){
suspended_timer_list_head = suspended_timer_list_tail = lua_touserdata(L, -1);
while(suspended_timer_list_tail->timer_next != NULL){
suspended_timer_list_tail = suspended_timer_list_tail->timer_next;
}
}
lua_pop(L, 1);
//get length of lua table containing the callback pointers
size_t registered_cb_qty = lua_objlen(L, -1);
//allocate a temporary array to hold the list of callback pointers
cb_registry_item_t** cb_reg_array = c_zalloc(sizeof(cb_registry_item_t*)*registered_cb_qty);
if(!cb_reg_array){
luaL_error(L, "%s: unable to suspend timers, out of memory!", __func__);
return;
}
uint8 index = 0;
//convert lua table cb_list to c array
lua_pushnil(L);
while(lua_next(L, -2) != 0){
if(lua_isuserdata(L, -1)){
cb_reg_array[index] = lua_touserdata(L, -1);
}
lua_pop(L, 1);
index++;
}
//the cb_list table is no longer needed, pop it from the stack
lua_pop(L, 1);
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
os_timer_t* timer_ptr = timer_list;
uint32 expire_temp = 0;
uint32 period_temp = 0;
void* arg_temp = NULL;
/* In this section, the SDK's timer_list is traversed to find any timers that have a registered callback pointer.
* If a registered callback is found, the timer is suspended by saving the difference
* between frc2_count and timer_expire then the timer is disarmed and placed into suspended_timer_list
* so it can later be resumed.
*/
while(timer_ptr != NULL){
os_timer_t* next_timer = (os_timer_t*)0xffffffff;
for(int i = 0; i < registered_cb_qty; i++){
if(timer_ptr->timer_func == cb_reg_array[i]->tmr_cb_ptr){
//current timer will be suspended, next timer's pointer will be needed to continue processing timer_list
next_timer = timer_ptr->timer_next;
//store timer parameters temporarily so the timer can be disarmed
if(timer_ptr->timer_expire < frc2_count)
expire_temp = 2; // 16 us in ticks (1 tick = ~3.2 us) (arbitrarily chosen value)
else
expire_temp = timer_ptr->timer_expire - frc2_count;
period_temp = timer_ptr->timer_period;
arg_temp = timer_ptr->timer_arg;
if(timer_ptr->timer_period == 0 && cb_reg_array[i]->suspend_policy == SWTIMER_RESTART){
SWTMR_DBG("Warning: suspend_policy(RESTART) is not compatible with single-shot timer(%p), changing suspend_policy to (RESUME)", timer_ptr);
cb_reg_array[i]->suspend_policy = SWTIMER_RESUME;
}
//remove the timer from timer_list so we don't have to.
os_timer_disarm(timer_ptr);
timer_ptr->timer_next = NULL;
//this section determines timer behavior on resume
if(cb_reg_array[i]->suspend_policy == SWTIMER_DROP){
SWTMR_DBG("timer(%p) was disarmed and will not be resumed", timer_ptr);
}
else if(cb_reg_array[i]->suspend_policy == SWTIMER_IMMEDIATE){
timer_ptr->timer_expire = 1;
SWTMR_DBG("timer(%p) will fire immediately on resume", timer_ptr);
}
else if(cb_reg_array[i]->suspend_policy == SWTIMER_RESTART){
timer_ptr->timer_expire = period_temp;
SWTMR_DBG("timer(%p) will be restarted on resume", timer_ptr);
}
else{
timer_ptr->timer_expire = expire_temp;
SWTMR_DBG("timer(%p) will be resumed with remaining time", timer_ptr);
}
if(cb_reg_array[i]->suspend_policy != SWTIMER_DROP){
timer_ptr->timer_period = period_temp;
timer_ptr->timer_func = cb_reg_array[i]->tmr_cb_ptr;
timer_ptr->timer_arg = arg_temp;
//add timer to suspended_timer_list
if(suspended_timer_list_head == NULL){
suspended_timer_list_head = timer_ptr;
suspended_timer_list_tail = timer_ptr;
}
else{
suspended_timer_list_tail->timer_next = timer_ptr;
suspended_timer_list_tail = timer_ptr;
}
}
}
}
//if timer was suspended, timer_ptr->timer_next is invalid, use next_timer instead.
if(next_timer != (os_timer_t*)0xffffffff){
timer_ptr = next_timer;
}
else{
timer_ptr = timer_ptr->timer_next;
}
}
//tmr_cb_ptr_array is no longer needed.
c_free(cb_reg_array);
//add suspended_timer_list pointer to swtimer table.
lua_pushstring(L, SUSP_LIST_STR);
lua_pushlightuserdata(L, suspended_timer_list_head);
lua_rawset(L, -3);
//pop swtimer table from stack
lua_pop(L, 1);
return;
}
void swtmr_resume_timers(){
lua_State* L = lua_getstate();
//get swtimer table
push_swtmr_registry_key(L);
lua_rawget(L, L_REGISTRY);
//get suspended_timer_list lightuserdata
lua_pushstring(L, SUSP_LIST_STR);
lua_rawget(L, -2);
//check for existence of swtimer table and the suspended_timer_list pointer userdata, return if not found
if(!lua_istable(L, -2) || !lua_isuserdata(L, -1)){
// not necessarily an error maybe there are legitimately no timers to resume
lua_pop(L, 2);
return;
}
os_timer_t* suspended_timer_list_ptr = lua_touserdata(L, -1);
lua_pop(L, 1); //pop suspended timer list userdata from stack
//since timers will be resumed, the suspended_timer_list lightuserdata can be cleared from swtimer table
lua_pushstring(L, SUSP_LIST_STR);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1); //pop swtimer table from stack
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
//this section does the actual resuming of the suspended timer(s)
while(suspended_timer_list_ptr != NULL){
os_timer_t* timer_list_ptr = timer_list;
//the pointer to next suspended timer must be saved, the current suspended timer will be removed from the list
os_timer_t* next_suspended_timer_ptr = suspended_timer_list_ptr->timer_next;
suspended_timer_list_ptr->timer_expire += frc2_count;
//traverse timer_list to determine where to insert suspended timer
while(timer_list_ptr != NULL){
if(suspended_timer_list_ptr->timer_expire > timer_list_ptr->timer_expire){
if(timer_list_ptr->timer_next != NULL){
//current timer is not at tail of timer_list
if(suspended_timer_list_ptr->timer_expire < timer_list_ptr->timer_next->timer_expire){
//insert suspended timer between current timer and next timer
suspended_timer_list_ptr->timer_next = timer_list_ptr->timer_next;
timer_list_ptr->timer_next = suspended_timer_list_ptr;
break; //timer resumed exit while loop
}
else{
//suspended timer expire is larger than next timer
}
}
else{
//current timer is at tail of timer_list and suspended timer expire is greater then current timer
//append timer to end of timer_list
timer_list_ptr->timer_next = suspended_timer_list_ptr;
suspended_timer_list_ptr->timer_next = NULL;
break; //timer resumed exit while loop
}
}
else if(timer_list_ptr == timer_list){
//insert timer at head of list
suspended_timer_list_ptr->timer_next = timer_list_ptr;
timer_list = timer_list_ptr = suspended_timer_list_ptr;
break; //timer resumed exit while loop
}
//suspended timer expire is larger than next timer
//timer not resumed, next timer in timer_list
timer_list_ptr = timer_list_ptr->timer_next;
}
//timer was resumed, next suspended timer
suspended_timer_list_ptr = next_suspended_timer_ptr;
}
return;
}
//this function registers a timer callback pointer in a lua table
void swtmr_cb_register(void* timer_cb_ptr, uint8 suspend_policy){
lua_State* L = lua_getstate();
if(!L){
//Lua has not started yet, therefore L_REGISTRY is not available.
//add timer cb to queue for later processing after Lua has started
add_to_reg_queue(timer_cb_ptr, suspend_policy);
return;
}
if(timer_cb_ptr){
size_t cb_list_last_idx = 0;
push_swtmr_registry_key(L);
lua_rawget(L, L_REGISTRY);
if(!lua_istable(L, -1)){
//swtmr does not exist, create and add to registry
lua_pop(L, 1);
lua_newtable(L);//push new table for swtmr.timer_cb_list
// add swtimer table to L_REGISTRY
push_swtmr_registry_key(L);
lua_pushvalue(L, -2);
lua_rawset(L, L_REGISTRY);
}
lua_pushstring(L, CB_LIST_STR);
lua_rawget(L, -2);
if(lua_istable(L, -1)){
//cb_list exists, get length of list
cb_list_last_idx = lua_objlen(L, -1);
}
else{
//cb_list does not exist in swtmr, create and add to swtmr
lua_pop(L, 1);// pop nil value from stack
lua_newtable(L);//create new table for swtmr.timer_cb_list
lua_pushstring(L, CB_LIST_STR); //push name for the new table onto the stack
lua_pushvalue(L, -2); //push table to top of stack
lua_rawset(L, -4); //pop table and name from stack and register in swtmr
}
//append new timer cb ptr to table
lua_pushnumber(L, cb_list_last_idx+1);
cb_registry_item_t* reg_item = lua_newuserdata(L, sizeof(cb_registry_item_t));
reg_item->tmr_cb_ptr = timer_cb_ptr;
reg_item->suspend_policy = suspend_policy;
lua_rawset(L, -3);
//clear items pushed onto stack by this function
lua_pop(L, 2);
}
return;
}
//this function adds the timer cb ptr to a queue for later registration after lua has started
static void add_to_reg_queue(void* timer_cb_ptr, uint8 suspend_policy){
if(!timer_cb_ptr)
return;
tmr_cb_queue_t* queue_temp = c_zalloc(sizeof(tmr_cb_queue_t));
if(!queue_temp){
//it's boot time currently and we're already out of memory, something is very wrong...
c_printf("\n\t%s:out of memory, system halted!\n", __FUNCTION__);
while(1)
system_soft_wdt_feed();
}
queue_temp->tmr_cb_ptr = timer_cb_ptr;
queue_temp->suspend_policy = suspend_policy;
queue_temp->next = NULL;
if(register_queue == NULL){
register_queue = queue_temp;
}
else{
tmr_cb_queue_t* queue_ptr = register_queue;
while(queue_ptr->next != NULL){
queue_ptr = queue_ptr->next;
}
queue_ptr->next = queue_temp;
}
if(!cb_register_task_id){
cb_register_task_id = task_get_id(process_cb_register_queue);//get task id from task interface
task_post_low(cb_register_task_id, false); //post task to process next item in queue
}
return;
}
static void process_cb_register_queue(task_param_t param, uint8 priority)
{
if(!lua_getstate()){
SWTMR_DBG("L== NULL, Lua not yet started! posting task");
task_post_low(cb_register_task_id, false); //post task to process next item in queue
return;
}
while(register_queue != NULL){
tmr_cb_queue_t* register_queue_ptr = register_queue;
void* cb_ptr_tmp = register_queue_ptr->tmr_cb_ptr;
swtmr_cb_register(cb_ptr_tmp, register_queue_ptr->suspend_policy);
register_queue = register_queue->next;
c_free(register_queue_ptr);
}
return;
}
#ifdef SWTMR_DEBUG
int print_timer_list(lua_State* L){
push_swtmr_registry_key(L);
lua_rawget(L, L_REGISTRY);
lua_pushstring(L, CB_LIST_STR);
lua_rawget(L, -2);
if(!lua_istable(L, -2) || !lua_istable(L, -1)){
lua_pop(L, 2);
return 0;
}
os_timer_t* suspended_timer_list_head = NULL;
os_timer_t* suspended_timer_list_tail = NULL;
lua_pushstring(L, SUSP_LIST_STR);
lua_rawget(L, -3);
if(lua_isuserdata(L, -1)){
suspended_timer_list_head = suspended_timer_list_tail = lua_touserdata(L, -1);
while(suspended_timer_list_tail->timer_next != NULL){
suspended_timer_list_tail = suspended_timer_list_tail->timer_next;
}
}
lua_pop(L, 1);
size_t registered_cb_qty = lua_objlen(L, -1);
cb_registry_item_t** cb_reg_array = c_zalloc(sizeof(cb_registry_item_t*)*registered_cb_qty);
if(!cb_reg_array){
luaL_error(L, "%s: unable to suspend timers, out of memory!", __func__);
return 0;
}
uint8 index = 0;
lua_pushnil(L);
while(lua_next(L, -2) != 0){
if(lua_isuserdata(L, -1)){
cb_reg_array[index] = lua_touserdata(L, -1);
}
lua_pop(L, 1);
index++;
}
lua_pop(L, 1);
os_timer_t* timer_list_ptr = timer_list;
c_printf("\n\tCurrent FRC2: %u\n", RTC_REG_READ(FRC2_COUNT_ADDRESS));
c_printf("\ttimer_list:\n");
while(timer_list_ptr != NULL){
bool registered_flag = FALSE;
for(int i=0; i < registered_cb_qty; i++){
if(timer_list_ptr->timer_func == cb_reg_array[i]->tmr_cb_ptr){
registered_flag = TRUE;
break;
}
}
c_printf("\tptr:%p\tcb:%p\texpire:%8u\tperiod:%8u\tnext:%p\t%s\n",
timer_list_ptr, timer_list_ptr->timer_func, timer_list_ptr->timer_expire, timer_list_ptr->timer_period, timer_list_ptr->timer_next, registered_flag ? "Registered" : "");
timer_list_ptr = timer_list_ptr->timer_next;
}
c_free(cb_reg_array);
lua_pop(L, 1);
return 0;
}
int print_susp_timer_list(lua_State* L){
push_swtmr_registry_key(L);
lua_rawget(L, L_REGISTRY);
if(!lua_istable(L, -1)){
return luaL_error(L, "swtmr table not found!");
}
lua_pushstring(L, SUSP_LIST_STR);
lua_rawget(L, -2);
if(!lua_isuserdata(L, -1)){
return luaL_error(L, "swtmr.suspended_list userdata not found!");
}
os_timer_t* susp_timer_list_ptr = lua_touserdata(L, -1);
c_printf("\n\tsuspended_timer_list:\n");
while(susp_timer_list_ptr != NULL){
c_printf("\tptr:%p\tcb:%p\texpire:%8u\tperiod:%8u\tnext:%p\n",susp_timer_list_ptr, susp_timer_list_ptr->timer_func, susp_timer_list_ptr->timer_expire, susp_timer_list_ptr->timer_period, susp_timer_list_ptr->timer_next);
susp_timer_list_ptr = susp_timer_list_ptr->timer_next;
}
return 0;
}
int suspend_timers_lua(lua_State* L){
swtmr_suspend_timers();
return 0;
}
int resume_timers_lua(lua_State* L){
swtmr_resume_timers();
return 0;
}
static const LUA_REG_TYPE test_swtimer_debug_map[] = {
{ LSTRKEY( "timer_list" ), LFUNCVAL( print_timer_list ) },
{ LSTRKEY( "susp_timer_list" ), LFUNCVAL( print_susp_timer_list ) },
{ LSTRKEY( "suspend" ), LFUNCVAL( suspend_timers_lua ) },
{ LSTRKEY( "resume" ), LFUNCVAL( resume_timers_lua ) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(SWTMR_DBG, "SWTMR_DBG", test_swtimer_debug_map, NULL);
#endif

View File

@ -3,6 +3,7 @@
#include "c_string.h"
#include "user_interface.h"
#include "smart.h"
#include "pm/swtimer.h"
#define ADDR_MAP_NUM 10
@ -500,6 +501,9 @@ void smart_end(){
os_timer_disarm(&smart_timer);
os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, (void *)1);
SWTIMER_REG_CB(station_check_connect, SWTIMER_RESUME);
//the function station_check_connect continues the Smart config process and fires the developers callback upon successful connection to the access point.
//If this function manages to get suspended, I think it would be fine to resume the timer.
os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat
}
}
@ -672,6 +676,9 @@ void smart_begin(int chnl, smart_succeed s, void *arg){
wifi_set_promiscuous_rx_cb(detect);
os_timer_disarm(&smart_timer);
os_timer_setfn(&smart_timer, (os_timer_func_t *)smart_next_channel, NULL);
SWTIMER_REG_CB(smart_next_channel, SWTIMER_RESUME);
//smart_next_channel switches the wifi channel
//I don't see a problem with resuming this timer
os_timer_arm(&smart_timer, TIME_OUT_PER_CHANNEL, 0); // no repeat
if(s){
@ -717,5 +724,6 @@ void station_check_connect(bool smart){
}
os_timer_disarm(&smart_timer);
os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, (void *)(int)smart);
//this function was already registered in the function smart_end.
os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat
}

View File

@ -1,52 +0,0 @@
#############################################################
# 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

View File

@ -1,632 +0,0 @@
/* 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

@ -40,6 +40,8 @@
#include "../crypto/digests.h"
#include "../crypto/mech.h"
#include "pm/swtimer.h"
#define PROTOCOL_SECURE "wss://"
#define PROTOCOL_INSECURE "ws://"
@ -560,6 +562,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) {
os_timer_disarm(&ws->timeoutTimer);
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_sendPingTimeout, conn);
SWTIMER_REG_CB(ws_sendPingTimeout, SWTIMER_RESUME)
os_timer_arm(&ws->timeoutTimer, WS_PING_INTERVAL_MS, true);
espconn_regist_recvcb(conn, ws_receiveCallback);
@ -706,6 +709,7 @@ static void dns_callback(const char *hostname, ip_addr_t *addr, void *arg) {
// Set connection timeout timer
os_timer_disarm(&ws->timeoutTimer);
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_connectTimeout, conn);
SWTIMER_REG_CB(ws_connectTimeout, SWTIMER_RESUME)
os_timer_arm(&ws->timeoutTimer, WS_CONNECT_TIMEOUT_MS, false);
if (ws->isSecure) {
@ -867,6 +871,7 @@ void ws_close(ws_info *ws) {
os_timer_disarm(&ws->timeoutTimer);
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_forceCloseTimeout, ws->conn);
SWTIMER_REG_CB(ws_forceCloseTimeout, SWTIMER_RESUME);
os_timer_arm(&ws->timeoutTimer, WS_FORCE_CLOSE_TIMEOUT_MS, false);
}
}

View File

@ -333,30 +333,32 @@ 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.
!!! attention
This is disabled by default. Modify `PMSLEEP_ENABLE` in `app/include/user_config.h` to enable it.
#### Syntax
`node.sleep({wake_gpio[, duration, int_type, resume_cb, preserve_mode]})`
<!---`node.sleep({wake_pin[, duration, int_type, resume_cb, preserve_mode]})`--->
`node.sleep({wake_pin[, 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.
<!--- timed light_sleep currently does not work, the 'duration' parameter is here as a place holder--->
<!--- * `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`
@ -379,15 +381,15 @@ Put NodeMCU in light sleep mode to reduce current consumption.
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)

View File

@ -62,11 +62,9 @@ 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
@ -184,61 +182,6 @@ 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.
@ -336,72 +279,6 @@ if not mytimer:stop() then print("timer not stopped, not registered?") end
- [`tmr.stop()`](#tmrstop)
- [`tmr.unregister()`](#tmrunregister)
## tmr.suspend()
Suspend an armed timer.
!!! attention
This is disabled by default. Modify `ENABLE_TIMER_SUSPEND` in `app/include/user_config.h` to enable it.
* 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.
!!! attention
This is disabled by default. Modify `ENABLE_TIMER_SUSPEND` in `app/include/user_config.h` to enable it.
!!! 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.

View File

@ -15,15 +15,4 @@ 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