Blog4 min readPLC-Ladder Team

Structured Text (ST) in PLC Programming: Syntax, Examples and When to Use It

Structured Text (ST) for PLC programming: variables, IF/CASE, FOR loops, timers and function blocks, with real examples and when to pick ST over ladder.

Structured Text (ST) is the text-based language of IEC 61131-3 - the one that looks like Pascal and reads like ordinary code. (If you searched for "structure text," you've found the right place; the official name is Structured Text, though the misspelling is common enough that half the industry types it.) Where ladder logic shines at interlocks and FBD at signal flow, Structured Text is the language for algorithms: loops, arrays, calculations, string handling, and any logic that would turn a graphical editor into wallpaper. This guide covers the syntax you actually need, working examples, and an honest answer to when ST is — and isn't — the right tool.

Structured Text Syntax: The Ten-Minute Version

An ST program declares its variables, then executes its statements top to bottom once per PLC scan — the same read-inputs / run-logic / write-outputs cycle every language shares. The skeleton:

PROGRAM Example
VAR
    Start, Stop_OK, Motor : BOOL;
    Temperature : REAL;
    PartCount   : INT;
    RunDelay    : TON;        (* function block instance *)
END_VAR

(* statements go here; each ends with a semicolon *)
Motor := (Start OR Motor) AND Stop_OK;
END_PROGRAM

The essentials:

  • Assignment is :=, comparison is =. Writing IF Motor = TRUE works, but IF Motor THEN is cleaner.
  • Strong types. BOOL, INT, DINT, REAL, TIME (T#5s, T#250ms), STRING. You convert explicitly: INT_TO_REAL(PartCount).
  • Comments are (* like this *) or // like this.
  • Operators: AND, OR, NOT, XOR for logic; + - * / MOD for math; < <= = <> >= > for comparison.

Decisions: IF and CASE

IF Temperature > 90.0 THEN
    CoolingValve := TRUE;
    Alarm := TRUE;
ELSIF Temperature > 75.0 THEN
    CoolingValve := TRUE;
    Alarm := FALSE;
ELSE
    CoolingValve := FALSE;
    Alarm := FALSE;
END_IF;

Notice both branches write both outputs. In ST, a variable you don't write keeps last scan's value — the leading cause of "the alarm never turns off" bugs. Either write every output on every path, or set defaults at the top of the scan.

CASE is the natural machine-state selector, and it's how ST does step sequences:

CASE Step OF
    0:  (* idle *)
        IF StartCmd THEN Step := 10; END_IF;
    10: (* filling *)
        FillValve := TRUE;
        IF LevelHigh THEN FillValve := FALSE; Step := 20; END_IF;
    20: (* mixing *)
        Mixer := TRUE;
        MixTimer(IN := TRUE, PT := T#30s);
        IF MixTimer.Q THEN Mixer := FALSE; MixTimer(IN := FALSE); Step := 0; END_IF;
END_CASE;

Loops: The Thing Graphical Languages Can't Do

FOR loops are ST's killer feature — and the reason vendor converters struggle to turn ST into rungs, since ladder has no loop construct at all (we dig into that in Why Ladder Logic Can't Loop). Scanning eight zone temperatures for the hottest:

MaxTemp := ZoneTemp[1];
FOR i := 2 TO 8 DO
    IF ZoneTemp[i] > MaxTemp THEN
        MaxTemp := ZoneTemp[i];
    END_IF;
END_FOR;

In ladder this is eight near-identical rung groups; in ST it's five lines that don't change when the machine grows to twelve zones. One warning: the whole loop runs within one scan. A WHILE loop waiting for a physical input will never see it change and will watchdog-fault the CPU. Loops iterate over data, never over time — time is what the scan cycle and timers are for.

Timers and Function Blocks in ST

Function blocks work exactly as in FBD; you just call them with named parameters instead of drawing wires:

RunDelay(IN := Motor, PT := T#5s);   (* call the TON instance this scan *)
Pump := RunDelay.Q;                  (* read its output pin *)

Call each instance exactly once per scan, unconditionally — bury the call inside an IF and the timer freezes whenever the branch isn't taken. The same rule and the same TON/TOF/CTU blocks appear in every language, which is the deeper point: an ST assignment, an FBD network and a ladder rung are three spellings of one logic. Motor := (Start OR Motor) AND Stop_OK; is the seal-in rung from Ladder Logic for Beginners, token for token.

When to Use Structured Text (and When Not To)

Use ST for: math and scaling, arrays and loops, state machines with many steps, recipes and data handling, string/communication work, and the insides of reusable custom function blocks. Once logic needs an index variable, ST wins outright.

Prefer ladder or FBD for: interlock chains and anything a maintenance tech must diagnose live at the machine. Online rung/block animation shows power flow at a glance; stepping through text under pressure is slower. A common professional pattern is ST on the inside, graphics on the outside: write the algorithm as a function block in ST, then drop that block into ladder or FBD where the plant can see it.

The equivalence between the notations isn't just philosophy — you can watch it. In the PLC-Ladder simulator, type the seal-in and timer lines from this post into the ST editor and flip to the ladder view: the same program animates as rungs, and edits in one view carry to the other. (How that translation actually works under the hood — expressions to contacts, function-block calls to boxes — is the subject of Structured Text to Ladder Logic Converter: How Conversion Actually Works.)

Start by rewriting logic you already know: the seal-in, an on-delay timer, a parts counter. When your ST version behaves identically to the rungs you trust, you haven't just learned a syntax — you've learned that the languages were one thing all along.