v1.132 ← Back to Simulator

ACC PLC Simulator

Browser-based Ladder Logic & Structured Text Simulator  ·  v1.132  ·  accautomation.ca

The ACC PLC Simulator is a fully functional, browser-based ladder logic simulator. It runs entirely in a single HTML file with no installation, no server, and no dependencies beyond a modern web browser.

Use it to learn ladder logic fundamentals, prototype control programs, demonstrate PLC concepts in a classroom, or connect to 3D industrial scenes for a visual simulation experience.

Key Features

  • Full ladder logic editor — contacts, rising/falling edge contacts, coils, timers (incl. retentive), counters (incl. up/down), compare and limit test, MOV
  • Math and data — ADD, SUB, MUL, DIV, MOD, INC, DEC, CPT expressions, SCL scaling, COP, FLL, PACK, UNPACK on 4000 data registers (D1–D4000) with INT, UINT, WORD, DINT and REAL types, plus indirect addressing with pointers (D[Dn])
  • Sequencing and program flow — bit shift registers (BSL, BSR), a drum sequencer (DRUM) with a step-table editor, jump and label (JMP, LBL) and master control zones (MCR, END MCR)
  • System bits & words — first scan, clock pulses, real-time clock and scan statistics (SB1–SB18, SW1–SW19)
  • Real-time scan cycle — 100 ms scan with live power-flow highlighting
  • Online editing — change a ladder or Structured Text program while it runs, review the marked changes, then ACCEPT to swap it in between two scans (see Online Editing)
  • Analog I/O — AX1–AX8 inputs (slider-driven, 0–4095) and AY1–AY4 outputs
  • Parallel branches — OR logic, up to any depth
  • Save / Load — 10 named slots in browser localStorage plus JSON file export/import
  • 3D Scene connection — BroadcastChannel link to interactive machine visualisations
  • No installation — single HTML file, works offline after first load
Tip: Press F5 to start the scan, F4 to stop. Double-click any element on the ladder to edit it.

Interface Tour

Layout of the main simulator window

Toolbar (top)

ControlDescription
▶ RUNStart the continuous scan cycle (100 ms interval). Also F5.
■ STOPHalt the scan. All outputs de-energise. Also F4.
⏭ STEPExecute exactly one scan. Only available in STOP mode with no scene connected. Fades out (disabled) when a 3D scene is linked.
✎ EDITShown in RUN only. Opens an online edit: change the program while it runs, then ACCEPT or CANCEL from the banner. Any edit in RUN opens one automatically. See Online Editing.
✕ CLEARDelete all rungs and reset all memory (with confirmation). Also clears the undo history and address-comment map. On the next page load the default Control Panel program will open (auto-restore is suppressed once). Refused while the program is running — stop first — and while an online edit is pending.
↩ UNDOReverse the last edit. Greyed out when no history is available. Also Ctrl+Z. In RUN with no edit pending, the previous program opens as an online edit to review and accept.
↪ REDORe-apply an undone edit. Also Ctrl+Y or Ctrl+Shift+Z.
☀ THEMEToggle between dark (default) and light theme. Your preference is saved and restored automatically on the next visit.
💾 SAVESave the current program to a named slot or file. While an online edit is pending, the running program is saved, not the edit.
📂 LOADRestore a previously saved program. Refused while an online edit is pending.
CONNECTOpen the 3D Scene browser to link a machine visualisation.
LED + StateGreen pulsing = RUN, red = STOP.
Scan counterShows last scan time (ms) and total scan count.

Zoom Controls

A − / 100% / + / 1:1 control cluster sits in the bottom-right corner of the ladder panel.

ControlDescription
−Zoom out 10% per click. Minimum 30%.
+Zoom in 10% per click. Maximum 300%.
1:1Reset zoom to 100%.
FITScale the whole program so every rung fits in the window at once.
Ctrl + ScrollHold Ctrl (⌘ on Mac) and scroll the mouse wheel over the ladder panel to zoom smoothly.
Fit on load: when the simulator opens, the ladder automatically scales so all rungs are visible — no scrolling needed to see the whole program. Zoom in with + to inspect detail, then press FIT to see everything again.

Left Panel — Instruction Palette

Click an instruction type to select it (it highlights in blue). You can then insert it into the ladder in two ways:

  • Right-click any contact on the ladder and choose Insert Before / Insert After. For a compare, the dialog also asks for the Data Type used to read D registers.
  • Drag and drop directly from the palette onto a rung or the Add New Rung strip (see below).
Hover help: rest the mouse on any palette item and a card opens beside it with a one-line description, a small rung picture, a timing chart where it helps (timers, counters, edges, compares, shift registers, drum) and the key fields. Press the ? on the item, the ? Help link in the card, or F1, and this help opens at that exact instruction — in one help tab that is reused, so repeated look-ups don't pile up tabs. F1 over an instruction on the ladder, or Help for … in a contact's right-click menu, does the same.

The address field pre-fills with the next available address for the selected type. A ◀ collapse tab sits on the right edge of the palette. Click it to collapse the panel to a thin strip, giving the ladder more horizontal space. Click the ▶ tab again to restore it.

Drag and Drop from the Palette

Every instruction in the palette can be dragged directly onto the ladder. This is the fastest way to build programs.

  • Drag onto an existing rung — a blue dashed border highlights the target rung as you hover. Drop to insert the instruction:
Instruction typeWhat happens on drop
XIC, XIO, P, NNew contact inserted before the output element. Edit modal opens immediately to confirm the address.
GRT, LES, GEQ, LEQ, EQU, NEQ, LIMNew compare block inserted before the output. Edit modal opens to set the address and comparand (or Low/High limits for LIM).
OTE, OTL, OTUOn a rung that ends in a coil, the coil type changes (address kept). On a rung that ends in a block, the block is replaced by a coil at the next free Y. Edit modal opens.
TON, TOF, RTO, CTU, CTD, CTUD, RES, MOV, and the Math and Move / Data blocksThe output element is replaced with the block. Edit modal opens to set the address and operands (preset for timers and counters, the count-down input for CTUD).
BSL, BSR, DRUM, JMP, MCRThe output element is replaced with the block. Edit modal opens (MCR shows its fixed address, MCR).
LBLInserted at the start of the rung. A rung takes one label; a second is refused.
END MCRInserts a new END MCR rung below the target rung — END MCR always sits on a rung of its own.

When a drop replaces the rung's output, the status bar says so — press UNDO to get it back. In RUN, a palette drop or a new rung opens an online edit instead of changing the running program.

  • Drag onto an Insert Strip (between rungs) or Add New Rung strip — a new rung is created at that position with a sensible default pairing (e.g. XIC → OTE, GRT → OTE, XIC → TON, XIC → DRUM, LBL · XIC → OTE; END MCR on its own). The edit modal opens on the primary element.
Tip: After dropping, the Edit dialog opens automatically so you can confirm or change the address without a second click. Press Enter to accept and close.

Centre — Ladder Canvas

The main programming area. Rungs are displayed as horizontal rails. In STOP mode, contact bars show bit state (green = energised). In RUN mode, powered wires and energised coils glow green.

  • Double-click any element to open the Edit dialog.
  • Double-click the rung header bar (the comment area) to edit the rung comment directly. A faint placeholder text appears on rungs with no comment.
  • Right-click any contact for the context menu (insert, delete, add parallel branch).
  • Drag the handle on the rung header to reorder rungs.
  • Hover between rungs to reveal the Insert Rung strip.

All of these work in RUN too: the first change opens an online edit. A full-width amber ONLINE EDIT bar with ACCEPT and CANCEL appears between the toolbar and the ladder, and changed rungs are marked EDIT, NEW or DELETED ON ACCEPT.

Right Panel — I/O Tabs

Eight tabs: I/O (used addresses summary), X, Y, C, T, CT, AN (analog), SYS (system bits and words). See the I/O Panel Tabs section for details.

A ▶ collapse tab sits on the left edge of the I/O panel. Click it to collapse the panel and give the ladder more space. Click the ◀ tab again to restore it.

Tip: Collapse both panels at once for a full-width ladder view — ideal on smaller screens or when editing a large program. The ladder automatically re-flows to fill the available space.

Light / Dark Theme

Click the ☀ THEME button in the toolbar to switch between the default dark theme and a softer light theme. The button label changes to 🌙 THEME when light mode is active.

In the light theme, energised contacts and I/O indicators use a bright vivid green with a glow halo to ensure ON states remain clearly distinguishable from OFF states.

Persistent: Your theme choice is saved in browser storage and restored automatically every time you open the simulator — no need to switch again after refreshing the page.

Status Bar (bottom)

Displays the current mode (RUN/STOP), contextual hints, and the system clock. After most actions a confirmation message appears here — including why an online-edit ACCEPT was refused and which outputs were left ON.

Quick Start

Get a rung running in under two minutes

  1. Open acc-plc-simulator.html in any modern browser.
  2. The default Start/Stop/Jog program loads automatically.
  3. Press F5 (or click RUN) — the LED turns green and the scan begins.
  4. On the X tab (right panel), click X1 to toggle the Start input ON — Y1 energises.
  5. Click X1 again to release it — Y1 stays on (seal-in circuit).
  6. Click X2 to pulse the Stop — Y1 de-energises.
  7. Press F4 to stop the scan.

Adding Your First Rung

  1. In STOP mode, hover near the bottom of the ladder until the ⊕ Add New Rung strip appears. Click it.
  2. The Rung Template picker opens — choose XIC → OTE for a simple contact/output rung.
  3. Double-click the new contact (address shown in amber) to open Edit. Change the address to X4 and click Apply.
  4. Double-click the output coil and change its address to Y4.
  5. Press F5 to run, then toggle X4 on the X tab to see Y4 energise.
Note: Programs auto-restore on page load from the most recent saved slot. Use 💾 SAVE to commit your work. If you click ✕ CLEAR, the next page load will open the default Control Panel program instead of the most recent save (your saved slots are not deleted — any subsequent SAVE or LOAD resumes normal auto-restore behaviour).

Rungs & Editing

Creating, modifying, and organising rungs

Inserting a Rung

Hover between any two existing rungs to reveal the ⊕ Insert rung after XXXX strip, or hover below the last rung for ⊕ Add New Rung. Clicking opens the Rung Template Picker with pre-built patterns.

Deleting a Rung

Click the ✕ circle button on the top-right of any rung header. A confirmation dialog prevents accidental deletion.

RUN mode protection: In RUN, deleting a rung — like any edit — opens an online edit. The rung keeps running, drawn as a ghost marked DELETED ON ACCEPT, until you press ACCEPT. Any coil it held ON stays ON.

Reordering Rungs

Drag the ⠿ handle on the left of the rung header to a new position. An amber drop-indicator line shows the insertion point. In RUN the move goes into an online edit; a moved rung is marked EDIT.

Editing Elements

Double-click any element to open the Edit Instruction dialog. You can change the instruction type, address, preset value, and the Address Comment. You can also edit the rung comment from here.

The dialog checks the address before applying it. An address that doesn't suit the instruction — a coil on a read-only system bit, a contact on a word such as SW5, a timer on X1 — is refused with a reason in the status bar, and the dialog stays open so you can correct it. If you change the instruction type, the address is kept when it still fits and replaced with the next free address when it doesn't. Turning a contact on an input (e.g. X1) into a coil suggests the next free output (Y), because driving an input is almost never intended.

Presets must be whole numbers from 0 to 99,999,999, and so must comparands and LIM limits in a compare that reads no D register. A compare that reads a D register takes constants that fit its Data Type — decimals and negatives with REAL, for example.

Address Comments

The comment field in the Edit dialog is labelled Address Comment because it is tied to the address, not to the individual element. If you set X1 to "Start Button", every contact, output, compare instruction, or block that references X1 anywhere in the program — even on a different rung, even with a different instruction type — will display the same comment automatically.

This matches industry-standard behaviour in major PLC IDEs and means you only have to document each address once.

  • Editing the comment for any instance of X1 updates all instances of X1 on apply.
  • Changing an element's address (e.g. from X1 to X2) updates the displayed comment to whatever is already stored for the new address.
  • Clearing the comment field and applying removes the comment for that address globally.
  • Comments are saved with the program (both in localStorage save slots and in exported JSON files).
Tip: Document addresses with meaningful names (e.g. "Conveyor Start PB", "Tank High Level Float") so the rung visually reads as intent rather than as anonymous register codes.

Editing the Rung Comment

Rung comments are different from address comments — they describe a whole rung's purpose ("Start / Stop seal-in", "Conveyor speed ramp") and apply only to that rung. Double-click anywhere on the rung header bar (the row showing the rung number and comment text) to open the Edit Rung Comment dialog. Type the new comment and click Apply or press Enter. Leave the field blank and apply to clear the comment.

Rungs with no comment show a faint "double-click to add comment…" placeholder to make the feature discoverable.

Tip: Rung comments are a great way to document your intent — for example "Start / Stop seal-in" or "Conveyor speed ramp". They are saved with the program and exported in JSON files.

Right-Click Context Menu

