add tmr module
This commit is contained in:
parent
e3e0d88fc1
commit
e203a5829c
|
@ -111,6 +111,12 @@ config LUA_MODULE_STRUCT
|
|||
Includes the struct module. This module provides [un]packing of raw
|
||||
byte strings into Lua values and vice versa.
|
||||
|
||||
config LUA_MODULE_TMR
|
||||
bool "Timer module"
|
||||
default "y"
|
||||
help
|
||||
Includes the timer module (recommended).
|
||||
|
||||
config LUA_MODULE_WIFI
|
||||
bool "WiFi module"
|
||||
default "y"
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
|
||||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include <stdint.h>
|
||||
#include "task/task.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/timers.h"
|
||||
|
||||
#define TIMER_MODE_OFF 3
|
||||
#define TIMER_MODE_SINGLE 0
|
||||
#define TIMER_MODE_SEMI 2
|
||||
#define TIMER_MODE_AUTO 1
|
||||
#define TIMER_IDLE_FLAG (1<<7)
|
||||
|
||||
#define STRINGIFY_VAL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_VAL(x)
|
||||
|
||||
// assuming system_timer_reinit() has *not* been called
|
||||
#define MAX_TIMEOUT_DEF 6870947 //SDK 1.5.3 limit (0x68D7A3)
|
||||
|
||||
static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF;
|
||||
static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
|
||||
|
||||
typedef struct{
|
||||
TimerHandle_t timer;
|
||||
int32_t cb_ref, self_ref;
|
||||
uint32_t interval;
|
||||
uint8_t mode;
|
||||
} tmr_struct_t;
|
||||
typedef tmr_struct_t *tmr_t;
|
||||
|
||||
static task_handle_t alarm_task_id;
|
||||
|
||||
static void timer_callback(TimerHandle_t xTimer)
|
||||
{
|
||||
tmr_t tmr = (tmr_t)pvTimerGetTimerID(xTimer);
|
||||
|
||||
task_post_high(alarm_task_id, (task_param_t)tmr);
|
||||
}
|
||||
|
||||
static void alarm_timer_task(task_param_t param, task_prio_t prio)
|
||||
{
|
||||
tmr_t tmr = (tmr_t)param;
|
||||
lua_State* L = lua_getstate();
|
||||
if (tmr->cb_ref == LUA_NOREF)
|
||||
return;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->cb_ref);
|
||||
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->cb_ref);
|
||||
tmr->cb_ref = LUA_NOREF;
|
||||
tmr->mode = TIMER_MODE_OFF;
|
||||
} else if (tmr->mode == TIMER_MODE_SEMI) {
|
||||
tmr->mode |= TIMER_IDLE_FLAG;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static tmr_t tmr_get( lua_State *L, int stack )
|
||||
{
|
||||
return (tmr_t)luaL_checkudata(L, stack, "tmr.timer");
|
||||
}
|
||||
|
||||
// Lua: tmr.register( self, interval, mode, function )
|
||||
static int tmr_register(lua_State* L)
|
||||
{
|
||||
int stack = 0;
|
||||
|
||||
tmr_t tmr = tmr_get(L, ++stack);
|
||||
|
||||
uint32_t interval = luaL_checkinteger(L, ++stack);
|
||||
luaL_argcheck(L, interval > 0 && interval <= MAX_TIMEOUT, stack, MAX_TIMEOUT_ERR_STR);
|
||||
|
||||
uint8_t mode = luaL_checkinteger(L, ++stack);
|
||||
luaL_argcheck(L, mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO, stack, "Invalid mode");
|
||||
|
||||
++stack;
|
||||
luaL_argcheck(L, lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION, stack, "Must be function");
|
||||
|
||||
if (tmr->timer) {
|
||||
// delete previous timer since mode change could be requested here
|
||||
xTimerDelete(tmr->timer, portMAX_DELAY);
|
||||
}
|
||||
|
||||
//get the lua function reference
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tmr->cb_ref);
|
||||
lua_pushvalue(L, stack);
|
||||
tmr->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
tmr->mode = mode | TIMER_IDLE_FLAG;
|
||||
tmr->interval = interval;
|
||||
tmr->timer = xTimerCreate("tmr",
|
||||
pdMS_TO_TICKS(interval),
|
||||
mode == TIMER_MODE_AUTO ? pdTRUE : pdFALSE,
|
||||
(void *)tmr,
|
||||
timer_callback);
|
||||
|
||||
if (!tmr->timer) {
|
||||
luaL_error(L, "FreeRTOS timer creation failed");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.start( self )
|
||||
static int tmr_start(lua_State* L)
|
||||
{
|
||||
tmr_t tmr = tmr_get(L, 1);
|
||||
|
||||
if (tmr->self_ref == LUA_NOREF) {
|
||||
lua_pushvalue(L, 1);
|
||||
tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
if (!tmr->timer || tmr->cb_ref == LUA_NOREF) {
|
||||
luaL_error(L, "timer not registered");
|
||||
}
|
||||
|
||||
// we return false if the timer is not idle
|
||||
if(!(tmr->mode & TIMER_IDLE_FLAG)) {
|
||||
lua_pushboolean(L, 0);
|
||||
} else {
|
||||
tmr->mode &= ~TIMER_IDLE_FLAG;
|
||||
if (xTimerStart(tmr->timer, portMAX_DELAY) != pdPASS) {
|
||||
luaL_error(L, "timer start failed");
|
||||
}
|
||||
lua_pushboolean(L, 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lua: tmr.alarm( self, interval, repeat, function )
|
||||
static int tmr_alarm(lua_State* L){
|
||||
tmr_register(L);
|
||||
return tmr_start(L);
|
||||
}
|
||||
|
||||
// Lua: tmr.stop( id / ref )
|
||||
static int tmr_stop(lua_State* L)
|
||||
{
|
||||
tmr_t tmr = tmr_get(L, 1);
|
||||
|
||||
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;
|
||||
xTimerStop(tmr->timer, portMAX_DELAY);
|
||||
lua_pushboolean(L, 1);
|
||||
} else {
|
||||
lua_pushboolean(L, 0);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lua: tmr.unregister( self )
|
||||
static int tmr_unregister(lua_State* L){
|
||||
tmr_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) {
|
||||
if (tmr->timer) {
|
||||
xTimerStop(tmr->timer, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
if (tmr->timer) {
|
||||
xTimerDelete(tmr->timer, portMAX_DELAY);
|
||||
}
|
||||
tmr->timer = NULL;
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tmr->cb_ref);
|
||||
tmr->cb_ref = LUA_NOREF;
|
||||
tmr->mode = TIMER_MODE_OFF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.interval( self, interval )
|
||||
static int tmr_interval(lua_State* L)
|
||||
{
|
||||
tmr_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) {
|
||||
tmr->interval = interval;
|
||||
if (!(tmr->mode & TIMER_IDLE_FLAG)) {
|
||||
xTimerStop(tmr->timer, portMAX_DELAY);
|
||||
if (xTimerChangePeriod(tmr->timer, tmr->interval, portMAX_DELAY) != pdPASS) {
|
||||
luaL_error(L, "cannot change period");
|
||||
}
|
||||
// stop again since xTimerChangePeriod will re-start the timer
|
||||
xTimerStop(tmr->timer, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lua: tmr.state( self, state )
|
||||
static int tmr_state(lua_State* L)
|
||||
{
|
||||
tmr_t tmr = tmr_get(L, 1);
|
||||
|
||||
if (tmr->mode == TIMER_MODE_OFF) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0);
|
||||
lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG));
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Lua: tmr.create()
|
||||
static int tmr_create( lua_State *L ) {
|
||||
tmr_t ud = (tmr_t)lua_newuserdata(L, sizeof(tmr_struct_t));
|
||||
if (!ud) return luaL_error(L, "not enough memory");
|
||||
luaL_getmetatable(L, "tmr.timer");
|
||||
lua_setmetatable(L, -2);
|
||||
ud->cb_ref = LUA_NOREF;
|
||||
ud->self_ref = LUA_NOREF;
|
||||
ud->mode = TIMER_MODE_OFF;
|
||||
ud->timer = NULL;
|
||||
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( "interval" ), LFUNCVAL( tmr_interval) },
|
||||
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
|
||||
{ LSTRKEY( "__gc" ), LFUNCVAL( tmr_unregister ) },
|
||||
{ LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
static const LUA_REG_TYPE tmr_map[] = {
|
||||
{ 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 ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
static int luaopen_tmr( lua_State *L ){
|
||||
luaL_rometatable(L, "tmr.timer", (void *)tmr_dyn_map);
|
||||
|
||||
alarm_task_id = task_get_id(alarm_timer_task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NODEMCU_MODULE(TMR, "tmr", tmr_map, luaopen_tmr);
|
|
@ -0,0 +1,207 @@
|
|||
# Timer Module
|
||||
| Since | Origin / Contributor | Maintainer | Source |
|
||||
| :----- | :-------------------- | :---------- | :------ |
|
||||
| 2014-12-12 | [Zeroday](https://github.com/funshine) | [dnc40085](https://github.com/dnc40085) | [tmr.c](../../../app/modules/tmr.c)|
|
||||
|
||||
The tmr module allows access to simple timers. It is aimed at setting up regularly occurring tasks and timing out operations.
|
||||
|
||||
What the tmr module is *not* however, is a time keeping module. While all timeouts are expressed in milliseconds, 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.
|
||||
|
||||
## tmr.create()
|
||||
|
||||
Creates a dynamic timer object.
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`timer` object
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
local mytimer = tmr.create()
|
||||
|
||||
-- oo calling
|
||||
mytimer:register(5000, tmr.ALARM_SINGLE, function (t) print("expired") end)
|
||||
mytimer:start()
|
||||
mytimer = nil
|
||||
```
|
||||
|
||||
# tmr Object
|
||||
## tmr.obj:alarm()
|
||||
|
||||
This is a convenience function combining [`tmr.obj:register()`](#tmrobjregister) and [`tmr.obj:start()`](#tmrobjstart) into a single call.
|
||||
|
||||
To free up the resources with this timer when done using it, call [`tmr.obj:unregister()`](#tmrobjunregister) on it. For one-shot timers this is not necessary, unless they were stopped before they expired.
|
||||
|
||||
#### Syntax
|
||||
`mytmr:alarm(interval_ms, mode, func())`
|
||||
|
||||
#### Parameters
|
||||
- `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))
|
||||
- `tmr.ALARM_SEMI` manually repeating alarm (call [`tmr.start()`](#tmrstart) to restart)
|
||||
- `tmr.ALARM_AUTO` automatically repeating alarm
|
||||
- `func(timer)` callback function which is invoked with the timer object as an argument
|
||||
|
||||
#### Returns
|
||||
`true` if the timer was started, `false` on error
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
if not tmr.create():alarm(5000, tmr.ALARM_SINGLE, function()
|
||||
print("hey there")
|
||||
end)
|
||||
then
|
||||
print("whoopsie")
|
||||
end
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.obj:register()`](#tmrobjregister)
|
||||
- [`tmr.obj:start()`](#tmrobjstart)
|
||||
- [`tmr.obj:unregister()`](#tmrobjunregister)
|
||||
|
||||
## tmr.obj:interval()
|
||||
|
||||
Changes a registered timer's expiry interval.
|
||||
|
||||
#### Syntax
|
||||
`mytmr:interval(interval_ms)`
|
||||
|
||||
#### Parameters
|
||||
- `interval_ms` new timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
mytimer = tmr.create()
|
||||
mytimer:register(10000, tmr.ALARM_AUTO, function() print("hey there") end)
|
||||
mytimer:interval(3000) -- actually, 3 seconds is better!
|
||||
mytimer:start()
|
||||
```
|
||||
|
||||
## tmr.obj:register()
|
||||
|
||||
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.obj:unregister()`](#tmrobjunregister) on it. For one-shot timers this is not necessary, unless they were stopped before they expired.
|
||||
|
||||
#### Syntax
|
||||
`mytmr:register(interval_ms, mode, func())`
|
||||
|
||||
#### Parameters
|
||||
- `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))
|
||||
- `tmr.ALARM_SEMI` manually repeating alarm (call [`tmr.start()`](#tmrunregister) to restart)
|
||||
- `tmr.ALARM_AUTO` automatically repeating alarm
|
||||
- `func(timer)` callback function which is invoked with the timer object as an argument
|
||||
|
||||
Note that registering does *not* start the alarm.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
mytimer = tmr.create()
|
||||
mytimer:register(5000, tmr.ALARM_SINGLE, function() print("hey there") end)
|
||||
mytimer:start()
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.obj:alarm()`](#tmrobjalarm)
|
||||
|
||||
## tmr.obj:start()
|
||||
|
||||
Starts or restarts a previously configured timer.
|
||||
|
||||
#### Syntax
|
||||
`mytmr:start()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`true` if the timer was started, `false` on error
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
mytimer = tmr.create()
|
||||
mytimer:register(5000, tmr.ALARM_SINGLE, function() print("hey there") end)
|
||||
if not mytimer:start() then print("uh oh") end
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.obj:register()`](#tmrobjregister)
|
||||
- [`tmr.obj:stop()`](#tmrobjstop)
|
||||
- [`tmr.obj:unregister()`](#tmrobjunregister)
|
||||
|
||||
## tmr.obj:state()
|
||||
|
||||
Checks the state of a timer.
|
||||
|
||||
#### Syntax
|
||||
`mytmr:state()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
(bool, int) or `nil`
|
||||
|
||||
If the specified timer is registered, returns whether it is currently started and its mode. If the timer is not registered, `nil` is returned.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
mytimer = tmr.create()
|
||||
print(mytimer:state()) -- nil
|
||||
mytimer:register(5000, tmr.ALARM_SINGLE, function() print("hey there") end)
|
||||
running, mode = mytimer:state()
|
||||
print("running: " .. tostring(running) .. ", mode: " .. mode) -- running: false, mode: 0
|
||||
```
|
||||
|
||||
## tmr.obj:stop()
|
||||
|
||||
Stops a running timer, but does *not* unregister it. A stopped timer can be restarted with [`tmr.obj:start()`](#tmrobjstart).
|
||||
|
||||
#### Syntax
|
||||
`mytmr:stop()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`true` if the timer was stopped, `false` on error
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
mytimer = tmr.create()
|
||||
if not mytimer:stop() then print("timer not stopped, not registered?") end
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.obj:register()`](#tmrobjregister)
|
||||
- [`tmr.obj:stop()`](#tmrobjstop)
|
||||
- [`tmr.obj:unregister()`](#tmrobjunregister)
|
||||
|
||||
## tmr.obj:unregister()
|
||||
|
||||
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
|
||||
`mytmr:unregister()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### See also
|
||||
[`tmr.obj:register()`](#tmrobjregister)
|
|
@ -37,6 +37,7 @@ pages:
|
|||
- 'node': 'en/modules/node.md'
|
||||
- 'sigma delta': 'en/modules/sigma-delta.md'
|
||||
- 'struct': 'en/modules/struct.md'
|
||||
- 'tmr': 'en/modules/tmr.md'
|
||||
- 'uart': 'en/modules/uart.md'
|
||||
- 'wifi': 'en/modules/wifi.md'
|
||||
|
||||
|
|
Loading…
Reference in New Issue