├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── timer.lua ├── timer_min.lua └── timer_simplifier.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 simen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nodemcu-timer 2 | ======================== 3 | Current version: v1.0.0 (stable) 4 | Compatible Lua version: 5.1.x 5 |
6 | 7 | ## Table of Contents 8 | 9 | 1. [Overiew](#Overiew) 10 | 2. [Installation](#Installation) 11 | 3. [APIs](#APIs) 12 | 4. [Example: LED Blinks](#LED) 13 | 14 | 15 | 16 | ## 1. Overview 17 | 18 | The [**nodemcu**](https://github.com/nodemcu/nodemcu-firmware) `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()`. 19 | 20 | **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()`](#API_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. 21 | 22 | If you like to code something in asynchronous style on **nodemcu**, might I sugguest [**lua-events**](https://github.com/simenkid/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. 23 | 24 | [**Note**] 25 | 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. 26 | (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.) 27 | 28 | 29 | ## 2. Installation 30 | 31 | Clone from github 32 | > $ git clone https://github.com/simenkid/nodemcu-timer.git 33 | 34 | or use npm install 35 | > $ npm install nodemcu-timer 36 | 37 | Just include the file `timer.lua` or use the minified one `timer_min.lua` in your project. 38 | 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. 39 | 40 | * FS/Memory footprint 41 | 42 | * timer.lua: ~3.9 KB(file size) / ~20 KB (heap available after loaded) 43 | * timer_min.lua: ~2.0 KB(size) / ~20 KB (heap) 44 | * timer.lc: ~3.2 KB(size) / ~25 KB (heap) 45 | * timer.lc + events.lc: ~17.7 KB (heap) 46 |
47 | 48 | 49 | ## 3. APIs 50 | 51 | * [timer.setTimeout()](#API_setTimeout) 52 | * [timer.clearTimeout()](#API_clearTimeout) 53 | * [timer.setInterval()](#API_setInterval) 54 | * [timer.clearInterval()](#API_clearInterval) 55 | * [timer.setImmediate()](#API_setImmediate) 56 | * [timer.clearImmediate()](#API_clearImmediate) 57 | * [timer.set()](#API_set) 58 | 59 | ************************************************* 60 | ### timer utility 61 | Exposed by `require 'timer'` 62 | 63 | ```lua 64 | local timer = require 'timer' -- or 'timer_min' 65 | ``` 66 | 67 |
68 | 69 | 70 | ### setTimeout(callback, delay[, ...]) 71 | 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. 72 | 73 | 74 | **Arguments:** 75 | 76 | 1. `callback` (_function_): The function to be scheduled. 77 | 2. `delay` (_number_): Time in milisecond, an even number would be better. 78 | 3. `...` (_variadic arguments_): The arguments pass to your callback. 79 | 80 | **Returns:** 81 | 82 | * (_object_) Timer-object. 83 | 84 | **Examples:** 85 | 86 | ```lua 87 | -- fires after 10 seconds 88 | local tobj = timer.setTimeout(function () 89 | print('I am fired') 90 | end, 10000) 91 | 92 | ``` 93 | 94 | ******************************************** 95 | 96 | 97 | ### clearTimeout(tobj) 98 | Removes the timeout timer-object from triggering. 99 | 100 | **Arguments:** 101 | 102 | 1. `tobj` (_object_): Timer-object to remove. 103 | 104 | **Returns:** 105 | 106 | * (_none_) nil 107 | 108 | **Examples:** 109 | 110 | ```lua 111 | local greet = function () 112 | print('Bonjour!') 113 | end 114 | 115 | local tmout = timer.setTimeout(greet, 5000) 116 | 117 | timer.clearTimeout(tmout) 118 | ``` 119 | 120 | ******************************************** 121 | 122 | 123 | ### setInterval(callback, delay, ...) 124 | 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. 125 | 126 | If `delay` is larger than 2147483647 ms (~25 days) or less than 2, **nodemcu-timer** will use 2 as the `delay`. 127 | 128 | **Arguments:** 129 | 130 | 1. `callback` (_function_): The function to be scheduled. 131 | 2. `delay` (_number_): Time in milisecond, an even number would be better. 132 | 3. `...` (_variadic arguments_): The arguments pass to your callback. 133 | 134 | **Returns:** 135 | 136 | * (_object_) Timer-object. 137 | 138 | **Examples:** 139 | 140 | ```lua 141 | local tobj = timer.setInterval(function () 142 | print('Hello') 143 | end, 2000) 144 | ``` 145 | 146 | ******************************************** 147 | 148 | 149 | ### clearInterval(tobj) 150 | Removes a time-object of interval from repeatly triggering. 151 | 152 | **Arguments:** 153 | 154 | 1. `tobj` (_object_): Timed-object to remove. 155 | 156 | **Returns:** 157 | 158 | * (_none_) nil 159 | 160 | **Examples:** 161 | 162 | ```lua 163 | local count = 0 164 | local repeater = timer.setInterval(function () 165 | count = count + 1 166 | print(count ' times triggered!') 167 | end, 1000) 168 | 169 | timer.clearInterval(repeater) 170 | ``` 171 | 172 | Be careful if you are trying to cancel the scheduled task inside itself. The following example won't work! 173 | 174 | ```lua 175 | local count = 0 176 | local repeater = timer.setInterval(function () 177 | count = count + 1 178 | if (count == 5) then 179 | -- This will not work. 180 | timer.clearInterval(repeater) 181 | end 182 | end, 1000) 183 | ``` 184 | 185 | 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: 186 | 187 | ```lua 188 | local count = 0 189 | local repeater -- declare first 190 | -- and then assign 191 | repeater = timer.setInterval(function () 192 | count = count + 1 193 | if (count == 5) then 194 | -- This will work. 195 | timer.clearInterval(repeater) 196 | end 197 | end, 1000) 198 | ``` 199 | 200 | ******************************************** 201 | 202 | 203 | ### setImmediate(callback, ...) 204 | 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. 205 | 206 | 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 schedule a long task for immediate execution as possible. 207 | 208 | 209 | **Arguments:** 210 | 211 | 1. `callback` (_function_): The function to be scheduled. 212 | 2. `...` (_variadic arguments_): The arguments pass to your callback. 213 | 214 | **Returns:** 215 | 216 | * (_object_) Timer-object. 217 | 218 | **Examples:** 219 | 220 | ```lua 221 | local tobj = timer.setImmediate(function () 222 | print(gpio.read(0)) 223 | end) 224 | ``` 225 | 226 | ******************************************** 227 | 228 | 229 | ### clearImmediate(tobj) 230 | Removes an immediate time-object from triggering. 231 | 232 | **Arguments:** 233 | 234 | 1. `tobj` (_object_): Timed-object to remove. 235 | 236 | **Returns:** 237 | 238 | * (_none_) nil 239 | 240 | **Examples:** 241 | 242 | ```lua 243 | local tobj = timer.setImmediate(function () 244 | print(gpio.read(0)) 245 | end) 246 | 247 | timer.clearImmediate(tobj) 248 | ``` 249 | 250 | 251 | ******************************************** 252 | 253 | 254 | ### set(tid) 255 | 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. 256 | 257 | You can use the property `timer.id` to know which timer is used as an internal one. 258 | 259 | **Arguments:** 260 | 261 | 1. `tid` (_number_): Id of the timer to use. If it is the same with the current id, nothing will happen. 262 | 263 | **Returns:** 264 | 265 | * (_none_) nil 266 | 267 | **Examples:** 268 | 269 | ```lua 270 | print(timer.id) -- 6 271 | 272 | timer.set(2) 273 | print(timer.id) -- 2 274 | ``` 275 | 276 | 277 | 278 | ## 4. Example: LED Blinks 279 | 280 | * 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) 281 | 282 | ```lua 283 | local timer = require 'timer' 284 | 285 | local LED_PIN1 = 0 286 | gpio.mode(LED_PIN1, gpio.OUTPUT) 287 | 288 | local sw1 = true 289 | timer.setInterval(function () 290 | if (sw1) then 291 | gpio.write(LED_PIN1, gpio.LOW) 292 | else 293 | gpio.write(LED_PIN1, gpio.HIGH) 294 | end 295 | sw1 = not sw1 296 | end, 1000) 297 | ``` 298 | 299 | * 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. 300 | 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 with `tmr`, 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. 301 | 302 | ```lua 303 | local timer = require 'timer' 304 | 305 | local LED_PIN1, LED_PIN2, LED_PIN3 = 0, 1, 2 306 | 307 | gpio.mode(LED_PIN1, gpio.OUTPUT) 308 | gpio.mode(LED_PIN2, gpio.OUTPUT) 309 | gpio.mode(LED_PIN3, gpio.OUTPUT) 310 | 311 | function blinkLED(led, times, interval) 312 | local sw, count, tobj = true, 0 313 | 314 | tobj = timer.setInterval(function () 315 | if (sw) then 316 | gpio.write(led, gpio.LOW) 317 | else 318 | gpio.write(led, gpio.HIGH) 319 | count = count + 1 320 | end 321 | sw = not sw 322 | 323 | if (count == times) then 324 | timer.clearInterval(tobj) 325 | gpio.write(led, gpio.HIGH) 326 | end 327 | end, interval) 328 | end 329 | 330 | timer.setTimeout(function () 331 | blinkLED(LED_PIN1, 5, 560) 332 | end, 1234) 333 | 334 | timer.setTimeout(function () 335 | blinkLED(LED_PIN2, 3, 1024) 336 | end, 3528) 337 | 338 | timer.setTimeout(function () 339 | blinkLED(LED_PIN3, 10, 200) 340 | end, 5104) 341 | ``` 342 | 343 |
344 | led demo 345 |
346 | ******************************************** 347 |
348 | ## License 349 | MIT 350 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodemcu-timer", 3 | "version": "1.0.1", 4 | "description": "A soft timer utility that allows you to schedule tasks with Javascript(/node.js) style APIs in your NodeMcu project on ESP8266", 5 | "main": "timer.lua", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/simenkid/nodemcu-timer.git" 12 | }, 13 | "keywords": [ 14 | "timer", 15 | "nodemcu", 16 | "esp8266" 17 | ], 18 | "author": "Simen Li", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/simenkid/nodemcu-timer/issues" 22 | }, 23 | "homepage": "https://github.com/simenkid/nodemcu-timer#readme" 24 | } 25 | -------------------------------------------------------------------------------- /timer.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | -- Timer Utility in Node.js Style 3 | -- LICENSE: MIT 4 | -- Simen Li 5 | ------------------------------------------------------------------------------ 6 | local timer = {} 7 | local _exequeImmed, exequeImmed, exeque, ttbl = {}, {}, {}, {} 8 | local tick, lock = 2, false 9 | 10 | timer.enable = false 11 | timer.id = 6 12 | 13 | local function rmEntry(tbl, pred) 14 | if (pred == nil) then return end 15 | local x, len = 0, #tbl 16 | for i = 1, len do 17 | local trusy, idx = false, (i - x) 18 | if (type(pred) == 'function') then trusy = pred(tbl[idx]) 19 | else trusy = tbl[idx] == pred 20 | end 21 | 22 | if (tbl[idx] ~= nil and trusy) then 23 | tbl[idx] = nil 24 | table.remove(tbl, idx) 25 | x = x + 1 26 | end 27 | end 28 | return tbl 29 | end 30 | 31 | local function checkloops() 32 | if (lock) then return else lock = true end 33 | local tobj 34 | for i, tob in ipairs(ttbl) do 35 | tob.delay = tob.delay - tick 36 | if (tob.delay <= tick) then 37 | table.insert(exeque, tob) 38 | ttbl = rmEntry(ttbl, tob) 39 | end 40 | end 41 | 42 | for i = 1, #_exequeImmed do table.insert(exequeImmed, _exequeImmed[i]) end 43 | rmEntry(_exequeImmed, function (v) return v ~= nil end) 44 | 45 | if (#exequeImmed > 0) then -- Immediately execute all targets 46 | for i, immed in ipairs(exequeImmed) do 47 | local status, err = pcall(immed.f, unpack(immed.argus)) 48 | if not (status) then print("Task execution fails: " .. tostring(err)) end 49 | end 50 | rmEntry(exequeImmed, function (v) return v ~= nil end) 51 | elseif (#exeque > 0) then 52 | tobj = exeque[1] 53 | table.remove(exeque, 1) 54 | elseif (#ttbl == 0) then 55 | tmr.stop(timer.id) 56 | timer.enable = false 57 | end 58 | 59 | if (tobj ~= nil) then -- Re-insert the repeatable tobj to table 60 | if (tobj.rp > 0) then 61 | tobj.delay = tobj.rp 62 | if (tobj.delay <= tick) then table.insert(exeque, tobj) 63 | else table.insert(ttbl, tobj) 64 | end 65 | end 66 | local status, err = pcall(tobj.f, unpack(tobj.argus)) 67 | if not (status) then print("Task execution fails: " .. tostring(err)) end 68 | end 69 | lock = false 70 | end 71 | 72 | function timer.start() 73 | tmr.alarm(timer.id, 2, 1, checkloops) -- tid = 6, intvl = 2ms, repeat = 1 74 | timer.enable = true 75 | end 76 | 77 | function timer.stop() 78 | tmr.stop(timer.id) 79 | timer.enable = false 80 | _exequeImmed = rmEntry(_exequeImmed, function (v) return v ~= nil end) 81 | exequeImmed = rmEntry(exequeImmed, function (v) return v ~= nil end) 82 | exeque = rmEntry(exeque, function (v) return v ~= nil end) 83 | ttbl = rmEntry(ttbl, function (v) return v ~= nil end) 84 | end 85 | 86 | function timer.set(tid) 87 | if (tid ~= timer.id) then 88 | timer.stop() 89 | timer.id = tid 90 | timer.start() 91 | end 92 | end 93 | 94 | function timer.setImmediate(fn, ...) 95 | local tobj = { delay = 0, f = fn, rp = 0, argus = {...} } 96 | table.insert(_exequeImmed, tobj) 97 | if (timer.enable == false) then timer.start() end 98 | return tobj 99 | end 100 | 101 | function timer.setTimeout(fn, delay, ...) 102 | local tobj = { delay = delay, f = fn, rp = 0, argus = {...} } 103 | if (delay <= tick or delay > 2147483646) then 104 | tobj.delay = tick 105 | table.insert(exeque, tobj) 106 | else 107 | table.insert(ttbl, tobj) 108 | end 109 | if (timer.enable == false) then timer.start() end 110 | return tobj 111 | end 112 | 113 | function timer.setInterval(fn, delay, ...) 114 | local tobj = timer.setTimeout(fn, delay, ...) 115 | tobj.rp = delay 116 | if (timer.enable == false) then timer.start() end 117 | return tobj 118 | end 119 | 120 | function timer.clearImmediate(tobj) 121 | _exequeImmed = rmEntry(_exequeImmed, tobj) 122 | exequeImmed = rmEntry(exequeImmed, tobj) 123 | end 124 | 125 | function timer.clearTimeout(tobj) 126 | rmEntry(exeque, tobj) 127 | rmEntry(ttbl, tobj) 128 | end 129 | 130 | timer.clearInterval = timer.clearTimeout 131 | 132 | return timer 133 | -------------------------------------------------------------------------------- /timer_min.lua: -------------------------------------------------------------------------------- 1 | local timer={}local a,b,c,d={},{},{},{}local e,g=2,false;timer.enable=false;timer.id=6; 2 | local function h(i,j)local k,l=0,#i;for m=1,l do local n,o=false,m-k;if type(j)=='function'then n=j(i[o])else n=i[o]==j end;if i[o]~=nil and n then i[o]=nil;table.remove(i,o)k=k+1 end end;return i end; 3 | local function p()if g then return else g=true end;local q;for m,r in ipairs(d)do r.delay=r.delay-e;if r.delay<=e then table.insert(c,r)d=h(d,r)end end; 4 | for m=1,#a do table.insert(b,a[m])end;h(a,function(s)return s~=nil end)if#b>0 then for m,t in ipairs(b)do local u,v=pcall(t.f,unpack(t.args))if not u then print("Task execution fails: "..tostring(v))end end; 5 | h(b,function(s)return s~=nil end)elseif#c>0 then q=c[1]table.remove(c,1)elseif#d==0 then tmr.stop(timer.id)timer.enable=false end; 6 | if q~=nil then if q.rp>0 then q.delay=q.rp;if q.delay<=e then table.insert(c,q)else table.insert(d,q)end end;local u,v=pcall(q.f,unpack(q.args))if not u then print("Task execution fails: "..tostring(v))end end; 7 | g=false end;function timer.start()tmr.alarm(timer.id,2,1,p)timer.enable=true end;function timer.stop()tmr.stop(timer.id)timer.enable=false; 8 | a=h(a,function(s)return s~=nil end)b=h(b,function(s)return s~=nil end)c=h(c,function(s)return s~=nil end)d=h(d,function(s)return s~=nil end)end; 9 | function timer.set(w)if w~=timer.id then timer.stop()timer.id=w;timer.start()end end;function timer.setImmediate(x,...)local q={delay=0,f=x,rp=0,args={...}}table.insert(a,q)if timer.enable==false then timer.start()end; 10 | return q end;function timer.setTimeout(x,delay,...)local q={delay=delay,f=x,rp=0,args={...}}if delay<=e or delay>2147483646 then q.delay=e;table.insert(c,q)else table.insert(d,q)end; 11 | if timer.enable==false then timer.start()end;return q end;function timer.setInterval(x,delay,...)local q=timer.setTimeout(x,delay,...)q.rp=delay;if timer.enable==false then timer.start()end;return q end; 12 | function timer.clearImmediate(q)a=h(a,q)b=h(b,q)end;function timer.clearTimeout(q)h(c,q)h(d,q)end;timer.clearInterval=timer.clearTimeout;return timer -------------------------------------------------------------------------------- /timer_simplifier.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | -- Timer Utility in Node.js Style 3 | -- LICENSE: MIT 4 | -- Simen Li 5 | ------------------------------------------------------------------------------ 6 | local tm, lock, ttbl, exec = { id = 6, enable = false, tick = 1000 }, false, {}, {} 7 | 8 | local function rm(tbl, pred) 9 | if (pred == nil) then return tbl end 10 | local x, len = 0, #tbl 11 | for i = 1, len do 12 | local trusy, idx = false, (i - x) 13 | if (type(pred) == 'function') then trusy = pred(tbl[idx]) 14 | else trusy = tbl[idx] == pred 15 | end 16 | 17 | if (trusy) then 18 | table.remove(tbl, idx) 19 | x = x + 1 20 | end 21 | end 22 | return tbl 23 | end 24 | 25 | local function chk() 26 | if (lock) then return else lock = true end 27 | if (#ttbl == 0) then tm.stop() return end 28 | 29 | for i, tob in ipairs(ttbl) do 30 | tob.delay = tob.delay - 1 31 | if (tob.delay == 0) then 32 | if (tob.rp > 0) then tob.delay = tob.rp end 33 | table.insert(exec, tob) 34 | end 35 | end 36 | 37 | for ii, tt in ipairs(exec) do 38 | rm(ttbl, tt) 39 | local status, err = pcall(tt.f, unpack(tt.argus)) 40 | if not (status) then print("Task execution fails: " .. tostring(err)) end 41 | if (tt.delay > 0) then table.insert(ttbl, tt) end 42 | exec[ii] = nil 43 | end 44 | lock = false 45 | end 46 | 47 | function tm.start() 48 | tmr.alarm(tm.id, tm.tick, 1, chk) -- tid = 6, intvl = 2ms, repeat = 1 49 | tm.enable = true 50 | end 51 | 52 | function tm.stop() 53 | tmr.stop(tm.id) 54 | tm.enable = false 55 | ttbl = rm(ttbl, function (v) return v ~= nil end) 56 | lock = false 57 | end 58 | 59 | function tm.set(tid) 60 | if (tid ~= tm.id) then 61 | tm.stop() 62 | tm.id = tid 63 | tm.start() 64 | end 65 | end 66 | 67 | function tm.setTimeout(fn, delay, ...) 68 | local tobj = { delay = delay, f = fn, rp = 0, argus = {...} } 69 | if (delay < 2) then tobj.delay = 1 end 70 | 71 | table.insert(ttbl, tobj) 72 | if (not tm.enable) then tm.start() end 73 | return tobj 74 | end 75 | 76 | function tm.setInterval(fn, delay, ...) 77 | local tobj = tm.setTimeout(fn, delay, ...) 78 | tobj.rp = tobj.delay 79 | return tobj 80 | end 81 | 82 | function tm.clear(tobj) 83 | tobj.rp = 0 84 | rm(exec, tobj) 85 | rm(ttbl, tobj) 86 | end 87 | 88 | return tm 89 | --------------------------------------------------------------------------------