exercises · South Africa
PLC alarm annunciator logic with first-out latch
PLC alarm annunciator logic the ISA way: latch, flash until acknowledged, steady while standing, and a first-out latch that shows which trip came first.
Difficulty: advanced · 60–90 minutes
This is a build-along exercise, not a reading page. You get a short job card of the kind a contractor actually receives, an I/O table to wire against, and a worked solution to check yourself with once your own version runs — plus the test sequence that proves it, because a program you haven't tried to break is a program you haven't tested. Sketch first, build second, test third. Same order as on site.
Open the simulator and build along →The job card
Job card: the compressor house at a Richards Bay packaging plant trips overnight and the morning shift finds three lamps lit — high discharge temperature, low oil pressure, motor overload — with no way to tell which one tripped first and dragged the others down with it. (Low oil pressure causes the temperature rise; the temperature rise causes the overload. Or was it the other way around?) Build a three-channel annunciator: every alarm latches and flashes until acknowledged, goes steady while it still stands, and clears only once acknowledged AND gone. On top of that, a first-out latch must mark which channel tripped first from a healthy panel, so the fitter starts at the cause instead of the symptoms. Horn on any new alarm, silenced by the acknowledge.
Read it the way a foreman hands it to you. Every requirement on that card is a test case: when you think the program is done, walk the card line by line and force each condition in the watch table. Any line without a matching test you actually ran means you're not done yet. That habit — card in one hand, watch table in the other — is what separates a programmer who commissions clean from one who gets the call-back at month end.
I/O assignment
Wire your simulator project to this table exactly. Half the value of an exercise like this is tag discipline: name the points the same way the table does and the solution steps further down will read straight onto your rungs without translation.
| Tag | Type | Address | Purpose |
|---|---|---|---|
TempOK | DI | %I0.0 | Discharge temperature switch, normally closed, fail-safe. TRUE = temperature healthy; opens (FALSE) on high temperature or broken wire. |
OilOK | DI | %I0.1 | Oil pressure switch, normally closed, fail-safe. TRUE = pressure healthy. |
OverloadOK | DI | %I0.2 | Motor overload auxiliary, normally closed, fail-safe. TRUE = not tripped. |
AckPB | DI | %I0.3 | Acknowledge pushbutton on the annunciator panel, normally open, momentary. |
Horn | DO | %Q0.0 | Alarm horn, sounds while any alarm is unacknowledged. |
TempLamp | DO | %Q0.1 | High temperature window lamp. Flashing = unacknowledged, steady = acknowledged and standing. |
OilLamp | DO | %Q0.2 | Low oil pressure window lamp, same flash/steady convention. |
OverloadLamp | DO | %Q0.3 | Motor overload window lamp, same convention. |
FirstOutLamp | DO | %Q0.4 | First-out indicator; in this build it marks the first-tripped channel by flashing that window at double rate. |
A note on the Type column: DI is a digital input, DO a digital output, AI and AO are analogue in and out, and M is an internal memory bit that never leaves the CPU. The addresses use IEC notation (%I, %Q, %M). If your head is in Allen-Bradley land, map %I0.0 to I:0/0 and carry on — the logic doesn't change, only the spelling of the addresses.
Think before you build
Don't open the ladder editor yet. The notes below are the design decisions that determine whether your program works first time or fights you for an hour. Read them, then sketch the rung shapes on paper. Pencil and the back of a delivery note is fine — most working programs start exactly there.
- Each channel carries two bits: Alarm (the condition, latched) and Acked. Four visible states fall out — normal, unacked-and-standing (flash), acked-and-standing (steady), and cleared. This is the classic annunciator sequence the ISA-18.1 hardware panels implemented in relays; ISA-18.2 carries the same thinking into modern alarm management.
- First-out is a one-shot decision: the first channel whose alarm rises while NO other alarm is active captures the first-out latch, and nothing can take it away until the panel returns to fully normal and acknowledged. The capture must be edge-based and mutually exclusive — two alarms in the same scan is a tie you must break deliberately (channel priority order is the honest answer; document it).
- All inputs are healthy-TRUE because the switches are NC fail-safe, so 'alarm condition' in logic is NOT TempOK and friends. A broken sensor wire then announces itself as an alarm instead of silently disarming a channel — for an annunciator, that property is the whole point of the wiring convention.
- Three channels of identical logic is a function block begging to exist. Build channel one flat, get it right, then fold it into an FB and instantiate three times — the exercise is half alarm logic, half learning when to stop copy-pasting rungs.
Step-by-step solution
Build one rung at a time and test after every rung. Never write the whole program and then test the lot — when five rungs go in untested and the machine misbehaves, you're debugging five suspects instead of one. The steps below follow that order. In the pseudo-rungs, ] [ is a normally-open examine, ]/[ is normally-closed, and ( ) is the output coil.
Rung 1: the flasher clock
One free-running two-TON flasher (the beacon exercise's pattern) generates FlashBit at 1 Hz, and a second pair generates FastFlash at 2 Hz for the first-out window. Every lamp on the panel shares these clocks so the whole annunciator blinks in step — easier on the eye and on fault photos.
// two cross-coupled TONs ── FlashBit (1 Hz)
// two more, T#250ms ── FastFlash (2 Hz)
Rungs 2-4: per-channel alarm and ack latches
Per channel: Alarm sets on NOT input-OK and holds until the condition clears AND it has been acknowledged — (NOT TempOK OR TempAlarm) AND NOT (TempAcked AND TempOK). Acked sets on AckPB while the alarm stands and falls with the alarm. Work through the four-state table by hand before trusting the rungs; every annunciator bug lives in these two latches.
// TempAlarm := (NOT TempOK OR TempAlarm) AND (NOT TempAcked OR NOT TempOK)
// TempAcked := (AckPB OR TempAcked) AND TempAlarm
Rung 5: the first-out capture
AnyAlarm is the OR of the three Alarm bits. On the rising edge of each channel's Alarm, if AnyAlarm was FALSE on the previous scan, that channel's FirstOut bit sets. Capture priority breaks same-scan ties in a fixed order. All FirstOut bits reset together only when the panel is fully clear — no alarms, nothing unacknowledged.
// rTemp(CLK := TempAlarm)
// rTemp.Q AND NOT AnyAlarmPrev ──(S)TempFirst
// NOT AnyAlarm ──(R)TempFirst, (R)OilFirst, (R)OvlFirst
// AnyAlarmPrev := AnyAlarm // last rung of the scan
Rungs 6-9: lamps and horn
Each window lamp: (Alarm AND NOT Acked AND FlashBit) OR (Alarm AND Acked) — flash when new, steady when owned. A channel holding the first-out latch overrides to FastFlash so it stands out across the room. The horn is any-alarm-unacknowledged: OR of (Alarm AND NOT Acked) across channels.
// TempLamp := (TempAlarm AND NOT TempAcked AND FlashBit)
// OR (TempAlarm AND TempAcked)
// OR (TempFirst AND FastFlash)
// Horn := OR of (Alarm AND NOT Acked) over all channels
Test the overnight-trip story
Replay the job card: trip oil first, then temperature 2 seconds later, then overload. All three windows flash, horn sounds, and the oil window flashes fast — first-out captured. Acknowledge: horn silent, all three steady, oil still marked. Clear temperature and overload: their lamps go dark; oil stays steady until its condition clears. Clear oil: panel dark, first-out released. Re-trip temperature alone: IT captures first-out this time. Then the edge cases: ack pressed with no alarms (nothing), an alarm that clears before anyone acknowledges (lamp keeps flashing until acked — the operator must still learn it happened), and two alarms forced in the same scan (your documented priority decides).
The structured text version
The same logic in IEC 61131-3 structured text — each output written as a boolean equation you can read aloud.
(* Three-channel first-out annunciator, channel logic shown for one *)
(* Flasher clocks *)
tOff(IN := NOT tOn.Q, PT := T#500ms); tOn(IN := tOff.Q, PT := T#500ms);
FlashBit := tOff.Q;
(* Channel: temperature *)
TempAlarm := (NOT TempOK OR TempAlarm) AND (NOT TempAcked OR NOT TempOK);
TempAcked := (AckPB OR TempAcked) AND TempAlarm;
rTemp(CLK := TempAlarm);
IF rTemp.Q AND NOT AnyAlarmPrev THEN TempFirst := TRUE; END_IF;
(* ... OilAlarm / OvlAlarm identical ... *)
AnyAlarm := TempAlarm OR OilAlarm OR OvlAlarm;
IF NOT AnyAlarm THEN TempFirst := FALSE; OilFirst := FALSE; OvlFirst := FALSE; END_IF;
TempLamp := (TempAlarm AND NOT TempAcked AND FlashBit) OR (TempAlarm AND TempAcked);
Horn := (TempAlarm AND NOT TempAcked) OR (OilAlarm AND NOT OilAcked) OR (OvlAlarm AND NOT OvlAcked);
AnyAlarmPrev := AnyAlarm; (* keep as the last statement *)
Ladder wins this argument when an electrician has to fault-find your program at 02:00 with a multimeter mindset — the rung looks like the circuit diagram it replaced, and that familiarity is worth real money on a breakdown. ST starts winning when the pattern repeats: ten pumps with the same interlock shape is one ST function called ten times, where ladder hands you ten near-identical rungs to keep in sync by hand forever. Learn both. Build the exercise in ladder first, then write the ST version and confirm the two behave identically in the simulator. That translation skill — same logic, two languages — is exactly what technical interviews and commissioning work both test.
Common mistakes
Every mistake below comes from a real program: either one of ours from years back, or one we were called in to fix. Check your build against the list before you call the exercise done.
- Capturing first-out on 'this alarm rose' without checking that the panel was clean. The second and third alarms also have rising edges; without the NOT-AnyAlarmPrev guard every new alarm steals the first-out flag and the panel tells the fitter nothing.
- Updating AnyAlarmPrev anywhere except the end of the scan. Put it before the capture rungs and the guard compares against the CURRENT scan — first-out then never captures at all, and the bug only shows with two alarms close together.
- Letting the acknowledge clear the alarm latch itself. Ack means 'a human has seen this', not 'this is fixed' — an acked-away latch hides a standing low-oil condition behind a dark panel, which is how the second compressor dies the same night.
- One ack button resetting first-out. The fitter acknowledges the noise at 06:00, the first-out clears with it, and the diagnostic evidence is gone before anyone read it. First-out releases only when the panel is truly clear.
- Hand-copying the channel logic three times with three sets of slightly different tag names. Channel two gets the ack rung's contact wrong, the panel works for months, and the one night it matters the oil lamp will not silence. Build the function block.
Most of these share one root cause: the rung shape doesn't match the intent, so the program passes the obvious test and fails the edge case. That's why the solution steps force the edge cases deliberately instead of stopping at "it starts and it stops". Steal that habit for every program you write from here on.
Take it further
Got it working first time? Good — now make it earn its keep. Each extension below changes the spec the way a real client does: after you've finished. Treat each one as a fresh job card, and re-test the whole program afterwards, not just the new part. Regressions hide in the rungs you didn't touch.
- Add a lamp-test input that forces every window and the horn while held — the first thing a real annunciator panel offers and a two-contact addition here.
- Add alarm timestamping: on each capture edge, copy the PLC clock into a per-channel time tag for the HMI — first-out tells you the order, timestamps tell you the gaps, and the gaps tell the story.
- Refactor the channel into a function block with inputs (Condition, Ack, FlashBit) and outputs (Lamp, HornRequest, First), instantiate it ten times, and feel the difference between three copied channels and ten instantiated ones — then read ISA-18.2 on alarm rationalisation before adding all ten.
If you build even one extension, screenshot the finished rungs and keep them somewhere organised. A folder of working, tested exercise solutions is the start of a portfolio — and hiring engineers ask candidates to explain a rung far more often than they ask to see certificates.
Run this in the simulator
The sandbox on the free tier lets you build the core rungs of this advanced exercise yourself — no card details, no install, signed up and on a rung inside two minutes. The watch table is the part that matters here: force the inputs, watch the outputs, and run the test sequence from the solution steps against a live scan cycle instead of imagining it. To be straight about what's paid: the guided version of this exercise — graded checkpoints, feedback on every submission, plus the fault-injection variants that break your program the way a real plant does — sits in the curriculum on the Basic tier at USD 12 per month and Pro at USD 29 per month, alongside the wiring track, sensor school and cert packs. Training centres and engineering departments wanting this in a lab should look at the Teams tier (USD 199 per seat per year, minimum 5 seats); the training-centres page carries the institutional details and the contact form. If you're an individual learning the trade, prove the core rungs in the free sandbox first and decide whether the graded track is worth the money. And once this one runs clean, line up the next exercise a notch harder — the step up is where the skill gets built.
Start in the free sandbox →Reference
ISA-18.2 alarm management standard covers the background theory behind this exercise, and it's worth twenty minutes of your time after the build — theory sticks better once your hands have done the work. The languages used here are defined by the IEC 61131-3 standard from iec.ch, and your CPU vendor's manual remains the canonical source for how a specific controller executes them.
What we don't claim
This site is not SAQA-registered, not MerSETA-accredited, and not an NQF-registered qualification provider. Our completion certificates are course-level only — they describe what you covered, not an NQF Level X qualification. The CCST cert from ISA is the portable industry credential we recommend; we are not an ISA cert delivery partner either, but our cert packs are CCST-aligned. The exercise on this page is practice material written by working programmers: finishing it proves the skill to yourself and to the simulator's progress tracking, not to a regulator.