PLC Programming SAPLC ProgrammingSOUTH AFRICA

learn · South Africa

PLC latching — seal-in, SET / RESET, and SR flip-flops

A practical PLC latching tutorial. Seal-in rungs, OTL / OTU set-reset, IEC SR / RS flip-flops, the priority trap, and worked examples in a free simulator.

PLC latching is the third instruction set you learn, after contacts-and-coils and timers-and-counters, and it is the one that turns a "reads inputs and drives outputs" program into something that actually has state. Every motor starter, every alarm bit, every step in a sequence relies on latching of some kind. The instructions are not complicated on their own. The trap is choosing the right pattern for the situation, and the trap that kills new techs is the priority question — which input wins when both are true at the same time. This tutorial walks the three patterns you will see in the field, the priority trap that is the conceptual heart of the topic, and the edge-trigger discipline that separates clean latch logic from the bug you will spend a Tuesday afternoon chasing.

Concrete instructions. Worked ASCII rungs you can copy into the simulator. One opinion stated outright: if you cannot say in a sentence why a motor-stop should be reset-priority and not set-priority, you should not be writing latching logic on a real machine yet. We will make sure you can.

Try the simulator →

Why you need latching

The motivating problem is the momentary push-button. A start button on a control panel is almost always a momentary contact — push it, it makes; release it, it breaks. The operator's finger touches the button for maybe two hundred milliseconds. The motor needs to keep running until somebody hits stop, which might be eight hours later.

If you wire the start button straight to the motor coil, the motor runs for two hundred milliseconds and stops the instant the operator releases the button. That is not a control system, that is a doorbell. What you need is a piece of memory inside the program that says "yes, the start was pressed, keep the output on until something tells me otherwise." That piece of memory is what the word "latch" refers to. A latch is a single bit of state that holds itself true after the trigger condition has gone away.

There are three patterns for building that bit of state in ladder logic and they are the three patterns this tutorial walks. Pattern one is the seal-in rung — old, universal, works on every PLC ever made. Pattern two is the OTL / OTU pair — Allen-Bradley's set-reset coil pair. Pattern three is the IEC 61131-3 SR / RS function block — a pre-built latch with a configurable priority. Each has a place. Each has a way of going wrong.

Pattern 1 — seal-in (the classic)

The seal-in rung is the canonical latching pattern and it is what every PLC textbook teaches first. The structure is a start contact in series with a stop contact, and a parallel branch of the output's own normally-open contact wrapped around the start.

   |   Start          Stop                          MotorRun
   |---| |--+-------|/|---------------------------( )---|
   |        |
   |   MotorRun
   |---| |--+

Walk it scan by scan. At rest, all bits are 0. Operator hits Start. On the next scan, the rung evaluates left to right. Start is true. The branch passes. Stop is normally-closed, so as long as the stop button is not pressed, the contact is true (think about that for a second — the contact is true because the input wire is energised, and a NC contact passes when the underlying input is true). Coil energises. MotorRun goes 1.

Operator releases the start button. On the next scan, Start is false. The Start contact is open. But the parallel branch — the MotorRun NO contact — sees its own coil bit at 1 from the previous scan, so the branch is true. Current path: through the MotorRun NO contact, through the NC stop contact, to the coil. The coil stays energised. The motor keeps running.

Operator hits Stop. The stop button is wired NC at the field, which means pressing it breaks the input circuit, which makes the input bit go 0. The NC contact in the rung evaluates 0-input as true, so wait — let me restate that, because this is where every newcomer gets confused. A NC button wired NC means: at rest, the button is closed and the input is energised, the input bit reads 1. When the operator presses, the button opens, the input loses power, the input bit reads 0. The NC contact in the program inverts: it is true when the underlying bit is 0. So pressing the stop button makes the input bit go 0, makes the NC program contact go true — wait. That is the opposite of what we want.

Read it again carefully. The standard SA / international convention: stop buttons are wired with NC contacts at the field, and the program reads them as NO contacts. So at rest, the input bit is 1 (button is closed, current flows). The program contact is NO, so it is true when the bit is 1. Pressing the stop button breaks the field circuit, the input bit goes 0, the program NO contact goes false, the rung breaks, the latch drops.

The point — and this is why the wiring convention exists — is fail-safe behaviour. If the wire to the stop button gets cut, or the button itself fails open, the input bit goes to 0, the latch drops, and the motor stops. A broken stop button stops the machine. That is the only acceptable failure mode for a stop. Do not wire stop buttons NO at the field "because the program is easier to read." Wire them NC. If the rung looks confusing, write a clarifying comment. A clearer rung is not worth a stop button that might fail dangerous.

