ladder logic

tank fill control

plc exercise

Tank Fill Control PLC Exercise: Ladder Logic

‌
Flat vector diagram of a tank fill control system showing level sensors, inlet valve, transfer pump, and connections to a PLC I/O rack for a ladder logic exercise

A tank fill sequence is one of the first real-world problems you should build in ladder logic. It has everything: sensor inputs, timed sequences, output interlocks, and a fault that can cause a physical mess if you get the logic wrong. This exercise walks you through a complete two-setpoint tank fill program in Studio 5000 (Allen-Bradley ControlLogix or CompactLogix), from the I/O table all the way to the fault latch and operator reset. You can adapt every rung to any platform.

What Is a Tank Fill Control PLC Exercise?

A tank fill control PLC exercise is a structured ladder logic program that automatically fills a tank to a high-level setpoint, stops filling, and waits until the level drops to a low-level setpoint before filling again. The PLC monitors discrete level switches, controls an inlet solenoid valve and a fill pump, and latches a fault if the tank overflows or if the fill cycle takes too long. It is the foundation for real liquid-handling applications like chemical dosing, buffer tanks, and process water systems.

The Scenario and I/O Table

Picture a 500-litre stainless buffer tank. Water enters through a pneumatic inlet valve; a transfer pump on the outlet feeds a downstream process. Two float switches monitor level: one at 20% (low) and one at 90% (high). A third switch at 98% is the high-high trip, wired hardwired AND in software. The HMI has a Start/Stop pushbutton and a Fault Acknowledge button.

Tag NameI/O TypeAddressDescription
Level_LowDILocal:1:I.Data.0Float switch, closes at 20% level
Level_HighDILocal:1:I.Data.1Float switch, closes at 90% level
Level_HighHighDILocal:1:I.Data.2Float switch, closes at 98% (trip)
HMI_StartDILocal:1:I.Data.3Operator start command from HMI
HMI_StopDILocal:1:I.Data.4Operator stop command from HMI
HMI_FaultAckDILocal:1:I.Data.5Fault acknowledge from HMI
Inlet_ValveDOLocal:2:O.Data.0Pneumatic inlet valve, energise to open
Fill_PumpDOLocal:2:O.Data.1Fill pump contactor
Tank fill exercise I/O table, Studio 5000 CompactLogix
Float switches are normally open (NO) in most catalogue listings, meaning the contact closes when the float rises to that level. Confirm this with your datasheet before writing a single rung. If you wire an NC switch into an XIC contact expecting NO behaviour, your tank fills to the top and stays there. I have seen it happen on a commissioning trip with a Gems Sensors LS-1800 that was ordered as NC by mistake.

If you need a refresher on how PNP and NPN level switches connect to your input module, the post on 3-wire sensor wiring for PNP and NPN devices covers it clearly. And if you are unsure whether your input module is sinking or sourcing, sinking vs sourcing PLC I/O explains the difference with real wiring diagrams.

Sequence Design Before You Write a Rung

Write down the sequence in plain English first. It sounds obvious, but skipping this step is why people end up with latching logic that never resets. Here is the sequence for this exercise:

  1. Operator presses HMI Start. System is enabled.
  2. If Level_Low is TRUE (tank at or below 20%), open Inlet_Valve.
  3. After a 2-second valve-open delay, start Fill_Pump.
  4. When Level_High goes TRUE (tank at 90%), stop Fill_Pump first, then close Inlet_Valve.
  5. Wait. When level drops and Level_Low goes TRUE again, repeat from step 2.
  6. If Level_HighHigh goes TRUE at any time, latch a High_High_Fault, stop everything, and wait for HMI_FaultAck.
  7. If the fill cycle runs longer than 120 seconds without reaching Level_High, latch a Fill_Timeout_Fault.
  8. Operator presses HMI Stop to disable the system cleanly.

Notice the pump stops before the valve closes in step 4. This prevents water hammer from closing the valve against a running pump. Small detail, but it matters on real pipework. You see the same sequencing logic in the PLC pump station exercise and in the bottle filling machine project, both of which extend this pattern with multiple pumps and product counters.

Tank Fill Ladder Logic: Rung by Rung

The ladder below is written for Studio 5000 notation. XIC is a normally open contact, XIO is a normally closed contact, OTL latches a bit ON, OTU unlatches it, and OSR is a one-shot rising edge. TON is a retentive-on-delay timer. If you are on Siemens TIA Portal, the logic maps directly: use SR/RS function blocks for latches and the IEC TON block. The XIC vs XIO explainer is worth bookmarking if contact types trip you up.

