exercises · South Africa
PLC silo simulation exercise, step by step
The classic PLC silo simulation exercise built properly: conveyor, fill valve and level eye sequenced as a state machine, in ladder and structured text.
Difficulty: intermediate · 45–75 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: a maize-meal bagging line at a Free State mill carries open bags on a slat conveyor under the silo discharge. The sequence the foreman wants: conveyor runs until a bag arrives at the fill point, conveyor stops, the fill valve opens until the level eye across the bag mouth is blocked, valve closes, conveyor restarts and carries the full bag clear while the next one comes in. Stop must halt everything mid-sequence; start must resume without dumping meal on the slats. This is the same silo simulation every training package ships — build it as a clean state machine, not a pile of interlocked coils.
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 |
|---|---|---|---|
StartPB | DI | %I0.0 | Start pushbutton, normally open, momentary. |
StopPB | DI | %I0.1 | Stop pushbutton, normally closed, wired fail-safe. TRUE = healthy and unpressed. |
BagAtFill | DI | %I0.2 | Proximity switch at the fill position, TRUE while a bag sits under the discharge. |
BagFull | DI | %I0.3 | Photoeye across the bag mouth at fill height, TRUE when meal blocks the beam. |
ConveyorRun | DO | %Q0.0 | Slat conveyor motor contactor. |
FillValve | DO | %Q0.1 | Pneumatic slide valve on the silo discharge, open while energised. |
FullLamp | DO | %Q0.2 | Panel lamp, on during the fill phase so the operator can see where the cycle is. |
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.
- Three states cover it: INDEXING (conveyor on, valve shut), FILLING (conveyor off, valve open), and DISCHARGING (conveyor on until the prox clears). Name them with constants. A state machine you can read aloud to the foreman is one you can fault-find at 02:00.
- Conveyor and valve must be mutually exclusive by construction — derive both outputs from the state value so no combination of inputs can open the valve over a moving bag.
- Stop should freeze the sequence, not abort it. Hold a Running bit ANDed into both outputs and keep the state variable untouched, so start resumes a half-filled bag instead of indexing it away half-empty.
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 running gate
Standard seal-in for the Running bit: (StartPB OR Running) AND StopPB. Every output rung later ANDs in Running, which means a stop freezes the machinery while the state machine remembers exactly where the cycle was.
// ──┬──[ ]StartPB──┬──[ ]StopPB──( )Running
// └──[ ]Running──┘
Rungs 2-4: state transitions
Three transition rungs move an integer State along: INDEXING goes to FILLING on the rising edge of BagAtFill; FILLING goes to DISCHARGING when BagFull comes true; DISCHARGING returns to INDEXING when BagAtFill drops as the full bag moves clear. Use edge contacts on the prox so a bag sitting on the switch cannot retrigger a fill.
// (State=INDEXING) AND rBagAtFill ── MOVE FILLING -> State
// (State=FILLING) AND BagFull ── MOVE DISCHARGING -> State
// (State=DISCHARGING) AND NOT BagAtFill ── MOVE INDEXING -> State
Rungs 5-7: decode outputs from state
ConveyorRun is Running AND (State=INDEXING OR State=DISCHARGING). FillValve is Running AND State=FILLING. FullLamp follows the FILLING state regardless of Running so the operator sees why nothing is moving when stopped mid-fill. No output is set anywhere else — one source of truth per coil.
// Running AND (State=INDEXING OR State=DISCHARGING) ──( )ConveyorRun
// Running AND (State=FILLING) ──( )FillValve
// (State=FILLING) ──( )FullLamp
Test the freeze-and-resume
Run a full cycle in the simulator first: index, fill, discharge, repeat. Then the test that separates this from the toy version: press stop halfway through a fill, confirm the valve shuts and the conveyor stays put, then press start and confirm the fill resumes on the same bag — the state must still read FILLING. Finally force BagFull permanently TRUE and confirm the machine indexes through without ever opening the valve onto an already-full bag.
The structured text version
The same logic in IEC 61131-3 structured text — each output written as a boolean equation you can read aloud.
(* Silo fill sequence, IEC 61131-3 ST *)
VAR CONSTANT INDEXING := 0; FILLING := 1; DISCHARGING := 2; END_VAR
Running := (StartPB OR Running) AND StopPB;
rBag(CLK := BagAtFill); (* R_TRIG *)
CASE State OF
INDEXING: IF rBag.Q AND NOT BagFull THEN State := FILLING; END_IF;
FILLING: IF BagFull THEN State := DISCHARGING; END_IF;
DISCHARGING: IF NOT BagAtFill THEN State := INDEXING; END_IF;
END_CASE;
ConveyorRun := Running AND ((State = INDEXING) OR (State = DISCHARGING));
FillValve := Running AND (State = FILLING);
FullLamp := (State = FILLING);
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.
- Building it as interlocked seal-in coils instead of a state machine. It works on the bench, then one odd input sequence — bag removed mid-fill, say — leaves two coils latched at once and the valve opens over bare slats.
- Triggering the fill on the prox level instead of its edge. The bag fills, discharges, and the moment the next bag lands the old TRUE level is still being evaluated; a slow conveyor gives you a double fill on the same rung logic.
- Letting stop reset the state variable to zero. The operator stops mid-fill to clear a torn bag, presses start, and the half-full bag gets indexed out while a fresh fill starts on nothing.
- No timeout on the fill state. A failed level eye means the valve stays open until someone notices the meal heap. A 30-second TON watchdog on FILLING that drops to a fault state costs three rungs.
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 fill-timeout fault with a beacon and a reset button — the watchdog pattern here is the same one the annunciator exercise formalises.
- Count completed bags with a CTU and stop after a pallet quantity of 40, lighting a pallet-change lamp; the bottle counting exercise has the counter details.
- Replace the single level eye with a loadcell analog input and stop the fill at a setpoint mass, with an adjustable in-flight compensation value — the first step from discrete into analog batching.
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 intermediate 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 — 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
plcprogramming.io simulator 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.