The seal-in pattern is simple and ports across every PLC platform. The disadvantage is that the priority of the latch — what wins if Start and Stop are pressed at the same instant — is implicit in the rung structure. In the rung above, Stop dominates: the Stop NC contact is in series with everything, so a true Stop breaks the rung regardless of what Start or the seal-in branch are doing. This is reset-priority and it is what you almost always want.

Pattern 2 — OTL / OTU pair

Allen-Bradley introduced a pair of coil instructions that split the latch into two separate rungs: OTL (Output Latch, also called SET on some brands) and OTU (Output Unlatch, also called RST or RESET).

   |   Start                                       MotorRun
   |---| |---------------------------------------( L )---|
   |
   |   Stop                                        MotorRun
   |---|/|---------------------------------------( U )---|

OTL is a "set-only" coil. When its rung is true, the bit goes to 1 and stays there even after the rung goes false. OTU is a "reset-only" coil. When its rung is true, the bit goes to 0 and stays there. Neither coil can drop the bit — only OTL can set, only OTU can clear.

This is sometimes easier to read than a seal-in because the start and stop logic live on separate rungs. The OTL on the first rung means "if the start condition is true, set the latch." The OTU on the second rung means "if the stop condition is true, clear the latch." Each rung can have its own preconditions added without tangling them up — a complex set of permissives gating the start, a different set of conditions gating the stop, and the coil bit itself behaves cleanly across the two.

A consequence worth knowing. On most Allen-Bradley platforms, OTL-set bits can survive a CPU power cycle if the underlying tag is in retentive memory. That is sometimes useful — an alarm bit that survives a brownout — and sometimes a footgun, because a latch you forget to clear at startup will appear to be "stuck" the next time the machine boots. The fix is either to use non-retentive memory for the latch or to add a first-scan rung that explicitly clears any latches that should not survive a restart.

The single most common bug with OTL / OTU is writing an OTL on a rung and forgetting to write an OTU anywhere reachable. The bit gets set, the bit never gets cleared. The lamp comes on, no operator action ever turns it off, somebody power-cycles the cabinet and the lamp comes back on because the bit was retentive. Search every OTL in your program and confirm each has at least one matching OTU somewhere with a sensible reset condition. If you find an OTL with no OTU, you have a bug.

Pattern 3 — IEC SR / RS function blocks

The IEC 61131-3 standard pins down two pre-built latch function blocks: SR and RS. Both have an S input, an R input, and a Q output. The names look almost identical and the difference is critical. See iec.ch/standards/iec-61131-3 for the vendor-neutral reference.

SR is set-priority. If S is true, Q goes true. If R is true and S is false, Q goes false. If both S and R are true at the same time, Q goes true — set wins.

RS is reset-priority. If R is true, Q goes false. If S is true and R is false, Q goes true. If both S and R are true at the same time, Q goes false — reset wins.

The two blocks differ in exactly one situation — what happens when both inputs come true simultaneously. The rest of the time they are identical. So the question of which to use becomes the question: in your application, what is the safer outcome when both inputs assert at the same instant?

For a motor start-stop, the answer is reset-priority. If somebody hits stop at the same moment somebody else hits start, the motor should be off. RS. For an alarm latch where a fault must be captured the moment it appears even if the operator is acknowledging, the answer is set-priority. If the fault and the acknowledge come true on the same scan, the alarm should latch. SR. The choice is not a style decision. It is a safety-and-correctness decision and the function block name is the place where you encode it.

This is the kind of detail that does not survive a cut-and-paste from one project to another. Always re-derive the priority from first principles for the application in front of you.

The priority trap

This is the conceptual core of the whole topic. Worked example.

A machine has a fault detector that monitors a temperature sensor. When the temperature exceeds a threshold, a fault bit goes true. There is a "Lockout" latch that we want to capture the fault — once the lockout latches, the machine cannot run until an operator presses an Acknowledge button. The fault bit is the SET. The Acknowledge button is the RESET. We want the latch.

Naive attempt one — use SR (set-priority).

   FaultActive ---S--+----+
                     | SR |---- Lockout
   AckPressed  ---R--+----+

The temperature exceeds threshold. FaultActive goes true. Lockout latches. Operator wants to clear. They look at the temperature, see it has dropped back under the threshold, FaultActive goes false, they press Ack. R goes true, S is false, Q goes to 0. Lockout clears. So far so good.

Now the temperature is hovering right at the threshold, oscillating slightly. FaultActive flickers on and off. Operator presses Ack at a moment when FaultActive happens to be true. With SR, set wins. The Ack does nothing. The lockout stays. The operator presses Ack again. Same problem. From the operator's point of view, the Ack button is broken. They cannot clear an active fault. That is the wrong behaviour for "set-priority" in this situation.