ActionDescription
Edit ElementOpens the Edit dialog for the clicked contact.
Insert Contact BeforeInserts a new contact immediately before the clicked one.
Insert Contact AfterInserts a new contact immediately after the clicked one.
Add Parallel BranchWraps the contact in a parallel group or adds a branch.
Edit Parallel GroupOpens the Parallel Branch Editor for the enclosing group.
Remove This BranchRemoves the branch containing the clicked contact.
Delete ContactRemoves the contact from the rung (minimum one input required).

Rung Structure Rules

  • Every rung must have at least one input contact and exactly one output element (coil or block) on the right rail.
  • Input contacts are evaluated left-to-right; the result drives the output.
  • Timer and counter blocks occupy the output position — wire continuation exits from their right side carrying the DN bit.
  • BSL, BSR, DRUM, JMP, MCR and END MCR only fit the last slot of a rung, never inside a parallel branch. END MCR sits on a rung of its own, with no contacts.
  • LBL sits at the top level of the rung (not in a branch) and never last — normally first.
  • The Edit dialog's type list greys out the instructions that do not fit the element's position, so a contact cannot be turned into a DRUM, nor an output into a LBL.
  • After every edit the editor also checks jumps, labels and MCR zones across the whole program and adds the first problem to the status bar (e.g. ⚠ rung 0004: JMP L1 has no matching LBL L1.). An import reports the number of problems and the first one. See Jump & Label and MCR Zones.

Contacts

Input-side bit-reading instructions

SymbolNameAddressDescription
-| |- XIC X, Y, C, SB, T/DN, CT/DN Examine If Closed (Normally Open). Passes power when the addressed bit is 1 (ON). The most common contact type.
-|/|- XIO X, Y, C, SB, T/DN, CT/DN Examine If Open (Normally Closed). Passes power when the addressed bit is 0 (OFF). Use for Stop buttons, interlocks, and fault conditions.
-|P|- P X, Y, C, SB, T/DN, CT/DN Rising Edge (positive transition). Passes power for one scan when the addressed bit goes from 0 to 1. Use it to act once per button press.
-|N|- N X, Y, C, SB, T/DN, CT/DN Falling Edge (negative transition). Passes power for one scan when the addressed bit goes from 1 to 0 — for example, when a button is released.

Address Examples

  • X1 — Digital input 1
  • Y3 — Digital output 3 (used as a contact to read back its state)
  • C100 — Internal control relay 100
  • T1/DN — Timer 1 Done bit
  • T1/EN — Timer 1 Enable bit
  • T1/TT — Timer 1 Timing bit
  • CT1/DN — Counter 1 Done bit
  • Bare T1 resolves to T1/DN automatically
  • SB3 — System bit: first scan (see System Bits & Words)

Contacts read bits. To test a word such as AX1, SW5 or T1/ACC, use a compare (GRT, LIM, …).

Edge Contacts (P and N)

An edge contact watches its bit, not the rung. It remembers the bit's state from the previous scan and passes power only on the scan where the bit changed. That memory updates every scan, even when contacts to its left are open.

  • Hold X1 ON and a -|P|- X1 contact is true for exactly one scan, then false until X1 is released and pressed again.
  • The edge memory starts at OFF each time the PLC enters RUN, so a bit that is already ON fires its rising edge once on the first scan — the same rule as an IEC R_TRIG in Structured Text.
  • In STOP mode edge contacts always show OFF. Use STEP to watch an edge fire: toggle the input, then STEP once.
Tip: The P → Toggle rung template builds a push-on / push-off circuit from one button using a rising edge.

In STOP Mode

Contact bars show the current bit state regardless of power flow. A green bar means the bit is 1. Wires remain dark — power flow is only shown in RUN mode.

Tip: Use Y1 as an XIC contact in a seal-in branch — it reads the physical output state, creating a latching circuit without a dedicated C relay.

Outputs

Output coil instructions — right-rail elements

SymbolNameAddressDescription
-( )- OTE Y, C Output Energize. Sets the bit to 1 when powered; resets it to 0 every scan when not powered. Standard output — mirrors rung power state each scan.
-(L)- OTL Y, C Output Latch (Set). Sets the bit to 1 when powered. The bit stays 1 even if the rung loses power. Use with OTU on a separate rung to unlatch.
-(U)- OTU Y, C, SB9–SB11 Output Unlatch (Reset). Resets the bit to 0 when powered. Used as the companion to OTL, and the only way a ladder program can clear the error flags SB9–SB11 (in Structured Text, assign FALSE).
STOP Mode: When the PLC switches to STOP, all Y and C bits are cleared. In STOP mode you can force Y outputs ON/OFF via the Y tab or I/O tab.

Timers

TON, TOF and RTO — time-based control

SymbolNameDescription
[TON] TON Timer On-Delay. When the rung becomes TRUE the accumulator starts counting. When Acc ≥ Preset the DN bit sets. When rung goes FALSE the timer resets (Acc = 0, DN = 0).
[TOF] TOF Timer Off-Delay. When the rung is TRUE the DN bit is immediately set. When the rung goes FALSE the accumulator starts counting. DN clears when Acc ≥ Preset.
[RTO] RTO Retentive Timer On. Times while the rung is TRUE, like TON, but keeps its accumulated time when the rung goes FALSE. Time adds up across separate ON periods until Acc ≥ Preset. Only a RES on the same address clears it.

Timer Parameters

  • Address — T1 through T256
  • Preset — time in milliseconds. 1000 = 1 second, 5000 = 5 seconds.
  • Accumulator — counts up from 0 each scan (approximately 100 ms steps).

Timer Bits

BitAddressDescription
DNT1/DN or T1Done — set when Acc ≥ Preset
ENT1/ENEnable — set while rung is TRUE (TON) or was TRUE (TOF)
TTT1/TTTiming — set while timer is actively counting (EN && !DN)
Tip: Use T1/TT as an XIC contact to create a flashing output — it toggles roughly every Preset milliseconds.

Reset

Add a RES block (address T1) on a separate rung. When that rung is powered, Acc resets to 0 and DN clears. An RTO needs this — it never resets itself.

RTO example: run-hours on a motor. XIC Y1 → RTO T5 accumulates whenever the motor runs, through any number of stops, and T5/DN flags the service interval. A maintenance reset button drives RES T5.

Reading the Accumulator

Compares can read a timer's elapsed time as T1/ACC (in milliseconds). For example, GRT T1/ACC 1500 is true once timer 1 has run for more than 1.5 s — useful for staged actions from a single timer.

Counters

CTU, CTD, CTUD and RES — event counting

SymbolNameDescription
[CTU] CTU Count Up. Increments the accumulator on each rising edge (OFF→ON) of the rung. When Acc ≥ Preset the DN bit sets.
[CTD] CTD Count Down. Decrements the accumulator on each rising edge. DN sets when Acc ≤ 0.
[CTUD] CTUD Count Up / Down. Counts up on each rising edge of the rung and down on each rising edge of a second bit — the Count-Down Input set in the Edit dialog (e.g. X4). DN sets when Acc ≥ Preset. Never goes below 0. If both edges arrive on the same scan they cancel out.
-(R)- RES Reset. When powered, clears the Acc to 0 and resets DN. Address must match the timer or counter to reset (e.g. CT1 or T1).

Counter Parameters

  • Address — CT1 through CT256
  • Preset — integer count target (no units)
  • DN bit — CT1/DN or bare CT1
Note: Rising-edge detection means you need the input to go OFF then ON again to increment — holding a contact ON does not keep counting.
CTUD example: parts in a buffer. Entry sensor X3 on the rung counts up, exit sensor X4 as the Count-Down Input counts down, and CT1/DN with Preset 10 means "buffer full". Read the live count in a compare as CT1/ACC.

Analog I/O

AX inputs (0–4095) and AY outputs (0–4095)

The simulator supports 12-bit analog channels using 12-bit AX/AY registers. All values are integers in the range 0–4095, representing 0–10 V (or 4–20 mA in real hardware).

Analog Inputs — AX

RegisterHow to setSource
AX1–AX8Drag slider on the AN tab, or connect a 3D sceneManual / Scene

Analog Outputs — AY

RegisterHow to setSource
AY1–AY4Driven by MOV and math blocks during RUN; manually typed in STOP modeProgram / Manual

Compare Instructions

SymbolNameCondition
[GRT]GRTGreater Than — passes power if A > Comparand
[LES]LESLess Than — passes if A < Comparand
[GEQ]GEQGreater or Equal — passes if A ≥ Comparand
[LEQ]LEQLess or Equal — passes if A ≤ Comparand
[EQU]EQUEqual — passes if A = Comparand
[NEQ]NEQNot Equal — passes if A ≠ Comparand
[LIM]LIMLimit Test — passes if Low ≤ A ≤ High (both limits included). If Low is greater than High the band wraps: it passes when A ≥ Low or A ≤ High, i.e. outside the gap between them.

Compare blocks sit in the input area of a rung and pass or block power flow on every scan based on the live value of A versus B (or the limits). B and the limits can be a constant or another word, such as D20 or AX2.

What A can be

  • AX1–AX8 — analog inputs
  • AY1–AY4 — analog outputs
  • T1/ACC — a timer's elapsed time in ms; CT1/ACC — a counter's current count
  • SW1–SW19 — system words, e.g. SW5 = hour of day
  • D1–D4000 — data registers, read as the compare's Data Type (INT unless you choose another — see Data Registers)
LIM in one element: LIM AX1 1000 3000 → OTE Y3 lights Y3 only while the level is inside its normal band — a job that otherwise takes two compares in series.

MOV — Move a Value

The MOV block occupies the output position. When the rung is powered it writes the Source value to the Destination, every scan. The source can be a constant (e.g. 2048) or any readable word: D1, AX1, AY2, SW6, T1/ACC or CT1/ACC. The destination is AY1–AY4 or a data register D1–D4000.

  • A value written to AY is rounded and limited to 0–4095, so a timer at 5000 ms moved to AY1 gives 4095.
  • When a side is a D register, MOV reads it as the Source type and writes it as the Destination type — this is how you convert, e.g. REAL D10 → INT D20 (rounded half away from zero: 2.5 → 3, −2.5 → −3).
  • A MOV of the constant 0 into SW17 clears the last error code; no other value or destination is accepted for system words.
Tip: For a 2-state analog output (low/high), use two rungs — one with GRT driving MOV to AY1 = 4095, another with LEQ driving MOV to AY1 = 0.

Math & Data Instructions

Calculate, scale, copy and pack values in data registers

These blocks sit in the output position and work on words: data registers D1–D4000, analog AX/AY, system words SW, and timer/counter accumulators. Drag them from the Math and Move / Data palette sections, then double-click to set their operands. Hover over a block or compare on the ladder to see the whole instruction.

They run on every scan the rung is true. XIC X1 → INC D1 adds 1 ten times a second while X1 is held. To act once per press, use a rising-edge contact: P X1 → INC D1.

Data Type

Every math block and compare has a Data Type — INT (default), UINT, WORD, DINT or REAL. It says how the block reads and writes D registers; AX, AY, SW and accumulators are always read as their own value. Integer types round results half away from zero; a result that does not fit wraps and sets the overflow flag. See Data Registers for the ranges and System Bits & Words for fault codes.

Math

SymbolNameWhat it does
[ADD]ADDDestination := A + B
[SUB]SUBDestination := A − B
[MUL]MULDestination := A × B
[DIV]DIVDestination := A ÷ B. Integer types truncate toward zero (−7 ÷ 2 = −3). B = 0 writes nothing and sets SB10.
[MOD]MODDestination := remainder of A ÷ B; the sign follows A (−7 MOD 2 = −1).
[INC]INCDestination := Destination + 1
[DEC]DECDestination := Destination − 1
[CPT]CPTDestination := an expression, e.g. (AX1 - 410) * 100 / 3685
[SCL]SCLLinear scaling: maps Input from In min…In max to Out min…Out max. Not clamped — an input outside its range gives an output outside its range.

A and B are constants or words. The destination is a D register, or AY1–AY4 (rounded and limited to 0–4095).

CPT expressions

  • Numbers, pointers D[Dn] and the words D, AX, AY, SW (move T1/ACC or CT1/ACC into a D register first).
  • Operators + - * / MOD **, unary minus and parentheses.
  • Functions ABS, SQRT, MIN(a,b,…), MAX(a,b,…), LIMIT(min,in,max), TRUNC, ROUND, EXPT(a,b).
  • With a REAL Data Type the expression is calculated in floating point. With an integer type every value and every division is whole (7 / 2 * 2 = 6), and decimal constants are refused — use REAL, or scale by whole numbers (* 3 / 2). Integer intermediates are exact up to 253.

Move / Data

SymbolNameWhat it does
[MOV]MOVCopies one value, converting between Source type and Destination type. See Analog I/O.
[COP]COPCopies Length words, unchanged, from a source start (D, AX, AY or SW) to a D start. Overlapping ranges copy correctly.
[FLL]FLLWrites one value into Length elements of the Data Type, starting at a D register (a REAL fill uses 2 × Length registers).
[PACK]PACKPacks 1–16 bits, starting at an X, Y or C address, into one D word. The first bit becomes bit 0.
[UNPK]UNPACKWrites bits 0–15 of a word (D, AX, AY or SW) to 1–16 Y or C bits, bit 0 first.

