Skip to content

PiPLC Ladder Language Reference

This document is a complete reference for PiPLC's ladder logic language and .plcproj file 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 0
  • O:1/7 — Output word 1, bit 7
  • B:3/15 — Internal bit word 3, bit 15

Word Addressing

Format: REGION:word

  • N:0 — Integer register 0
  • N: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 like T: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:0 through T:63)
  • Preset: 0..32767 in timeBase units (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 timeBase units (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 timeBase units (default ms). For long run-hour totals use timeBase="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:0 through C: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:x or C: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:
    <Instruction type="SCL" source="N:0" dest="N:1" inMin="0" inMax="1023" outMin="0" outMax="100" column="5" />
    

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).

<Instruction type="BNOT" sourceA="N:0" dest="N:1" column="5" />

3.10 Data Transfer (Output Instruction)

MOV — Move

  • Behavior: Copies source value to destination. Supports immediate constants.
  • Attributes: source, dest
  • XML:
    <Instruction type="MOV" source="N:0" dest="N:1" column="5" />
    <Instruction type="MOV" source="0" dest="N:5" column="5" />
    

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:
    <Instruction type="SCHEDULED_INPUT" scheduledDateTime="2026-12-31T23:59:00" column="0" />
    

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:
    <Instruction type="WEEKLY_SCHEDULED_INPUT" enabledDays="62" scheduledTime="08:00" column="0" />
    

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

<Instruction type="DATE_GEQ" targetDate="2026-06-01" column="0" />

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:
    <Instruction type="INTERVAL" address="V:0" days="0" hours="1" minutes="30" column="0" />
    

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 preset milliseconds.
  • Attributes: preset (ms), address (T: address for state storage, optional)

TOF — Timer Off-Delay

  • Output stays TRUE for preset milliseconds 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 debounceTime milliseconds.
  • Attributes: debounceTime (ms)

Counter

  • Output goes TRUE after the input has transitioned to TRUE preset times.
  • 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:

Base Contact → [Decorator 1] → [Decorator 2] → ... → Rung Power

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>
  • version must 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:
    <RemoteConnection>
      <Host>192.168.1.100</Host>
      <Port>5000</Port>
      <ContextId>optional-id</ContextId>
      <ContextName>optional-name</ContextName>
    </RemoteConnection>
    

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)

Rung: (StartPB OR SealIn) AND NOT StopPB → OTE SealIn
- 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 control

State 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
...
Each state uses a B: bit. OTU clears the current state, OTL sets the next.

Timed Sequence

Rung 0: XIC Enable → TON T:0 (5000ms)
Rung 1: XIC T:0.DN → OTE Output
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>
Output is TRUE for exactly one scan when input transitions from FALSE to TRUE.

Pulse Generator (Clock)

Rung: XIO ClockBit [TON 500ms] [TOF 500ms] → OTE ClockBit
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> with version attribute
  • 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 (not I:0)
  • Word addresses (N:) use only word: N:0 (not N: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 type and column attributes
  • Timer/counter instructions require address and preset
  • Math binary instructions require sourceA, sourceB, and dest
  • Math unary instructions require sourceA and dest (no sourceB)
  • Comparison instructions require sourceA and sourceB
  • MOV requires source and dest
  • 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 id must 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

  1. Always output complete, valid XML — never partial files
  2. Always include the <?xml version="1.0" encoding="UTF-8"?> header
  3. Set version to "3.2"
  4. Always include <SymbolTable> with a <Symbol> for every address used in the program
  5. Use meaningful, descriptive variable names in PascalCase or camelCase (e.g., StartButton, MotorOutput, BatchCounter)
  6. Add description to every symbol explaining its purpose

Rung Design

  1. Use sequential rung id values starting from 0
  2. Add a comment to every rung describing the logic in plain language
  3. Assign column values: input instructions start at 0, increment by 1; output instructions at column 10
  4. Place input instructions (XIC, XIO, comparisons) on the left, output instructions (OTE, timers, math) on the right
  5. Each rung should implement one logical concept

Address Allocation

  1. Allocate I: addresses for physical inputs (buttons, sensors, switches)
  2. Allocate O: addresses for physical outputs (motors, lights, valves)
  3. Use B: internal bits for intermediate logic (seal-in relays, state flags, internal conditions)
  4. Use N: registers for numeric values (setpoints, calculations, counters)
  5. Allocate T: addresses sequentially: T:0, T:1, T:2, ...
  6. Allocate C: addresses sequentially: C:0, C:1, C:2, ...
  7. Allocate V: addresses for interval timers: V:0, V:1, ...

Efficiency

  1. Use OTL/OTU pairs for state machines — they retain state and are clearer than OTE for multi-state logic
  2. Use OTE for outputs that should directly mirror a condition each scan
  3. Use B: internal bits to break complex logic into readable intermediate steps rather than creating overly long rungs
  4. Prefer branches (OR logic) over duplicate rungs when multiple conditions should produce the same output
  5. When using timers as standalone instructions in rungs, check the .DN sub-element in subsequent rungs to act on completion
  6. When you need edge detection (trigger on transition, not level), use the OSR or OSF decorator

Explanation

  1. Before the XML output, briefly explain the program's purpose and I/O assignments
  2. After the XML output, summarize the logic flow: what each rung does and how they work together