Saltar al contenido principal

Módulo Timer

Timer llama a una función tuya pasado un tiempo (after) o cada cierto tiempo (every): para un aviso, un parpadeo, leer un sensor de vez en cuando. El tiempo lo cuenta el sistema por su cuenta, pero la función la ejecuta tu programa, entre una instrucción y otra: no corre en paralelo con él (ver Cuándo se ejecuta).

Captura del módulo Timer de JARU para programar callbacks, tareas periódicas y retardos temporizados

use Timer

Plataformas​

Funciona igual en Windows y en todas las placas. Hay hasta 32 timers a la vez: con los 32 en uso, after() y every() devuelven un valor de error y no crean nada.

Funciones​

FunciónQué haceDevuelve
after(ms, función, dato)Llama a la función una vez, pasados ms milisegundos.El número del timer
every(ms, función, dato)Llama a la función cada ms milisegundos.El número del timer
clear(id)Para un timer.true

Los errores no se lanzan: se devuelven, como valor:

  • con un número de argumentos que no es el suyo, ERR_INVALID_ARG_NUMBER;
  • con un tiempo que no es un número, algo que no es una función o un dato que no es un número, ERR_TYPE_MISMATCH;
  • sin sitio para otro timer: after() devuelve ERR_INDEX_OUT_OF_RANGE, y every(), ERR_OUT_OF_MEMORY.

try no captura esos errores, y en un if un valor de error cuenta como verdadero. Un número de timer siempre es mayor que 0: compruébalo con id > 0. Más sobre los valores de error en Errores y excepciones.

after​

after(ms, función, dato) llama a la función una vez, pasados ms milisegundos. Devuelve el número del timer.

use Timer

var hecho = false

func aviso(id, periodico, dato)
println("Ya: timer ", id, ", periódico ", periodico, ", dato ", dato)
hecho = true
end

var id = Timer.after(500, aviso, 7)
println("Creado el timer ", id)

// El programa tiene que seguir vivo para que salte
while (not hecho)
pause(10)
end

Lo que escribe:

Creado el timer 1
Ya: timer 1, periódico false, dato 7
ParámetroQué es
msLos milisegundos que espera. Admite decimales, pero cuenta la parte entera.
funciónLa función a la que llama: ver La función del timer.
datoOpcional: un número que recibe la función. Con decimales, se queda la parte entera (2.7 llega como 2). De serie, 0.

Después de llamar a la función, el timer se borra solo: no hace falta clear().

every​

every(ms, función, dato) llama a la función cada ms milisegundos, hasta que la paras con clear(). Los parámetros son los de after(). Devuelve el número del timer.

use Timer

var veces = 0

func tic(id, periodico, dato)
veces = veces + 1
println("Tic ", veces)
if (veces == 3) then
Timer.clear(id) // se para a sí mismo
end
end

Timer.every(200, tic)

while (veces < 3)
pause(10)
end

Lo que escribe:

Tic 1
Tic 2
Tic 3

Usa un intervalo de 1 ms o más: con 0, en Windows salta una sola vez.

clear​

clear(id) para el timer y lo borra: su función ya no se llama, aunque tuviera un disparo esperando. Devuelve true, también si el timer no existe o ya había terminado. Un id que no es un entero devuelve ERR_TYPE_MISMATCH.

use Timer

func nunca(id, periodico, dato)
println("Esto no sale")
end

var id = Timer.after(1000, nunca)
println(Timer.clear(id)) // true
println(Timer.clear(id)) // true: ya no existe, y no pasa nada

La función del timer​

La función recibe tres parámetros, siempre:

ParámetroQué es
idEl número del timer que la llama.
periodicotrue si es de every(), false si es de after().
datoEl número que pasaste al crear el timer, o 0.
Tres parámetros, ni uno más ni uno menos

Si la función no tiene exactamente tres parámetros, el timer no la llama, y no avisa: parece que no salta. Aunque no uses los tres, decláralos.

Cómo se pasa la función​

use Timer

var hecho = false

func miAviso(id, periodico, dato)
println("Función")
end