Examples

  • Level in percent: XIC SB1 → SCL AX1 0…4095 → 0…100 → D20, then GRT D20 80 → OTE Y3.
  • Count parts: P X1 → INC D1; reset with XIC X2 → MOV 0 → D1.
  • Engineering units: CPT D10 (REAL) := (AX1 - 410) * 100 / 3685 turns a 4–20 mA signal into 0–100.0 %.
  • Fault alarm: XIC SB12 → OTE Y8; acknowledge with XIC X8 → OTU SB9, OTU SB10, OTU SB11 and MOV 0 → SW17.
Checks in the Edit dialog: constants must fit the Data Type (whole numbers for integer types), a DINT or REAL register needs a second word (not D4000), and lengths must stay inside their table. A program imported from a file is checked with the same rules — the status bar reports any element that fails.

Pointers (D[Dn])

Indirect addressing — pick the register at run time

A pointer is written D[D5]: the register inside the brackets (D5, the pointer register) holds a number, and the instruction uses the register with that number. If D5 holds 102, MOV D[D5] → D20 copies D102 into D20. Change D5 to 103 and the same rung copies D103 instead. This is how one rung can step through a table of values — recipes, setpoints, a log.

Where pointers can be used

InstructionFields that accept D[Dn]
MOVSource and destination
ADD SUB MUL DIV MODA, B and destination
INC DECDestination
SCLInput and destination
CPTDestination, and terms inside the expression: D[D5] * 2 + D10
COPSource start and destination start
FLLFill value and destination start
PACK / UNPACKThe D word (PACK destination, UNPACK source) — not the bits
GRT LES GEQ LEQ EQU NEQ LIMA, B, and LIM's Low and High

Contacts, coils, timers, counters and RES do not take pointers. Only a D register can be the pointer: D[5], D[AX1] and D[D[D5]] are refused. Case and spaces don't matter — d[ d5 ] is stored as D[D5].

How it is resolved

  • The pointer register is read as INT — a plain register number, whatever the instruction's Data Type.
  • It is resolved every time the instruction runs, so a new pointer value takes effect the next time the instruction runs — in the same scan when the pointer is calculated on an earlier rung, or earlier on the same rung.
  • The target must fit inside D1–D4000 for the access: a DINT or REAL needs two registers, so its pointer can go up to 3999; for COP and FLL the whole run must fit.
  • A pointer outside that range is a range fault: SB11 and SB12 turn on, SW17 = 4, SW18 = the rung, and the instruction writes nothing. A compare reads the bad operand as 0.
  • While the PLC runs, each block shows where its pointers point: D[D5]→D102, or D[D5]→? when the pointer is out of range. Hover over a block for the whole instruction.
The pointer register holds a register number, not a value. Writing a DINT or REAL into it (or using it as a counter that runs past 4000) points it somewhere unexpected. Keep pointer registers apart from your data, and give them their own address comments.

Example — recipe select

  • Setpoints for three recipes sit in D100, D101 and D102 (loaded once with XIC SB3 → MOV).
  • The recipe number 0–2 is in D1 (set by pushbuttons with P X1 → MOV 0 → D1, and so on).
  • XIC SB1 → ADD 100 + D1 → D2 builds the pointer.
  • XIC SB1 → MOV D[D2] → D10 copies the selected setpoint into the working register D10.
  • GEQ AX1 D10 → OTE Y1 then compares the level with whichever setpoint is selected.

Structured Text writes the same idea as D[100 + recipe]; ST's D[...] takes any expression (see ST Overview).

Bit Shift Registers (BSL / BSR)

Move a row of bits one position per pulse — part tracking on a conveyor

A shift register is a row of ordinary bits — C or Y — that moves along by one position on each rising edge of its rung, like a counter counts. A data bit is copied in at one end, every other bit moves one place, and the bit that falls off the other end can be kept in an unload bit. Use it to follow parts down a conveyor: shift once per part pitch, put "this part is bad" in at the entry, and the bit arrives at the reject station exactly when the part does. BSL and BSR are in the Shift / Sequence palette section.

SymbolNameWhat it does on each rising edge
[BSL]BSLBit Shift Left. Every bit moves to the next higher address. The data bit enters at the first bit; the last bit (first + length − 1) goes to the unload bit.
[BSR]BSRBit Shift Right. Every bit moves to the next lower address. The data bit enters at the last bit; the first bit goes to the unload bit.

Fields

Dialog fieldAccepts
First bit of the registerC1–C256 or Y1–Y16. Not X — inputs belong to the field.
Number of bits in the register (1–256)The whole register must stay in its table: 8 bits from C250 run past C256 and are refused. A register cannot run from C into Y.
Data bitAny readable bit: X1, C5, T1/DN, CT2/DN, SB1 …
Unload bit (optional)A C or Y bit that receives the bit shifted out. Leave empty to discard it.

BSL and BSR sit in the output position (the last slot of the rung) and pass the rung's power through. The block shows the register range and length, the data bit (IN) with a dot for its state, the unload bit (OUT), an arrow for the direction, and the register as a row of dots — the first 16 bits, then +N for the rest. Hover over the block for the whole instruction.

Example — reject tracking

  • P X1 → BSL C100 ×8, data bit X2, unload C200 — X1 is a sensor that pulses once per bottle pitch, X2 is "bottle is faulty" from an inspection sensor at the entry.
  • Each pulse moves the pattern up one place: a faulty bottle seen at C100 reaches C105 five pulses later.
  • XIC C105 → OTE Y3 fires the reject pusher when the faulty bottle is in front of it.

The P → BSL rung template builds the first rung: a rising-edge contact driving a BSL on 8 free C bits, with the next free X as the pulse input and the following free X as the data bit.

Behaviour

  • One shift per rising edge. The rung must go false and true again for the next shift. Unlike a counter, the block's edge memory is reset on every STOP→RUN, so a rung that is already true shifts once on the first scan.
  • The data bit is read at the moment of the shift.
  • Register bits are ordinary bits. Read them with contacts, set or clear them with OTL/OTU, or clear the register with OTU rungs (or UNPACK from a D register that holds 0, up to 16 bits at a time) — nothing special is needed.
  • Inside an MCR zone that is off, or in a rung skipped by a JMP, the register sees no edge and does not shift.
  • All register, data and unload bits appear in the I/O tab's used-address list.

Common mistakes

  • A chattering pulse source. The block shifts on every rising edge of its rung, so XIC X1 works without a P contact — but a sensor that chatters shifts once per bounce. Debounce the pulse source, or drive the rung from a timer done bit.
  • Expecting the register to survive STOP. STOP clears every Y and C bit, so the tracking pattern is lost. Loading or importing a program clears it too.
  • Off-by-one stations. With BSL the data bit lands in the first bit on the first shift, so after n shifts it is at first bit + n − 1. Count stations from the bit the data enters.
  • Overlapping registers. Two shift registers, or a register and other logic, writing the same C bits corrupt each other. Give each register its own block of bits and an address comment.

Drum Sequencer (DRUM)

Step through output patterns on time, on events, or both

A drum sequencer is a table of steps. Each step says which outputs are ON, how long to stay in the step, and optionally which bit must be ON before moving on. The current step number is kept in a D register. Traffic lights, wash cycles and fixed machine sequences are the classic uses — one block replaces a pile of timers and interlocks. DRUM is in the Shift / Sequence palette section.

Fields

Dialog fieldAccepts
Step registerA D register, D1–D4000. Holds the current step 1…N, read and written as INT.
OutputsFirst output bit (a Y or C bit) and the number of outputs, 1–16. The run must stay inside its table.
Reset bit (optional)Any readable bit, e.g. X8. While it is ON the drum goes to step 1 and stays there.
Done bit (optional)A Y or C bit, ON for one scan each time the drum wraps from the last step back to step 1.
Edit steps…Opens the Drum Steps table. The summary beside the button shows the number of steps and the total timed length.

The step table

Click Edit steps… in the Edit dialog. The Drum Steps dialog has one row per step and these columns:

  • One checkbox per output, headed with its address (Y1, Y2 …). Tick the outputs that are ON in that step.
  • Time (ms) — how long the step lasts, 0–99,999,999. 0 means no time condition.
  • Event — a bit that must be ON before the step can end, e.g. X3 or T4/DN. Empty means no event condition.

+ Add step appends a step (up to 16); − Remove last deletes the last one (at least 1 remains). Save steps checks the table and returns to the Edit dialog — the steps are stored when you press Apply there. Cancel or Esc discards the table edits; Enter saves steps. Set the number of outputs before opening the table: the table shows that many columns, and ticks beyond it are dropped when you save.

In a saved file each step is {p, t, ev}: p is the output pattern as a number 0–65535, where bit 0 is the first output.

How it runs

  • Rung true: the current step's pattern is written to the outputs every scan and the step timer runs (real elapsed time, up to 500 ms per scan, like a timer).
  • The drum moves to the next step when both conditions are met: the step's time has elapsed (or is 0) and its event bit is ON (or has none). A step with time 0 and no event lasts one scan. The new step's pattern is written in the same scan, and the step time starts again from 0.
  • After the last step it returns to step 1, and the done bit is ON for that one scan.
  • Rung false: the drum pauses. The step and the time already spent in it are held, and the outputs are not written — they keep whatever they last had, so drive them OFF elsewhere (or use C bits and gate the real outputs) if they must drop.
  • Reset bit ON is checked every scan, rung true or false: step := 1, step time := 0, and the drum does not advance while reset stays ON.
  • A step register value outside 1…N — written by a MOV, typed on the D tab, or left over after steps were removed — is treated as step 1 and corrected. It is not a fault.
  • The step register is a normal D register: it keeps its value through STOP/RUN and is saved with the program, so the drum resumes at the saved step. The time spent in the step is not saved and restarts from 0 on every STOP→RUN, like a TON.

The block shows the step register, STEP 2 and 2/4 (current step of total), the time in the step against its time (or no time), the event bit with a dot for its state, and the step's output pattern as dots.

