Timer module
Timer calls one of your functions after a while (after) or every so often (every): for a reminder, a blink, reading a sensor now and then. The system keeps the time by itself, but your program runs the function, between one instruction and the next: it does not run in parallel with it (see When it runs).

use Timer
Platforms
It works the same on Windows and on every board. There can be up to 32 timers at once: with the 32 in use, after() and every() return an error value and create nothing.
Functions
| Function | What it does | Returns |
|---|---|---|
after(ms, function, data) | Calls the function once, after ms milliseconds. | The timer number |
every(ms, function, data) | Calls the function every ms milliseconds. | The timer number |
clear(id) | Stops a timer. | true |
The errors are not thrown: they are returned, as a value:
- with the wrong number of arguments,
ERR_INVALID_ARG_NUMBER; - with a time that is not a number, something that is not a function or data that is not a number,
ERR_TYPE_MISMATCH; - with no room for another timer:
after()returnsERR_INDEX_OUT_OF_RANGE, andevery(),ERR_OUT_OF_MEMORY.
try does not catch those errors, and in an if an error value counts as true. A timer number is always greater than 0: check it with id > 0. More on error values in Errors and exceptions.
after
after(ms, function, data) calls the function once, after ms milliseconds. It returns the timer number.
use Timer
var done = false
func notice(id, periodic, data)
println("Now: timer ", id, ", periodic ", periodic, ", data ", data)
done = true
end
var id = Timer.after(500, notice, 7)
println("Created timer ", id)
// The program has to stay alive for it to fire
while (not done)
pause(10)
end
What it prints:
Created timer 1
Now: timer 1, periodic false, data 7
| Parameter | What it is |
|---|---|
ms | The milliseconds it waits. It takes decimals, but only the whole part counts. |
function | The function it calls: see The timer function. |
data | Optional: a number the function receives. With decimals, the whole part stays (2.7 arrives as 2). By default, 0. |
After calling the function, the timer deletes itself: there is no need for clear().
every
every(ms, function, data) calls the function every ms milliseconds, until you stop it with clear(). The parameters are those of after(). It returns the timer number.
use Timer
var count = 0
func tick(id, periodic, data)
count = count + 1
println("Tick ", count)
if (count == 3) then
Timer.clear(id) // it stops itself
end
end
Timer.every(200, tick)
while (count < 3)
pause(10)
end
What it prints:
Tick 1
Tick 2
Tick 3
Use an interval of 1 ms or more: with 0, on Windows it fires only once.
clear
clear(id) stops the timer and deletes it: its function is no longer called, even if a firing was waiting. It returns true, also if the timer does not exist or had already finished. An id that is not an integer returns ERR_TYPE_MISMATCH.
use Timer
func never(id, periodic, data)
println("This does not show")
end
var id = Timer.after(1000, never)
println(Timer.clear(id)) // true
println(Timer.clear(id)) // true: it no longer exists, and nothing happens
The timer function
The function always receives three parameters:
| Parameter | What it is |
|---|---|
id | The number of the timer that calls it. |
periodic | true if it comes from every(), false if from after(). |
data | The number you passed when creating the timer, or 0. |
If the function does not have exactly three parameters, the timer does not call it, and it does not warn you: it looks as if it never fires. Even if you do not use all three, declare them.
How to pass the function
use Timer
var done = false
func myNotice(id, periodic, data)
println("Function")
end
class TrafficLight
def init()
this.state = 0
end
def change(id, periodic, data)
this.state = (this.state + 1) % 3
end
end
var crossing = TrafficLight()
// 1) The function, by its name, without quotes
Timer.after(100, myNotice)
// 2) A lambda, without declaring a separate function
Timer.after(200, fn(id, p, d):
println("Lambda")
end)
// 3) A method of an object: it runs with its this
Timer.every(100, crossing.change)
// 4) The name of a global function, in quotes
Timer.after(300, "myNotice")
Timer.after(400, fn(id, p, d):
done = true
end)
while (not done)
pause(10)
end
- With a method (
crossing.change), the timer keeps the object alive while the timer exists: there is no need to keep it in another variable. It is let go withclear(), or when anafter()timer fires. - With the name in quotes, the function is looked up when the timer fires, among the global functions: it does not work for methods. If it does not exist, nothing happens. The other three forms are checked when the timer is created.
When it runs
The timer keeps the time by itself, but the VM runs the function inside your program:
- Only while the program is still alive. When the program ends its timers are deleted, and whatever had not fired yet no longer fires. That is why the examples end in a loop.
- At the VM's safe points: when a loop goes round, and when a function is called or returns. Never inside
pause(): whatever fires during a pause waits until it ends. That is why the function can arrive somewhat later than asked. - One at a time. While a timer function runs, no other one starts: the rest wait their turn.
A timer function has to be short: while it runs, your program waits, and so do the other timers.
Full example
Three LEDs that light up in turn every half second, and a second timer that turns everything off after 3 seconds. The LEDs are on pins 2, 4 and 5: change them for those of your board.
use Timer, GPIO
const LED_RED = 2
const LED_GREEN = 4
const LED_BLUE = 5
var state = 0
var running = true
func lightCycle(id, periodic, data)
GPIO.write(LED_RED, 0)
GPIO.write(LED_GREEN, 0)
GPIO.write(LED_BLUE, 0)
if (state == 0) then
GPIO.write(LED_RED, 1)
elsif (state == 1) then
GPIO.write(LED_GREEN, 1)
else
GPIO.write(LED_BLUE, 1)
end
state = (state + 1) % 3
end
// The data is the number of the cycle timer, to stop it
func turnAllOff(id, periodic, cycleId)
Timer.clear(cycleId)
GPIO.write(LED_RED, 0)
GPIO.write(LED_GREEN, 0)
GPIO.write(LED_BLUE, 0)
running = false
end
GPIO.pinmode(LED_RED, GPIO.OUTPUT)
GPIO.pinmode(LED_GREEN, GPIO.OUTPUT)
GPIO.pinmode(LED_BLUE, GPIO.OUTPUT)
var cycle = Timer.every(500, lightCycle)
Timer.after(3000, turnAllOff, cycle)
while (running)
pause(10)
end
println("System off")
Pitfalls and known issues
- The program has to stay alive. If it ends, its timers are deleted without firing.
- Nothing fires inside
pause(). A long pause delays every timer function until it ends. - The function has to have three parameters. With any other number, the timer does not call it and does not warn you.
- Errors are returned, not thrown. Check the timer number with
id > 0. - At most 32 timers. If you ask for one more, you get an error value and it isn't created. If you need many, use a single
every()timer that takes care of several tasks. - The data can only be a number, and it arrives without decimals.
every()with 0 ms fires only once on Windows: use 1 ms or more.
All the known issues of JARU, together, are in Known issues.