Learn PLCs free
Intermediate1 hourLevel controlPump alternationAlarm logicInterlocks

Water Tank Level Control

Water tank level control is one of the most-asked PLC projects on PLCS.net. This example covers two-pump alternating duty/standby, level-driven start/stop, high-high alarm trip, and runtime tracking.

Sequence overview showing inputs flowing into PLC logic and outputs driving actuators, with this example named Water Tank Level Control.Process flow: inputs → logic → outputsInputsLSL, LSH, LSHH5 signalsPLC LogicLevel control · Pump alternationIntermediate · 1 hourOutputsPump1_Run, Pump2_Run, Alarm_HiHi3 signals

I/O list

TagTypeDescription
LSLDILow level switch (start pumps)
LSHDIHigh level switch (stop pumps)
LSHHDIHigh-high level switch (alarm trip)
Pump1_RunDOPump 1 run command
Pump2_RunDOPump 2 run command
Pump1_OLDIPump 1 overload trip
Pump2_OLDIPump 2 overload trip
Alarm_HiHiDOHigh-high alarm horn

Structured Text code

PROGRAM main
VAR
    DutyPump      : INT := 1;        (* 1 or 2 *)
    Filling       : BOOL;             (* TRUE while filling *)
    Pump1Hours    : DINT;             (* runtime hours retentive *)
    Pump2Hours    : DINT;
    AlarmLatched  : BOOL;
    SwapTimer     : TON;
END_VAR

(* High-high latches the alarm and trips both pumps until reset *)
IF LSHH OR Pump1_OL OR Pump2_OL THEN
    AlarmLatched := TRUE;
END_IF;

(* Start filling on low level if not in alarm *)
IF LSL AND NOT AlarmLatched THEN
    Filling := TRUE;
END_IF;

(* Stop filling on high level *)
IF LSH OR AlarmLatched THEN
    Filling := FALSE;
END_IF;

(* Run the duty pump while filling *)
Pump1_Run := Filling AND DutyPump = 1 AND NOT Pump1_OL AND NOT AlarmLatched;
Pump2_Run := Filling AND DutyPump = 2 AND NOT Pump2_OL AND NOT AlarmLatched;

(* Alarm output *)
Alarm_HiHi := AlarmLatched;

(* Swap duty pump every 168 hours (1 week) of duty pump runtime *)
SwapTimer(IN := Filling, PT := T#1h);
IF SwapTimer.Q THEN
    IF DutyPump = 1 THEN Pump1Hours := Pump1Hours + 1; ELSE Pump2Hours := Pump2Hours + 1; END_IF;
    SwapTimer(IN := FALSE);
END_IF;

IF (DutyPump = 1 AND Pump1Hours >= 168) OR (DutyPump = 2 AND Pump2Hours >= 168) THEN
    IF DutyPump = 1 THEN DutyPump := 2; Pump1Hours := 0; ELSE DutyPump := 1; Pump2Hours := 0; END_IF;
END_IF;

How it works

Level sensors: LSL (low) starts the cycle; LSH (high) stops it; LSHH is the alarm-trip safety.

Alarm latching: any of LSHH or pump overloads sets AlarmLatched, which trips both pumps until manually reset (not shown — would be a separate ResetBtn).

Duty pump alternation: every 168 hours of duty pump run-time, the system swaps to the other pump for even wear.

Retentive runtime: Pump1Hours and Pump2Hours are retentive (survive power loss) so duty rotation isn't reset by power blips.

More examples