Naive attempt two — use RS (reset-priority).

   FaultActive ---S--+----+
                     | RS |---- Lockout
   AckPressed  ---R--+----+

Temperature exceeds threshold. FaultActive goes true. R is false. Lockout latches. Operator presses Ack. R goes true, Q goes 0, lockout clears. Operator releases Ack. Now imagine the temperature is still over the threshold. FaultActive is still true. R is now false (Ack released). S is true. Q goes back to 1. The lockout re-latches. That part works.

The trap. Operator presses and holds Ack. R is true. The fault clears and FaultActive goes false. The operator is still holding Ack. R is still true. Now the temperature spikes again. FaultActive goes true. But R is true — operator is still holding Ack — so Q stays 0. The lockout never latches the new fault. The machine keeps running through a fault condition because the operator was holding the reset button.

Both naive attempts fail. The fix is to break the level-trigger and use an edge-trigger on the SET side. The fault should latch on the rising edge of FaultActive, not on the level. Use a one-shot — ONS on Allen-Bradley, R_TRIG on IEC.

   FaultActive ----+R_TRIG+---S--+----+
                   |      |      | RS |---- Lockout
                   +------+      +----+
   AckPressed  ---------------R--+

Now S only pulses true for one scan on each fresh fault rising edge. Reset-priority means a held Ack still wins for that one scan, but the next time the fault re-asserts a fresh rising edge generates a fresh one-scan S pulse, and unless Ack is held during that exact scan, the lockout latches. Combine with a discipline of not using maintained Ack buttons (operators press and release, they do not hold) and the logic behaves correctly. The full clean answer is reset-priority RS plus an edge-trigger on the set.

That is the priority trap. Both raw priority choices fail. The combination of reset-priority plus edge-trigger on the set is the pattern that does what you actually want.

Edge triggers and one-shots — ONS, R_TRIG, F_TRIG

A one-shot is a one-scan true pulse generated on the rising or falling edge of an underlying bit. Allen-Bradley calls it ONS. IEC 61131-3 calls it R_TRIG (rising) or F_TRIG (falling). The implementation is trivial — store the previous scan's value of the input, compare to the current scan's value, output is true for one scan when previous was 0 and current is 1.

When latching from a level-true condition, you almost always want to latch on the rising edge, not the level. The reason is the priority trap above. If the SET input stays true for many scans, and the RESET input pulses, the latch behaviour depends on which input was true at the moment of the pulse — that is brittle. By edge-triggering the SET, you guarantee that the only time the latch can re-set is on a fresh transition of the underlying condition, which makes the logic resistant to the operator-holding-the-reset case and similar.

A worked example. A part-counter wants to count parts past a sensor and latch a "batch full" bit when the count reaches the target. The sensor pulse is short — the part flies past in 80 ms. Edge-trigger the count input with R_TRIG. Use the rising-edge pulse as the count input to a CTU, not the level. If you used the level, a part that happened to sit under the sensor at a slow conveyor speed would generate one count for every scan it covered the sensor — fifty counts for one part. With an edge-triggered input, one part is one count, regardless of conveyor speed. Same general lesson: latches and counters care about transitions, not levels, and the discipline of edge-triggering the inputs that drive them is part of clean ladder.

Persistent vs volatile

A latched bit is just a bit in memory, and what survives a power cycle depends on which memory area the bit lives in.

On Siemens S7-1200 / S7-1500, M-area memory bits (memory bits %M0.0 and similar) are volatile by default — the value is lost on power cycle. To get persistence you put the bit inside a Data Block with the "retain" attribute set. The retain attribute says "preserve this value across CPU restarts in battery-backed or Flash-backed memory." Without it, every restart clears the latch.

On Allen-Bradley CompactLogix and ControlLogix, retentive vs non-retentive depends on tag attributes. A BOOL tag in a controller-scope tag list with the Retain checkbox set will preserve its value across power cycles. Without retain, the tag is reset to its default on power-up. OTL and OTU coils respect the retain attribute of their target tag — OTL on a retentive tag survives a restart, OTL on a non-retentive tag does not.

On Mitsubishi FX, the M-device range is split into latched and unlatched ranges (M0–M499 are unlatched by default, M500 onwards are latched, depending on parameter settings). Pick the device based on whether you want persistence.

The rule. Decide deliberately whether each latch should survive a restart. Alarms, lockouts, and machine-state bits often should. Operator-pressed start buttons usually should not (a power cycle should drop the motor for safety). Document the decision in your design notes so the next person reading the program understands why you used the memory area you used.

Worked example — start-stop with safety chain

