|
@@ -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 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){
|
|
|
- uint32_t id = luaL_checkinteger(L, 1);
|
|
|
+ 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");
|
|
|
+ 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,13 +247,12 @@ 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);
|
|
|
+ luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
|
|
|
if(tmr->mode != TIMER_MODE_OFF){
|
|
|
tmr->interval = interval;
|
|
|
if(!(tmr->mode&TIMER_IDLE_FLAG)){
|
|
@@ -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 ) },
|
|
@@ -321,7 +379,8 @@ static const LUA_REG_TYPE tmr_map[] = {
|
|
|
{ LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
|
|
|
{ LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) },
|
|
|
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
|
|
|
- { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) },
|
|
|
+ { 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);
|
|
|
}
|
|
@@ -341,7 +404,7 @@ int luaopen_tmr( lua_State *L ){
|
|
|
ets_timer_disarm(&rtc_timer);
|
|
|
ets_timer_setfn(&rtc_timer, rtc_callback, NULL);
|
|
|
ets_timer_arm_new(&rtc_timer, 1000, 1, 1);
|
|
|
- return 0;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
NODEMCU_MODULE(TMR, "tmr", tmr_map, luaopen_tmr);
|