Add support for dynamic timers, deprecating the static ones (#1424)
This commit is contained in:
parent
a499716cc1
commit
f1afe7b6ab
|
@ -71,7 +71,7 @@ static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
|
|||
|
||||
typedef struct{
|
||||
os_timer_t os;
|
||||
sint32_t lua_ref;
|
||||
sint32_t lua_ref, self_ref;
|
||||
uint32_t interval;
|
||||
uint8_t mode;
|
||||
}timer_struct_t;
|
||||
|
@ -95,11 +95,17 @@ static timer_struct_t alarm_timers[NUM_TMR];
|
|||
static os_timer_t rtc_timer;
|
||||
|
||||
static void alarm_timer_common(void* arg){
|
||||
timer_t tmr = &alarm_timers[(uint32_t)arg];
|
||||
timer_t tmr = (timer_t)arg;
|
||||
lua_State* L = lua_getstate();
|
||||
if(tmr->lua_ref == LUA_NOREF)
|
||||
return;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
|
||||
if (tmr->self_ref == LUA_REFNIL) {
|
||||
uint32_t id = tmr - alarm_timers;
|
||||
lua_pushinteger(L, id);
|
||||
} else {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref);
|
||||
}
|
||||
//if the timer was set to single run we clean up after it
|
||||
if(tmr->mode == TIMER_MODE_SINGLE){
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
|
||||
|
@ -108,7 +114,11 @@ static void alarm_timer_common(void* arg){
|
|||
}else if(tmr->mode == TIMER_MODE_SEMI){
|
||||
tmr->mode |= TIMER_IDLE_FLAG;
|
||||
}
|
||||
lua_call(L, 0, 0);
|
||||
if (tmr->mode != TIMER_MODE_AUTO && tmr->self_ref != LUA_REFNIL) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref);
|
||||
tmr->self_ref = LUA_NOREF;
|
||||
}
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
|
||||
// Lua: tmr.delay( us )
|
||||
|
@ -135,20 +145,32 @@ static int tmr_now(lua_State* L){
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Lua: tmr.register( id, interval, mode, function )
|
||||
static int tmr_register(lua_State* L){
|
||||
static timer_t tmr_get( lua_State *L, int stack ) {
|
||||
// Deprecated: static 0-6 timers control by index.
|
||||
luaL_argcheck(L, (lua_isuserdata(L, stack) || lua_isnumber(L, stack)), 1, "timer object or numerical ID expected");
|
||||
if (lua_isuserdata(L, stack)) {
|
||||
return (timer_t)luaL_checkudata(L, stack, "tmr.timer");
|
||||
} else {
|
||||
uint32_t id = luaL_checkinteger(L, 1);
|
||||
luaL_argcheck(L, platform_tmr_exists(id), 1, "invalid timer index");
|
||||
return &alarm_timers[id];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.register( id / ref, interval, mode, function )
|
||||
static int tmr_register(lua_State* L){
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
uint32_t interval = luaL_checkinteger(L, 2);
|
||||
uint8_t mode = luaL_checkinteger(L, 3);
|
||||
//Check if provided parameters are valid
|
||||
MOD_CHECK_ID(tmr, id);
|
||||
|
||||
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
|
||||
luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode");
|
||||
luaL_argcheck(L, (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION), 4, "Must be function");
|
||||
//get the lua function reference
|
||||
lua_pushvalue(L, 4);
|
||||
sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
timer_t tmr = &alarm_timers[id];
|
||||
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
|
||||
ets_timer_disarm(&tmr->os);
|
||||
//there was a bug in this part, the second part of the following condition was missing
|
||||
|
@ -157,15 +179,19 @@ static int tmr_register(lua_State* L){
|
|||
tmr->lua_ref = ref;
|
||||
tmr->mode = mode|TIMER_IDLE_FLAG;
|
||||
tmr->interval = interval;
|
||||
ets_timer_setfn(&tmr->os, alarm_timer_common, (void*)id);
|
||||
ets_timer_setfn(&tmr->os, alarm_timer_common, tmr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.start( id )
|
||||
// Lua: tmr.start( id / ref )
|
||||
static int tmr_start(lua_State* L){
|
||||
uint8_t id = luaL_checkinteger(L, 1);
|
||||
MOD_CHECK_ID(tmr,id);
|
||||
timer_t tmr = &alarm_timers[id];
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
if (tmr->self_ref == LUA_NOREF) {
|
||||
lua_pushvalue(L, 1);
|
||||
tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
//we return false if the timer is not idle
|
||||
if(!(tmr->mode&TIMER_IDLE_FLAG)){
|
||||
lua_pushboolean(L, 0);
|
||||
|
@ -177,17 +203,21 @@ static int tmr_start(lua_State* L){
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Lua: tmr.alarm( id, interval, repeat, function )
|
||||
// Lua: tmr.alarm( id / ref, interval, repeat, function )
|
||||
static int tmr_alarm(lua_State* L){
|
||||
tmr_register(L);
|
||||
return tmr_start(L);
|
||||
}
|
||||
|
||||
// Lua: tmr.stop( id )
|
||||
// Lua: tmr.stop( id / ref )
|
||||
static int tmr_stop(lua_State* L){
|
||||
uint8_t id = luaL_checkinteger(L, 1);
|
||||
MOD_CHECK_ID(tmr,id);
|
||||
timer_t tmr = &alarm_timers[id];
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
if (tmr->self_ref != LUA_REFNIL) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref);
|
||||
tmr->self_ref = LUA_NOREF;
|
||||
}
|
||||
|
||||
//we return false if the timer is idle (of not registered)
|
||||
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){
|
||||
tmr->mode |= TIMER_IDLE_FLAG;
|
||||
|
@ -199,11 +229,15 @@ static int tmr_stop(lua_State* L){
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Lua: tmr.unregister( id )
|
||||
// Lua: tmr.unregister( id / ref )
|
||||
static int tmr_unregister(lua_State* L){
|
||||
uint8_t id = luaL_checkinteger(L, 1);
|
||||
MOD_CHECK_ID(tmr,id);
|
||||
timer_t tmr = &alarm_timers[id];
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
if (tmr->self_ref != LUA_REFNIL) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tmr->self_ref);
|
||||
tmr->self_ref = LUA_NOREF;
|
||||
}
|
||||
|
||||
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
|
||||
ets_timer_disarm(&tmr->os);
|
||||
if(tmr->lua_ref != LUA_NOREF)
|
||||
|
@ -213,11 +247,10 @@ static int tmr_unregister(lua_State* L){
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.interval( id, interval )
|
||||
// Lua: tmr.interval( id / ref, interval )
|
||||
static int tmr_interval(lua_State* L){
|
||||
uint8_t id = luaL_checkinteger(L, 1);
|
||||
MOD_CHECK_ID(tmr,id);
|
||||
timer_t tmr = &alarm_timers[id];
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
uint32_t interval = luaL_checkinteger(L, 2);
|
||||
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
|
||||
if(tmr->mode != TIMER_MODE_OFF){
|
||||
|
@ -230,11 +263,10 @@ static int tmr_interval(lua_State* L){
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.state( id )
|
||||
// Lua: tmr.state( id / ref )
|
||||
static int tmr_state(lua_State* L){
|
||||
uint8_t id = luaL_checkinteger(L, 1);
|
||||
MOD_CHECK_ID(tmr,id);
|
||||
timer_t tmr = &alarm_timers[id];
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
if(tmr->mode == TIMER_MODE_OFF){
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
|
@ -307,8 +339,34 @@ static int tmr_softwd( lua_State* L ){
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.create()
|
||||
static int tmr_create( lua_State *L ) {
|
||||
timer_t ud = (timer_t)lua_newuserdata(L, sizeof(timer_struct_t));
|
||||
if (!ud) return luaL_error(L, "not enough memory");
|
||||
luaL_getmetatable(L, "tmr.timer");
|
||||
lua_setmetatable(L, -2);
|
||||
ud->lua_ref = LUA_NOREF;
|
||||
ud->self_ref = LUA_NOREF;
|
||||
ud->mode = TIMER_MODE_OFF;
|
||||
ets_timer_disarm(&ud->os);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Module function map
|
||||
|
||||
static const LUA_REG_TYPE tmr_dyn_map[] = {
|
||||
{ LSTRKEY( "register" ), LFUNCVAL( tmr_register ) },
|
||||
{ LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
|
||||
{ LSTRKEY( "start" ), LFUNCVAL( tmr_start ) },
|
||||
{ LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
|
||||
{ LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) },
|
||||
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
|
||||
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) },
|
||||
{ LSTRKEY( "__gc" ), LFUNCVAL( tmr_unregister ) },
|
||||
{ LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
static const LUA_REG_TYPE tmr_map[] = {
|
||||
{ LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
|
||||
{ LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
|
||||
|
@ -322,6 +380,7 @@ static const LUA_REG_TYPE tmr_map[] = {
|
|||
{ LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) },
|
||||
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
|
||||
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) },
|
||||
{ LSTRKEY( "create" ), LFUNCVAL( tmr_create ) },
|
||||
{ LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) },
|
||||
{ LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) },
|
||||
{ LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) },
|
||||
|
@ -330,8 +389,12 @@ static const LUA_REG_TYPE tmr_map[] = {
|
|||
|
||||
int luaopen_tmr( lua_State *L ){
|
||||
int i;
|
||||
|
||||
luaL_rometatable(L, "tmr.timer", (void *)tmr_dyn_map);
|
||||
|
||||
for(i=0; i<NUM_TMR; i++){
|
||||
alarm_timers[i].lua_ref = LUA_NOREF;
|
||||
alarm_timers[i].self_ref = LUA_REFNIL;
|
||||
alarm_timers[i].mode = TIMER_MODE_OFF;
|
||||
ets_timer_disarm(&alarm_timers[i].os);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,11 @@ It is aimed at setting up regularly occurring tasks, timing out operations, and
|
|||
|
||||
What the tmr module is *not* however, is a time keeping module. While most timeouts are expressed in milliseconds or even microseconds, the accuracy is limited and compounding errors would lead to rather inaccurate time keeping. Consider using the [rtctime](rtctime.md) module for "wall clock" time.
|
||||
|
||||
NodeMCU provides 7 timers, numbered 0-6. It is currently up to the user to keep track of which timers are used for what.
|
||||
NodeMCU provides 7 static timers, numbered 0-6, and dynamic timer creation function [`tmr.create()`](#tmrcreate).
|
||||
|
||||
!!! attention
|
||||
|
||||
Static timers are deprecated and will be removed later.
|
||||
|
||||
## tmr.alarm()
|
||||
|
||||
|
@ -18,7 +22,7 @@ This is a convenience function combining [`tmr.register()`](#tmrregister) and [`
|
|||
To free up the resources with this timer when done using it, call [`tmr.unregister()`](#tmrunregister) on it. For one-shot timers this is not necessary, unless they were stopped before they expired.
|
||||
|
||||
#### Parameters
|
||||
- `id` timer id (0-6)
|
||||
- `id`/`ref` timer id (0-6) or object
|
||||
- `interval_ms` timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
|
||||
- `mode` timer mode:
|
||||
- `tmr.ALARM_SINGLE` a one-shot alarm (and no need to call [`tmr.unregister()`](#tmrunregister))
|
||||
|
@ -33,10 +37,46 @@ To free up the resources with this timer when done using it, call [`tmr.unregist
|
|||
if not tmr.alarm(0, 5000, tmr.ALARM_SINGLE, function() print("hey there") end) then print("whoopsie") end
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.register()`](#tmrregister)
|
||||
- [`tmr.start()`](#tmrstart)
|
||||
- [`tmr.unregister()`](#tmrunregister)
|
||||
|
||||
## tmr.create()
|
||||
|
||||
Creates a dynamic timer object.
|
||||
|
||||
Dynamic timer can be used instead of numeric ID in control functions. Also can be controlled in object-oriented way.
|
||||
|
||||
Functions supported in timer object:
|
||||
|
||||
- [`t:alarm()`](#tmralarm)
|
||||
- [`t:register()`](#tmrregister)
|
||||
- [`t:start()`](#tmrstart)
|
||||
- [`t:stop()`](#tmrstop)
|
||||
- [`t:unregister()`](#tmrunregister)
|
||||
- [`t:state()`](#tmrstate)
|
||||
- [`t:interval()`](#tmrinterval)
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`timer` object
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
local mytimer = tmr.create()
|
||||
|
||||
-- oo calling
|
||||
mytimer:register(5000, tmr.ALARM_SINGLE, function (t) print("expired"); t:unregister() end)
|
||||
mytimer:start()
|
||||
|
||||
-- with self parameter
|
||||
tmr.register(mytimer, 5000, tmr.ALARM_SINGLE, function (t) print("expired"); tmr.unregister(t) end)
|
||||
tmr.start(mytimer)
|
||||
```
|
||||
|
||||
## tmr.delay()
|
||||
|
||||
Busyloops the processor for a specified number of microseconds.
|
||||
|
@ -64,10 +104,10 @@ tmr.delay(100)
|
|||
Changes a registered timer's expiry interval.
|
||||
|
||||
#### Syntax
|
||||
`tmr.interval(id, interval_ms)`
|
||||
`tmr.interval(id/ref, interval_ms)`
|
||||
|
||||
#### Parameters
|
||||
- `id` timer id (0-6)
|
||||
- `id`/`ref` timer id (0-6) or object
|
||||
- `interval_ms` new timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
|
||||
|
||||
#### Returns
|
||||
|
@ -105,10 +145,10 @@ Configures a timer and registers the callback function to call on expiry.
|
|||
To free up the resources with this timer when done using it, call [`tmr.unregister()`](#tmrunregister) on it. For one-shot timers this is not necessary, unless they were stopped before they expired.
|
||||
|
||||
#### Syntax
|
||||
`tmr.register(id, interval_ms, mode, func)`
|
||||
`tmr.register(id/ref, interval_ms, mode, func)`
|
||||
|
||||
#### Parameters
|
||||
- `id` timer id (0-6)
|
||||
- `id`/`ref` timer id (0-6) or object
|
||||
- `interval_ms` timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
|
||||
- `mode` timer mode:
|
||||
- `tmr.ALARM_SINGLE` a one-shot alarm (and no need to call [`tmr.unregister()`](#tmrunregister))
|
||||
|
@ -126,7 +166,8 @@ tmr.register(0, 5000, tmr.ALARM_SINGLE, function() print("hey there") end)
|
|||
tmr.start(0)
|
||||
```
|
||||
#### See also
|
||||
[`tmr.alarm()`](#tmralarm)
|
||||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.alarm()`](#tmralarm)
|
||||
|
||||
## tmr.softwd()
|
||||
|
||||
|
@ -158,10 +199,10 @@ complex_stuff_which_might_never_call_the_callback(on_success_callback)
|
|||
Starts or restarts a previously configured timer.
|
||||
|
||||
#### Syntax
|
||||
`tmr.start(id)`
|
||||
`tmr.start(id/ref)`
|
||||
|
||||
#### Parameters
|
||||
`id` timer id (0-6)
|
||||
`id`/`ref` timer id (0-6) or object
|
||||
|
||||
#### Returns
|
||||
`true` if the timer was started, `false` on error
|
||||
|
@ -172,6 +213,7 @@ tmr.register(0, 5000, tmr.ALARM_SINGLE, function() print("hey there") end)
|
|||
if not tmr.start(0) then print("uh oh") end
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.register()`](#tmrregister)
|
||||
- [`tmr.stop()`](#tmrstop)
|
||||
- [`tmr.unregister()`](#tmrunregister)
|
||||
|
@ -181,10 +223,10 @@ if not tmr.start(0) then print("uh oh") end
|
|||
Checks the state of a timer.
|
||||
|
||||
#### Syntax
|
||||
`tmr.state(id)`
|
||||
`tmr.state(id/ref)`
|
||||
|
||||
#### Parameters
|
||||
`id` timer id (0-6)
|
||||
`id`/`ref` timer id (0-6) or object
|
||||
|
||||
#### Returns
|
||||
(bool, int) or `nil`
|
||||
|
@ -201,10 +243,10 @@ running, mode = tmr.state(0)
|
|||
Stops a running timer, but does *not* unregister it. A stopped timer can be restarted with [`tmr.start()`](#tmrstart).
|
||||
|
||||
#### Syntax
|
||||
`tmr.stop(id)`
|
||||
`tmr.stop(id/ref)`
|
||||
|
||||
#### Parameters
|
||||
`id` timer id (0-6)
|
||||
`id`/`ref` timer id (0-6) or object
|
||||
|
||||
#### Returns
|
||||
`true` if the timer was stopped, `false` on error
|
||||
|
@ -243,10 +285,10 @@ Stops the timer (if running) and unregisters the associated callback.
|
|||
This isn't necessary for one-shot timers (`tmr.ALARM_SINGLE`), as those automatically unregister themselves when fired.
|
||||
|
||||
#### Syntax
|
||||
`tmr.unregister(id)`
|
||||
`tmr.unregister(id/ref)`
|
||||
|
||||
#### Parameters
|
||||
`id` timer id (0-6)
|
||||
`id`/`ref` timer id (0-6) or object
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
|
Loading…
Reference in New Issue