class Semaforo
def init()
this.estado = 0
end
def cambiar(id, periodico, dato)
this.estado = (this.estado + 1) % 3
end
end

var cruce = Semaforo()

// 1) La función, por su nombre, sin comillas
Timer.after(100, miAviso)

// 2) Una lambda, sin declarar ninguna función aparte
Timer.after(200, fn(id, p, d):
println("Lambda")
end)

// 3) Un método de un objeto: se ejecuta con su this
Timer.every(100, cruce.cambiar)

// 4) El nombre de una función global, entre comillas
Timer.after(300, "miAviso")

Timer.after(400, fn(id, p, d):
hecho = true
end)
while (not hecho)
pause(10)
end
  • Con un método (cruce.cambiar), el timer mantiene vivo el objeto mientras exista el timer: no hace falta guardarlo en otra variable. Se suelta al hacer clear(), o al saltar un timer de after().
  • Con el nombre entre comillas, la función se busca al saltar el timer, entre las funciones globales: no vale para métodos. Si no existe, no pasa nada. Las otras tres formas se comprueban al crear el timer.

Cuándo se ejecuta​

El timer mide el tiempo por su cuenta, pero la función la ejecuta la VM dentro de tu programa:

  • Solo mientras el programa sigue vivo. Al terminar el programa se borran sus timers, y lo que faltaba por saltar ya no salta. Por eso los ejemplos acaban en un bucle.
  • En los puntos seguros de la VM: al dar la vuelta un bucle, y al llamar a una función o volver de ella. Nunca dentro de pause(): lo que salte durante una pausa espera a que acabe. Por eso la función puede llegar algo más tarde de lo pedido.
  • De una en una. Mientras corre una función de un timer no empieza otra: las que faltan esperan su turno.

Una función de timer tiene que ser corta: mientras corre, tu programa espera, y los demás timers también.

Ejemplo completo​

Tres LED que se encienden por turnos cada medio segundo, y un segundo timer que lo apaga todo a los 3 segundos. Los LED van en los pines 2, 4 y 5: cámbialos por los de tu placa.

use Timer, GPIO

const LED_ROJO = 2
const LED_VERDE = 4
const LED_AZUL = 5

var estado = 0
var encendido = true

func cicloLuces(id, periodico, dato)
GPIO.write(LED_ROJO, 0)
GPIO.write(LED_VERDE, 0)
GPIO.write(LED_AZUL, 0)
if (estado == 0) then
GPIO.write(LED_ROJO, 1)
elsif (estado == 1) then
GPIO.write(LED_VERDE, 1)
else
GPIO.write(LED_AZUL, 1)
end
estado = (estado + 1) % 3
end

// El dato es el número del timer del ciclo, para pararlo
func apagarTodo(id, periodico, cicloId)
Timer.clear(cicloId)
GPIO.write(LED_ROJO, 0)
GPIO.write(LED_VERDE, 0)
GPIO.write(LED_AZUL, 0)
encendido = false
end

GPIO.pinmode(LED_ROJO, GPIO.OUTPUT)
GPIO.pinmode(LED_VERDE, GPIO.OUTPUT)
GPIO.pinmode(LED_AZUL, GPIO.OUTPUT)

var ciclo = Timer.every(500, cicloLuces)
Timer.after(3000, apagarTodo, ciclo)

while (encendido)
pause(10)
end
println("Sistema apagado")

Trampas y problemas conocidos​

  • El programa tiene que seguir vivo. Si termina, sus timers se borran sin saltar.
  • Nada salta dentro de pause(). Una pausa larga retrasa todas las funciones de timer hasta que acaba.
  • La función tiene que tener tres parámetros. Con otro número, el timer no la llama y no avisa.
  • Los errores se devuelven, no se lanzan. Comprueba el número del timer con id > 0.
  • Como mucho 32 timers. Si pides uno más, te devuelve un valor de error y no lo crea. Si necesitas muchos, usa un solo timer de every() que se ocupe de varias tareas.
  • El dato solo puede ser un número, y llega sin decimales.
  • every() con 0 ms salta una sola vez en Windows: usa 1 ms o más.

Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.