Timers
Timers are fundamental building blocks in TSharkRex recipes. They let you introduce time-based
behavior: delays before activation, hold periods after deactivation, periodic polling, and
debouncing of noisy signals. TSharkRex provides two timer types - TON
(on-delay) and TOF (off-delay) - which cover the vast majority of
timing needs.
TOF over TON + manual reset when
appropriate, write expressions inline rather than storing intermediate variables, and avoid
allocating timers you do not need.
TON - On-Delay Timer
TON delays the activation of its output. The output Q goes
TRUE only after the input IN has been continuously TRUE
for the duration specified by PT. If IN goes FALSE
before the time elapses, the timer resets and Q remains FALSE.
Parameters
| Parameter | Type | Direction | Description |
|---|---|---|---|
IN |
BOOL | Input | Timer input - starts timing when TRUE |
PT |
UDINT | Input | Preset time in milliseconds (use T# literals) |
Q |
BOOL | Output | TRUE when IN has been TRUE for at least PT |
ET |
UDINT | Output | Elapsed time in milliseconds (counts up from 0 to PT) |
Timing Behavior
IN: _____|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|__________
|<--- PT --->|
Q: _____|_____________|‾‾‾‾‾|__________
ET: 0 0 ... PT PT 0
When IN goes TRUE, ET starts counting up. When
ET reaches PT, Q goes TRUE. When
IN goes FALSE, both Q and ET reset
to zero immediately.
Basic Example
Wait 2 seconds after ignition is detected before enabling the CAN controller:
VAR
TMR_STARTUP : TON;
SIGNAL_TANDNING : BOOL;
canReady : BOOL;
END_VAR;
TMR_STARTUP(IN := SIGNAL_TANDNING, PT := T#2s);
canReady := TMR_STARTUP.Q;
Reset Example
Reset the timer by driving IN to FALSE:
VAR
TMR_DELAY : TON;
startCondition : BOOL;
resetCondition : BOOL;
END_VAR;
// Timer runs only when start is TRUE and reset is FALSE
TMR_DELAY(IN := startCondition AND NOT resetCondition, PT := T#5s);
Self-Resetting Periodic Timer
One of the most common patterns in TSharkRex is the self-resetting timer, which creates periodic execution. Feed the inverted output back into the input:
VAR
TMR_PERIODIC : TON;
END_VAR;
TMR_PERIODIC(IN := NOT TMR_PERIODIC.Q, PT := T#1s);
IF TMR_PERIODIC.Q THEN
// This block executes every 1 second
TMR_PERIODIC(IN := FALSE); // Reset for next cycle
END_IF;
How it works:
TMR_PERIODIC.Qstarts asFALSE, soNOT FALSE = TRUE- the timer starts.- After 1 second,
QgoesTRUE. - The
IFbody executes and immediately resets the timer withIN := FALSE. - On the next scan,
QisFALSEagain, so the cycle repeats.
Practical Example: CAN DID Polling
VAR
TMR_POLL : TON;
CANSEND : CAN_TX;
SENDDATA : ARRAY[0..7] OF BYTE;
END_VAR;
// Poll vehicle speed every 200ms
TMR_POLL(IN := NOT TMR_POLL.Q, PT := T#200ms);
IF TMR_POLL.Q THEN
SENDDATA[0] := 0x03;
SENDDATA[1] := 0x22;
SENDDATA[2] := 0xF4;
SENDDATA[3] := 0x0D;
CANSEND(ENABLE := TRUE, ID := 0x7E0, EXT := FALSE, DATALENGTH := 8, DATA := SENDDATA);
TMR_POLL(IN := FALSE);
END_IF;
TOF - Off-Delay Timer
TOF is the opposite of TON: it delays the deactivation
of its output. When IN goes TRUE, Q goes
TRUE immediately. When IN goes FALSE, Q
remains TRUE for the duration PT before turning off.
Parameters
| Parameter | Type | Direction | Description |
|---|---|---|---|
IN |
BOOL | Input | Timer input - output follows immediately on rising edge |
PT |
UDINT | Input | Off-delay time in milliseconds |
Q |
BOOL | Output | TRUE while IN is TRUE, then stays TRUE for PT after IN goes FALSE |
ET |
UDINT | Output | Elapsed time since IN went FALSE (counts up to PT) |
Timing Behavior
IN: _____|‾‾‾‾‾‾|____________________
|<--- PT --->|
Q: _____|‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|____
ET: 0 0 0 ... PT 0
Example: Hold Output After Signal Disappears
Keep an output active for 1 second after the ignition signal goes away - useful for graceful shutdown sequences:
VAR
TMR_DELAY : TOF;
SIGNAL_TANDNING : BOOL;
END_VAR;
TMR_DELAY(IN := SIGNAL_TANDNING, PT := T#1s);
Example: CAN Keep-Alive After Ignition Off
VAR
TMR_KEEPALIVE : TOF;
SIGNAL_TANDNING : BOOL;
canActive : BOOL;
END_VAR;
// Keep CAN communication alive for 5 seconds after ignition off
TMR_KEEPALIVE(IN := SIGNAL_TANDNING, PT := T#5s);
canActive := TMR_KEEPALIVE.Q;
Common Timer Patterns
Debouncing
Noisy signals (mechanical switches, proximity sensors) can rapidly toggle between
TRUE and FALSE. Use TON to filter out transient
spikes:
VAR
TMR_DEBOUNCE : TON;
rawSwitch : BOOL;
stableSwitch : BOOL;
END_VAR;
// Signal must be stable for 50ms before it is accepted
TMR_DEBOUNCE(IN := rawSwitch, PT := T#50ms);
stableSwitch := TMR_DEBOUNCE.Q;
TON and TOF:
VAR
TMR_ON : TON;
TMR_OFF : TOF;
rawSignal : BOOL;
debouncedSignal : BOOL;
END_VAR;
TMR_ON(IN := rawSignal, PT := T#50ms);
TMR_OFF(IN := TMR_ON.Q, PT := T#50ms);
debouncedSignal := TMR_OFF.Q;
Timeout Detection (CAN Bus)
Detect when a CAN response has not arrived within an expected time window. This is critical for error handling - if the ECU does not respond, you should stop waiting and take a fallback action:
VAR
TMR_TIMEOUT : TON;
waitingForResponse : BOOL;
responseReceived : BOOL;
timeout : BOOL;
END_VAR;
TMR_TIMEOUT(IN := waitingForResponse AND NOT responseReceived, PT := T#500ms);
timeout := TMR_TIMEOUT.Q;
IF timeout THEN
// ECU did not respond within 500ms - handle error
waitingForResponse := FALSE;
END_IF;
Periodic Polling
As shown in the self-resetting timer section, periodic
execution is achieved by feeding the inverted output back into the input. Vary the
PT value to control the polling rate:
| Interval | PT Value |
Typical Use |
|---|---|---|
| 50ms | T#50ms |
Fast signals (RPM, wheel speed) |
| 100ms | T#100ms |
Standard DID polling |
| 200ms | T#200ms |
Moderate signals (speed, temperature) |
| 500ms | T#500ms |
Slow signals (battery voltage, ambient temp) |
| 1s | T#1s |
Status checks, heartbeats |
Delayed Shutdown
When the ignition turns off, wait a period before entering sleep mode. This gives time for final CAN messages and graceful cleanup:
VAR
TMR_SHUTDOWN : TON;
SIGNAL_TANDNING : BOOL;
shouldSleep : BOOL;
CANMODE : CAN_MODE;
END_VAR;
// Start shutdown timer when ignition is off
TMR_SHUTDOWN(IN := NOT SIGNAL_TANDNING, PT := T#10s);
shouldSleep := TMR_SHUTDOWN.Q;
IF shouldSleep THEN
CANMODE(MODE := CAN_MODE_SLEEP, BAUDRATE := 500);
END_IF;
CAN_MODE call in the IF block will be called on every scan cycle
once the timer expires. For a production recipe, combine this with an INIT-style
flag or use a state machine to ensure CAN_MODE is called only once.