PLC Programming SAPLC ProgrammingSOUTH AFRICA

learn · South Africa

PLC timers and counters — TON, TOF, RTO, CTU, CTD

A practical PLC timers and counters tutorial. TON, TOF, RTO behaviour, CTU / CTD counters, common bugs, and worked examples in a free browser simulator.

PLC timers and counters are the second thing you learn after contacts and coils, and they are where the gap between "I can read a rung" and "I can write a program that survives a Monday morning" actually opens up. The instructions look small on the page — a rectangle with three or four parameters — but each one carries a behaviour that most newcomers have to get burned by once before they remember it. This tutorial walks the three timer types (TON, TOF, RTO), the three counters (CTU, CTD, CTUD), the time-base traps that bite people on every major brand, and the bugs we have watched learners chase for hours when they could have spotted them in two minutes.

Same approach as the ladder logic page. Concrete instructions. Real worked examples in ASCII rungs you can copy into the simulator. One opinion stated outright: if you cannot explain why RTO needs a manual reset, you will write your first hard-to-find bug within the week. We will make sure you can.

Try the simulator →

Three timers, three different behaviours

Every modern PLC ships three timer instructions. The names differ between brands but the behaviours are the same and they are defined in IEC 61131-3, the cross-vendor standard that pins down what TON, TOF and RTO mean. Read past the brand vocabulary and the underlying behaviours are identical.

TON — Timer On-Delay. Input goes true, the accumulator counts up to the preset, the Done bit flips on when ACC reaches PRE. Input goes false at any point and the accumulator snaps back to zero, Done bit clears. The mental model is a delay: "wait this long after the start condition becomes true, then turn something on." Used for things like "run the conveyor for two seconds before allowing the diverter to open," or "wait 500 ms after a sensor pulse before reading a measurement to let the value settle."

In words, the TON timing diagram looks like this. Input goes 0→1 at t=0. ACC starts climbing at the rate of the time base — one tick per millisecond, say. ACC reaches PRE at t=PRE. Done bit goes 0→1 at that same instant and stays at 1 as long as the input stays true. Input goes 1→0 — Done bit immediately drops to 0 and ACC snaps to 0. There is no carry-over and no memory. Every fresh true edge restarts the count from zero.

TOF — Timer Off-Delay. The mirror image. Input goes false, the accumulator counts up to preset. Done bit is true while the timer is still running but the input is gone — meaning Done is true between the input falling edge and ACC reaching PRE. Input goes true at any point and ACC resets, Done clears. Used for "keep the cooling fan running for thirty seconds after the heater turns off," or "hold the conveyor diverter open for two seconds after the last item passes the sensor."

The TOF diagram. Input is 1, ACC is 0, Done is 1 (some vendors define Done differently — read your manual; the IEC behaviour is Done = NOT(input AND ACC≥PRE)). Input drops 1→0 at t=0. ACC starts climbing. Until ACC = PRE, the timer is "still running" and Done remains in its delayed state. At ACC = PRE the timer expires and Done flips. If the input goes back to 1 mid-count, ACC snaps to 0 and the timer resets.

RTO — Retentive Timer On-Delay. Same as TON in the on-direction — input true, ACC climbs, Done at PRE. The difference is the off-direction. When the input goes false, the accumulator does not reset. It freezes at whatever value it had reached. When the input next goes true, the accumulator picks up from that frozen value and continues climbing. The only way to clear an RTO is an explicit reset — the RES instruction on Allen-Bradley, the R input on a Siemens IEC timer, an explicit assignment on CODESYS.

That difference is why RTO exists. It lets you accumulate run-time across multiple periods — for example, "this motor has been running cumulatively for X hours since the last service interval." TON cannot do that. TON forgets the moment its input goes false. RTO remembers, and remembering is exactly the trap. We come back to it in section four.

TON in detail

Allen-Bradley CompactLogix syntax. The timer is a structured tag of type TIMER with members .PRE (preset, DINT in milliseconds), .ACC (accumulator, DINT in milliseconds), .EN (enable bit, true while the input is true), .TT (timer-timing bit, true while ACC is climbing toward PRE), and .DN (done bit, true when ACC ≥ PRE).

   |   StartConv                          T_Conveyor
   |---| |--------------------------------+TON+--+
   |                                      |Pre 5000
   |                                      |Acc xxxx
   |                                      +----+
   |
   |   T_Conveyor.DN                            DivertOK
   |---| |---------------------------------------( )---|

