Before naming a latch, find two histories that demand different answers. In the first, a carton is between the sensors because a transfer was accepted. In the second, someone placed it there while the machine was idle. The present photoeye values can be identical. The machine should not treat the two situations as the same accepted job. Write the distinguishing fact in plain words before creating the variable.
Some facts belong to this scan; others belong to the story
A carton sensor tells you what is observed now. An accepted Start request tells you something that happened earlier and still matters. Those are different kinds of information. If you use only present inputs, releasing a momentary Start button stops the request. If you retain everything indiscriminately, yesterday's request may reappear when it is no longer appropriate.
Memory should have a purpose, an owner, and a clearing rule. Ask four questions for every stored Boolean: what sets it, what clears it, what wins when both happen, and what should it contain after initialization? If you cannot answer those questions, you have not finished designing the memory.
A latch is a decision about persistence
Consider a teaching fan with momentary Start and Stop buttons. Once Start is accepted, a run request should remain until Stop. The following complete small program demonstrates stop-dominant working memory. It is illustrative IEC-style Structured Text; the target environment supplies I/O mapping and task configuration.
PROGRAM FanRequestExample
VAR
StartButton : BOOL;
StopButton : BOOL;
RunRequest : BOOL := FALSE;
FanCmd : BOOL;
END_VAR
IF StopButton THEN
RunRequest := FALSE;
ELSIF StartButton THEN
RunRequest := TRUE;
END_IF;
FanCmd := RunRequest;
END_PROGRAM
When neither button is active, no assignment changes RunRequest, so the program's working state persists between calls. Stop wins if both inputs are true because the first branch is selected and the second is skipped.
This example intentionally does not solve restart inhibition, equipment faults, or safety. In fact, if Start remains held while Stop is released, the request becomes true again. That may violate the real contract. A simple latch is a useful mechanism, not a complete motor-control specification.
An edge is an observed change
If a command must be new, detect a transition from false to true. A rising-edge detector remembers the previous sample and compares it with the current sample. Its output is a one-invocation event, assuming you call it consistently in the relevant task.
(* Excerpt: PreviousStart is persistent program state. *)
StartEdge := StartButton AND NOT PreviousStart;
PreviousStart := StartButton;
Calculate the edge before updating the previous sample. Reversing the two lines destroys the evidence, as we saw in the scan chapter. A standard function block encapsulates the same kind of state. For example, Beckhoff's R_TRIG exposes CLK and a one-cycle Q indication on a detected rising edge. Its documented interface is a concrete library reference, not a promise that every vendor spells every parameter identically. Beckhoff R_TRIG documentation.
An edge detector only detects transitions that reach its calls. It cannot recover a pulse missed by the input system, and it cannot decide whether a transition was an intentional human request or electrical bounce.
Require release before accepting a new Start
Suppose the contract requires a released button to be observed before a Start can be accepted. This also prevents an already-held button from starting the teaching model at initialization. Use an arming state, initialized false.
(* Excerpt; StartArmed and RunRequest initialize FALSE. *)
StartEvent := FALSE;
IF NOT StartButton THEN
StartArmed := TRUE;
ELSIF StartArmed THEN
StartEvent := TRUE;
StartArmed := FALSE;
END_IF;
IF StopButton OR OperationalFault THEN
RunRequest := FALSE;
ELSIF StartEvent AND Ready THEN
RunRequest := TRUE;
END_IF;
The event is consumed even if readiness is false. Holding Start while the station later becomes ready does not create another event. That is deliberate: a rejected request is not stored for future surprise execution.
Trace the awkward case. Start and Stop are pressed together after Start was released. StartEvent appears, but Stop wins and the event is consumed. Releasing Stop while keeping Start held does not restart. The operator must release Start and press it again.
For a full application, decide whether losing readiness also clears StartArmed, and how mode changes and initialization affect it. Those choices should come from the behaviour contract. Do not add retention to this variable by habit.
Operational faults need a lifecycle too
A fault condition and a latched fault record are different. ArrivalTimedOut may become false when the sequence leaves its seeking state. If the alarm disappears immediately with it, the operator loses the reason the machine stopped.
Store the fault until its reset conditions are satisfied. Separately display whether the underlying condition is still active. Reset should acknowledge or clear an eligible record; it should not secretly request a new production cycle.
Avoid one global reset assignment scattered across dozens of routines. Give each fault owner responsibility for its clearing conditions, and expose whether reset succeeded. Some faults can clear immediately, while others require a physical check or recovery procedure. A universal reset button can be a shared request without being a universal promise.
Working memory is not automatically power-loss memory
A variable surviving from one scan to the next is not the same as a variable surviving controller restart. Retention, persistence, initialization, downloads, and memory reset depend on the runtime and configuration. Check those rules on the target, then test the actual restart cases.
Retaining a completed production count may be useful. Retaining a run request may cause an inappropriate restart. Retaining a state number does not prove that the physical mechanism stayed in that state while power was absent. Choose retained data by meaning, not by convenience.
There is also a distinction between retaining history and trusting it. A stored “last carton was partly filled” record can guide recovery while commands remain off. It need not authorize resuming the dispenser.
Try it
An alarm latch is set by JamDetected and cleared by ResetButton. The current code has two independent IF statements: the first sets the latch, and the second clears it. Predict the final value when both inputs are true. Then rewrite the logic so an active jam cannot be cleared, and explain why a reset should not start the conveyor.
Work through the answer
With the described order, the second assignment wins and the alarm ends false, even though the jam is still detected. A reader may see both instructions and assume fault detection has priority, but execution order says otherwise.
IF JamDetected THEN
JamLatched := TRUE;
ELSIF ResetEvent THEN
JamLatched := FALSE;
END_IF;
This version gives the active fault priority. ResetEvent should follow the project's request rules rather than relying on a permanently held reset input. The conveyor's run state remains a separate owner with a separate Start requirement. Clearing the alarm means the fault record is eligible to clear; it says nothing about carton position or whether the operator intends motion.
The next chapter translates these ideas into ladder diagrams, where memory can look deceptively like electrical wiring even though execution still follows program rules.