PiPLC Ladder Language Reference¶
This document is a complete reference for PiPLC's ladder logic language and
.plcprojfile format. It is designed to be given to an LLM so it can generate valid, efficient PiPLC programs.
1. Overview¶
PiPLC is a soft PLC (Programmable Logic Controller) that executes ladder logic programs. Ladder logic is a graphical programming language where programs are organized as rungs on a ladder. Each rung is a horizontal circuit: power flows from left to right through input instructions (contacts, comparisons) and energizes output instructions (coils, timers, counters, math) when all input conditions are met.
Scan Cycle¶
The PLC engine executes all rungs top-to-bottom in a continuous loop: 1. Read Inputs — Capture physical/virtual input states into memory 2. Execute Rungs — Evaluate each rung left-to-right, top-to-bottom 3. Write Outputs — Push output states from memory to physical/virtual outputs 4. Repeat
Power Flow Model¶
- Each rung starts with power on the left rail
- Input instructions (contacts, comparisons) either pass or block power based on their condition
- Elements in series (same rung, left-to-right) implement AND logic — all must pass power
- Elements in parallel (branches with multiple paths) implement OR logic — any path passing power is sufficient
- Output instructions (coils, timers, math) execute their action when they receive power
2. Memory Regions & Addressing¶
Memory Regions¶
| Prefix | Name | Type | Address Format | Range | Description |
|---|---|---|---|---|---|
I: | Input | Bit | I:word/bit | Words 0-7, Bits 0-31 | Physical/virtual inputs |
O: | Output | Bit | O:word/bit | Words 0-7, Bits 0-31 | Physical/virtual outputs |
B: | Internal Bit | Bit | B:word/bit | Words 0-63, Bits 0-31 | Internal relay/flag storage |
N: | Integer | Word | N:word | Words 0-255 | 32-bit signed integers |
T: | Timer | Struct | T:word | 0-63 | Timer structures |
C: | Counter | Struct | C:word | 0-63 | Counter structures |
V: | Interval | Struct | V:word | 0-63 | Interval timer structures |
Bit Addressing¶
Format: REGION:word/bit
I:0/0— Input word 0, bit 0O:1/7— Output word 1, bit 7B:3/15— Internal bit word 3, bit 15
Word Addressing¶
Format: REGION:word
N:0— Integer register 0N:10— Integer register 10
Timer/Counter Sub-Elements¶
Format: REGION:word.SUBELEMENT
Timer sub-elements (T:): | Sub-Element | Type | Description | |-------------|------|-------------| | .DN | BOOL | Done — TRUE when ACC >= PRE | | .TT | BOOL | Timer Timing — TRUE while actively timing | | .EN | BOOL | Enable — TRUE when rung has power | | .PRE | INT | Preset value, in the timer's timeBase units (default ms) | | .ACC | INT | Accumulated value, same units |
Counter sub-elements (C:): | Sub-Element | Type | Description | |-------------|------|-------------| | .DN | BOOL | Done — TRUE when ACC >= PRE (CTU) or ACC <= 0 (CTD) | | .OV | BOOL | Overflow | | .UN | BOOL | Underflow | | .CU | BOOL | Count Up enable | | .CD | BOOL | Count Down enable | | .PRE | INT | Preset value (target count) | | .ACC | INT | Accumulated value (current count) |
Examples: T:0.DN, T:2.ACC, C:0.DN, C:1.PRE
Immediate Constants¶
Bare integers can be used as source operands in math and comparison instructions: - 5, -10, 42, 0
3. Instruction Reference¶
3.1 Contacts (Input Instructions)¶
Contacts read a bit from memory and control whether power flows through the rung.
XIC — Examine If Closed (Normally Open)¶
- Behavior: Passes power if the addressed bit = 1 (TRUE)
- Address: Any bit address (
I:,O:,B:, or sub-element likeT:0.DN,C:0.DN) - XML:
<Instruction type="XIC" address="I:0/0" column="0" />
XIO — Examine If Open (Normally Closed)¶
- Behavior: Passes power if the addressed bit = 0 (FALSE)
- Address: Any bit address
- XML:
<Instruction type="XIO" address="I:0/2" column="1" />
Contacts can also have decorators attached (see Section 4).
3.2 Coils (Output Instructions)¶
Coils write to a bit based on whether the rung has power.
OTE — Output Energize¶
- Behavior: Sets bit to 1 when rung has power, clears to 0 when rung has no power. Re-evaluated every scan.
- Address: Any bit address (
O:,B:, etc.) - XML:
<Instruction type="OTE" address="O:0/0" column="10" />
OTL — Output Latch (Set)¶
- Behavior: Sets bit to 1 when rung has power. Does nothing when rung has no power (bit retains its state). Must be cleared by OTU.
- Address: Any bit address
- XML:
<Instruction type="OTL" address="B:0/0" column="10" />
OTU — Output Unlatch (Reset)¶
- Behavior: Clears bit to 0 when rung has power. Does nothing when rung has no power (bit retains its state).
- Address: Any bit address
- XML:
<Instruction type="OTU" address="B:0/0" column="10" />
3.3 Timers (Output Instructions)¶
Timers accumulate time when their rung condition is met.
Timer preset & resolution. preset (and the running ACC) are 16-bit, so they hold 0..32767. The unit is set by an optional timeBase attribute — "ms" (default, omit it), "s", "min", or "hr". Pick the smallest unit that covers the interval; a millisecond timer caps at ~32.7 s. Examples: 5 s → preset="5000" (ms) or preset="5" timeBase="s"; 10 minutes → preset="10" timeBase="min". Omit timeBase entirely for milliseconds so the file matches the default form.
TON — Timer On-Delay¶
- Behavior: When rung goes TRUE, starts accumulating time. When ACC >= PRE, the DN bit goes TRUE. When rung goes FALSE, ACC resets to 0 and DN clears.
- Address: Timer address (
T:0throughT:63) - Preset: 0..32767 in
timeBaseunits (default ms; e.g. 5000 = 5 s) - XML:
<Instruction type="TON" address="T:0" preset="5000" column="5" /> - XML (5-minute delay):
<Instruction type="TON" address="T:0" preset="5" timeBase="min" column="5" />
TOF — Timer Off-Delay¶
- Behavior: DN is TRUE while rung is TRUE. When rung goes FALSE, starts timing. When ACC >= PRE, DN goes FALSE. Rung going TRUE resets ACC.
- Address: Timer address
- Preset: 0..32767 in
timeBaseunits (default ms) - XML:
<Instruction type="TOF" address="T:1" preset="2000" column="5" />
RTO — Retentive Timer On¶
- Behavior: Like TON but ACC is retained when rung goes FALSE (does not reset). Requires RES instruction to clear.
- Address: Timer address
- Preset: 0..32767 in
timeBaseunits (default ms). For long run-hour totals usetimeBase="hr"or"min". - XML:
<Instruction type="RTO" address="T:2" preset="10000" column="5" />
3.4 Counters (Output Instructions)¶
Counters count rising edges (FALSE-to-TRUE transitions) of their rung condition.
CTU — Count Up¶
- Behavior: Increments ACC on each rising edge. DN goes TRUE when ACC >= PRE.
- Address: Counter address (
C:0throughC:63) - Preset: Target count value
- XML:
<Instruction type="CTU" address="C:0" preset="10" column="5" />
CTD — Count Down¶
- Behavior: Decrements ACC on each rising edge. DN goes TRUE when ACC <= 0.
- Address: Counter address
- Preset: Starting count value
- XML:
<Instruction type="CTD" address="C:1" preset="5" column="5" />
3.5 Reset (Output Instruction)¶
RES — Reset¶
- Behavior: Clears the accumulated value and all status bits of a timer or counter.
- Address: Timer or counter address (
T:xorC:x) - XML:
<Instruction type="RES" address="C:0" column="10" />
3.6 Math Instructions (Output Instructions)¶
Math instructions perform integer arithmetic. They execute when the rung has power.
Binary Math (two operands)¶
| Type | Operation | Formula |
|---|---|---|
ADD | Addition | Dest = SourceA + SourceB |
SUB | Subtraction | Dest = SourceA - SourceB |
MUL | Multiplication | Dest = SourceA × SourceB |
DIV | Division | Dest = SourceA ÷ SourceB (integer division) |
MOD | Modulo | Dest = SourceA % SourceB |
Attributes: sourceA, sourceB, dest — each is an N: address or immediate constant.
XML:
<Instruction type="ADD" sourceA="N:0" sourceB="N:1" dest="N:2" column="5" />
<Instruction type="MUL" sourceA="N:0" sourceB="10" dest="N:1" column="5" />
Unary Math (one operand)¶
| Type | Operation | Formula |
|---|---|---|
NEG | Negate | Dest = -SourceA |
ABS | Absolute value | Dest = |SourceA| |
Attributes: sourceA, dest (no sourceB).
XML:
<Instruction type="NEG" sourceA="N:0" dest="N:1" column="5" />
<Instruction type="ABS" sourceA="N:0" dest="N:1" column="5" />
3.7 Scale Instruction (Output)¶
SCL — Scale¶
- Behavior: Linear scaling:
Dest = (Source - InMin) × (OutMax - OutMin) / (InMax - InMin) + OutMin - Attributes:
source,dest,inMin,inMax,outMin,outMax(all integers) - XML:
3.8 Comparison Instructions (Input Instructions)¶
Comparison instructions compare two values and control power flow. They are input instructions — they pass or block power.
| Type | Operation | Passes power when |
|---|---|---|
EQU | Equal | SourceA == SourceB |
NEQ | Not Equal | SourceA != SourceB |
LES | Less Than | SourceA < SourceB |
LEQ | Less or Equal | SourceA <= SourceB |
GRT | Greater Than | SourceA > SourceB |
GEQ | Greater or Equal | SourceA >= SourceB |
Attributes: sourceA, sourceB — each is an N: address, T:/C: sub-element, or immediate constant.
XML:
<Instruction type="GRT" sourceA="N:0" sourceB="100" column="0" />
<Instruction type="EQU" sourceA="C:0.ACC" sourceB="5" column="0" />
3.9 Bitwise/Logical Instructions (Output Instructions)¶
Binary Bitwise (two operands)¶
| Type | Operation | Formula |
|---|---|---|
BAND | Bitwise AND | Dest = SourceA & SourceB |
BOR | Bitwise OR | Dest = SourceA | SourceB |
BXOR | Bitwise XOR | Dest = SourceA ^ SourceB |
BSL | Bit Shift Left | Dest = (SourceA << SourceB) | IN |
BSR | Bit Shift Right | Dest = (SourceA >> SourceB) | (IN << 31) |
Attributes: sourceA, sourceB, dest. BSL/BSR also have optional ctrl (control bit address for shift-in value).
<Instruction type="BAND" sourceA="N:0" sourceB="N:1" dest="N:2" column="5" />
<Instruction type="BSL" sourceA="N:0" sourceB="N:1" dest="N:2" ctrl="B:0/0" column="5" />
Unary Bitwise (one operand)¶
| Type | Operation | Formula |
|---|---|---|
BNOT | Bitwise NOT | Dest = ~SourceA |
N2B | Number to Bit | Dest = (1 << SourceA) if 0-31, else 0 |
Attributes: sourceA, dest (no sourceB).
3.10 Data Transfer (Output Instruction)¶
MOV — Move¶
- Behavior: Copies source value to destination. Supports immediate constants.
- Attributes:
source,dest - XML:
3.11 Scheduled & Date Instructions (Input Instructions)¶
These are input instructions that control power flow based on date/time.
SCHEDULED_INPUT — One-Time Scheduled Trigger¶
- Behavior: Passes power (TRUE) for exactly one scan when the current date/time matches the scheduled time (minute precision).
- Attributes:
scheduledDateTime— ISO format:yyyy-MM-ddTHH:mm:ss - XML:
WEEKLY_SCHEDULED_INPUT — Weekly Recurring Trigger¶
- Behavior: Passes power (TRUE) for one scan when the current day/time matches. Uses a day bitmask.
- Attributes:
enabledDays— Bitmask integer: bit 0 = Sunday, bit 1 = Monday, ..., bit 6 = Saturday. Example: 62 = Monday-Friday (0b0111110)scheduledTime— Format:HH:mm- XML:
Day bitmask reference: | Day | Bit | Value | |-----|-----|-------| | Sunday | 0 | 1 | | Monday | 1 | 2 | | Tuesday | 2 | 4 | | Wednesday | 3 | 8 | | Thursday | 4 | 16 | | Friday | 5 | 32 | | Saturday | 6 | 64 | | Mon-Fri | 1-5 | 62 | | Every day | 0-6 | 127 | | Weekends | 0,6 | 65 |
Date Compare Instructions (Input)¶
- Behavior: Compare current date against a target date. Passes power based on the comparison result.
| Type | Passes power when |
|---|---|
DATE_LES | Current date < target |
DATE_LEQ | Current date <= target |
DATE_EQU | Current date == target |
DATE_GEQ | Current date >= target |
DATE_GRT | Current date > target |
Attributes: targetDate — ISO format: yyyy-MM-dd
3.12 Interval Instruction (Input)¶
INTERVAL — Periodic Trigger¶
- Behavior: Passes power (TRUE) for exactly one scan at regular intervals. The interval is defined by days, hours, and minutes components. Requires a V: address for persistent state.
- Attributes:
address(V: address),days,hours,minutes - XML:
4. Decorators (Contact Modifiers)¶
Decorators modify the output of contact instructions (XIC/XIO only). They chain after the base contact evaluation. Multiple decorators can be stacked.
XML Format¶
<Instruction type="XIC" address="I:0/0" column="0">
<Decorators>
<Decorator type="Debounce" debounceTime="50" />
<Decorator type="TON" address="T:0" preset="2000" />
</Decorators>
</Instruction>
Decorator Types¶
TON — Timer On-Delay¶
- Output goes TRUE only after input has been TRUE continuously for
presetmilliseconds. - Attributes:
preset(ms),address(T: address for state storage, optional)
TOF — Timer Off-Delay¶
- Output stays TRUE for
presetmilliseconds after input goes FALSE. - Attributes:
preset(ms),address(T: address, optional)
OSR — One-Shot Rising¶
- Output is TRUE for exactly one scan on a FALSE-to-TRUE input transition.
- Attributes: None required.
OSF — One-Shot Falling¶
- Output is TRUE for exactly one scan on a TRUE-to-FALSE input transition.
- Attributes: None required.
Debounce¶
- Output changes only after input has been stable for
debounceTimemilliseconds. - Attributes:
debounceTime(ms)
Counter¶
- Output goes TRUE after the input has transitioned to TRUE
presettimes. - Attributes:
preset(count),address(C: address, optional)
Evaluation Order¶
Decorators are evaluated in document order (first to last). Each decorator's output becomes the next decorator's input:
5. Branches (OR Logic)¶
Branches create parallel paths in a rung. Power flows if any path has continuity (OR logic).
XML Format¶
<Branch>
<Path>
<!-- Elements in series (AND logic within this path) -->
<Instruction type="XIC" address="I:0/0" column="0" />
</Path>
<Path>
<Instruction type="XIC" address="I:0/1" column="0" />
</Path>
<Path>
<Instruction type="XIC" address="B:0/0" column="0" />
</Path>
</Branch>
Rules¶
- A
<Branch>contains 2 or more<Path>elements - Each
<Path>contains instructions evaluated in series (AND) - The branch passes power if ANY path passes power (OR)
- Branches can be nested (a Path can contain another Branch)
- Branches can appear anywhere in a rung alongside regular instructions
Logic Example¶
<!-- Logic: (A OR B) AND C AND D -->
<Rung id="0" comment="(A OR B) AND C AND D">
<Branch>
<Path>
<Instruction type="XIC" address="I:0/0" column="0" />
</Path>
<Path>
<Instruction type="XIC" address="I:0/1" column="0" />
</Path>
</Branch>
<Instruction type="XIC" address="I:0/2" column="5" />
<Instruction type="OTE" address="O:0/0" column="10" />
</Rung>
6. .plcproj XML Format¶
6.1 Root Element & Metadata¶
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>Program Name</Name>
<Description>Optional description</Description>
</Metadata>
<!-- ... -->
</PLCProject>
versionmust be"3.2"(current format)<Name>is required (can be empty)<Description>is optional<HmiFilePath>is optional — path to an associated HMI project file<RemoteConnection>is optional — for remote engine connections:
6.2 Symbol Table¶
<SymbolTable>
<Symbol name="StartButton" address="I:0/0" type="BOOL" description="Start pushbutton" />
<Symbol name="MotorOut" address="O:0/0" type="BOOL" description="Motor contactor" />
<Symbol name="SealIn" address="B:0/0" type="BOOL" description="Seal-in relay" />
<Symbol name="Speed" address="N:0" type="DINT" description="Motor speed setpoint" />
<Symbol name="RunTimer" address="T:0" type="TIMER" description="Run delay timer" />
<Symbol name="PartCount" address="C:0" type="COUNTER" description="Part counter" />
</SymbolTable>
Symbol attributes: - name — Unique identifier (letters, digits, underscore; starts with letter or underscore) - address — Valid PLC address - type — One of: BOOL, INT, DINT, REAL, TIMER, COUNTER, INTERVAL - description — Optional documentation
6.3 Programs, Rungs & Instructions¶
<Programs>
<Program name="Program Name" type="Main">
<Rungs>
<Rung id="0" comment="Description of this rung's logic">
<Instruction type="XIC" address="I:0/0" column="0" />
<Instruction type="OTE" address="O:0/0" column="10" />
</Rung>
<Rung id="1" title="Optional Title" comment="Optional comment">
<!-- More elements -->
</Rung>
</Rungs>
</Program>
</Programs>
Rung attributes: - id — Sequential integer starting from 0 (required) - number — Optional display number - title — Optional short title - comment — Optional description of the rung logic
6.4 Watch List (Optional)¶
<WatchList>
<Watch address="I:0/0" />
<Watch address="O:0/0" />
<Watch address="T:0.ACC" />
</WatchList>
Specifies addresses to monitor during runtime debugging.
Complete File Template¶
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>PROGRAM_NAME</Name>
<Description>DESCRIPTION</Description>
</Metadata>
<SymbolTable>
<!-- One Symbol per address used -->
</SymbolTable>
<Programs>
<Program name="PROGRAM_NAME" type="Main">
<Rungs>
<!-- Rungs here -->
</Rungs>
</Program>
</Programs>
<WatchList>
<!-- Optional watch addresses -->
</WatchList>
</PLCProject>
7. Complete Examples¶
7.1 Button Controls LED (Minimal)¶
The simplest possible program: pressing a button lights an LED.
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>Blink LED</Name>
<Description>Press button to light LED</Description>
</Metadata>
<SymbolTable>
<Symbol name="Button" type="BOOL" address="I:0/0" description="Momentary pushbutton input" />
<Symbol name="LED" type="BOOL" address="O:0/0" description="LED output" />
</SymbolTable>
<Programs>
<Program name="Blink LED" type="Main">
<Rungs>
<Rung id="0" comment="Button controls LED">
<Instruction type="XIC" address="I:0/0" column="0" />
<Instruction type="OTE" address="O:0/0" column="10" />
</Rung>
</Rungs>
</Program>
</Programs>
</PLCProject>
7.2 Motor Start/Stop with Seal-In¶
Classic industrial motor control: start button latches motor on, stop button turns it off, with overload protection.
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>Motor Control</Name>
<Description>Motor start/stop with seal-in relay and overload protection</Description>
</Metadata>
<SymbolTable>
<Symbol name="StartPB" type="BOOL" address="I:0/0" description="Start pushbutton (NO)" />
<Symbol name="StopPB" type="BOOL" address="I:0/1" description="Stop pushbutton (NC wired, NO in logic)" />
<Symbol name="Overload" type="BOOL" address="I:0/2" description="Thermal overload contact (NC wired)" />
<Symbol name="MotorOut" type="BOOL" address="O:0/0" description="Motor contactor output" />
<Symbol name="SealIn" type="BOOL" address="B:0/0" description="Motor seal-in relay" />
<Symbol name="RunLight" type="BOOL" address="O:0/1" description="Motor running indicator" />
</SymbolTable>
<Programs>
<Program name="Motor Control" type="Main">
<Rungs>
<Rung id="0" comment="Start circuit: (Start OR SealIn) AND NOT Stop AND NOT Overload = SealIn">
<Branch>
<Path>
<Instruction type="XIC" address="I:0/0" column="0" />
</Path>
<Path>
<Instruction type="XIC" address="B:0/0" column="0" />
</Path>
</Branch>
<Instruction type="XIO" address="I:0/1" column="5" />
<Instruction type="XIO" address="I:0/2" column="6" />
<Instruction type="OTE" address="B:0/0" column="10" />
</Rung>
<Rung id="1" comment="Motor output follows seal-in relay">
<Instruction type="XIC" address="B:0/0" column="0" />
<Instruction type="OTE" address="O:0/0" column="10" />
</Rung>
<Rung id="2" comment="Running indicator light">
<Instruction type="XIC" address="B:0/0" column="0" />
<Instruction type="OTE" address="O:0/1" column="10" />
</Rung>
</Rungs>
</Program>
</Programs>
</PLCProject>
How it works: - Rung 0: Start button (I:0/0) OR seal-in relay (B:0/0) energizes seal-in, provided stop (I:0/1) and overload (I:0/2) are not active. The XIO instructions check normally-closed contacts — they pass power when the bit is 0 (not pressed/not tripped). - Rung 1-2: When seal-in is energized, motor and indicator follow. - Pressing stop opens the XIO contact, breaking the seal-in circuit.
7.3 Counter with Reset¶
Counts input pulses and resets after reaching the target.
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>Pulse Counter</Name>
<Description>Count external pulses, reset after reaching preset</Description>
</Metadata>
<SymbolTable>
<Symbol name="PulseIn" type="BOOL" address="B:0/0" description="Pulse input signal" />
<Symbol name="PulseCounter" type="COUNTER" address="C:0" description="Pulse counter" />
<Symbol name="BatchDone" type="BOOL" address="O:0/0" description="Batch complete output" />
</SymbolTable>
<Programs>
<Program name="Pulse Counter" type="Main">
<Rungs>
<Rung id="0" comment="Count pulses, target = 10">
<Instruction type="XIC" address="B:0/0" column="0" />
<Instruction type="CTU" address="C:0" preset="10" column="5" />
</Rung>
<Rung id="1" comment="When count reached, energize batch done output">
<Instruction type="XIC" address="C:0.DN" column="0" />
<Instruction type="OTE" address="O:0/0" column="10" />
</Rung>
<Rung id="2" comment="Auto-reset counter when done">
<Instruction type="XIC" address="C:0.DN" column="0" />
<Instruction type="RES" address="C:0" column="10" />
</Rung>
</Rungs>
</Program>
</Programs>
</PLCProject>
7.4 Timed Sequence with Decorators¶
A 1-second clock pulse generator using decorator timers.
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>Clock Pulse Generator</Name>
<Description>1-second symmetric clock using decorator timers</Description>
</Metadata>
<SymbolTable>
<Symbol name="DEC_TON_0" address="T:0" type="TIMER" description="Auto-created for decorator" />
<Symbol name="DEC_TOF_1" address="T:1" type="TIMER" description="Auto-created for decorator" />
<Symbol name="ClockBit" address="B:0/0" type="BOOL" description="1-second clock pulse" />
</SymbolTable>
<Programs>
<Program name="Clock Pulse Generator" type="Main">
<Rungs>
<Rung id="0" comment="1-second pulser: XIO with TON+TOF decorators creates oscillator">
<Instruction type="XIO" address="B:0/0" column="0">
<Decorators>
<Decorator type="TON" address="T:0" preset="500" />
<Decorator type="TOF" address="T:1" preset="500" />
</Decorators>
</Instruction>
<Instruction type="OTE" address="B:0/0" column="10" />
</Rung>
</Rungs>
</Program>
</Programs>
</PLCProject>
7.5 Math and Comparison¶
Arithmetic operations with conditional output.
<?xml version="1.0" encoding="UTF-8"?>
<PLCProject version="3.3">
<Metadata>
<Name>Math Example</Name>
<Description>Add two values and trigger output when sum exceeds threshold</Description>
</Metadata>
<SymbolTable>
<Symbol name="Enable" type="BOOL" address="I:0/0" description="Enable calculation" />
<Symbol name="ValueA" type="DINT" address="N:0" description="First operand" />
<Symbol name="ValueB" type="DINT" address="N:1" description="Second operand" />
<Symbol name="Sum" type="DINT" address="N:2" description="Result of addition" />
<Symbol name="Alarm" type="BOOL" address="O:0/0" description="High value alarm" />
</SymbolTable>
<Programs>
<Program name="Math Example" type="Main">
<Rungs>
<Rung id="0" comment="Calculate sum when enabled">
<Instruction type="XIC" address="I:0/0" column="0" />
<Instruction type="ADD" sourceA="N:0" sourceB="N:1" dest="N:2" column="5" />
</Rung>
<Rung id="1" comment="Alarm when sum exceeds 1000">
<Instruction type="GRT" sourceA="N:2" sourceB="1000" column="0" />
<Instruction type="OTE" address="O:0/0" column="10" />
</Rung>
</Rungs>
</Program>
</Programs>
</PLCProject>
8. Common Patterns¶
Seal-In Circuit (Latch with Start/Stop)¶
- Start button sets seal-in relay via OR branch - Seal-in contact maintains circuit after start button is released - Stop button (XIO) breaks circuit - This is the fundamental pattern for motor start/stop controlState Machine (OTL/OTU)¶
For multi-state sequences, use OTL/OTU on internal bits:
Rung 0: XIC State1 AND Condition → OTU State1, OTL State2
Rung 1: XIC State2 AND Condition → OTU State2, OTL State3
...
Timed Sequence¶
Output turns on 5 seconds after enable.Edge Detection (One-Shot)¶
Use OSR decorator to detect rising edges:
<Instruction type="XIC" address="I:0/0" column="0">
<Decorators>
<Decorator type="OSR" />
</Decorators>
</Instruction>
Pulse Generator (Clock)¶
Creates an oscillating bit with configurable on/off times.Counter with Auto-Reset¶
Rung 0: XIC PulseInput → CTU C:0 (preset=N)
Rung 1: XIC C:0.DN → (action)
Rung 2: XIC C:0.DN → RES C:0
9. Validation Rules¶
File Structure¶
- Must have
<?xml version="1.0" encoding="UTF-8"?>header - Root element must be
<PLCProject>withversionattribute - Must contain
<Metadata>,<SymbolTable>, and<Programs>sections - Use version
"3.2"for full feature support
Addresses¶
- Bit addresses (I:, O:, B:) require word AND bit:
I:0/0(notI:0) - Word addresses (N:) use only word:
N:0(notN:0/0) - Timer/counter addresses use word with optional sub-element:
T:0,T:0.DN - Bit range: 0-31 per word
- All word indices must be non-negative
Instructions¶
- Every instruction must have
typeandcolumnattributes - Timer/counter instructions require
addressandpreset - Math binary instructions require
sourceA,sourceB, anddest - Math unary instructions require
sourceAanddest(nosourceB) - Comparison instructions require
sourceAandsourceB - MOV requires
sourceanddest - SCL requires
source,dest,inMin,inMax,outMin,outMax
Variables¶
- Names must match:
[a-zA-Z_][a-zA-Z0-9_]* - Names must be unique within the symbol table
- Type must match the address region
Rungs¶
- Rung
idmust be sequential starting from 0 - Elements appear in document order (left-to-right evaluation)
- Each rung should have at least one element
- Outputs (coils, timers, math) are typically the last element(s) in a rung
10. Generation Guidelines for LLMs¶
When generating .plcproj files, follow these rules:
Structure¶
- Always output complete, valid XML — never partial files
- Always include the
<?xml version="1.0" encoding="UTF-8"?>header - Set version to
"3.2" - Always include
<SymbolTable>with a<Symbol>for every address used in the program - Use meaningful, descriptive variable names in PascalCase or camelCase (e.g.,
StartButton,MotorOutput,BatchCounter) - Add
descriptionto every symbol explaining its purpose
Rung Design¶
- Use sequential rung
idvalues starting from 0 - Add a
commentto every rung describing the logic in plain language - Assign
columnvalues: input instructions start at 0, increment by 1; output instructions at column 10 - Place input instructions (XIC, XIO, comparisons) on the left, output instructions (OTE, timers, math) on the right
- Each rung should implement one logical concept
Address Allocation¶
- Allocate I: addresses for physical inputs (buttons, sensors, switches)
- Allocate O: addresses for physical outputs (motors, lights, valves)
- Use B: internal bits for intermediate logic (seal-in relays, state flags, internal conditions)
- Use N: registers for numeric values (setpoints, calculations, counters)
- Allocate T: addresses sequentially: T:0, T:1, T:2, ...
- Allocate C: addresses sequentially: C:0, C:1, C:2, ...
- Allocate V: addresses for interval timers: V:0, V:1, ...
Efficiency¶
- Use
OTL/OTUpairs for state machines — they retain state and are clearer thanOTEfor multi-state logic - Use
OTEfor outputs that should directly mirror a condition each scan - Use
B:internal bits to break complex logic into readable intermediate steps rather than creating overly long rungs - Prefer branches (OR logic) over duplicate rungs when multiple conditions should produce the same output
- When using timers as standalone instructions in rungs, check the
.DNsub-element in subsequent rungs to act on completion - When you need edge detection (trigger on transition, not level), use the OSR or OSF decorator
Explanation¶
- Before the XML output, briefly explain the program's purpose and I/O assignments
- After the XML output, summarize the logic flow: what each rung does and how they work together