Read the rung. When StartConv is true, the TON instruction T_Conveyor is enabled. .EN goes true. .TT goes true and stays true while .ACC climbs from 0 toward 5000 ms. When .ACC reaches 5000, .DN goes true, .TT goes false, the second rung sees T_Conveyor.DN as true, and DivertOK energises. From the operator's point of view: hit start, conveyor begins, and after a five-second settle the divert valve is allowed to open. Release StartConv at any point and the timer snaps back — .ACC goes to 0, .DN goes false, DivertOK drops on the next scan.

A practical thing to notice: .TT and .DN are mutually exclusive in normal operation. While timing, TT is true and DN is false. After done, DN is true and TT is false. This matters because .TT is the bit you use for "during the delay" logic — for example flashing a warning lamp while the conveyor is settling — and .DN is the bit you use for "delay finished" logic.

TOF in detail

Same structured tag, different behaviour. The classic example is fan run-on after a process stop.

   |   ProcessRun                         T_FanRunOn
   |---| |--------------------------------+TOF+--+
   |                                      |Pre 30000
   |                                      |Acc xxxx
   |                                      +----+
   |
   |   T_FanRunOn.DN                            CoolingFan
   |---| |---------------------------------------( )---|

While ProcessRun is true, the TOF is in its rest state. The exact bit semantics are vendor-specific so check your manual, but the practical contract on Rockwell is: while the input is true, .DN is true and .ACC is 0. When ProcessRun falls 1→0, .ACC starts climbing. .DN stays true during the count. When .ACC reaches .PRE, .DN flips false. So CoolingFan runs while ProcessRun is true, continues to run for thirty seconds after ProcessRun drops, then de-energises. If ProcessRun comes back true mid-count, .ACC snaps to 0 and the cooling fan keeps running uninterrupted.

Use TOF whenever you want a graceful run-on after a stop. Pump cool-down. Fan run-on after the burner cuts out. Conveyor coast-time after the last product clears the line. Mechanical inertia is real — solenoids and contactors do not stop instantly — and TOF is the instruction for letting your software respect that.

RTO — the retentive trap

Here is the section every new tech ought to read twice. RTO is TON with one difference and that difference will eat your week if you do not internalise it.

   |   MotorRunning                       T_MotorHours
   |---| |--------------------------------+RTO+--+
   |                                      |Pre 3600000
   |                                      |Acc xxxx
   |                                      +----+
   |
   |   ServiceReset                        T_MotorHours
   |---| |---------------------------------( RES )---|
   |
   |   T_MotorHours.DN                          ServiceDueLamp
   |---| |---------------------------------------( )---|

The first rung accumulates run-time on the motor in milliseconds. With a preset of 3,600,000 ms (one hour), the timer fires its done bit after one cumulative hour of running. The motor stops, restarts, stops, restarts — across all those cycles .ACC keeps climbing. That is the point.

The second rung is the one nobody adds the first time. RTO requires an explicit reset. In Allen-Bradley land the RES instruction zeroes both .ACC and .DN for the timer it points at. Without the RES rung, the timer hits one hour of cumulative run-time, the lamp comes on, and there is no software path to clear it. Maintenance presses the service-acknowledge button on the HMI and nothing happens.

The textbook bug — and we have watched it ship to a real plant — is the new tech who reaches for RTO when they wanted TON. They wanted "delay 5 seconds before opening the divert." They picked RTO out of the dropdown because the name sounded similar. They forgot the RES. The first time the plant ran, the divert opened after five seconds and worked fine. The second time, the cumulative .ACC was already past 5000 ms from the previous run, and the divert opened instantly the moment the start condition went true. Worse than instantly — the lingering done bit meant the divert had been open the whole time the line was idle. The fix is one extra rung with an RES instruction and a clear understanding of which timer needs accumulator memory and which does not.

The rule. TON for delays. RTO for cumulative run-time meters and similar memory-required cases. RTO with no matching RES is always a bug. If you can write the same logic with TON, prefer TON. Reach for RTO only when you genuinely need the accumulator to survive across input transitions.

Time bases — milliseconds vs seconds

This section catches everyone, including techs with five years of experience switching brands.

Allen-Bradley CompactLogix and ControlLogix. Time is in milliseconds. The TIMER tag's .PRE and .ACC are DINT values measured in ms. Five seconds is 5000. One hour is 3600000. There is no separate time base parameter — it is always ms on the modern Logix family.

