AutomationDirect stated in 2017 that it had no plans to implement IEC 61131-3 in its PLCs, so a project that assumes structured text, function block diagram, user-defined functions or user-defined types has to be checked against the current programming software before any code is written. That statement is a roadmap position from a legacy forum post, not a feature list. Read the current software documentation to see what the editor supports today. The sections below build the workaround in order, and each ends with a check that must pass before the next.
Confirm what the current software supports before writing any logic
Do not chase a firmware update, a hidden ST editor or a file-import filter hoping ST appears. IEC 61131-3 defines five languages (ladder diagram, function block diagram, structured text, sequential function chart, instruction list) plus user-defined function blocks, functions and data types. A controller that does not implement the standard does not offer them as a group, and code from an IEC-based runtime does not paste across.
Open the current programming software help and release notes for your controller. Fill in this table with yes/no answers and the software version you read them from.
| Capability | Why the project needs it | Where to read the answer |
|---|---|---|
| Structured text editor | Array loops, string handling, math-heavy routines | Language or editor list in the software help |
| Function block diagram | Existing FBD code, block-style reuse | Language or editor list |
| Sequential function chart / Grafcet | Sequences written as steps and transitions | Language or editor list |
| User-defined block with private memory | Per-instance timers, edge memory, latches | Instruction set and user-block documentation |
| User-defined types: nesting, arrays inside, arrays of types | Equipment records such as pump or valve structures | Data-type and tag documentation |
Check: every row has a yes/no with a date and software version. Rows marked no drive the next section.
Count the ST, UDT and block dependencies before you commit
AutomationDirect's 2016 survey, run by itself and with Control Engineering, drove the no-plans position. 85% of Control Engineering respondents and 75% of AutomationDirect customers were not familiar or only somewhat familiar with IEC 61131-3. Respondents rated ladder diagram most important (85%), then function block diagram (49%), custom function blocks (48%), structured text (29%), sequential function charts (27%), instruction list (23%), C (10%) and Basic (10%).
Those numbers describe a ladder-first user base. Integrators who work across brands report a different mix: some ST on most non-AutomationDirect projects, at roughly 60/40 ladder to ST. They also rank nested user-defined types, arrays inside types, arrays of types, and user-defined blocks with private static memory (as in the Siemens S7 series) as very important. If your standard project looks like that second profile, the workaround below is a stopgap.
| Dependency in your design | Fit on a ladder-only controller | Action |
|---|---|---|
| Timers, counters, contact logic, simple math | Native | Write as ladder |
| Loop-heavy, array or string routines | High effort, timing changes | Convert (next section) or change platform |
| SFC / Grafcet sequences | No editor | Rebuild as a step-number state machine |
| Repeated equipment blocks with private state | Copy per instance by hand | Dedicated memory per copy |
| Nested types, arrays in types, arrays of types | No equivalent data structure | Address-stride map; change platform if the design depends on true UDTs |
Check: the table is filled for your actual project. Any design that depends on nested UDTs or static-variable blocks at scale is a platform-change decision. Stop here and make it before building anything.
Convert ST routines to ladder instead of porting code
The quick fix is to transcribe ST line by line into long ladder rungs. It fails in two places: exclusive branches and loops.
- IF / ELSIF / ELSE: build mutually exclusive rungs with compare contacts, so only one branch energizes per scan.
- CASE: hold the selector in one step register and use one compare-equal contact per case.
- Boolean assignments: convert to series/parallel contacts.
- Math assignments: use the math or expression instruction in your instruction set. Confirm from the instruction reference that it accepts the operators and data types you need.
- FOR loops: a loop finishes inside one scan in ST. In ladder you either unroll it, which adds scan time, or process one index per scan, which changes response time. Pick per routine and document it.
Rung order matters. Ladder executes top to bottom, so a rung that reads a value written further down reads the previous scan's value, where ST would have used the value just assigned. Place producers above consumers.
Check: bench-test each converted routine with an input vector that includes boundary values and compare outputs to the original ST or to hand-calculated results. Test the per-scan-index case across a full sweep of the index.
Replace user-defined types with an address-stride map
Without nested types, arrays inside types or arrays of types, an equipment record becomes a flat block of addresses with a fixed stride per instance. Define the map once, on paper, before any rung refers to it.
addr(field) = BASE + (n - 1) * STRIDE + field_offset
Example layout (illustrative values, not product addresses):
STRIDE = 4
field_offset: Run = 0, Fault = 1, Setpoint = 2, Actual = 3
Range rule for an array of N records:
last_used = BASE + (N - 1) * STRIDE + max(field_offset)
last_used must be less than the BASE of the next block
The wrong fix is a naming convention with no address discipline, such as P01_Run, P02_Run assigned wherever free memory happens to be. It works until someone adds instance 9 and no indexed loop can reach it. Use consistent stride even if some fields sit unused.
Check: for every block, calculate last_used with the formula above and confirm it stays below the next block's base. Cross-reference the software's address usage view for overlaps.
Give every repeated block its own memory
A function block instance carries static variables: previous-scan state for edge detection, timer accumulators, latched states. Without user blocks, that memory does not exist per call. A subroutine called for two machines shares one set of one-shot bits and one timer, so the second call overwrites the first call's edge memory and the timer restarts or misses. The symptom is one machine that works and a second that intermittently misses starts or trips early.
- Assign each instance its own timer, one-shot, latch and step-register addresses from the stride map.
- Copy the ladder block per instance, or use one indexed block if your instruction set supports indirect addressing. Verify indirect support in the instruction reference before relying on it.
- Place any retentive state, such as latched faults or step registers, in memory that the controller retains through power loss. Check the memory type in the software's memory documentation.
Check: force instance 1 and instance 2 into opposite states at the same time. Start both, stop one, fault the other. Each must respond independently, then swap the states and repeat.
Flag the workaround as temporary in the project
Separate the two fixes in the paperwork. The temporary restore is the ladder conversion plus the stride map on this controller. The permanent repair is a platform that implements structured text, nested and array-capable user-defined types, and user blocks with private static memory.
- Keep the original ST source outside the PLC as the reference for the converted ladder.
- Put a header comment on each duplicated block naming its source block and the instance number.
- Stop building workarounds when a change to one block has to be copied by hand into every instance, or when the memory map needs a redesign for each new machine type. That is a platform decision, not a programming one.
Check: the documentation lists every duplicated block with its source, so a future edit to one is applied to all.
Run the end-to-end check before release
- Re-read the capability table against the software version you are shipping and confirm the dated yes/no answers still hold.
- Replay the converted-routine test vectors from the conversion step on the final program, not on the bench copy.
- Run the two-instance isolation test on every repeated block, including the last instance in the stride map.
- Cycle power and confirm retentive step registers and latches restore to the intended state and that non-retentive memory clears as designed.
- Read scan time from the controller diagnostics with unrolled loops and duplicated blocks in place, and compare it to the watchdog or scan-time limit shown in the software. Look up the limit; do not assume one.
- Recalculate
last_usedfor each array with the range rule and confirm no overlap.
FAQ
Why does AutomationDirect not offer structured text or function blocks in its PLCs?
A 2017 statement said its 2016 survey showed limited IEC 61131-3 acceptance: 85% of Control Engineering respondents and 75% of its customers were unfamiliar or only somewhat familiar with the standard. It concluded implementing it was not warranted then. That is a 2017 position, so check the current software help for what the editor supports today.
Why does one ladder subroutine misbehave when I call it for two machines?
The subroutine shares one set of timer, one-shot and latch addresses across both calls, so the second call overwrites the first's edge memory. Give each machine its own set of addresses from an address-stride map and test both in opposite states at once.
When should I stop and call AutomationDirect support?
Call official AutomationDirect technical support when the capability table has an unresolved row, and have the programming software version and controller model ready to ask whether the current release supports ST, nested user-defined types or user blocks with private memory. If the design needs nested types or static-variable blocks and support cannot confirm them, stop converting and select a platform that implements them. Do not release a ladder workaround that has not passed the two-instance isolation and power-cycle checks.