A real motor start. The motor needs an E-stop chain, a thermal overload, a guard-door-closed interlock, and an operator start button.

   |  EStop  Overload  DoorClosed  Start                 MotorRun
   |--| |---| |-------| |---------+---| |--+----------( )---|
   |                                |       |
   |                                |       |
   |                                |  MotorRun
   |                                +---| |--+

Left to right. EStop is a NC field button wired so the program contact is NO and reads 1 at rest — pressing the E-stop drops the input to 0 and breaks the rung. Overload is the contactor's auxiliary thermal contact, also NC field, NO program, reads 1 when not tripped. DoorClosed is a guard-door switch, similarly fail-safe. The seal-in branch wraps around the Start button only — the safety chain stays in series with everything, so any safety break drops the motor regardless of the latch state. That is the correct shape. Putting the seal-in around the entire chain instead of around just the Start button is a classic newcomer mistake — it would let the seal-in keep the motor running through an open E-stop, which is exactly the failure mode the safety chain is meant to prevent.

The motor stops on three independent paths. Open E-stop. Tripped overload. Open guard door. Any one breaks the rung, drops MotorRun, drops the seal-in. The Start button has to be pressed again to restart, regardless of which safety condition recovered, because the seal-in is gone. That is a correct safety re-arm requirement under most regional regulations and matches the South African machine-safety standard for category-1 stops.

If you build this in the simulator and toggle each input independently, you can watch each safety condition drop the motor, and you can confirm that releasing each safety condition does not auto-restart the motor — Start must be pressed again. That manual re-arm requirement is the practical reason the seal-in is wrapped around just the Start button and not the safety chain.

Common bugs

Five latching bugs that catch almost every new programmer.

Wrong priority. Already covered in detail. Set vs reset priority is not a style choice. Pick deliberately based on which outcome is safer when both inputs assert simultaneously, and add edge-triggering on the input that should not retrigger on a held condition.

Levels triggering retrigger. A latch that re-sets on every scan a condition is true is not really a latch — it is just a copy. If you want one-fault, one-latch, one-acknowledge behaviour, edge-trigger the set input with a one-shot. Re-read the priority-trap section if this still feels abstract.

Latch held by ghost reference. You write what looks like a clean OTL / OTU pair. The bit refuses to clear. Eventually you find an extra OTL on some forgotten rung in a sub-routine that fires every scan on a condition you forgot was true, holding the bit set. The fix is search-everywhere for every coil that writes to the latch tag. Modern IDEs have cross-reference tools — use them. On Allen-Bradley Studio 5000, right-click a tag and pick "Cross Reference" and you see every rung that reads or writes the tag. Use the same discipline for every latch tag you debug.

Seal-in around the wrong contacts. The seal-in branch should wrap only around the start condition, not the entire safety chain. A seal-in around the safety chain defeats the safety chain. Look hard at the parallel branch on every seal-in rung you write and confirm the start contact is the only thing inside the loop.

Forgetting first-scan initialisation. A retentive latch that was set when the CPU lost power will be set again the moment the CPU comes back. If that is not what you want, write a first-scan rung that explicitly clears the latch on power-up. Allen-Bradley exposes a first-scan flag (S:FS or _FirstScan); Siemens has the equivalent in OB100. Use it when needed.

For background on the digital-electronics origin of SR latches and flip-flops — useful if you want to understand the underlying gate-level behaviour — the Wikipedia article on flip-flops is a clean reference. The IEC 61131-3 SR and RS function blocks are the ladder-logic abstraction over those underlying gate-level circuits, and the priority semantics are the same.

Practice in the simulator

Latching is one of those topics where reading is half and doing is the other half. Open the simulator and load the latch preset. Build the seal-in motor start rung from section eight. Toggle each input. Watch the motor latch on Start and stay on. Watch each safety condition drop it. Confirm that releasing the safety condition does not auto-restart — Start has to be pressed again.

Then build the same logic with OTL / OTU and confirm the behaviour is identical. Then build it with an SR function block and an RS function block, side by side, and find a way to press both inputs at the same scan — toggle both bits in the watch window simultaneously. See SR set the output. See RS clear the output. See the priority-trap behaviour first hand.

Then build the alarm-lockout example from section five. Wire it as RS with an R_TRIG on the set. Press and hold the Ack button. Pulse the fault input. Watch the latch behave correctly even with Ack held. That is the worked-example proof of the priority-trap resolution.

Twenty minutes of toggling inputs in the simulator and the latching reflexes will be there. The next step after latching is sequence logic — chaining latches with timers to build state machines, the pattern that runs almost every batch process and pick-and-place line. Latching is the building block. Build the reflex layer here first.

Try the simulator →

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