PLC Programming SAPLC ProgrammingSOUTH AFRICA

exercises · South Africa

PLC counter exercise: bottle counting line

A PLC counter exercise from a real bottling line: CTU counts 24 bottles per crate, stops the infeed, handles photoeye chatter and the reset edge cases.

Difficulty: beginner · 20–40 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 juice bottling line in Nelspruit packs 24 bottles to a crate. The packer currently counts by eye and every few crates one goes out short, which the retailer fines back per incident. There is already a photoeye across the infeed to the packing table. Count bottles with a CTU: at 24, light the crate-full lamp and stop the infeed conveyor until the packer swaps crates and presses reset. If the crate-present switch shows no crate under the chute, counting must not run at all. The photoeye sees bottle necks, so a wobbling bottle can break the beam twice — deal with it.

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.

TagTypeAddressPurpose
BottleEyeDI%I0.0Retro-reflective photoeye across the infeed, TRUE while a bottle blocks the beam.
CratePresentDI%I0.1Limit switch under the crate position, TRUE while a crate sits under the chute.
ResetPBDI%I0.2Packer's reset pushbutton, normally open, pressed after each crate swap.
InfeedRunDO%Q0.0Infeed conveyor contactor; stops while a full crate waits to be swapped.
CrateFullLampDO%Q0.1Amber lamp at the packing table, on when the count reaches 24.

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.

  • The IEC CTU counts on the rising edge of its CU input internally — you do not add your own edge contact in ladder. Knowing what the instruction already does stops you from double-filtering and missing every second bottle.
  • Debounce the photoeye with a short TON (50 ms) before the counter, so a bottle that wobbles in the beam counts once. The right debounce time comes from the line: at one bottle per second, a 50 ms filter is invisible; at ten per second it would eat real bottles.
  • Tie the counter reset to crate removal as well as the button. If the packer pulls the crate without pressing reset, a stale count of 24 against a fresh crate means the next crate ships with zero new bottles counted.

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: debounce the photoeye

A TON with IN from BottleEye and PT of T#50ms, producing a clean BottleSeen bit from the done output. A genuine bottle blocks the beam far longer than 50 ms at this line speed; a flicker from a wobble or a shiny label does not survive the filter.

// BottleEye ──[TON tDebounce, PT := T#50ms]
// tDebounce.Q ──( )BottleSeen

Rung 2: the CTU

CTU with CU from BottleSeen, PV of 24, and R from the reset conditions in the next step. Gate CU with CratePresent so bottles passing while no crate is in position are not counted into a crate that does not exist. The counter's Q bit is your crate-full signal.

// BottleSeen AND CratePresent ──[CTU cBottles, PV := 24]

Rung 3: reset on button or crate removal

Reset the counter on ResetPB OR NOT CratePresent. The button covers the normal swap; the crate-removal path covers the packer who yanks the full crate first and presses nothing. Either way the next crate starts from zero, which is the only honest count.

// ResetPB OR NOT CratePresent ── R input of cBottles

Rungs 4-5: lamp and infeed interlock

CrateFullLamp follows cBottles.Q directly. InfeedRun runs while the count is below preset and a crate is present: NOT cBottles.Q AND CratePresent. The infeed therefore also pauses during a crate swap, which stops bottles piling against a closed chute.

// Rung 4: cBottles.Q ──( )CrateFullLamp
// Rung 5: NOT cBottles.Q AND CratePresent ──( )InfeedRun

Test with deliberate abuse

Pulse BottleEye 24 times and confirm the lamp lands exactly on the 24th, not the 23rd or 25th. Then abuse it: chatter the eye with 20 ms pulses (must not count), pulse it with no crate present (must not count), pull the crate at a count of 13 and return it (must restart from zero), and hold the reset button down while bottles pass (counter must stay at zero until released — note what your platform does here).

The structured text version

The same logic in IEC 61131-3 structured text — each output written as a boolean equation you can read aloud.

(* Bottle counter, IEC 61131-3 ST *)
tDebounce(IN := BottleEye, PT := T#50ms);
cBottles(CU := tDebounce.Q AND CratePresent,
         R  := ResetPB OR NOT CratePresent,
         PV := 24);
CrateFullLamp := cBottles.Q;
InfeedRun     := NOT cBottles.Q AND CratePresent;

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.

  • Rolling your own counter with ADD and no edge detection. The add executes every scan the eye is blocked, so one bottle adds 40-odd counts and the lamp lights on the first bottle of every crate.
  • Counting the raw photoeye with no debounce. A bottle that rocks in the beam, or sunlight flicker on a reflective label, breaks the beam twice and crates go out two bottles short — the exact fault this job card exists to kill.
  • Forgetting that the IEC reset input dominates: while R is held true the counter ignores CU on most platforms. A reset button that sticks, or a crate switch that bounces, silently swallows bottles with no fault shown.
  • Putting the crate-full decision on a compare rung against the CV value but resetting the counter elsewhere, so for one scan the count is 24 with the compare not yet evaluated. Use the counter's own Q output; it changes in the same instruction that counts.

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.

  • Track total bottles per shift in a second counter that the crate reset does not touch, and move the value to an HMI tag — production will ask for this number within a week of the line working.
  • Add a reject solenoid that fires on a fallen-bottle signal from a second eye, and subtract rejects from the crate count with a CTUD. The parking garage exercise covers up-down counting in detail.
  • Alarm if a crate takes more than 90 seconds to fill at normal line speed — that gap usually means the eye is misaligned and bottles are passing uncounted.

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

Every beginner exercise on this site, this one included, runs on the simulator's free tier — 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. The full curriculum — the structured version of these exercises with feedback on every submission — plus the wiring track, sensor school and cert packs sit in the Basic tier at USD 12 per month and Pro at USD 29 per month. 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, start on the free tier, finish the beginner set, and decide from there.

Run this exercise on the free tier →

Reference

Programmable logic controller on Wikipedia 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.

By PLC Programming SA · Last updated 2026-06-12