First-out fault capture: which interlock tripped first, so operators stop guessing

When a line stops with a screen full of alarms, the one that matters is the one that tripped first. Here's how to latch it in a Logix PLC, in ladder or structured text, with a timestamp and an operator reset, and show it on an Ignition screen.

A screen full of alarms, one cause

Picture a packaging line that stops in the middle of a shift. The alarm screen shows guard door open, jam detected, and low air pressure, all active. The operator does the obvious thing: clears the jam, closes the door, and restarts. A few minutes later it stops again, with the same alarms.

What actually happened is that the plant air dipped first. A pneumatic gate sagged, the product jammed behind it, and the operator opened the guard door to clear the jam. The air dip was the cause. The other two were the line reacting to it, plus the operator reacting to the line. But the alarm list sorts by time, newest first, and the cause sits at the bottom looking like just another alarm.

The alarm list versus the order things happened Left: an alarm list sorted newest first shows Guard door open, then Jam detected, then Low air pressure. Right: the order it actually happened was low air pressure first, which caused the jam, and then the operator opened the guard door to clear it. The first-out is low air pressure. What the alarm list shows (newest first) Guard door open Jam detected Low air pressure All three are active. Nothing says which one started it. What actually happened 1stLow air pressure FIRST OUT 2ndJam detected (a gate sagged) 3rdGuard door open (to clear it) Fix the jam and the door, restart, and it trips again.
An example, not a real job. The alarm list is right about what's active, but it can't tell you the order. The first-out can.

This is where guessing starts. One person blames the photoeye, another resets the drive, and the line runs fine until it doesn't. Without a first-out, the next trip gets diagnosed from memory.

The idea: latch the first one

A first-out (sometimes called a first-up or first-fault indicator) is simple:

  1. Give every interlock a bit in one word, so "anything tripped" is just word not equal to zero.
  2. The first scan that sees a non-zero word, record which bit it was, copy the whole word, take the time, and set a latch. While the latch is on, nothing else can overwrite the record.
  3. The operator resets the first-out from the HMI, and the PLC only accepts it once every interlock is healthy again. Then it's armed for the next trip.

It doesn't replace the alarm list. It adds one line on top of it that answers the question everyone asks first: what stopped it?

In the PLC (Studio 5000 / Logix)

The example uses six interlocks on one line so the tag names stay short. Map your own interlocks into the Trip bits in rung 0; the rest doesn't change. Lower bit numbers win a tie, so put the interlocks most likely to be the real cause (air, power, E-stop) in the lowest bits.