Tank Fill Control: System Enable, Fill Sequence, Fault Latch, and Timeout (Studio 5000). Ladder logic (12 rungs): Rung 0: examine if HMI_Start is on (XIC), then examine if HMI_Stop is off (XIO), then examine if High_High_Fault is off (XIO), then examine if Fill_Timeout_Fault is off (XIO), then latch output System_Enabled (OTL). Rung 1: examine if HMI_Stop is on (XIC), then unlatch output System_Enabled (OTU). Rung 2: examine if System_Enabled is on (XIC), then examine if Level_High is off (XIO), then examine if High_High_Fault is off (XIO), then latch output Fill_Active (OTL). Rung 3: examine if Level_High is on (XIC), then unlatch output Fill_Active (OTU). Rung 4: examine if Fill_Active is on (XIC), then energize output Inlet_Valve (OTE). Rung 5: examine if Fill_Active is on (XIC), then TON on Valve_Open_Delay. Rung 6: examine if Valve_Open_Delay.DN is on (XIC), then examine if Level_High is off (XIO), then examine if High_High_Fault is off (XIO), then energize output Fill_Pump (OTE). Rung 7: examine if Fill_Active is on (XIC), then TON on Fill_Timeout. Rung 8: examine if Fill_Timeout.DN is on (XIC), then examine if Level_High is off (XIO), then examine if Timeout_OS is on (XIC), then latch output Fill_Timeout_Fault (OTL). Rung 9: examine if Level_HighHigh is on (XIC), then examine if HH_OS is on (XIC), then latch output High_High_Fault (OTL). Rung 10: examine if High_High_Fault is on (XIC), then examine if HMI_FaultAck is on (XIC), then examine if Level_HighHigh is off (XIO), then unlatch output High_High_Fault (OTU). Rung 11: examine if Fill_Timeout_Fault is on (XIC), then examine if HMI_FaultAck is on (XIC), then examine if Fill_Active is off (XIO), then unlatch output Fill_Timeout_Fault (OTU). Rung 1 latches System_Enabled on HMI Start, blocked by either fault. Rung 2 unlatches on HMI Stop. Rung 3 latches Fill_Active when the tank is below the high-level setpoint; rung 4 unlatches it when Level_High trips. Rung 5 drives the Inlet_Valve output. Rung 6 starts the 2-second valve-open delay timer. Rung 7 energises Fill_Pump only after the valve delay expires and the tank is not full. Rung 8 runs the 120-second fill timeout timer. Rung 9 latches Fill_Timeout_Fault on a one-shot if the timer expires before Level_High closes. Rung 10 latches High_High_Fault on the rising edge of the 98% switch. Rungs 11-12 unlatch each fault only when the operator acknowledges AND the triggering condition has cleared.