Siemens S7. Two systems coexist depending on TIA Portal version and CPU type. Legacy S5 timers use S5TIME literals like S5T#5s or S5T#500ms and carry their own timing infrastructure inside the CPU. IEC timers — the ones you should be using on S7-1200 and S7-1500 — take a TIME value with literals like T#5s, T#500ms, T#1m30s. Siemens documents the IEC TON / TOF / TONR instructions in the S7-1200/1500 system manual. If you mix S5 and IEC timers in the same project you can usually get away with it, but the two have different reset semantics and different memory footprints. Pick one and stay there.

Mitsubishi FX and Q series. This is the classic source of newcomer fault. The timer's time base is determined by the timer number range. On the FX series, T0 to T199 are 100 ms timers — a preset of 50 means 5.0 seconds, not 50 ms. T200 to T245 are 10 ms timers — a preset of 50 means 0.5 seconds. T246 to T255 are retentive 1 ms timers. So the same numeric preset means different wall-clock durations depending on which timer number you used, and copy-pasting a preset value from a 100 ms timer into a 10 ms timer silently changes the behaviour by a factor of ten. New techs miss this because the timer number looks like a name, not a parameter. Treat it as a parameter. Always check the manual for the exact ranges on your specific FX or Q model — they shifted between generations.

CODESYS / IEC 61131-3. The standard TON function block takes a TIME input with literals like T#5s. The behaviour is uniform across CODESYS-based PLCs — Beckhoff TwinCAT, Wago, Schneider M218 and so on. If you write IEC-standard ladder you write the same TON and it ports.

The opinion: when you start a new project, write down the time-base convention on the first page of your design document and stick to it. Mixing milliseconds and seconds in the same program is one of those things that works fine for a year and then fails at a customer site at 3am because somebody patched a value with a calculator that assumed the wrong unit.

Counters — CTU, CTD, CTUD

Counters are simpler than timers because there is no time base. They count edges.

CTU — Count Up. Each rising edge of the input increments the accumulator. When the accumulator reaches the preset, the done bit goes true. The accumulator continues to count past the preset (subject to the overflow rules in the next section). Used for "count items past a sensor and divert the tenth one," or "count operator button presses for a maintenance log."

CTD — Count Down. Each rising edge decrements. The done bit fires at zero or at a target depending on the brand. Less common in everyday work. Mostly used as half of an up/down pair.

CTUD — Count Up/Down. A single counter with two edge inputs. CU rising edge increments, CD rising edge decrements. Useful for parking-bay style logic — count cars in, count cars out, the accumulator reflects the current count.

A worked CTU example, Allen-Bradley syntax. A photo-electric sensor pulses every time a bottle passes a station. Every tenth bottle, fire a divert solenoid for one scan and reset the counter.

   |   PE_Sensor                          C_BottleCount
   |---| |--------------------------------+CTU+--+
   |                                      |Pre 10
   |                                      |Acc xxxx
   |                                      +----+
   |
   |   C_BottleCount.DN                          DivertSol
   |---| |---------------------------------------( )---|
   |
   |   C_BottleCount.DN                          C_BottleCount
   |---| |---------------------------------( RES )---|

When PE_Sensor rising-edges, .ACC increments. At ACC = 10, .DN goes true. The second rung fires DivertSol. The third rung resets the counter on the same scan, so on the next scan ACC is back to 0 and DN is back to false, ready for the next ten. Tight, simple, and a reasonable starting pattern for any "every Nth event" requirement.

The Siemens IEC equivalent is the CTU function block with CU, R, PV, CV and Q parameters where R is the synchronous reset input — wire it to your reset condition directly on the function block and there is no separate RES rung needed. The behaviour is the same; the ergonomics differ.

Counter reset and overflow

Reset behaviour varies and you have to know the difference.

Allen-Bradley. The RES instruction zeroes .ACC and clears .DN. Without an RES rung somewhere, a CTU at preset stays at preset and .DN stays true forever. The pattern in the previous section — done bit feeding both the action coil and the RES rung — is the standard self-clearing counter idiom.

Siemens IEC. The function block has a synchronous R input. Wire your reset condition to it and the counter clears whenever R is true at the start of the scan.

Mitsubishi. The RST instruction clears the counter device. Behaviour mirrors AB.

CODESYS. The IEC CTU function block takes a RESET input. Same as Siemens IEC.

Overflow is the case nobody plans for and a few people get bitten by. What happens when a CTU keeps counting past its preset and reaches the upper limit of its data type?