Tags (program scope)
NameTypePurpose
EStop_OKBOOLE-stop circuit healthy. Read from the safety controller's status, monitor only
Guard_ClosedBOOLGuard door closed (status only; the door's safety function stays in the safety system)
Motor_OL_OKBOOLMain motor overload healthy
VFD_FaultedBOOLDrive fault status bit
Air_OKBOOLAir pressure switch made
Jam_DetectedBOOLYour existing jam bit (already timed or debounced)
TripDINTOne bit per interlock, 1 = tripped. Bits 0–5 used here; the bit order is the tie-break order
FirstOutCapturedBOOLOn once a first-out is recorded; blocks any later trip from overwriting it
FirstOutDINTWhich interlock tripped first: 0 = none, 1–6 = Trip bit number + 1
FirstOutBitsDINTCopy of Trip at the moment of capture, so a same-scan tie is visible
FirstOutTimeDINT[7]Controller clock at capture: year, month, day, hour, minute, second, µs (UTC on v16+)
FO_ResetReqBOOLReset request from the HMI button

Both versions below do the same thing in the same order, with the same tag names. Ladder is shown first. Switch to Structured Text if that's your plant standard.

Ladder

Four rungs in one ladder routine, top to bottom. Open "as text" under any rung to copy it into the Studio 5000 rung editor.

Drawn the way the Studio 5000 ladder editor lays it out: inputs on the left, outputs pushed to the right rail. The small numbers with arrows are where the editor shows live values; they read 0 here because nothing is online.

On a phone, swipe sideways to see the whole rung.

Interlocks to trip bits, 1 = tripped. E-stop is the safety controller's status, monitor only0EStop_OKTrip.0Guard_ClosedTrip.1Motor_OL_OKTrip.2VFD_FaultedTrip.3Air_OKTrip.4Jam_DetectedTrip.5
Rung 0 as text
[XIO(EStop_OK)OTE(Trip.0),XIO(Guard_Closed)OTE(Trip.1),XIO(Motor_OL_OK)OTE(Trip.2),XIC(VFD_Faulted)OTE(Trip.3),XIO(Air_OK)OTE(Trip.4),XIC(Jam_Detected)OTE(Trip.5)];
First trip since the last reset: record it once. Lowest bit wins a same-scan tie1FirstOutCapturedNEQNot EqualSource ATrip0 ←Source B0MOVMoveSourceTrip0 ←DestFirstOutBits0 ←GSVGet System ValueClass NameWallClockTimeInstance NameAttribute NameDateTimeDestFirstOutTime[0]0 ←CLRClearDestFirstOut0 ←Trip.0EQUEqualSource AFirstOut0 ←Source B0MOVMoveSource1DestFirstOut0 ←Trip.1EQUEqualSource AFirstOut0 ←Source B0MOVMoveSource2DestFirstOut0 ←Trip.2EQUEqualSource AFirstOut0 ←Source B0MOVMoveSource3DestFirstOut0 ←Trip.3EQUEqualSource AFirstOut0 ←Source B0MOVMoveSource4DestFirstOut0 ←Trip.4EQUEqualSource AFirstOut0 ←Source B0MOVMoveSource5DestFirstOut0 ←Trip.5EQUEqualSource AFirstOut0 ←Source B0MOVMoveSource6DestFirstOut0 ←LFirstOutCaptured
Rung 1 as text
XIO(FirstOutCaptured)NEQ(Trip,0)[MOV(Trip,FirstOutBits),GSV(WallClockTime,,DateTime,FirstOutTime[0]),CLR(FirstOut),XIC(Trip.0)EQU(FirstOut,0)MOV(1,FirstOut),XIC(Trip.1)EQU(FirstOut,0)MOV(2,FirstOut),XIC(Trip.2)EQU(FirstOut,0)MOV(3,FirstOut),XIC(Trip.3)EQU(FirstOut,0)MOV(4,FirstOut),XIC(Trip.4)EQU(FirstOut,0)MOV(5,FirstOut),XIC(Trip.5)EQU(FirstOut,0)MOV(6,FirstOut),OTL(FirstOutCaptured)];
Operator reset: only when every interlock is healthy2FO_ResetReqEQUEqualSource ATrip0 ←Source B0CLRClearDestFirstOutBits0 ←CLRClearDestFirstOut0 ←UFirstOutCaptured
Rung 2 as text
XIC(FO_ResetReq)EQU(Trip,0)[CLR(FirstOutBits),CLR(FirstOut),OTU(FirstOutCaptured)];
Always clear the request, so a refused reset can't fire later3FO_ResetReqUFO_ResetReq
Rung 3 as text
XIC(FO_ResetReq)OTU(FO_ResetReq);
(End)
All 4 rungs as text
[XIO(EStop_OK)OTE(Trip.0),XIO(Guard_Closed)OTE(Trip.1),XIO(Motor_OL_OK)OTE(Trip.2),XIC(VFD_Faulted)OTE(Trip.3),XIO(Air_OK)OTE(Trip.4),XIC(Jam_Detected)OTE(Trip.5)];
XIO(FirstOutCaptured)NEQ(Trip,0)[MOV(Trip,FirstOutBits),GSV(WallClockTime,,DateTime,FirstOutTime[0]),CLR(FirstOut),XIC(Trip.0)EQU(FirstOut,0)MOV(1,FirstOut),XIC(Trip.1)EQU(FirstOut,0)MOV(2,FirstOut),XIC(Trip.2)EQU(FirstOut,0)MOV(3,FirstOut),XIC(Trip.3)EQU(FirstOut,0)MOV(4,FirstOut),XIC(Trip.4)EQU(FirstOut,0)MOV(5,FirstOut),XIC(Trip.5)EQU(FirstOut,0)MOV(6,FirstOut),OTL(FirstOutCaptured)];
XIC(FO_ResetReq)EQU(Trip,0)[CLR(FirstOutBits),CLR(FirstOut),OTU(FirstOutCaptured)];
XIC(FO_ResetReq)OTU(FO_ResetReq);

How the ladder lines up with the structured text

  • Rung 0 is the six Trip.n := lines. An XIO is the NOT: the bit turns on when a "healthy" input goes off.
  • Rung 1 is the IF NOT FirstOutCaptured AND Trip <> 0 block. Branch legs run top to bottom, so CLR FirstOut runs before the six decode legs, and OTL FirstOutCaptured runs last.
  • The EQU FirstOut 0 on each decode leg is the ELSIF. Once a leg writes a number, every leg below it sees a non-zero FirstOut and skips. That's what makes the lowest bit win a same-scan tie. Don't reorder the legs.
  • Rungs 2 and 3 are the nested IF FO_ResetReq block, split in two so each rung reads plainly. Keep rung 3 below rung 2, or rung 2 never sees the request.

Structured Text

(* First-out fault capture: which interlock tripped first *)

(* 1. Interlocks to trip bits. 1 = tripped. *)
Trip.0 := NOT EStop_OK;        (* safety controller status: monitor only *)
Trip.1 := NOT Guard_Closed;
Trip.2 := NOT Motor_OL_OK;
Trip.3 := VFD_Faulted;
Trip.4 := NOT Air_OK;
Trip.5 := Jam_Detected;

(* 2. First trip since the last reset: record it once *)
IF NOT FirstOutCaptured AND Trip <> 0 THEN
    FirstOutBits := Trip;                              (* everything that tripped in this scan *)
    GSV(WallClockTime, , DateTime, FirstOutTime[0]);   (* UTC on v16+ *)
    FirstOut := 0;
    IF Trip.0 THEN FirstOut := 1;                      (* lowest bit wins a same-scan tie *)
    ELSIF Trip.1 THEN FirstOut := 2;
    ELSIF Trip.2 THEN FirstOut := 3;
    ELSIF Trip.3 THEN FirstOut := 4;
    ELSIF Trip.4 THEN FirstOut := 5;
    ELSIF Trip.5 THEN FirstOut := 6;
    END_IF;
    FirstOutCaptured := 1;
END_IF;

(* 3. Operator reset: only when every interlock is healthy *)
IF FO_ResetReq THEN
    IF Trip = 0 THEN
        FirstOutBits := 0;
        FirstOut := 0;
        FirstOutCaptured := 0;
    END_IF;
    FO_ResetReq := 0;    (* always clear it, so a refused reset can't fire later *)
END_IF;

One routine, any task. If the line's run logic is in another routine, schedule this one ahead of it (see gotchas).

Nothing in this logic stops or starts the machine. It only watches. Your existing interlock logic still does the stopping, and the safety system still does the safety functions.

In Ignition

Bring FirstOut, FirstOutBits, and FO_ResetReq in as OPC tags (in a Line1 folder in these examples). Then build a small banner near the top of the line's main screen:

Example first-out banner on an Ignition screen A dark banner reads: First out, Low air pressure, since Nov 3, 2:14:07 PM. A small tag says 1 more tripped in the same scan. A Reset first-out button sits on the right. FIRST OUT Low air pressure since Nov 3, 2:14:07 PM + more tripped in the same scan Reset first-out
A mock-up of the banner described below. Put it where operators look first, near the start button.
  • The first-out text. A Label with an expression binding on its text. Keep the names short enough to read from across the aisle:
// Label text: which interlock tripped first
case({[default]Line1/FirstOut},
    1, "E-stop",
    2, "Guard door open",
    3, "Main motor overload",
    4, "Drive fault",
    5, "Low air pressure",
    6, "Jam detected",
    "No trip")
  • When it happened. A second Label, bound to the timestamp of the FirstOut tag:
// Label text: when Ignition saw it (only while there is a first-out)
if({[default]Line1/FirstOut} > 0,
   "since " + dateFormat({[default]Line1/FirstOut.timestamp}, "MMM d, h:mm:ss a"),
   "")

That's when Ignition saw the change, so it's as close as your tag group's poll rate. The exact controller time is in FirstOutTime (UTC on v16+) if you need it for a report.

  • Same-scan ties. A small badge whose visible property uses this expression. It shows when more than one bit was on at capture:
// Visible: more than one interlock tripped in the same scan
{[default]Line1/FirstOut} > 0 &&
{[default]Line1/FirstOutBits} != (1 << ({[default]Line1/FirstOut} - 1))
  • The reset button. A Button with a script action on onActionPerformed. Set the Write Permissions on FO_ResetReq to your operator role, so only signed-in users can reset it:
# Reset button > Events > Component > onActionPerformed (script action)
def runAction(self, event):
    system.tag.writeBlocking(["[default]Line1/FO_ResetReq"], [True])

The PLC decides whether the reset is accepted. If an interlock is still tripped, the request is cleared and the first-out stays up, which is what you want: the banner shouldn't go blank while the cause is still there.

To see patterns over time, historize FirstOut on change, or add an alarm on it (mode Not Equal, setpoint 0) so each first-out lands in the alarm journal. After a few weeks you can see which interlock really stops the line most often, instead of which one people remember.

Gotchas

Scan order decides "first"

The PLC sees inputs once per scan (more exactly, as each module's RPI updates them), so "first" means first scan, not first millisecond. Two trips closer together than that look simultaneous. If the line's run logic is in another routine, schedule this one ahead of it, so it sees the trip before anything reacts to it.

Same-scan ties

When two bits come on in the same scan, the lower bit number wins and FirstOutBits keeps the rest. Put the likely root causes (air, power, E-stop) in the low bits. If you truly need the order inside one scan, that's a job for timestamped sequence-of-events input modules, not ladder logic.

Reset only when healthy

The reset is refused while any trip bit is on, and the request is always cleared, so a press made too early doesn't sit there and fire later. Decide with operations whether the line should also require a first-out reset before restart. That's fine in standard logic, as long as it isn't part of a safety function.

Latch vs. fault array

This latches one first-out until someone resets it, which is what an operator needs. If you want a history of every trip in order, that's a different tool: a small FIFO of fault codes and times, or the Ignition alarm journal. Many lines use both.

Don't mask safety circuits

The E-stop and guard bits here are status bits read from the safety controller or a monitoring contact, for display only. Never route a safety function through first-out logic, and never let an HMI reset touch a safety reset. Safety-rated functions stay in the safety system.

Stopped machines trip too

Opening a guard door on a stopped line will capture a first-out that didn't stop anything. If you only care about trips that stopped a running machine, add your run bit to the capture condition, and make sure this routine runs before the logic that drops that run bit.

The short version

Put every interlock in one word, latch the first non-zero bit with a timestamp, keep a copy of the whole word for ties, and only let the operator reset it once everything is healthy. Then put it at the top of the screen, so the first question after a stop is answered before anyone starts guessing.

← Previous: Run hours you can trust · All tutorials

Tired of guessing why the line stopped?

We can add first-out capture to your existing interlocks and put it on your Ignition screens, without touching your safety system. It's also a good thing to bring up during a free PLC/SCADA health check.