Tank Fill Control: System Enable, Fill Sequence, Fault Latch, and Timeout (Studio 5000)Ladder logic
Toggle inputs
Rung 0
Ladder logic rung: examine if HMI_Start is on (XIC), then examine if HMI_Stop is off (XIO), then examine if High_High_Fault is off (XIO), then examine if Fill_Timeout_Fault is off (XIO), then latch output System_Enabled (OTL) examine if HMI_Start is on (XIC), then examine if HMI_Stop is off (XIO), then examine if High_High_Fault is off (XIO), then examine if Fill_Timeout_Fault is off (XIO), then latch output System_Enabled (OTL) XIC HMI_Start HMI_Start HMI_Start XIO HMI_Stop HMI_Stop HMI_Stop XIO High_High_Fault High_High_Fault High_High_Fault XIO Fill_Timeout_Fault Fill_Timeout_Fault Fill_Timeout_Fault OTL System_Enabled System_Enabled System_Enabled L
Rung 1
Ladder logic rung: examine if HMI_Stop is on (XIC), then unlatch output System_Enabled (OTU) examine if HMI_Stop is on (XIC), then unlatch output System_Enabled (OTU) XIC HMI_Stop HMI_Stop HMI_Stop OTU System_Enabled System_Enabled System_Enabled U
Rung 2
Ladder logic rung: examine if System_Enabled is on (XIC), then examine if Level_High is off (XIO), then examine if High_High_Fault is off (XIO), then latch output Fill_Active (OTL) examine if System_Enabled is on (XIC), then examine if Level_High is off (XIO), then examine if High_High_Fault is off (XIO), then latch output Fill_Active (OTL) XIC System_Enabled System_Enabled System_Enabled XIO Level_High Level_High Level_High XIO High_High_Fault High_High_Fault High_High_Fault OTL Fill_Active Fill_Active Fill_Active L
Rung 3
Ladder logic rung: examine if Level_High is on (XIC), then unlatch output Fill_Active (OTU) examine if Level_High is on (XIC), then unlatch output Fill_Active (OTU) XIC Level_High Level_High Level_High OTU Fill_Active Fill_Active Fill_Active U
Rung 4
Ladder logic rung: examine if Fill_Active is on (XIC), then energize output Inlet_Valve (OTE) examine if Fill_Active is on (XIC), then energize output Inlet_Valve (OTE) XIC Fill_Active Fill_Active Fill_Active OTE Inlet_Valve Inlet_Valve Inlet_Valve
Rung 5
Ladder logic rung: examine if Fill_Active is on (XIC), then TON on Valve_Open_Delay examine if Fill_Active is on (XIC), then TON on Valve_Open_Delay XIC Fill_Active Fill_Active Fill_Active TON Valve_Open_Delay T#2s 0 TONTimerValve_Open_DelayValve_Open_DelayPresetT#2sT#2sAccum00
Rung 6
Ladder logic rung: examine if Valve_Open_Delay.DN is on (XIC), then examine if Level_High is off (XIO), then examine if High_High_Fault is off (XIO), then energize output Fill_Pump (OTE) examine if Valve_Open_Delay.DN is on (XIC), then examine if Level_High is off (XIO), then examine if High_High_Fault is off (XIO), then energize output Fill_Pump (OTE) XIC Valve_Open_Delay.DN Valve_Open_Delay.DN Valve_Open_Delay.DN XIO Level_High Level_High Level_High XIO High_High_Fault High_High_Fault High_High_Fault OTE Fill_Pump Fill_Pump Fill_Pump
Rung 7
Ladder logic rung: examine if Fill_Active is on (XIC), then TON on Fill_Timeout examine if Fill_Active is on (XIC), then TON on Fill_Timeout XIC Fill_Active Fill_Active Fill_Active TON Fill_Timeout T#120s 0 TONTimerFill_TimeoutFill_TimeoutPresetT#120sT#120sAccum00
Rung 8
Ladder logic rung: examine if Fill_Timeout.DN is on (XIC), then examine if Level_High is off (XIO), then examine if Timeout_OS is on (XIC), then latch output Fill_Timeout_Fault (OTL) examine if Fill_Timeout.DN is on (XIC), then examine if Level_High is off (XIO), then examine if Timeout_OS is on (XIC), then latch output Fill_Timeout_Fault (OTL) XIC Fill_Timeout.DN Fill_Timeout.DN Fill_Timeout.DN XIO Level_High Level_High Level_High OSR Timeout_OS Timeout_OS Timeout_OS OSR OTL Fill_Timeout_Fault Fill_Timeout_Fault Fill_Timeout_Fault L
Rung 9
Ladder logic rung: examine if Level_HighHigh is on (XIC), then examine if HH_OS is on (XIC), then latch output High_High_Fault (OTL) examine if Level_HighHigh is on (XIC), then examine if HH_OS is on (XIC), then latch output High_High_Fault (OTL) XIC Level_HighHigh Level_HighHigh Level_HighHigh OSR HH_OS HH_OS HH_OS OSR OTL High_High_Fault High_High_Fault High_High_Fault L
Rung 10
Ladder logic rung: examine if High_High_Fault is on (XIC), then examine if HMI_FaultAck is on (XIC), then examine if Level_HighHigh is off (XIO), then unlatch output High_High_Fault (OTU) examine if High_High_Fault is on (XIC), then examine if HMI_FaultAck is on (XIC), then examine if Level_HighHigh is off (XIO), then unlatch output High_High_Fault (OTU) XIC High_High_Fault High_High_Fault High_High_Fault XIC HMI_FaultAck HMI_FaultAck HMI_FaultAck XIO Level_HighHigh Level_HighHigh Level_HighHigh OTU High_High_Fault High_High_Fault High_High_Fault U
Rung 11
Ladder logic rung: examine if Fill_Timeout_Fault is on (XIC), then examine if HMI_FaultAck is on (XIC), then examine if Fill_Active is off (XIO), then unlatch output Fill_Timeout_Fault (OTU) examine if Fill_Timeout_Fault is on (XIC), then examine if HMI_FaultAck is on (XIC), then examine if Fill_Active is off (XIO), then unlatch output Fill_Timeout_Fault (OTU) XIC Fill_Timeout_Fault Fill_Timeout_Fault Fill_Timeout_Fault XIC HMI_FaultAck HMI_FaultAck HMI_FaultAck XIO Fill_Active Fill_Active Fill_Active OTU Fill_Timeout_Fault Fill_Timeout_Fault Fill_Timeout_Fault U
energizedTip: click a contact in the diagram to flip its bit.
Rung 1 latches System_Enabled on HMI Start, blocked by either fault. Rung 2 unlatches on HMI Stop. Rung 3 latches Fill_Active when the tank is below the high-level setpoint; rung 4 unlatches it when Level_High trips. Rung 5 drives the Inlet_Valve output. Rung 6 starts the 2-second valve-open delay timer. Rung 7 energises Fill_Pump only after the valve delay expires and the tank is not full. Rung 8 runs the 120-second fill timeout timer. Rung 9 latches Fill_Timeout_Fault on a one-shot if the timer expires before Level_High closes. Rung 10 latches High_High_Fault on the rising edge of the 98% switch. Rungs 11-12 unlatch each fault only when the operator acknowledges AND the triggering condition has cleared.