The honest answer is "it depends, verify before assuming." On Allen-Bradley the .ACC is a DINT — signed 32-bit integer — and counting past 2,147,483,647 wraps to negative on most CPU firmware versions. On Siemens IEC counters with INT CV the range is −32,768 to 32,767 and behaviour at the boundary is documented as saturation in some references and wrap in others depending on firmware. On Mitsubishi the range depends on the counter device class — 16-bit counters saturate, 32-bit counters can wrap. Modern CODESYS-based runtimes saturate by default but the behaviour is configurable.

Do not trust your memory on this. If your application can plausibly count past the preset without resetting — and many can, especially totalisers and lifetime counters — write a small test program, leave it running, and verify the actual behaviour on the actual CPU and firmware you will deploy on. Anything else is wishful thinking. The right defensive move is to reset counters on a known periodic event — start of shift, start of batch, midnight rollover — well before any conceivable overflow window.

Common bugs and how to find them

Five timer-and-counter bugs that catch almost every new programmer.

RTO without RES. Already covered. Symptom: the lamp stays on after the run-time has ostensibly been acknowledged, or the cumulative timer fires "instantly" on second start because the previous accumulator was preserved. Find it by searching the program for every RTO instruction and confirming each has a matching RES rung with a sensible reset condition. If the project has fifty RTOs and you find one without an RES, you have your bug.

Time-base confusion. "I set the preset to 5 and it fired immediately." On most modern AB and Siemens IEC code, 5 is 5 ms, not 5 seconds. On a Mitsubishi 100 ms timer it is 500 ms. Always read the time base from the manual for the specific timer instruction and PLC model you are using. If a timer fires faster or slower than expected by an order of magnitude, time base is the first thing to check.

Counter reset on the wrong condition. A CTU resetting on the same edge that increments it will miss counts or never advance. A counter with no reset at all will saturate or wrap. Walk the reset path: when does R go true, what is the duration, does the reset condition overlap the count condition? Race conditions hide here.

Comparison-on-equality instead of greater-or-equal. This is the off-by-one waiting to happen. If your downstream logic looks like IF ACC = 10 THEN ... instead of IF ACC ≥ 10 THEN ..., the action fires on exactly one scan. If anything else clears or increments the accumulator on that same scan — and "anything else" includes the RES rung you wrote one section ago — the action may not fire at all on some scans. Use ≥ not =. The counter's own done bit .DN is the safe equivalent because it stays true between PRE and reset.

Timer / counter inside a conditional rung. Putting a TON inside a branch that is sometimes taken and sometimes not means the timer is "enabled" sometimes and "not enabled" other times in the same scan. Behaviour is well-defined per the IEC standard but rarely what the programmer intended. Newcomers often draw a TON inside an IF-equivalent branch expecting the timer to "pause" while the branch is not taken. It does not pause — it resets, because the enable bit went false. If you need a pause-and-resume timer, RTO is the right instruction, with a deliberate gating condition on the input. Do not try to fake it with TON in a conditional path.

The systematic way to find these is to step through your program in the simulator with the watch window open, bringing each timer or counter into view, watching the accumulator, the enable bit, the timer-timing bit, the done bit, and the action bit. Five seconds of watching tells you more than thirty minutes of staring at a printout.

Try it in the simulator

Reading about timers and counters is the first half. The second half is loading them in a simulator, varying the preset, and watching the bits move scan by scan. Open the simulator and load the timer preset. The default rung is a TON with a five-second preset wired to a delayed coil — exactly the rung pictured in the worked example above.

Run it. Toggle the input. Watch .ACC climb in real time, watch .TT go true while it climbs, watch .DN flip when it hits the preset. Now drop the preset to 500 ms and toggle the input again. The timer fires almost immediately. Now raise it to 30000 ms and watch the slow climb. Now interrupt the input mid-count and watch .ACC snap to zero — that is TON behaviour. Switch the instruction to RTO and repeat. Now the accumulator freezes when the input drops and resumes when it returns. Add a RES rung. See the difference.

Then try counters. Load a CTU preset, fire the count input by hand, watch ACC climb to 10, watch the done bit and the divert action fire on the same scan, watch the RES clear it on the next scan. Vary the preset. Try CTUD and watch up-counts and down-counts cancel. Twenty minutes of toggling inputs in the simulator and you will have the timer and counter reflexes that take other learners months to build on real hardware.

When the timer and counter patterns feel automatic, the next step is sequence logic — chained timers driving a state machine, the pattern that runs almost every batch process and pick-and-place line. Build the reflex layer here first.

Try the simulator →

By PLC Programming SA · Last updated 2026-05-05