Example — traffic light

  • XIC X1 → DRUM D10, outputs Y1 ×3 (green, amber, red — the Traffic Light scene's north–south lamps), reset X8.
  • Step 1: Y3 (red), 5000 ms. Step 2: Y1 (green), 4000 ms. Step 3: Y2 (amber), 1500 ms.
  • Give step 2 the event X2 (a car waiting on the side road) and the light stays green until at least 4 s have passed and a car is waiting.

The DRUM → outputs rung template inserts XIC → DRUM on the next free D register with four outputs and four 2-second steps, one output per step.

Common mistakes

  • One held event bit on consecutive steps. The event is a level, checked every scan. If two steps in a row use the same event bit and it stays ON, the second step's event condition is already met, so it ends as soon as its own time has passed (on the next scan if its time is 0). Use a rising-edge contact into a C bit, or a different bit per step.
  • Expecting outputs to drop when the rung goes false. They hold. Put the outputs on C bits and drive the real Y outputs with rungs that include the run condition.
  • Sharing the step register. Another instruction writing the same D register moves the drum (the new step's time starts from 0). Give it its own register and an address comment; read it with a compare (EQU D10 3) to act on a step.
  • Two drums on the same outputs. Both write every scan they run; the lower rung wins.

Jump and Label (JMP / LBL)

Skip a block of rungs while a condition is true

A JMP with power makes the scan skip forward to the rung that holds the matching LBL. The rungs in between are not executed at all on that scan. Use it to leave out a section of logic — a manual-mode section while in auto, or a rarely needed calculation. JMP and LBL are in the Program Control palette section.

SymbolNameAddressDescription
[JMP]JMPL1–L256Jump to Label. Output position. When its rung is true, the scan continues at the rung that holds LBL with the same number.
[LBL]LBLL1–L256Label. Input side, normally the first element of a rung. Always passes power — it only marks where a jump lands.

Rules

  • Forward only. The LBL must be on a later rung than the JMP.
  • Each label number is used once. Several JMPs may jump to the same label.
  • A LBL sits at the top level of a rung (not in a parallel branch), and not as the last element — the rung needs an output after it. One label per rung.
  • A jump may not go into or out of an MCR zone; keep the JMP and its LBL in the same zone (or both outside any zone).
  • The rung holding the LBL does run; only the rungs between the JMP rung and the LBL rung are skipped.

What a skipped rung does

  • Nothing is evaluated. OTE coils keep their last state — they are not turned off.
  • Timers do not run and do not reset; counters do not count; word blocks, drums and shift registers do not execute.
  • Edge contacts do not update their memory, so a bit that changed while skipped can fire its edge on the first scan the rung runs again.
  • A JMP inside the skipped rungs does not execute.
  • The rung is drawn dimmed with SKIPPED (JMP L5) in its top-right corner, and its contacts show their bit states without power flow.

Example — manual section

  • Rung 0004: XIC C100 → JMP L1 — C100 = auto mode.
  • Rungs 0005–0007: jog and manual-move rungs.
  • Rung 0008: LBL L1 · XIC X1 → OTE Y1 — the jump lands here and the rung runs normally.

Make sure the manual rungs leave their outputs OFF before auto is selected — a skipped OTE holds.

Checks

The editor checks jumps and labels after every edit and on import, and puts the first problem in the status bar, e.g. ⚠ rung 0004: JMP L1 has no matching LBL L1. The program still runs. At run time a JMP that finds no LBL further down is ignored and raises a range fault: SB11 and SB12 ON, SW17 = 4, SW18 = the JMP's rung (see System Bits & Words).

Common mistakes

  • Jumping backwards to repeat rungs. Not supported: the scan already repeats every 100 ms. Use the next scan instead.
  • Expecting skipped outputs to turn off. They hold. Turn them off before the jump, or use OTL/OTU deliberately.
  • Timing across a jump. A TON in a skipped section freezes its accumulator; a timer that must keep timing belongs outside the jump.

Master Control Zones (MCR / END MCR)

Turn off the power rail for a group of rungs

An MCR rung opens a zone and an END MCR rung closes it. While the MCR rung is true, the rungs in the zone run normally. While it is false, every rung in the zone is scanned with the power rail OFF — as if all their conditions were false. Use it for a section of a machine that must drop out together, such as everything powered by one guard circuit. MCR and END MCR are in the Program Control palette section.

SymbolNameDescription
[MCR]MCRMaster Control. Output position. Its rung condition switches the zone below ON or OFF. In RUN the block shows ZONE ON or ZONE OFF.
[END MCR]END MCREnd of Master Control Zone. On a rung of its own with no contacts. Closes the zone; rungs after it are not affected.

Neither has an address — the dialog shows MCR in a read-only field. In the palette, END MCR is labelled [END] End MCR Zone; dropping it on a rung inserts a new END MCR rung below that rung.

Inside a zone that is OFF

InstructionBehaviour
OTETurns OFF
OTL / OTUHold — a latched bit stays latched
TONResets, as with a false rung
TOFStarts timing, as with a false rung — its DN drops when the preset runs out
RTOHolds its time
Counters, BSL / BSRSee no rising edge — a true rung when the zone turns back on counts or shifts once. A CTUD's CD input is read every scan and still counts down.
MOV, math and data blocksDo not execute
DRUMPauses; its reset bit still works
JMPDoes not jump

Unlike a jump, the zone's rungs are scanned: contacts and edge memory are still evaluated. Zone rungs carry a side bracket on the left rail (orange while the zone is off) and show MCR OFF with an orange tint.

Example — guarded section

  • Rung 0010: XIC X5 → MCR — X5 = guard closed.
  • Rungs 0011–0013: conveyor and pusher outputs.
  • Rung 0014: END MCR.

The MCR zone rung template inserts all three parts: an XIC → MCR rung, one rung inside the zone, and the END MCR rung.

An MCR is not a safety device. It is program logic. Real machines remove power to hazardous outputs with hard-wired safety circuits, never only with an MCR.

Rules and checks

  • No nesting: an MCR inside an open zone is an error — close the first zone with END MCR.
  • An END MCR with no MCR above it is an error.
  • An MCR with no END MCR runs to the end of the program. It is reported but still loads and runs.
  • A JMP may not jump into or out of a zone.

As with jumps, the editor reports the first problem in the status bar after each edit and on import.

Common mistakes

  • Latching inside a zone. OTL bits stay ON when the zone goes off. Use OTE for outputs that must drop with the zone, or unlatch them outside it.
  • Contacts on the END MCR rung. END MCR closes the zone unconditionally; it takes no conditions.
  • Forgetting the END MCR. Every rung below the MCR then follows the zone.

Parallel Branches

OR logic — multiple paths to the output

A parallel group connects two or more contact paths in parallel. If any branch passes power the output is energised — this is Boolean OR logic.

Adding a Parallel Branch

  • Right-click a contact and choose Add Parallel Branch. The contact is wrapped in a two-branch group; the new branch contains a default XIC contact.
  • Or click the ‖+ button that appears below an existing parallel group (in RUN this opens an online edit).

Editing the Parallel Group

Right-click any contact in the group and choose Edit Parallel Group, or double-click the left rail of the group, to open the Parallel Branch Editor. Apply keeps the changes as one undo step; Cancel (or closing the dialog, or pressing RUN/STOP while it is open) discards everything changed in it. In RUN, opening the editor starts an online edit, which closes again if you cancel with nothing changed.

In the editor you can:

  • Add/remove contacts within each branch (series AND logic inside a branch)
  • Add new branches (more OR paths)
  • Remove branches (minimum 2 required; removing down to 1 unwraps the group)

Series contacts inside a branch

Each branch can contain multiple contacts in series — all must be TRUE for that branch to pass power (AND logic within the OR group).

Example: Start OR Seal-In — classic motor starter: Branch 1 = Start PB (XIC X1), Branch 2 = Run relay (XIC C1) in series with Jog interlock (XIO C2).

Memory Map

All addressable registers in the simulator

PrefixRangeTypeDescription
XX1–X16Digital InputPhysical inputs. Toggle on X tab or I/O tab. Can be driven by a connected 3D scene.
YY1–Y16Digital OutputPhysical outputs. Set by OTE/OTL/OTU rungs. Sent to connected scene each scan.
CC1–C256Internal RelayProgram-internal bits. Not physical I/O. Toggle on C tab. Use C100+ by convention to avoid conflicts.
TT1–T256TimerEach T register holds EN, DN, Acc, Pre. Addressed as T1 (DN), T1/DN, T1/EN, T1/TT.
CTCT1–CT256CounterEach CT register holds DN, Acc, Pre. Addressed as CT1 (DN) or CT1/DN.
AXAX1–AX8Analog Input12-bit read-only input registers (0–4095). Set by slider on AN tab or by a connected scene.
AYAY1–AY4Analog Output12-bit output registers (0–4095). Written by MOV and math blocks. Sent to connected scene. Manually editable in STOP mode.
DD1–D4000Data Register16-bit words for math and data. Read and written as INT, UINT, WORD, DINT or REAL by each instruction; DINT and REAL use two registers. Keep their values through RUN/STOP and are saved with the program. See Data Registers.
SBSB1–SB18System BitRead-only status bits maintained by the simulator: always on/off, first scan, scan toggle, clock pulses, error flags, scene and gamepad status.
SWSW1–SW19System WordRead-only 16-bit words: real-time clock, scan timing, scan count, time in RUN, error code, scene count.

Timer and counter accumulators are also readable as words: T1/ACC (ms) and CT1/ACC (count).

Internal only: D, SB and SW are not mapped to Modbus and are not sent to scenes.
Addressing tip: C relays conventionally start at C100 so they cannot be confused with timer/counter numbers. The simulator enforces this default in nextAddr().

Data Registers (D)

D1–D4000 · untyped 16-bit words, read by each instruction as its Data Type

A data register holds 16 bits. It has no type of its own: the instruction that uses it decides how those bits are read — the same way registers work in most PLCs. The default is INT.

Data TypeRegistersRange
INT1−32,768 … 32,767
UINT10 … 65,535
WORD10 … 65,535 (a bit pattern — shown in hex on blocks and on the D tab)
DINT2 — Dn low word, Dn+1 high word−2,147,483,648 … 2,147,483,647
REAL2 — Dn low word, Dn+1 high word32-bit floating point, about ±3.4e38, ~7 significant digits

Constants used with REAL are stored the same way as REAL registers (32-bit), so EQU D1 0.1 is true after MOV 0.1 → D1. Comparing REAL values for exact equality is still risky after arithmetic — prefer GEQ/LEQ or LIM.

Two-word types overlap. A DINT or REAL at D10 also uses D11. Put the next value at D12, not D11. D4000 cannot hold a DINT or REAL.

Keeping values

  • D values stay through RUN/STOP and through undo — like retentive memory in a real controller.
  • They are saved with a ladder program: in save slots, exported files and the automatic restore. Loading or importing a ladder program brings its own values; a file without saved values starts with every D at 0.
  • CLEAR in ladder mode sets every D register to 0.
  • Structured Text programs do not carry D values: ST save, load, import and CLEAR leave the registers as they are.

The D tab

  • Used by the program lists the registers your rungs touch, including the second word of DINT/REAL values and COP/FLL ranges (the first 64; the rest are reached with Go to).
  • Go to moves the 16-register window; ◀ ▶ step by 16.
  • The format box sets how each register is shown: DEC, UDEC, HEX, BIN, DINT or REAL. DINT and REAL show Dn and Dn+1 together. A register your program writes as DINT or REAL shows in that format automatically, and one written as WORD shows in HEX. Formats are saved with the program.
  • In STOP, type a new value and press Enter; values that do not fit the format are refused. In a HEX field, type 0x1F40 or FF for hex — plain digits such as 255 are taken as decimal. In RUN the values are read-only.

Pointers

An instruction can reach a register through another one: D[D5] means "the register whose number is in D5". See Pointers (D[Dn]).

Structured Text

ST reads and writes the same registers as D[n], always as INT. See ST Overview.

System Bits & Words

SB1–SB18 and SW1–SW19 — status the simulator maintains for your program

System bits (SB) and system words (SW) report what the PLC itself is doing: whether this is the first scan, a set of clock pulses, the time of day, how long the scan takes. Your program reads them like any other address — SB with contacts, SW with compares and MOV. They are refreshed once at the start of every scan, so every rung in one scan sees the same values.

System Bits (SB)

AddressMeaningAccess
SB1Always ONRead-only
SB2Always OFFRead-only
SB3First scan — ON for the first scan after entering RUN, and for the first STEP after STOP, an edit or a program loadRead-only
SB4Scan toggle — alternates ON / OFF every scanRead-only
SB50.5 s clock (250 ms ON, 250 ms OFF)Read-only
SB61 s clock (500 ms ON, 500 ms OFF)Read-only
SB71 min clock (30 s ON, 30 s OFF)Read-only
SB8reserved
SB9Math overflow — result wrapped (sticky)Clear with OTU
SB10Divide by zero / invalid result (sticky)Clear with OTU
SB11Index / range fault (sticky)Clear with OTU
SB12Any error — ON while any of SB9–SB11 is ONRead-only
SB13–16reserved
SB17At least one 3D scene linkedRead-only
SB18Gamepad connectedRead-only

System Words (SW)

AddressMeaningUnits
SW1Yeare.g. 2026
SW2Month1–12
SW3Day1–31
SW4Day of week1 = Monday … 7 = Sunday
SW5Hour0–23
SW6Minute0–59
SW7Second0–59
SW8reserved
SW9Last scan execution time — how long solving the rungs tookµs
SW10Maximum scan execution time — reset on entering RUN; STEP scans also count toward itµs
SW11Actual interval since the previous scanms (nominally 100)
SW12reserved
SW13 / SW14Scan count, low word / high word (count = SW13 + SW14 × 65536)scans
SW15 / SW16Time in RUN, low word / high words
SW17Last error code (0 = none)Clear with MOV 0
SW18Rung number of the last error
SW19Number of connected 3D scenes

Things worth knowing

  • Why there's no 100 ms clock. The scan runs every 100 ms. A clock with a 100 ms period would be sampled once per cycle, so the program would see it stuck ON, stuck OFF, or flickering at random. A clock bit needs a period of at least two scans to be seen reliably — so the fastest clock is SB5 (0.5 s), and SB4 toggles every scan for anything faster. Real PLCs have the same limit: a signal changing faster than the scan cannot be read reliably.
  • Clocks follow real time, not the scan count, so they stay accurate even when scans jitter.
  • Two kinds of scan time. SW9 is the time spent solving your logic — usually well under 1 ms. SW11 is how often the PLC actually scans. They answer different questions: "is my program heavy?" versus "how fast is the PLC cycling?"
  • The clock keeps running in STOP, as a real controller's does — watch it on the SYS tab. Scan-related words (SW9–SW16) only change when a scan runs.
  • Error flags and codes. The math and data instructions report problems through SB9–SB12, SW17 and SW18 — see the table below. The flags are sticky: they stay ON until your program clears them, so a fault that lasted one scan is never missed.
  • Read-only is enforced. The Edit dialog refuses a coil on any SB except an OTU on SB9–SB11, and a MOV into any SW except SW17.
  • Comments. System addresses show their built-in description on the ladder until you give them your own address comment.

Fault codes (SW17)

SW17FaultFlagWhat was written
0No fault
1Overflow — an integer result did not fit its Data TypeSB9The wrapped value (32767 + 1 → −32768)
2Divide by zero (DIV, MOD, CPT — including 0 raised to a negative power — or SCL with In min = In max)SB10Nothing
3Invalid result — infinity or not a number (e.g. SQRT(-1), a REAL beyond ±3.4e38, or a CPT result too large to compute such as 10 ** 400)SB10Nothing
4Range — an address or block runs past the end of its table (e.g. a REAL at D4000, COP past D4000), or a pointer D[Dn] holds a register number outside D1–D4000; also a JMP whose label is not found further down the program (the jump is ignored)SB11Nothing

SW18 holds the rung number of the most recent fault, as shown on the ladder. Faults are raised only by scans (RUN or STEP), never by the STOP-mode display. Clear them with OTU SB9 / OTU SB10 / OTU SB11 and MOV 0 → SW17; SB12 follows by itself. Ladder CLEAR, Load and Import (and the automatic restore when the page opens) also clear them, as a new program starts with no faults.

Common uses: XIC SB3 → MOV … initialises values once at start-up · XIC SB6 → OTE Y1 flashes a light at 1 Hz · LIM SW5 7 17 → OTE Y4 enables something during working hours · GRT SW10 5000 alarms when a scan ever took more than 5 ms.

I/O Panel Tabs

Right-side panel — nine tabs for monitoring and forcing

TabContentsInteractive?
I/OAddresses referenced in the current program, plus the block held by each connected scene. Shows X toggles, Y force-LEDs, C toggles, T/CT progress bars, AX sliders, AY bars, and any system bits (LEDs), system words and data registers (values) the program uses.Full — all controls active (SB/SW read-only)
XAll X1–X16 digital inputsClick to toggle ON/OFF
YAll Y1–Y16 digital outputsSTOP: click LED to force ON/OFF
CC1–C16 internal relaysClick to toggle ON/OFF
TActive timers — Acc, Pre, DN, progress barRead-only
CTActive counters — Acc, Pre, DN, progress barRead-only
ANAX1–AX8 sliders (input simulation) + AY1–AY4 bars with editable valuesAX: drag slider. AY: type value in STOP mode.
DData registers: the ones your program uses, then a 16-register window you can move with Go to and ◀ ▶. Each register has a display format (DEC, UDEC, HEX, BIN, DINT, REAL).STOP: type a value and press Enter. RUN: read-only.
SYSReal-time clock, all system bits SB1–SB18 and system words SW1–SW19Read-only
I/O tab: The most efficient view during development — it shows the addresses you actually use, so you don't hunt through 16 X inputs to find the ones in your program. A connected scene's addresses appear here too, even before your program references them, so you can watch a scene's inputs as soon as it links.

Run / Stop / Step

PLC operating modes

STOP Mode

The PLC is halted. No scan cycle runs. All Y and C outputs are de-energised and AY registers are cleared. You can:

  • Edit the program (add, move, delete rungs and elements) — changes apply immediately
  • Toggle X inputs and C relays to preview contact states
  • Force Y outputs ON/OFF via the Y tab or I/O tab
  • Manually set AY values via the AN tab or I/O tab
  • Execute a single scan with STEP — only available when no scene is connected

RUN Mode

The scan cycle executes every 100 ms. The ladder canvas shows live power flow (green = energised). Inputs from a connected scene update each scan.

The running program is protected. Any edit in RUN — or the ✎ EDIT button — opens an online edit: changes go to a pending copy while the running program keeps scanning, and ACCEPT swaps the copy in between two scans. This works for ladder and Structured Text. LOAD, Import and CLEAR are refused while an edit is pending (ladder CLEAR needs STOP in any case).

Pressing ■ STOP while an edit is pending makes the pending copy the program, since nothing is running any more.

STEP Mode

While in STOP mode and with no scene connected, press STEP to execute exactly one scan. Useful for tracing logic one step at a time without continuous scanning. The first STEP after STOP, an edit or a load counts as a first scan (SB3 ON). STEP is also the way to watch an edge contact fire.

Scene connected: The STEP button is automatically disabled (faded) whenever a 3D scene is linked to the simulator. This prevents stepping through program logic while a live scene is driving inputs — reconnect or use RUN mode instead. STEP re-enables as soon as the scene disconnects.

Online Editing

Change the program while it runs — ladder and Structured Text  ·  v1.127 / ST v1.019

Online editing lets you change a running program without stopping the machine. Your changes go into a pending copy; the program that is scanning keeps running unchanged until you press ACCEPT. Then the new program is swapped in between two scans. Nothing is cleared: timers, counters, D registers and bits keep their values.

STOP vs RUN

ModeWhat an edit does
STOPEdits apply immediately, as they always have. There is nothing running to protect, so there is no online edit and no ✎ EDIT button.
RUN, nothing pendingThe running program is protected. The first edit opens an online edit automatically.
RUN, edit pendingThe running program keeps scanning. Every edit goes to the pending copy. Everything you can do in STOP — insert, delete and move rungs, palette drops, templates, the Edit dialog, branches, rung comments, drum steps — works here.

Starting an Edit

  • Ladder: make any edit in RUN — double-click an element and Apply, drop an instruction from the palette, insert or delete a rung, drag a rung, edit a rung comment. Or press ✎ EDIT in the toolbar (shown in RUN only) to open an empty edit first.
  • Structured Text: start typing in the editor while the program runs, or press ✎ EDIT. The compiled program keeps scanning; the editor now holds the pending source.

An amber bar appears across the full width of the window, between the toolbar and the editor, and pushes the editor down (ladder and ST alike):

ONLINE EDIT2 changes (1 inserted · 1 modified)ACCEPTCANCEL

An edit that an action opened on its own closes again if the action ends with nothing changed — for example the Parallel Branch Editor cancelled, or an invalid Apply followed by Cancel. (Most dialogs open the edit only when you press Apply, pick a template or confirm a delete, so cancelling them never opens one.) An edit opened with ✎ EDIT (or by UNDO/REDO in RUN) stays open, even with 0 changes, until you press ACCEPT or CANCEL.

In ladder the count is rungs inserted, modified and deleted. In ST it is the number of changed lines, plus the number of compile errors while there are any. The banner stays until you ACCEPT, CANCEL or STOP.

What You See — Ladder

MarkerMeaning
EDIT 0003Modified rung (amber). A rung that was moved to a new position also counts as modified. Click the badge to show the running version below it, labelled RUNNING NOW — replaced on ACCEPT; click again to hide it.
NEW 0004Inserted rung (green). It runs after ACCEPT.
DELETED ON ACCEPTA rung you deleted is still running, so it stays on screen as a greyed ghost with a red dashed line, where it used to be. The label at the right end of its header gives its running rung number.
  • Unchanged rungs keep showing live power flow from the running program.
  • Changed and new rungs show no power flow — they are not running yet. Their contacts show bit state, as in STOP.
  • Values on the I/O panel, the SYS and D tabs, connected scenes and Modbus follow the running program throughout. The used-address lists on the I/O panel show the addresses of both the running and the pending program.

What You See — Structured Text

  • Line numbers in the gutter are marked: green = added line, amber = changed line, a red line across the gutter = lines were removed at that point (lines removed from the very end show as a red line under the last line).
  • The marks and the banner count update when typing pauses (about 0.25 s), together with the live compile.
  • Live compile and the error list work as in STOP.
  • The Live Values column and the Variables & Blocks watch keep showing the running program. Values beside changed lines are hidden, because that code is not running yet.

ACCEPT

  1. Checks first. Ladder runs the full checks on every element plus the jump, label and MCR rules. ST compiles the pending source. Any problem refuses the accept: the status bar shows the first problem and its rung (ladder) or line (ST), for example Can't accept — rung 0003: …. The edit stays pending so you can fix it.
  2. Confirmation. The dialog lists what changes:
    • Ladder: rungs inserted · modified · deleted.
    • ST: changed lines, variables added, removed or with a changed type, function blocks added or removed.
    • Both: outputs that are ON now and that nothing in the new program writes — they will stay ON (see the trap at the end of this page). In ladder any coil (OTE, OTL or OTU), shift register, drum or UNPACK on the bit counts as writing it, so a removed coil is not listed while an OTU of the same bit remains.
  3. Swap. Press Accept (Back returns to the edit). The swap happens between two scans — no scan ever runs part old, part new. The next scan runs the new program. The status bar confirms it and repeats any output left ON.
  4. One undo step. The whole online edit becomes a single step in the undo history.

Accepting with no changes simply closes the edit.

What Carries Over on ACCEPT

ItemLadderStructured Text
Matched byElement identity (an element you edited stays the same element) and instruction type.Name (case-insensitive) and type.
KeptEdge memory of P/N contacts, counters and shift registers; a DRUM's time in the current step.VAR values; ARRAY values at indices that still exist (same element type); function-block instances — timer ET/Q, counter CV, edge memory; a user FUNCTION_BLOCK's inner variables, matched the same way even if its definition changed.
FreshNew elements, or an element whose type changed: edge memory OFF, drum step time 0.New variables, variables with a changed type, new array indices and new instances: initial value.
Initializers—Not re-applied. Changing count : INT := 0; to := 10; does not set count to 10 — it keeps its running value.
MemoryNever cleared by an accept: X, Y, C, T, CT, AX, AY, D, SB, SW and fault flags keep their values.
Removed instructionsBits keep their last state. A timer or counter whose instruction is removed keeps its value until a RES (or CLEAR, or a load).

CANCEL

Discards the pending copy. The running program was never changed, so nothing happens to the machine. You are asked to confirm only when there are changes: Discard or Keep editing. In ST, the running source is put back in the editor.

Undo and Redo

  • Inside an edit (ladder), ↩ UNDO / ↪ REDO step through the pending edit's own changes. Your normal history is set aside and comes back when the edit closes. In ST, use the editor's own text undo (Ctrl+Z in the code box).
  • In RUN with nothing pending (ladder), UNDO or REDO does not swap anything directly: it opens the previous (or next) program in the history as an online edit, with markers, for you to review and ACCEPT or CANCEL. Accepting it unchanged moves along the history instead of adding a step, so repeated UNDO + ACCEPT walks back one step at a time, and REDO + ACCEPT walks forward again.

Other Controls While an Edit Is Pending

ControlBehaviour
■ STOP / F4Nothing is running any more, so the pending copy becomes the program — no ACCEPT checks or dialog. The status bar says so and reports any problem, as editing in STOP does. Outputs clear as on any STOP.
💾 SAVE / ExportSaves the running program. The status bar says the pending edit was not saved.
LOAD, Import, CLEAR, ST Examples (load into editor), LADDER ↔ ST switchRefused — the status bar says "Accept or cancel the online edit before …" (loading a program, importing, clearing, switching editors).
Leaving or reloading the pageThe browser asks you to confirm — the pending edit would be lost.
⏭ STEPSTOP only, so it never meets a pending edit.

Files and save slots never contain a pending edit, so the ladder JSON and .st formats are unchanged.

Try It — Control Panel Scene

Uses the default Start/Stop/Jog program (✕ CLEAR, then reload, if you have replaced it). Rung 0000 seals in Y1; rung 0001 drives the jog relay C1.

  1. CONNECT the Control Panel scene and press F5. Press Start — the run light (Y1) comes on.
  2. Add a rung while running. Hover below the last rung and click ⊕ Add New Rung, choose XIC → OTE, set the contact to Y1 and the coil to Y2. The banner appears and the rung shows NEW 0002, with no power flow. The light is still on — the running program has not changed.
  3. Press ACCEPT. The dialog shows Rungs inserted: 1. Accept — Y2 turns on in the Y tab on the next scan.
  4. Spring the trap. With the light still on, double-click the Y1 coil on rung 0000, change it to Y5 and Apply. Click the EDIT 0000 badge to compare it with the running rung.
  5. Press ACCEPT. The dialog warns: "These outputs are ON and nothing in the new program writes them — they will stay ON: Y1". Accept, then press Stop on the panel. The light stays on: no rung writes Y1 any more.
  6. Recover. Press ↩ UNDO. The previous program opens as an online edit (EDIT 0000). ACCEPT it. The dialog now warns that Y5 will stay ON — the same trap in reverse, since nothing writes Y5 any more. Rung 0000 writes Y1 again and Stop works; press STOP to clear Y5.
ST version: switch to ST, RUN the default program (the same Start/Stop/Jog circuit) and press Start. While the light is on, change the start of the seal-in line from Y1 := to Y2 := — the line turns amber in the gutter and its live values disappear. The accept dialog lists Y1 as staying ON.

Common Mistakes — the Online-Edit Trap

  • Deleting or readdressing a coil leaves the output ON. An OTE only writes while it is scanned. Remove it and the bit keeps its last state — exactly as on a real controller. Read the "will stay ON" list before you accept. To turn the output off: leave the coil in with a false rung for one scan, add an OTU, or STOP (which clears outputs).
  • Timers and counters keep their values. Lowering a preset below the accumulated value makes the done bit come on at once. Removing a RES rung leaves the count where it is.
  • ST initializers are not re-applied. Changing := 0 to := 10 does nothing to a running variable. Rename it, change its type, or write it from code if you need the new value.
  • Changing an element's type resets its memory. Turning an XIC into a P contact, or a CTU into a CTUD, starts its edge memory fresh — a bit that is already ON can fire a rising edge on the first scan.
  • Pending is not running. Testing on the scene before ACCEPT tests the old program. Watch for the banner.
  • SAVE saves the running program, not your pending edit. Accept first, then save.

Scan Cycle

How the simulator executes your program each 100 ms

Read Inputs
(X, AX from scene)
→
System Update
(SB, SW latched)
→
Execute Program
(Rung 0 → last)
→
Write Outputs
(Y, AY to scene)
→
Update Display
(redraw ladder + I/O)

Execution Order

  1. System bits and words (first scan, clocks, real-time clock, scan count) are updated once, before the first rung.
  2. Rungs execute from Rung 0000 to the last rung, top to bottom.
  3. Within each rung, elements are evaluated left to right.
  4. Parallel branches: all branches are evaluated; the rung is powered if any branch is TRUE.
  5. An OTE writes its rung's result each time its rung is scanned; an OTE in a rung skipped by a JMP keeps its last state.
  6. OTL/OTU bits persist across scans — they only change when a latch/unlatch rung is powered.
  7. A JMP with power skips the following rungs up to the rung holding its LBL; skipped rungs are not evaluated. Rungs inside an MCR zone whose MCR rung is false run with the power rail off. See Jump & Label and MCR Zones.

Timing Resolution

The nominal scan interval is 100 ms. Timer accumulators increment by the actual elapsed time since the last scan (capped at 500 ms). The scan time counter in the toolbar shows the most recent execution time in milliseconds. Your program can read the same figures: SW9 (execution time, µs), SW10 (maximum) and SW11 (actual interval, ms).

Online Edits and the Scan

While an online edit is pending, every scan runs the running program; the pending copy is not executed. ACCEPT swaps the programs between two scans, so no scan ever runs part old, part new. Memory is not touched by the swap — outputs, timers, counters and D registers carry straight on. See Online Editing.

Forcing I/O

Manually overriding bit and register states

Forcing X Inputs

Click any X button on the X tab or I/O tab at any time (RUN or STOP). In RUN mode the forced value is used in the next scan immediately. In STOP mode it triggers a display refresh of contact states.

Forcing Y Outputs

Y bits can only be forced in STOP mode. Click the LED on the Y tab or I/O tab to toggle. The change propagates to a connected scene immediately via BroadcastChannel.

Forcing C Relays

Click any C button on the C tab or I/O tab at any time. Useful for simulating latch states or internal flags without having to construct a full rung to set them.

Forcing AX Analog Inputs

Drag the slider on the AN tab or I/O tab for any AX channel. The new value takes effect on the next scan in RUN mode, or updates contact comparisons immediately in STOP mode.

Forcing AY Analog Outputs

In STOP mode only, type a value (0–4095) into the AY input field on the AN tab or I/O tab and press Enter. In RUN mode the field is read-only — the program controls AY via MOV and math blocks.

Save & Load

Program storage — browser slots and JSON files

Saving

  1. Click 💾 SAVE in the toolbar.
  2. Type a name in the Program Name field.
  3. Click Save — the program is stored in browser localStorage under that name.
  4. If a slot with the same name already exists it is overwritten. Up to 10 slots are kept.

Loading

  1. Click 📂 LOAD.
  2. Click a slot to load it (confirmation required to prevent accidental overwrite).
  3. Memory resets on load: X, Y, C, T, CT, AX and AY clear, fault flags (SB9–SB12, SW17, SW18) clear, and the D registers take the values saved with the program.

Auto-Restore

On page load the simulator automatically restores the most recently saved slot. No manual action is needed between sessions.

File Export / Import

In the Save/Load dialog, use ⬇ Export .json to download the program as a JSON file. Use ⬆ Import .json to load a previously exported file — useful for sharing programs between computers.

Online edit pending: SAVE and Export store the running program, and the status bar says the pending edit was not saved. LOAD and Import are refused until you ACCEPT or CANCEL the edit. Saved files never contain a pending edit, so the file formats are unchanged.
Warning: Browser localStorage is cleared by "Clear site data" in browser settings. Export to JSON for long-term backup.

Rung Templates

Pre-built rung patterns for common tasks

When inserting a new rung the Rung Template Picker opens. Addresses are auto-filled with the next available address for each type — you will rarely need to edit them manually.

TemplateDescription
XIC → OTEOne normally-open contact energises one output. Starting point for most rungs.
XIC → OTLContact sets (latches) an output. Use with XIC → OTU to create a Set/Reset pair.
XIC → OTUContact resets (unlatches) an output.
PAR → OTETwo contacts in parallel (OR) driving an output. Useful for start/seal-in circuits.
XIC → TONOn-delay timer — preset 2 seconds. Double-click block to change preset.
XIC → TOFOff-delay timer — preset 2 seconds.
XIC → CTUCount-Up counter — preset 10 counts.
XIC → RESReset rung targeting a counter by default. Edit the address to target a timer.
GRT → OTEAnalog compare — output ON when AX > 2048 (mid-scale). Edit comparand as needed.
GRT → OTL/OTUTwo-rung hysteresis band — inserts both the Set (high threshold) and Reset (low threshold) rungs together.
XIC → MOVMove a constant value to an AY register when the contact is ON.
XIC → RTORetentive timer — preset 5 seconds. Keeps its time when the rung drops; add a RES rung to clear it.
XIC → CTUDUp/down counter — the rung counts up, the next free X input counts down. Preset 10.
LIM → OTEOutput ON while AX is between 1000 and 3000.
P → INCCounts button presses in a D register: a rising-edge contact makes INC add 1 once per press.
SCL → DScales AX 0–4095 to 0–100 in a D register every scan.
P → TogglePush-on / push-off from one button. Inserts two rungs: a rising edge makes a one-scan pulse, and the pulse flips the output.
P → BSLShift a part bit along C bits on each pulse: a rising-edge contact drives a BSL on 8 free C bits, with the next free X as the pulse input and the following free X as the data bit. See Shift Registers.
DRUM → outputsStep through output patterns on time: XIC drives a DRUM on the next free D register with 4 outputs and four 2-second steps. Double-click the block, then Edit steps…. See Drum Sequencer.
MCR zoneRungs that run only while a condition is ON. Inserts three rungs: XIC → MCR, one rung inside the zone, and END MCR. See MCR Zones.

Connecting a 3D Scene

Linking the simulator to an interactive machine visualisation

3D Scenes are separate HTML files that display animated industrial machines. When connected to the simulator they exchange I/O state in real time via the browser's BroadcastChannel API — no server or network connection is needed.

How to Connect

  1. Place the scene HTML file in the same folder as acc-plc-simulator.html.
  2. Ensure acc-scenes.js is also in the same folder and lists the scene.
  3. Click CONNECT in the simulator toolbar.
  4. The Connect dialog lists all available scenes from acc-scenes.js.
  5. Click LAUNCH ↗ next to the scene you want — it opens in a new browser window.
  6. Once the scene loads, the CONNECT button changes to LINKED with a pulsing blue dot.
  7. The connected scene name appears next to the button — or N SCENES when more than one is connected.
Tip: Open the scene in a separate window (not a tab) for best performance. A dedicated window gets its own browser process and runs at full speed.

Disconnecting

Close the scene window. The simulator detects the loss of heartbeat messages within 2 seconds and resets the CONNECT button to its idle state.

Connecting Several Scenes at Once

From v1.120 the simulator can drive more than one scene at the same time, so a single program can run several machines. Earlier versions allowed only one scene and disabled the other LAUNCH buttons with BUSY; that restriction is gone.

Two scenes both using X1–X3 would fight over the same inputs, so the simulator gives each scene its own block of PLC memory and translates between them. The scene keeps its own numbering internally; your program uses the shifted addresses.

The rule is one addition: PLC address = scene address + that scene's offset, applied separately to X and to Y.

What the Connect Dialog Shows

ElementMeaning
PLC MEMORY stripX1–X16 and Y1–Y16 as cells, coloured per connected scene, red where two scenes contend. Hovering a cell names its owner.
+ ADD SCENE ↗Replaces LAUNCH ↗ once a scene is already connected.
SUGGESTEDThe offset was worked out just now to clear the scenes already connected. Editable before you launch.
STOREDAn offset you set earlier, kept from a previous session. It is not a suggestion, and it may no longer suit the scenes currently connected.
↻ suggestMoves that scene to the lowest free block.
clear of: …Names the connected scenes that the suggested offset is avoiding.
A scene holds its block until its window is closed. A scene window you have forgotten about still reserves its addresses, which makes the suggested offset for the next scene look arbitrary. The dialog's legend lists everything connected right now.

Reading a Scene's Addresses

A scene mapped to a non-zero offset relabels its own I/O rows to the PLC address and states the mapping in the panel header — for example INPUTS X (scene X1–X3 → PLC X4–X6). Those are the addresses to write your program against. The scene's About panel, its I/O map and its event log keep the scene's own numbering, because they describe the machine, which does not move.

Analog does not follow an offset. A scene using AX or AY must be the only connected scene, or the first one launched, so that its offset is 0. Analog on an offset scene is dropped silently. Digital I/O is unaffected.
Modbus is never offset. Offsets exist only on the simulator-to-scene link, and a scene cannot use both links at once, so a scene's Modbus address is always its own documented tag number.

How Many Scenes Can Run at Once

The practical limit is the 16-point memory map, not the browser. All six live scenes together would need 32 X and 21 Y, so they do not fit. Two scenes almost always fit; the widest pair — Palletizer and Tank Fill, ten inputs each — cannot run together. Four is the most that fits, and only one combination of four does. When there is no room the dialog says so rather than silently overlapping.

Standalone Scenes

Running a 3D scene without the simulator

Each scene HTML file is fully self-contained and can be opened directly in a browser without the simulator running. In standalone mode the scene operates as a 3D demonstration — you can interact with the machine but PLC logic is not executed.

Opening a Scene Standalone

  1. Open the scene HTML file directly in your browser (e.g. acc-conveyor-scene.html).
  2. The scene will initialise and display the machine in its default state.
  3. Interact with the machine elements as designed (click buttons, observe animations).

Connecting the Simulator Later

With the scene already open in one window, open acc-plc-simulator.html in a second window. The scene will detect the simulator's presence via BroadcastChannel and link automatically — you do not need to re-open the scene.

Note: BroadcastChannel only works between pages in the same browser on the same device. It does not work across different computers or browsers.

Use Cases for Standalone Scenes

  • Demonstrating machine operation without needing to explain ladder logic
  • Exploring the 3D environment before writing the control program
  • Classroom display on a projector while students write programs on their own machines

Scene I/O Mapping

Which signals are exchanged between the simulator and a scene

The BroadcastChannel (acc-plc-link) carries two message types each direction:

Scene → Simulator (Inputs)

SignalTypeDescription
X1–X16DigitalPhysical pushbuttons, limit switches, sensors in the scene. From v1.120 a scene sends only the addresses it owns, as an address-keyed object ({X1:true, X2:false}) rather than a padded array — a padded array would assert that every other scene's inputs were OFF.
AX1–AX8Analog 0–4095Analog sensors — e.g. position pot, pressure transducer, temperature. Arrive as integer array.

Simulator → Scene (Outputs)

SignalTypeDescription
Y1–Y16DigitalMotor contactors, solenoids, pilot lights, alarms. Sent as boolean array after every scan.
AY1–AY4Analog 0–4095Speed reference, valve position, display value. Sent as integer array after every scan.

Scene I/O Chips

When you open the CONNECT dialog, each scene card shows coloured chips listing its specific I/O assignments — green chips are inputs to the simulator (from the scene), olive chips are outputs from the simulator (to the scene).

Example: A conveyor scene might use X1 = Start button, X2 = Stop button, X3 = Part sensor, Y1 = Conveyor motor, AX1 = Speed pot (0–4095), AY1 = Motor speed reference.

Keyboard Shortcuts

All keyboard shortcuts available in the simulator

KeyAction
F1Open the help at the instruction under the mouse — a palette item, an ST list item, or an instruction on the ladder. Otherwise opens the help home (ST mode: the ST Instruction List).
F5Start RUN mode (continuous scan)
F4Switch to STOP mode
Ctrl+ZUndo last edit
Ctrl+YRedo (re-apply undone edit)
Ctrl+Shift+ZRedo (alternative shortcut)
EscClose any open dialog or modal. In the Drum Steps table, closes only the table (edits discarded) and returns to the Edit dialog.
EnterApply / confirm the currently open dialog. In the Drum Steps table, Enter is Save steps.
Dbl-clickOpen Edit dialog for any ladder element
Dbl-click headerEdit the rung comment
Right-clickOpen context menu for a contact

Undo / Redo

The simulator maintains an undo history of up to 50 states. A snapshot is taken automatically before every edit operation, including:

  • Inserting or deleting a contact
  • Editing an instruction (address, type, preset, comment)
  • Adding, editing, or removing parallel branches
  • Inserting or deleting a rung
  • Dragging rungs to reorder them

The ↩ UNDO and ↪ REDO buttons in the toolbar are greyed out when there is nothing to undo or redo. Loading a program (via LOAD or Import) and using CLEAR both reset the history to start fresh.

Note: In RUN, Undo/Redo never changes the running program directly. During an online edit they step through the edit's own changes; an accepted edit is one history step. With nothing pending, Undo or Redo opens the previous or next program as an online edit for you to ACCEPT or CANCEL — edge contacts and counters keep their memory, so it does not make them fire again. In the ST editor, Ctrl+Z is the code box's own text undo. The scan cycle itself never adds history entries. See Online Editing.

Gamepad

Drive simulator inputs with any standard USB or Bluetooth controller

The simulator supports any gamepad recognised by the browser's standard W3C Gamepad API — Xbox, PlayStation, generic USB HID, and most Bluetooth controllers. Plug in a controller, press any button, and the toolbar's 🎮 NO PAD badge turns green and shows the controller name.

Why use a gamepad? Analog sticks and triggers map cleanly to AX1–AX5 (0–4095) — the same range a real potentiometer or 4–20 mA transmitter produces. Face buttons act exactly like momentary pushbuttons. This is the closest browser-side input to a real industrial control surface.

Default Mapping

ControlAddressBehaviour
A / CrossX1Momentary — ON while held
B / CircleX2Momentary
X / SquareX3Momentary
Y / TriangleX4Momentary
D-pad ↑X5Momentary
D-pad ↓X6Momentary
D-pad ←X7Momentary
D-pad →X8Momentary
LB / L1X9Momentary
RB / R1X10Momentary
LT / L2 (analog)AX10–4095, 0.05 deadzone at rest
RT / R2 (analog)AX20–4095, 0.05 deadzone at rest
Left stick YAX30–4095, centred at 2048
Left stick XAX40–4095, centred at 2048
Right stick Y—Zoom — hold up to zoom in, hold down to zoom out (one 5% step every 250 ms while held)
Right stick XAX50–4095, centred at 2048
Right stick click—Reset zoom to 100% (same as the 1:1 button)
START / Options—RUN (same as F5)
SELECT / Share—STOP (same as F4)
Left stick click—STEP (one scan, STOP mode only)

Keyboard Still Works

The gamepad is purely additive. F5, F4, Ctrl+Z, Ctrl+Y, Esc, Enter and every other shortcut behave identically whether a controller is plugged in or not. The two paths call the same functions — they do not conflict.

Interaction with Scenes

When a 3D scene is connected and is driving an X or AX address, the gamepad cannot override that address. The scene's sensor or pushbutton wins. The gamepad still drives the remaining unused addresses — for example, the Conveyor scene uses X1–X4, so the gamepad's D-pad (X5–X8) and shoulder buttons (X9–X10) remain available to inject extra test inputs.

Browser Requirements

  • Chrome / Edge / Brave — full support including triggers and rumble
  • Firefox — full support; some Bluetooth pads require pairing through OS settings first
  • Safari — partial support; trigger axis polarity may be inverted on some pads
  • First-input requirement: the Gamepad API only reports a controller after the user presses a button or moves a stick. If the badge stays grey after plugging in, give it a button press to wake it
No installation: Gamepad support is built into the simulator. As long as the operating system recognises your controller, the simulator will see it. No drivers, no extra software.

Address Reference

Complete syntax for all register types

AddressTypeRangeNotes
XnDigital InX1–X16Boolean. Toggleable from X/IO tabs.
YnDigital OutY1–Y16Boolean. Forceable in STOP mode.
CnInternalC1–C256Boolean. Use C100+ by convention.
TnTimerT1–T256Resolves to Tn/DN.
Tn/DNTimerT1–T256Done bit — Acc ≥ Pre.
Tn/ENTimerT1–T256Enable bit — rung powered.
Tn/TTTimerT1–T256Timing bit — EN && !DN.
CTnCounterCT1–CT256Resolves to CTn/DN.
CTn/DNCounterCT1–CT256Done bit — Acc ≥ Pre.
AXnAnalog InAX1–AX8Integer 0–4095. Read by compares, MOV and math.
AYnAnalog OutAY1–AY4Integer 0–4095. Written by MOV and math blocks (clamped).
Tn/ACCTimer wordT1–T256Elapsed time in ms. Readable in compares and as a MOV or math source.
CTn/ACCCounter wordCT1–CT256Current count. Readable in compares and as a MOV or math source.
DnData RegisterD1–D400016-bit word, read as the instruction's Data Type. Through a pointer: D[D5] uses the register whose number is in D5. DINT and REAL use Dn (low word) and Dn+1. ST: D[n] as INT.
LnLabelL1–L256Jump target for JMP / LBL. Not memory — a label number only. Each label is used by one LBL.
SBnSystem BitSB1–SB18Read-only. SB9–SB11 can be cleared with OTU.
SWnSystem WordSW1–SW19Read-only. SW17 can be cleared with MOV 0.

Example Programs

Common ladder logic patterns

1 — Start / Stop / Seal-In (Motor Starter)

RungLogicPurpose
0000(XIC X1 || XIC C1) && XIC X2 → OTE C1Start PB or Seal-In, gated by Stop PB. C1 = Run relay.
0001XIC C1 → OTE Y1Run relay drives motor output.

2 — Jog Circuit (no seal-in)

RungLogicPurpose
0000(XIC X1 || XIC C1) && XIC X2 && XIO C2 → OTE C1Normal run, interlocked against jog (C2).
0001XIC X3 && XIO C1 → OTE C2Jog PB, interlocked against run.
0002(XIC C1 || XIC C2) → OTE Y1Motor runs on either relay.

3 — On-Delay Timer (Fan delay)

RungLogicPurpose
0000XIC X1 → TON T1 Pre=5000Start 5-second timer when X1 is ON.
0001XIC T1 → OTE Y1Y1 energises 5 s after X1 goes ON.

4 — Analog Threshold (High-level alarm)

RungLogicPurpose
0000GRT AX1 > 3276 → OTE Y3Alarm ON when AX1 > 80% (3276/4095). Drag AX1 slider to test.

5 — Hysteresis Band (Heating control)

RungLogicPurpose
0000GRT AX1 > 2500 → OTL Y4Heater OFF when temperature rises above setpoint.
0001LES AX1 < 1500 → OTU Y4Heater ON when temperature falls below lower threshold.

6 — Push-On / Push-Off (one button)

RungLogicPurpose
0000-|P|- X1 → OTE C100One-scan pulse each time the button is pressed.
0001(XIC C100 && XIO Y1) || (XIO C100 && XIC Y1) → OTE Y1The pulse flips Y1; between pulses Y1 holds. Insert it with the P → Toggle template.

7 — Working-Hours Light (real-time clock)

RungLogicPurpose
0000LIM SW5 7…17 && LIM SW4 1…5 → OTE Y1Y1 ON 07:00–17:59, Monday to Friday.
0001XIO Y1 && XIC SB6 → OTE Y2Outside those hours, Y2 flashes at 1 Hz.

8 — Accumulated Run Time

RungLogicPurpose
0000XIC X1 → RTO T5 Pre=10000X1 = motor running feedback. Adds up run time across every start and stop.
0001XIC T5 → OTE Y6Service light after 10 s of total running.
0002XIC X8 → RES T5Maintenance reset.

9 — Bottle Tracking (shift register)

RungLogicPurpose
0000-|P|- X1 → BSL C100 ×8, data X2, unload C200X1 pulses once per bottle pitch; X2 = bottle faulty at the entry.
0001XIC C105 → OTE Y3Reject pusher fires when the faulty bottle reaches the sixth position.

10 — Traffic Light (drum sequencer)

RungLogicPurpose
0000XIC X1 → DRUM D10, outputs Y1 ×3, reset X8Steps: Y3 red 5 s · Y1 green 4 s · Y2 amber 1.5 s, then back to red.
0001EQU D10 2 → OTE Y4Pedestrian "wait" lamp while the drum is on step 2 (green).

Structured Text (ST)

IEC 61131-3 textual programming  ·  acc-st.js v1.020

Structured Text is a second way to write a program — a text-based PLC language that looks like Pascal, defined by the IEC 61131-3 standard. Use the LADDER | ST toggle in the toolbar to switch modes. A program is written in ladder or ST; both drive the same I/O (X, Y, C, AX, AY) and the same 100 ms scan, so every 3D scene and the Modbus bridge work with either, unchanged.

Switching to ST

Click ST in the toolbar (the LANG · LADDER · ST switch at the top-left). The ladder canvas is replaced by a code editor with a line-number gutter and a live error list. Press ▶ RUN (or F5) to execute; the program is compiled first, and RUN is refused until any errors are fixed. The I/O panel, Connect, gamepad, Save/Load, and Step all work exactly as in ladder mode.

Error highlighting: as you type, the editor checks your code. Any error turns its line number red in the gutter and paints a red band across that line, and the message below explains it in plain language — for example, using = instead of := for an assignment reads "Use ':=' for assignment, not '='." Click a message to jump to the line.
Hover help: move the mouse over a keyword (VAR, IF, TON, CASE…) or an I/O address (X1, Y2, AX1) and a short explanation with an example pops up — handy while you're learning the language.
Instruction list: in ST mode the left side shows every keyword, data type, statement, operator, timer / counter / edge block, function and address ST understands. Hover an item for a card with a description, an example and — for the timers, counters and edge triggers — a timing chart. Click an item to insert complete, working code at the cursor, with the part you normally change highlighted — just type over it. A statement (IF, CASE, FOR …) goes on its own lines: on an empty line it fills it, at the start of a line it goes above it, anywhere else on the next line, so it never splits an existing statement. A timer, counter or edge block also gets an instance declared in VAR with a free name such as tmr1; a FOR loop declares its counter i if needed; a data type (BOOL, INT …) declares a new variable in VAR. In RUN, inserting opens an online edit like any other change. Press ? or F1 on an item to open its entry in the ST Instruction List.

The right-hand I/O tab lists the addresses your ST program actually uses and updates as you type, so a newly added output like Y2 := NOT X2; appears there right away.

Use the zoom control at the bottom-right of the editor (or hold Ctrl and scroll the mouse wheel) to enlarge or shrink the code, just like the ladder editor.

Tip: Click ☰ Examples in the ST bar for ready-to-run programs — seal-in, a 0.5 s flasher, a parts counter, analog tank control, and a traffic sequence. An example opens in a read-only preview window so your current program is untouched: read it, Copy the parts you want, or Load into editor to replace your program (you'll be asked to confirm). The default program is the Control Panel Start/Stop/Jog seal-in — the same circuit as the default ladder.

CLEAR in the toolbar wipes whichever program you're editing — in ST mode it clears the Structured Text (after a confirm); in ladder mode it clears the ladder. The two are independent. CLEAR, LOAD, loading an example and the LADDER | ST switch are refused while an online edit is pending.

Watching the logic run (online monitoring)

While the program runs, ST shows live state three ways:

  • Live Values column — a pane beside the code, aligned line-by-line, listing each address and variable used on that line with its current value. True conditions are highlighted green; false ones are greyed, so you can read which parts of each expression are satisfied. Analog values and counters show their number; function-block outputs show too (e.g. flash.Q = 1, onDelay.ET = 400). It refreshes every scan. Toggle it with the 👁 Live Values button.
  • Variables & Blocks watch — the right-hand pane; a compact list of every variable and function-block instance (Q / ET / CV).
  • I/O panel — the shared X / Y / C / AX / AY tabs, exactly as in ladder mode.

You can edit while the program runs: typing in the editor (or pressing ✎ EDIT) opens an online edit. The compiled program keeps running; added and changed lines are marked green and amber in the gutter, their live values are hidden, and ACCEPT compiles the new source and swaps it in between two scans. Variables and function blocks that keep their name and type keep their values — initializers are not re-applied. Drag the dividers between the program, Live Values, and Variables & Blocks panes to resize them; your widths are remembered.

Addresses (shared with ladder)

These names are the "wired" I/O — use them directly, do not declare them in VAR:

NameMeaningTypeAccess
X1–X16Discrete inputsBOOLread / write
Y1–Y16Discrete outputsBOOLread / write
C1–C256Internal relaysBOOLread / write
AX1–AX8Analog inputsINT 0–4095read / write
AY1–AY4Analog outputsINT 0–4095read / write
T1–T256Ladder timer done bitsBOOLread only
CT1–CT256Ladder counter done bitsBOOLread only
SB1–SB18System bits (first scan, clocks, …)BOOLread only · SB9–SB11 may be cleared with := FALSE
SW1–SW19System words (clock, scan timing, …)INTread only · SW17 may be cleared with := 0
D[1]–D[4000]Data registers, shared with ladderINT (−32768…32767, wraps)read / write · index may be an expression: D[i + 1]

D on its own is an error — always write D[n]. A constant index outside 1–4000 is a compile error; a computed one stops the PLC with a run-time error. If your program (or a FUNCTION / FUNCTION_BLOCK) declares its own variable called D, that variable is used instead of the data registers. ST reads and writes D only as INT; use ladder instructions for DINT, REAL and the other types.

Your own working variables go in a VAR ... END_VAR block. ST timing and counting use IEC function blocks (see next page), not the ladder T/CT table.

Hello, seal-in

VAR
  Running : BOOL;
END_VAR

// Start OR sealed-in, gated by Stop (N.C.)
Running := (X1 OR Running) AND NOT X2;
Y1 := Running;   // motor / pilot light

Press RUN, then in the X or I/O tab toggle X1 ON — Y1 latches on and stays on after you release X1. Toggle X2 to drop it.

ST Syntax & Function Blocks

What the ST engine supports

Data types

BOOL, INT/DINT/WORD (integer — values truncate on assignment), REAL (floating point), TIME (use T# literals like T#500ms, T#2s, T#1m30s), and ARRAY[lo..hi] OF TYPE. Identifiers are case-insensitive.

Operators

Boolean AND OR XOR NOT; comparison = <> < > <= >=; arithmetic + - * / MOD **. Assignment is :=. Boolean AND/OR short-circuit; divide/MOD by zero yields 0.

Control flow

IF cond THEN ... ELSIF cond2 THEN ... ELSE ... END_IF;

CASE step OF
  0:     Y1 := TRUE;
  1, 2:  Y2 := TRUE;
  3..5:  Y3 := TRUE;
ELSE     Y4 := TRUE;
END_CASE;

FOR i := 1 TO 8 BY 1 DO ... END_FOR;
WHILE cond DO ... END_WHILE;
REPEAT ... UNTIL cond END_REPEAT;
Scan model: the whole program runs top-to-bottom every scan. Use function-block timers for delays — never a WHILE loop to "wait". A per-scan loop budget stops runaway loops so a bad WHILE TRUE reports an error instead of freezing the browser.

Function blocks

Declare an instance in VAR, call it each scan with named parameters, read its outputs as members (.Q, .ET, .CV):

VAR onDelay : TON; END_VAR

onDelay(IN := X1, PT := T#2s);
Y1 := onDelay.Q;      // TRUE 2 s after X1
AY1 := onDelay.ET;    // elapsed ms
BlockInputsOutputsPurpose
TONIN, PTQ, ETOn-delay timer
TOFIN, PTQ, ETOff-delay timer
TPIN, PTQ, ETPulse timer
CTUCU, R, PVQ, CVCount up (Q when CV≥PV)
CTDCD, LD, PVQ, CVCount down (Q when CV≤0)
CTUDCU, CD, R, LD, PVQU, QD, CVCount up and down (QU when CV≥PV, QD when CV is 0; never below 0)
R_TRIGCLKQRising-edge one-scan pulse
F_TRIGCLKQFalling-edge one-scan pulse

Standard functions

ABS, SQRT, MIN, MAX, LIMIT(min,in,max), TRUNC, ROUND, SEL, SHL, SHR, and converters. You can also define your own FUNCTION and FUNCTION_BLOCK.

// scale analog 0–4095 to 0–100 %
VAR pct : INT; END_VAR
pct := LIMIT(0, AX1 * 100 / 4095, 100);

Save, load, share

In ST mode the toolbar SAVE / LOAD (and the ST bar's own buttons) store named ST programs in the browser, and Export / Import .st writes plain text files. Your source auto-saves as you type. Comments use // or (* ... *).

Standard syntax: the editor uses standard IEC 61131-3 — the same ST concepts transfer to Siemens, Rockwell, CODESYS, and other platforms.

ST Instruction List

Every keyword, block, function and address the Structured Text engine understands · acc-st.js v1.020

The same list sits on the left of the editor in ST mode. Hover an item there for a quick card, click it to insert it at the cursor, or press ? / F1 to jump here.

Program structure

PROGRAMProgram wrapper

Optional wrapper around a program. A bare list of declarations and statements works too.

PROGRAM Main
VAR
  run : BOOL;
END_VAR
run := X1;
END_PROGRAM
VARVariables

Declares your working variables: name : TYPE, with an optional initial value. Do not declare I/O addresses (X1, Y1 …) — they already exist.

VAR
  count : INT := 0;
  running : BOOL;
END_VAR
VAR_INPUTBlock inputs / outputs

Inside a FUNCTION or FUNCTION_BLOCK: VAR_INPUT lists the inputs, VAR_OUTPUT the outputs (read them as inst.name).

VAR_INPUT  inp : BOOL; END_VAR
VAR_OUTPUT out : BOOL; END_VAR
FUNCTIONYour own function

A reusable calculation that returns one value. Assign the result to the function name. Functions have no memory between calls.

FUNCTION Pct : INT
  VAR_INPUT raw : INT; END_VAR
  Pct := raw * 100 / 4095;
END_FUNCTION
FUNCTION_BLOCKYour own block

A reusable block that keeps its own memory between scans. Declare an instance in VAR, call it each scan, read its outputs as inst.name.

FUNCTION_BLOCK Debounce
  VAR_INPUT  inp : BOOL; END_VAR
  VAR_OUTPUT out : BOOL; END_VAR
  VAR t : TON; END_VAR
  t(IN := inp, PT := T#50ms);
  out := t.Q;
END_FUNCTION_BLOCK
// (* *)Comments

Notes for people, ignored by the PLC: // to the end of the line, or (* … *) and /* … */ around a block.

// start the pump
(* several
   lines *)

Data types

BOOLTrue / false

A bit: TRUE or FALSE.

running : BOOL;
INTWhole number

A whole number. Decimals are dropped when you assign to it. DINT, SINT, UINT and LINT behave the same here.

count : INT := 0;
REALDecimal number

A number with decimals (floating point). LREAL behaves the same.

level : REAL;
TIMEDuration

A duration in milliseconds, written with a T# literal: T#500ms, T#2s, T#1m30s, T#1h.

delay : TIME := T#2s;
WORDBit-group word

A whole number used as a group of bits. WORD, DWORD and BYTE are treated like INT here.

flags : WORD;
ARRAYList of values

A fixed list you index with [ ]. The bounds are part of the declaration.

steps : ARRAY[1..8] OF BOOL;
steps[3] := TRUE;

Statements

:=Assignment

Stores the value on the right in the variable or output on the left. Every statement ends with a semicolon.

Y1 := X1 AND NOT X2;
IFCondition

Runs statements only when a condition is true. Add ELSIF for more tests and ELSE for everything else.

IF X1 THEN
  Y1 := TRUE;
ELSIF X2 THEN
  Y1 := FALSE;
END_IF;
CASESelect by number

Runs one branch chosen by an integer value. Labels can be single values, lists (1, 2) or ranges (3..5). Ideal for step sequences.

CASE step OF
  0: Y1 := TRUE;
  1, 2: Y2 := TRUE;
  3..5: Y3 := TRUE;
ELSE
  step := 0;
END_CASE;
FORCounted loop

Repeats a fixed number of times in one scan. Use BY for a step other than 1.

FOR i := 1 TO 8 DO
  steps[i] := FALSE;
END_FOR;
WHILELoop while true

Repeats while a condition stays true — inside ONE scan. Never use it to wait for an input; use a timer or a state variable instead.

WHILE i < 10 DO
  i := i + 1;
END_WHILE;
REPEATLoop until true

Runs the body, then repeats until the condition is true. The body always runs at least once.

REPEAT
  i := i + 1;
UNTIL i >= 10 END_REPEAT;
EXITLeave a loop

Leaves the innermost FOR, WHILE or REPEAT loop at once.

IF found THEN EXIT; END_IF;
CONTINUENext loop pass

Skips the rest of this loop pass and starts the next one.

IF NOT steps[i] THEN CONTINUE; END_IF;
RETURNLeave the block

Leaves the current program, FUNCTION or FUNCTION_BLOCK for this scan.

IF NOT enable THEN RETURN; END_IF;

Operators

AND &Both true

TRUE only when both sides are TRUE. & is the same.

Y1 := X1 AND X2;
OREither true

TRUE when either side is TRUE.

Y1 := X1 OR X2;
XORDifferent

TRUE when the two sides differ.

Y1 := X1 XOR X2;
NOTInvert

Turns TRUE into FALSE and FALSE into TRUE.

Y1 := NOT X1;
= <> < >Compare

Compare two values: =, <> (not equal), <, >, <=, >=. The result is BOOL. Comparisons bind tighter than AND / OR.

Y1 := AX1 > 2048;
Y2 := (count >= 5) AND X1;
+ - * /Arithmetic

+ − * / on numbers; MOD gives the remainder, ** raises to a power. / gives a decimal result — storing it in an INT drops the decimals. Dividing by 0 gives 0.

pct := AX1 * 100 / 4095;
r := count MOD 10;

Timers, counters, edges

TONOn-delay timer

Q turns ON after IN has been TRUE for PT. ET is the elapsed time. IN going FALSE resets it.

Inputs: IN, PT   Outputs: Q, ET — declare an instance in VAR (name : TON;), call it once every scan, read outputs as name.Q.

tmr1(IN := X1, PT := T#2s);
Y1 := tmr1.Q;
TOFOff-delay timer

Q is ON while IN is TRUE and stays ON for PT after IN goes FALSE.

Inputs: IN, PT   Outputs: Q, ET — declare an instance in VAR (name : TOF;), call it once every scan, read outputs as name.Q.

tmr1(IN := X1, PT := T#2s);
Y1 := tmr1.Q;
TPPulse timer

When IN goes TRUE, Q gives one pulse of exactly PT, however long IN stays on.

Inputs: IN, PT   Outputs: Q, ET — declare an instance in VAR (name : TP;), call it once every scan, read outputs as name.Q.

pls1(IN := X1, PT := T#1s);
Y1 := pls1.Q;
CTUCount up

CV counts up on each rising edge of CU. Q turns ON when CV ≥ PV. R resets CV to 0.

Inputs: CU, R, PV   Outputs: Q, CV — declare an instance in VAR (name : CTU;), call it once every scan, read outputs as name.Q.

cnt1(CU := X1, R := X2, PV := 3);
Y1 := cnt1.Q;
CTDCount down

CV counts down on each rising edge of CD. LD loads PV into CV. Q turns ON when CV reaches 0.

Inputs: CD, LD, PV   Outputs: Q, CV — declare an instance in VAR (name : CTD;), call it once every scan, read outputs as name.Q.

cnt1(CD := X1, LD := X2, PV := 3);
Y1 := cnt1.Q;
CTUDCount up / down

CV counts up on CU and down on CD (never below 0). QU when CV ≥ PV, QD when CV is 0. R resets, LD loads PV.

Inputs: CU, CD, R, LD, PV   Outputs: QU, QD, CV — declare an instance in VAR (name : CTUD;), call it once every scan, read outputs as name.Q.

cnt1(CU := X1, CD := X2, R := X3, PV := 10);
Y1 := cnt1.QU;
R_TRIGRising edge

Q is TRUE for exactly one scan when CLK goes FALSE → TRUE.

Inputs: CLK   Outputs: Q — declare an instance in VAR (name : R_TRIG;), call it once every scan, read outputs as name.Q.

rise1(CLK := X1);
IF rise1.Q THEN count := count + 1; END_IF;
F_TRIGFalling edge

Q is TRUE for exactly one scan when CLK goes TRUE → FALSE.

Inputs: CLK   Outputs: Q — declare an instance in VAR (name : F_TRIG;), call it once every scan, read outputs as name.Q.

fall1(CLK := X1);
Y1 := fall1.Q;

Functions

ABSAbsolute value

Removes the sign: ABS(-5) = 5.

err := ABS(sp - pv);
SQRTSquare root

Square root of a number.

flow := SQRT(dp) * k;
MINSmallest

The smallest of two or more values.

low := MIN(AX1, AX2);
MAXLargest

The largest of two or more values.

high := MAX(AX1, AX2);
LIMITClamp

LIMIT(MN, IN, MX) keeps IN between MN and MX.

pct := LIMIT(0, raw * 100 / 4095, 100);
TRUNCDrop decimals

Cuts off the decimals: TRUNC(2.9) = 2, TRUNC(-2.9) = -2.

whole := TRUNC(level);
ROUNDRound

Rounds to the nearest whole number; an exact .5 rounds up (2.5 → 3, −2.5 → −2).

n := ROUND(level);
EXPTPower

EXPT(base, exponent) — the same as base ** exponent.

area := EXPT(r, 2) * 3.14159;
SELSelect one of two

SEL(G, IN0, IN1) returns IN0 when G is FALSE and IN1 when G is TRUE.

AY1 := SEL(X1, 0, 4095);
SHL / SHRShift bits

SHL(v, n) shifts the bits of v left by n places; SHR shifts right.

mask := SHL(1, bitNo);
REAL_TO_INT …Type conversion

REAL_TO_INT (rounds), INT_TO_REAL, BOOL_TO_INT (TRUE → 1), INT_TO_BOOL (non-zero → TRUE).

n := REAL_TO_INT(level);
on := INT_TO_BOOL(count);

Addresses

X1–X16Inputs

Discrete inputs (BOOL). Scenes write them; toggle them in the X or I/O panel while testing.

Y1 := X1;
Y1–Y16Outputs

Discrete outputs (BOOL) you write with := . Scenes read them.

Y1 := X1 AND NOT X2;
C1–C256Internal relays

Memory bits shared with the ladder — like control relays.

C1 := X1 OR C1;
AX1–AX8Analog inputs

Analog inputs, 0–4095 (INT).

pct := AX1 * 100 / 4095;
AY1–AY4Analog outputs

Analog outputs, 0–4095 (INT). Values are clamped to that range.

AY1 := 2048;
T1 / CT1Ladder timer & counter bits

The ladder timer and counter done bits — read-only in ST. For timing and counting in ST use TON / CTU blocks.

Y2 := T1;
SB1–SB18System bits

Read-only status bits: SB1 always ON, SB3 first scan, SB5/SB6/SB7 0.5 s / 1 s / 1 min clocks, SB9–SB12 fault flags (SB9–SB11 can be cleared with := FALSE).

IF SB3 THEN count := 0; END_IF;
Y1 := SB6;   // 1 Hz flasher
SW1–SW19System words

Read-only status words: real-time clock (SW1–SW7), scan time and count, error code SW17 (clear with := 0) and rung SW18.

Y1 := (SW5 >= 7) AND (SW5 < 18);
D[1–4000]Data registers

The ladder D registers, read and written here as INT (wraps at ±32767). The index can be any expression.

D[10] := AX1;
FOR i := 1 TO 5 DO D[100 + i] := 0; END_FOR;

Values

TRUE / FALSEBoolean values

The two BOOL values. TRUE is 1 / ON, FALSE is 0 / OFF.

Y1 := TRUE;
T#…Time literal

A duration: T#250ms, T#2s, T#1m30s, T#1h, T#1d. Stored in milliseconds.

tmr1(IN := X1, PT := T#1m30s);