Why the Fault Latch Uses OSR (One-Shot Rising)

Driving OTL directly from Level_HighHigh without a one-shot seems simpler, but it creates a problem: if the float switch bounces or the level hovers right at the setpoint, the OTL instruction fires repeatedly and the unlatch logic fights it. The OSR catches exactly one rising edge per event. The fault latches once, stays latched, and only clears on a deliberate acknowledge. This pattern is described in more detail in the post on normally open vs normally closed contacts in ladder logic.

The High_High_Fault bit must also feed a hardwired interlock, not just ladder logic. If your PLC CPU faults or the program stops executing, the software latch is useless. Wire Level_HighHigh through a safety relay or a hardwired contactor to cut power to the Inlet_Valve independently. The emergency stop circuit wiring post explains the categories of hardwired protection.

The Fill Timeout: Picking a Realistic Preset

120 seconds is a placeholder. You need to calculate it for your tank. Take the tank volume between Level_Low and Level_High (say 400 litres), divide by your minimum inlet flow rate (say 5 litres per second), and add 30% margin. In this example: 400/5 = 80 seconds, plus 30% = 104 seconds, round to 110. If your inlet flow rate changes with supply pressure, use the worst-case low-flow rate. The TON, TOF and TONR timer explainer is useful if you want to understand how the timer accumulator behaves across scan cycles.

Adding a Cycle Counter for Maintenance Tracking

Once the basic sequence is working, add a CTU counter to track fill cycles. Drive it from an OSR on the rising edge of Level_High (the fill-complete event). Display the accumulated value on your HMI and reset it after a maintenance interval. The PLC counter instructions post shows exactly how CTU, CTD and CTUD work, which saves time when you wire in the reset logic.

Extending the Exercise: Analog Level Feedback

The two-switch version is solid for learning, but real tanks often have a 4-20 mA ultrasonic or hydrostatic level transmitter alongside the discrete switches. The transmitter gives you a continuous percentage so you can display a level bar on the HMI and catch a slow leak (level dropping when Fill_Active is FALSE). Wiring and scaling that signal is covered step by step in PLC analog input wiring for 4-20 mA and the matching 4-20 mA scaling formula guide. Once you have the raw count, use the analog scaling calculator to check your EU conversion numbers before you type them into the program.

Flat vector illustration of two tank fill control ladder rungs showing level switch contacts, OTL fill active latch, and TON fill timeout fault logic for a PLC exercise
Key ladder rungs: Fill_Active latch driven by level switches (left) and the 120-second fill timeout fault latch (right).

Testing and Troubleshooting the Exercise

Before connecting real field devices, simulate the program by forcing input bits in online mode. Run through every branch:

  • Force HMI_Start TRUE: System_Enabled should latch ON.
  • Force Level_Low TRUE with Level_High FALSE: Fill_Active should latch, Inlet_Valve output should energise, and Fill_Pump should come on after 2 seconds.
  • Force Level_High TRUE: Fill_Pump should drop out immediately, then Inlet_Valve should de-energise as Fill_Active unlatches.
  • Force Level_HighHigh TRUE: High_High_Fault should latch, Inlet_Valve and Fill_Pump should both drop out. Confirm they stay out even if you force Level_Low TRUE.
  • Acknowledge the fault with HMI_FaultAck TRUE while Level_HighHigh is FALSE: High_High_Fault should unlatch.
  • Let Fill_Timeout accumulate to 120 seconds without Level_High going TRUE: Fill_Timeout_Fault should latch.

If you do not have hardware, the post on how to simulate ladder logic without a PLC covers software emulation options. For testing with real I/O, how to test a PLC input and PLC I/O fault diagnosis with a multimeter are the right next reads.

If you want to go further with this exercise on a Siemens platform, the S7-1200 first program in TIA Portal guide shows you how to create the program block and download it, and S7-1200 timers in TIA Portal maps the TON block directly to the IEC syntax you will use instead of the Studio 5000 style shown here.

The tank fill sequence is a great foundation. From here, try the PLC pump station exercise to add duty/standby pump alternation on top of your fill logic, or work through the car wash PLC exercise to practice multi-step sequencing with timer-driven state transitions. If you want to push the analog side, add a 4-20 mA level transmitter and scale it with the steps in 4-20 mA scaling in a PLC using ST and ladder.

Related Blogs