nodemcu-timer
v1.0.1
Published
A soft timer utility that allows you to schedule tasks with Javascript(/node.js) style APIs in your NodeMcu project on ESP8266
Downloads
1
Readme
nodemcu-timer
Current version: v1.0.0 (stable)
Compatible Lua version: 5.1.x
Table of Contents
1. Overview
The nodemcu tmr
module has 7 timers (id=0~6) for you to schedule your things with tmr.alarm()
. You would face a problem of managing callbacks with the specified timer ids to start or stop the scheduled alarms. This is the reason why nodemcu-timer comes out. With nodemcu-timer, you don't have to worry about whether a timer is available or not. It is a soft timer utility that allows you to schedule tasks with Javascript(/node.js) style APIs in your nodemcu project, i.e., setTimeout()
and clearTimeout()
.
nodemcu-timer uses a single timer to simulate the behavior of setTimeout()
, setInterval()
, and setImmediate()
. Only one task executes when a tick fires (excepts those scheduled by setImmediate()
), thus nodemcu-timer does not guarantee that the callback will fire at exact timing but as close as possilbe. When a callback is setImmediate, it will be executed at right next tick immediately even if there are other tasks being due at the same time. The internal timer will automatically start when a scheduled task enqueues, and automatically stopped when there is no task in queues. nodemcu-timer uses only a single timer (id=6 by default) internally, and you are free to use other 5 timers that tmr
provides.
If you like to code something in asynchronous style on nodemcu, might I sugguest lua-events? They are working well with each other to help arrange your asynchronous flow, e.g. your function can defer its callback and return right away.
[Note]
This module internally polls its task queues every 2ms. When you call setTimeout()
and setInterval()
, it is better to give the delay
with an even number, e.g., setTimeout(callback, 2000)
will fire the callback in 2 seconds. It is okay for delay
to be odd, it will minus 1 to be even implicitly and will result in a timing error as short as 1ms. I think this small error is negligible if you are scheduling your task with an interval beyond few hundreds of ms.
(I didn't use a tick of 1ms, because tmr
is a bad ass when you give him a repeat interval of 1ms. If you do so, nodemcu will crash and I don't know why.)
2. Installation
Clone from github
$ git clone https://github.com/simenkid/nodemcu-timer.git
or use npm install
$ npm install nodemcu-timer
Just include the file timer.lua
or use the minified one timer_min.lua
in your project.
If you are with the nodemcu on ESP8266, it would be good for you to compile *.lua
text file into *.lc
bytecode to further lower memory usage.
FS/Memory footprint
- timer.lua: ~3.9 KB(file size) / ~20 KB (heap available after loaded)
- timer_min.lua: ~2.0 KB(size) / ~20 KB (heap)
- timer.lc: ~3.2 KB(size) / ~25 KB (heap)
- timer.lc + events.lc: ~17.7 KB (heap)
3. APIs
- timer.setTimeout()
- timer.clearTimeout()
- timer.setInterval()
- timer.clearInterval()
- timer.setImmediate()
- timer.clearImmediate()
- timer.set()
timer utility
Exposed by require 'timer'
local timer = require 'timer' -- or 'timer_min'
setTimeout(callback, delay[, ...])
Schedules a one-time callback after delay
ms. This API returns a timer-object(tobj
) for possible use with clearTimeout(). You can also pass parameters to the callback via the variadic arguments.
Arguments:
callback
(function): The function to be scheduled.delay
(number): Time in milisecond, an even number would be better....
(variadic arguments): The arguments pass to your callback.
Returns:
- (object) Timer-object.
Examples:
-- fires after 10 seconds
local tobj = timer.setTimeout(function ()
print('I am fired')
end, 10000)
clearTimeout(tobj)
Removes the timeout timer-object from triggering.
Arguments:
tobj
(object): Timer-object to remove.
Returns:
- (none) nil
Examples:
local greet = function ()
print('Bonjour!')
end
local tmout = timer.setTimeout(greet, 5000)
timer.clearTimeout(tmout)
setInterval(callback, delay, ...)
Schedules a callback to be executed every delay
ms. This API returns a timer-object(tobj
) for possible use with clearInterval(). You can also pass parameters to the callback via the variadic arguments.
If delay
is larger than 2147483647 ms (~25 days) or less than 2, nodemcu-timer will use 2 as the delay
.
Arguments:
callback
(function): The function to be scheduled.delay
(number): Time in milisecond, an even number would be better....
(variadic arguments): The arguments pass to your callback.
Returns:
- (object) Timer-object.
Examples:
local tobj = timer.setInterval(function ()
print('Hello')
end, 2000)
clearInterval(tobj)
Removes a time-object of interval from repeatly triggering.
Arguments:
tobj
(object): Timed-object to remove.
Returns:
- (none) nil
Examples:
local count = 0
local repeater = timer.setInterval(function ()
count = count + 1
print(count ' times triggered!')
end, 1000)
timer.clearInterval(repeater)
Be careful if you are trying to cancel the scheduled task inside itself. The following example won't work!
local count = 0
local repeater = timer.setInterval(function ()
count = count + 1
if (count == 5) then
-- This will not work.
timer.clearInterval(repeater)
end
end, 1000)
This is because the repeater
variable is not referencing properly. It is a problem of Lua. All you have to do is to decalre your local variable first, and then assign something to it. Here is an example:
local count = 0
local repeater -- declare first
-- and then assign
repeater = timer.setInterval(function ()
count = count + 1
if (count == 5) then
-- This will work.
timer.clearInterval(repeater)
end
end, 1000)
setImmediate(callback, ...)
Schedules a callback to be immediately executed at next tick, its priority is higher than those tasks set by setTimeout()
and setInterval()
. This API returns a timer-object(tobj
) for possible use with clearImmediate(). You can also pass parameters to the callback via the variadic arguments.
The callbacks for immediate execution enqueues in the order in which they were created. All immediate callbacks in the queue will be invoked right away when a tick fires. If you queue an immediate callback from inside an executing callback, that immediate callback won't be invoked until the next tick comes. Remember that do not shcedule a long task for immediate execution as possible.
Arguments:
callback
(function): The function to be scheduled....
(variadic arguments): The arguments pass to your callback.
Returns:
- (object) Timer-object.
Examples:
local tobj = timer.setImmediate(function ()
print(gpio.read(0))
end)
clearImmediate(tobj)
Removes an immediate time-object from triggering.
Arguments:
tobj
(object): Timed-object to remove.
Returns:
- (none) nil
Examples:
local tobj = timer.setImmediate(function ()
print(gpio.read(0))
end)
timer.clearImmediate(tobj)
set(tid)
Change the internal timer accroding to the specified timer id (default is 6). Be aware of that all callbacks in queue will be cleared when you change to a new timer. It is suggested to use this function within your code of initialization only.
You can use the property timer.id
to know which timer is used as an internal one.
Arguments:
tid
(number): Id of the timer to use. If it is the same with the current id, nothing will happen.
Returns:
- (none) nil
Examples:
print(timer.id) -- 6
timer.set(2)
print(timer.id) -- 2
4. Example: LED Blinks
- The first old-fashioned example is to repeatly turn on an LED for 1 second and turn if off for another second. (my LED is configured with active-low to gpio)
local timer = require 'timer'
local LED_PIN1 = 0
gpio.mode(LED_PIN1, gpio.OUTPUT)
local sw1 = true
timer.setInterval(function ()
if (sw1) then
gpio.write(LED_PIN1, gpio.LOW)
else
gpio.write(LED_PIN1, gpio.HIGH)
end
sw1 = not sw1
end, 1000)
- The second one shows a generic blinkLED() function used to blink LEDs. It's reentrant and you don't have to worry about which timer is available. Instead, what you have to do is to manage a time-object(a scheduled task) and not the timer itself.
This example drives three LEDs that blink with different rate and repeat with different times. Each of them is triggered at 1234ms, 3528ms, and 5104ms after scheduled. Try to schedule these simple blinking things withtmr
, and you may find that it is really a pain in the ass. This is why I made nodemcu-timer to shcedule things - to fix my own ass.
local timer = require 'timer'
local LED_PIN1, LED_PIN2, LED_PIN3 = 0, 1, 2
gpio.mode(LED_PIN1, gpio.OUTPUT)
gpio.mode(LED_PIN2, gpio.OUTPUT)
gpio.mode(LED_PIN3, gpio.OUTPUT)
function blinkLED(led, times, interval)
local sw, count, tobj = true, 0
tobj = timer.setInterval(function ()
if (sw) then
gpio.write(led, gpio.LOW)
else
gpio.write(led, gpio.HIGH)
count = count + 1
end
sw = not sw
if (count == times) then
timer.clearInterval(tobj)
gpio.write(led, gpio.HIGH)
end
end, interval)
end
timer.setTimeout(function ()
blinkLED(LED_PIN1, 5, 560)
end, 1234)
timer.setTimeout(function ()
blinkLED(LED_PIN2, 3, 1024)
end, 3528)
timer.setTimeout(function ()
blinkLED(LED_PIN3, 10, 200)
end, 5104)