PLC Arrays, Data Handling and Math Instructions
Build safe PLC array and data-handling logic with bounded indexes, deterministic loops, copy/fill instructions, numeric type guards, scan traces and vendor-specific implementation notes.
Review status: Vendor-neutral method reviewed against IEC 61131-3:2025 and current official Rockwell Studio 5000, Siemens STEP 7 V20, CODESYS, Schneider EcoStruxure Machine Expert and Beckhoff TwinCAT array documentation; exact declaration syntax, type promotion, conversion, bounds checking, fault response, copy atomicity, overlap behavior, task preemption, execution time and safety suitability require target CPU, firmware, compiler and project verification
Direct answer: treat an array operation as a bounded data transaction
A PLC array is an indexed collection of elements that share a declared type. Safe array logic proves four things before it reads or writes an element: the array owner, the valid lower and upper bounds, the meaning of each element, and the maximum work the controller can perform in one task invocation. A correct value at the wrong index is still a control defect.
For a fixed eight-sample buffer, declare the bounds explicitly, reject or diagnose any index outside them, use a wider accumulator than the individual samples where necessary, guard division by the valid-sample count, and measure the worst-case execution time. If the array can be modified by another task, I/O update or communication service while it is processed, define whether the calculation needs a consistent snapshot and use the target platform's supported synchronization or staging method.
| Data task | Safe default | Evidence required before release |
|---|---|---|
| read or write one element | validate the runtime index before access | low, high, just-below and just-above boundary tests |
| loop across every element | derive loop limits from the declared contract | inclusive bound trace and measured worst-case task time |
| sum or average values | use a suitable accumulator and count only accepted values | overflow analysis, invalid-value policy and zero-count test |
| move one value | verify source/destination types and conversion | negative, maximum, fractional and invalid cases as applicable |
| copy a block | prove length units, source/destination extents and overlap behavior | byte/element count, sentinel values around the destination and snapshot requirement |
| fill an array | write only the intended elements and control when it executes | first/last element, adjacent data and one-shot/command behavior |
| search or compare | define not-found, duplicate and quality behavior | empty/no-match, first/last match and repeated-value tests |
| process a large buffer | split work over scans only when partial completion is acceptable | progress state, data version, deadline and restart/cancel behavior |
This guide owns the integrated array, indexing, data-movement, numeric and scan-budget workflow. The existing PLC math instructions guide remains the focused owner for ADD, SUB, MUL, DIV, MOD and compute expressions. The PLC data types guide owns the complete type catalogue. The indirect addressing definition owns the concise direct/indexed/indirect distinction. This page connects those pieces into tested array applications without replacing them.
Define the data contract before writing a loop
An array declaration answers only part of the engineering question. ARRAY[0..7] OF REAL describes eight values and their element type. It does not state whether they are temperatures, whether a bad-quality sample is represented by a separate Boolean, which task writes them, whether the values form a coherent snapshot, or what should happen if an operator requests sample 9.
Write a compact contract for every production array:
| Contract field | Example for an eight-channel temperature buffer | Why it matters |
|---|---|---|
| owner | acquisition function block writes; calculation routine reads | prevents hidden multiple writers |
| bounds | indices 0 through 7 inclusive | prevents off-by-one assumptions |
| element meaning | channel number maps to an approved instrument list | stops valid values being assigned to the wrong equipment |
| element type | REAL engineering value plus separate validity/status |
avoids treating a sentinel number as healthy data |
| units | degrees Celsius | prevents scale or unit mismatch |
| update model | all channels staged, then version increments | tells readers when a coherent set is available |
| invalid policy | exclude invalid channels; alarm if fewer than six are valid | makes average behavior deterministic |
| consumer deadline | completed within one 20 ms task invocation | turns scan performance into a testable requirement |
| restart behavior | initialize validity false; do not reuse an unverified retained buffer | prevents plausible stale data after startup |
Use a structure array when every element needs related fields:
TYPE ST_ChannelSample :
STRUCT
Value : REAL;
Valid : BOOL;
StatusCode : UINT;
Sequence : UDINT;
END_STRUCT
END_TYPE
VAR_GLOBAL CONSTANT
cFirstChannel : DINT := 0;
cLastChannel : DINT := 7;
END_VAR
VAR_GLOBAL
aSamples : ARRAY[cFirstChannel..cLastChannel] OF ST_ChannelSample;
END_VAR
This IEC-style example is a portable design sketch, not paste-ready code for every tool. Current CODESYS, TwinCAT and Schneider documentation supports explicit lower/upper limits and arrays of derived types, but syntax, accepted constant types, initialization and variable-length parameters vary. Rockwell Logix array declarations are normally created as tags with a dimension, and zero-based indexing is central to that environment. Siemens STEP 7 supports array types and symbolic indexing, with exact instruction and type availability depending on the target CPU and TIA version.
Array bounds are inclusive—and vendor fault behavior differs
For ARRAY[0..7], the valid indexes are 0, 1, 2, 3, 4, 5, 6 and 7. The array has eight elements because the count is upper - lower + 1. A loop using 0 TO 8 performs nine iterations and attempts one invalid access. A loop using 0 TO 6 silently leaves the final element stale.
Where supported for the array form in use, derive limits with LOWER_BOUND and UPPER_BOUND:
FOR i := LOWER_BOUND(aSamples, 1) TO UPPER_BOUND(aSamples, 1) DO
(* bounded work *)
END_FOR;
The dimension argument 1 means the first dimension in the cited CODESYS/Schneider/TwinCAT family documentation. Do not assume every compiler version supports the same operators for both fixed and variable-length arrays. When a target lacks bound introspection, define named constants once and use the same constants for the declaration, validation and loop.
Validate before the array reference is evaluated
The guard must surround the reference itself:
IndexValid := (RequestedIndex >= cFirstChannel)
AND (RequestedIndex <= cLastChannel);
IF IndexValid THEN
SelectedValue := aSamples[RequestedIndex].Value;
SelectedValid := aSamples[RequestedIndex].Valid;
ELSE
SelectedValid := FALSE;
ArrayIndexFault := TRUE;
END_IF;
Do not calculate SelectedValue := aSamples[RequestedIndex].Value and then inspect the index. By then the invalid access has already occurred. Avoid “fixing” every bad index with an unconditional clamp unless the requirement explicitly says the nearest valid element is an acceptable substitute. Clamping recipe 12 to recipe 9 can apply the wrong product settings while hiding the original defect.
Platform behavior is not portable. Current Rockwell documentation states that an out-of-range Logix array subscript can generate a major fault, including type 4 code 20 for the documented instruction context. Siemens warns that indirect addressing is calculated at runtime and can access or overwrite incorrect values. CODESYS-derived environments can use implicit CheckBounds behavior, but whether it clamps, reports or raises an exception depends on the generated or customized check and project settings. A code review that says “the runtime will catch it” is therefore incomplete.
Boundary acceptance matrix
| Test | Requested index | Expected result |
|---|---|---|
| lower boundary | 0 | element 0 read or written exactly once |
| interior | 3 | element 3 selected; neighbors unchanged |
| upper boundary | 7 | element 7 selected exactly once |
| below lower | -1 | access blocked, invalid flag/alarm set, no array element changes |
| above upper | 8 | access blocked, invalid flag/alarm set, no array element changes |
| extreme input | minimum/maximum value the source tag can carry | arithmetic cannot wrap into a plausible valid index |
| startup | retained or externally written index before first normal scan | initialization and validation prevent accidental access |
These cases must be run on the exact target or an approved representative test environment. A browser exercise can teach the reasoning but cannot reproduce vendor runtime faults or task scheduling.
Worked example: calculate a quality-aware array average
The requirement is to calculate the minimum, maximum and arithmetic mean of eight channels. Invalid samples must not influence the result. When no samples are valid, the result must be marked invalid and division must not execute. The operation must finish within its task budget.
VAR
i : DINT;
ValidCount : UDINT;
SumWide : LREAL;
Minimum : REAL;
Maximum : REAL;
Mean : REAL;
ResultValid: BOOL;
END_VAR
ValidCount := 0;
SumWide := 0.0;
ResultValid := FALSE;
FOR i := cFirstChannel TO cLastChannel DO
IF aSamples[i].Valid THEN
IF ValidCount = 0 THEN
Minimum := aSamples[i].Value;
Maximum := aSamples[i].Value;
ELSE
IF aSamples[i].Value < Minimum THEN
Minimum := aSamples[i].Value;
END_IF;
IF aSamples[i].Value > Maximum THEN
Maximum := aSamples[i].Value;
END_IF;
END_IF;
SumWide := SumWide + REAL_TO_LREAL(aSamples[i].Value);
ValidCount := ValidCount + 1;
END_IF;
END_FOR;
IF ValidCount > 0 THEN
Mean := LREAL_TO_REAL(SumWide / UDINT_TO_LREAL(ValidCount));
ResultValid := TRUE;
END_IF;
The explicit conversion function names are representative IEC-style notation. Confirm exact names and rounding behavior in the target compiler. A wider LREAL accumulator reduces the chance of losing range or precision compared with summing into the individual REAL element type, but it does not make a result mathematically exact or guarantee the target supports efficient 64-bit floating-point execution.
Initializing minimum and maximum to zero is wrong for an all-positive or all-negative valid set. The first accepted sample is a defensible initializer. The ValidCount > 0 guard prevents divide by zero. If at least six valid channels are required, replace the final criterion with that explicit minimum and expose the rejected count.
Scan-by-scan trace
Assume the valid samples are 12.5, invalid, 15.0, -2.0, 10.5, invalid, 14.0, 10.0:
| Iteration/index | Value | Valid | ValidCount after | SumWide after | Minimum | Maximum |
|---|---|---|---|---|---|---|
| start | — | — | 0 | 0.0 | not yet assigned | not yet assigned |
| 0 | 12.5 | yes | 1 | 12.5 | 12.5 | 12.5 |
| 1 | unknown | no | 1 | 12.5 | 12.5 | 12.5 |
| 2 | 15.0 | yes | 2 | 27.5 | 12.5 | 15.0 |
| 3 | -2.0 | yes | 3 | 25.5 | -2.0 | 15.0 |
| 4 | 10.5 | yes | 4 | 36.0 | -2.0 | 15.0 |
| 5 | unknown | no | 4 | 36.0 | -2.0 | 15.0 |
| 6 | 14.0 | yes | 5 | 50.0 | -2.0 | 15.0 |
| 7 | 10.0 | yes | 6 | 60.0 | -2.0 | 15.0 |
| complete | — | — | 6 | 60.0 | -2.0 | 15.0 |
The accepted mean is 60.0 / 6 = 10.0. Record the result alongside ResultValid, ValidCount, the input version and a completion sequence. A bare 10.0 cannot tell a consumer whether it came from eight channels, six channels or an old retained calculation.
Control array work against the scan budget
An eight-element calculation is usually small, but the engineering method must survive when eight becomes eight hundred. A FOR loop normally completes all iterations during the current program invocation. It does not automatically spread work over future scans. If its upper limit depends on configuration or received data, the worst-case path can grow after commissioning.
Measure the task with representative data and simultaneous load. Include communication, HMI access, diagnostics and other scheduled programs. The watchdog is a final fault boundary, not the acceptable design target. Also measure the application deadline: a calculation can remain below the watchdog yet still make an output, alarm or sequence response too late.
| Strategy | Use when | State that must be explicit | Main risk |
|---|---|---|---|
| process all elements each scan | bounded work is comfortably inside the measured budget | input snapshot/version and result completion | later array growth consumes the margin |
| process N elements per scan | partial progress is acceptable and deadline spans scans | current index, running result, data version, busy/done/error | mixed datasets or consumers reading partial output |
| process on command in a slower task | result is not needed every fast-control scan | request/acknowledge, task ownership and result age | priority/preemption and stale result assumptions |
| use a platform file instruction mode | instruction semantics and mode match the need | control structure, position, completion/error and retrigger policy | copied examples hide vendor-specific multi-scan behavior |
| move work outside the PLC | algorithm/data volume exceeds deterministic controller role | interface contract, quality, timeout and fallback | external result treated as always available or authoritative |
Multi-scan chunking pattern
The following design sketch processes at most four elements per invocation. It restarts if the producer changes InputVersion during the calculation, so one result cannot silently combine two datasets.
IF Start AND NOT Busy THEN
Busy := TRUE;
Done := FALSE;
WorkIndex := cFirstChannel;
CapturedVersion := InputVersion;
RunningSum := 0.0;
RunningValidCount := 0;
END_IF;
IF Busy THEN
IF InputVersion <> CapturedVersion THEN
Busy := FALSE;
DataChangedError := TRUE;
ELSE
ElementsThisScan := 0;
WHILE (WorkIndex <= cLastChannel)
AND (ElementsThisScan < 4) DO
IF aSamples[WorkIndex].Valid THEN
RunningSum := RunningSum
+ REAL_TO_LREAL(aSamples[WorkIndex].Value);
RunningValidCount := RunningValidCount + 1;
END_IF;
WorkIndex := WorkIndex + 1;
ElementsThisScan := ElementsThisScan + 1;
END_WHILE;
IF WorkIndex > cLastChannel THEN
IF RunningValidCount > 0 THEN
Mean := LREAL_TO_REAL(
RunningSum / UDINT_TO_LREAL(RunningValidCount));
ResultValid := TRUE;
ELSE
ResultValid := FALSE;
END_IF;
ResultVersion := CapturedVersion;
Busy := FALSE;
Done := TRUE;
END_IF;
END_IF;
END_IF;
Done needs defined semantics: one scan, latched until acknowledged, or a sequence number change. Start needs edge or handshake behavior so it cannot restart the operation continuously. Cancellation, startup and a mid-operation recipe change all need tests. For tightly coupled control data, copying the producer's completed staging buffer into a consumer snapshot may be simpler than aborting and retrying; use only a copy/synchronization mechanism supported by the target.
Move, copy, fill, search and compare instructions
Data-handling instruction names look similar across platforms, but the operands can use different units and the runtime may treat interruption or overlap differently.
| Intent | Rockwell Logix examples | Siemens S7-1200/1500 examples | IEC/CODESYS-style approach | Verification focus |
|---|---|---|---|---|
| move one compatible value | MOV |
MOVE |
typed assignment | conversion, range and enable behavior |
| copy a contiguous block | COP or CPS |
MOVE_BLK, UMOVE_BLK, MOVE_BLK_VARIANT where supported |
array/structure assignment or a bounded loop supported by the compiler | length unit, extent, interruption and overlap |
| fill a block | FLL |
FILL_BLK, UFILL_BLK |
bounded loop or supported initialization/assignment | first/last destination and retrigger behavior |
| operate across a file/array | FAL |
loop or appropriate array instruction | bounded FOR loop/function |
all-at-once versus distributed work and fault state |
| search/compare | FSC, compare instructions |
compare/array instructions by CPU/version | bounded loop with explicit not-found result | duplicates, first/last match and data quality |
| obtain array size/bounds | SIZE for documented dimensions |
LOWER_BOUND, UPPER_BOUND where supported |
LOWER_BOUND, UPPER_BOUND for supported array forms |
dimension numbering and version support |
Rockwell COP and CPS are byte-oriented transfers with destination-element length
Current Rockwell documentation states that COP and CPS perform a straight byte-to-byte copy over contiguous memory. The Length operand represents the number of destination elements, so Length := 10 copies different byte counts when the destination is ten SINT, INT, DINT or structure elements. This is why “copy 10” is not a complete review statement.
CPS provides synchronous-copy behavior so other tasks or I/O updates do not change the participating data until the copy completes in the documented context. That consistency has a scheduling cost and is not a reason to use CPS blindly for large structures. The same official reference warns that a copy can write beyond a member array while remaining inside the containing base tag. Surround the destination with known sentinel fields during a bench test and prove that only the intended bytes change.
COP(SourceArray[0], DestinationArray[0], 10)
This conceptual call is correct only when both arrays, element types, lengths and controller family match the reviewed instruction documentation. Do not infer type conversion from a raw block copy. If the types differ, the byte pattern may remain intact while its interpreted value changes.
Siemens block moves distinguish interruptible and uninterruptible work
Current S7-1200 documentation lists MOVE_BLK, UMOVE_BLK, MOVE_BLK_VARIANT, FILL_BLK and UFILL_BLK among move operations. The U variants communicate an important design choice: uninterruptible work changes task responsiveness. Confirm supported CPU, operands, count semantics, optimized/non-optimized data restrictions and runtime behavior in the exact STEP 7 help for the target. Do not translate a Rockwell COP length directly into a Siemens count without re-deriving the contract.
Never assume overlapping-copy behavior
If source and destination ranges overlap, a forward element-by-element copy can overwrite a value before it is read. Some runtime functions act like a raw memory copy; others may define safe behavior only for certain directions or operands. Unless the target documentation explicitly guarantees the required overlap behavior, copy into a separate staging array and then commit it, or implement a direction-aware bounded algorithm with tests for both overlap directions.
For a recipe commit, a staging design is usually easier to audit:
- write and validate
StagingRecipe; - confirm range, units, permissions, product identity and checksum/version;
- copy or assign the complete staging value to
ActiveRecipethrough one defined owner; - increment
ActiveRecipeVersion; and - let equipment logic acknowledge only the complete new version.
Math instructions begin with type and failure policy
The same arithmetic expression can produce different results depending on operand types, evaluation rules, destination conversion and controller fault configuration. Select types from the credible range and required resolution, not from habit.
| IEC-family type | Typical width | Nominal range or role | Array/math review question |
|---|---|---|---|
SINT |
8-bit signed | -128 to 127 | can a count, intermediate or imported byte exceed this? |
USINT / BYTE |
8-bit unsigned/bit string | 0 to 255 | is it a number or a raw bit/byte contract? |
INT |
16-bit signed | -32,768 to 32,767 | can addition or multiplication exceed the element range? |
UINT / WORD |
16-bit unsigned/bit string | 0 to 65,535 | can a negative engineering result occur? |
DINT |
32-bit signed | -2,147,483,648 to 2,147,483,647 | is it wide enough for the accumulator and subscript expression? |
UDINT / DWORD |
32-bit unsigned/bit string | 0 to 4,294,967,295 | will a subtraction underflow or be reinterpreted? |
LINT / ULINT |
64-bit integer | large signed/unsigned range | does the CPU/compiler support it efficiently and consistently? |
REAL |
usually 32-bit float | approximate wide-range numeric value | is its precision adequate at the operating magnitude? |
LREAL |
usually 64-bit float | wider precision/range | is target support and execution time acceptable? |
The ranges above align with current Schneider IEC-type documentation, but memory layout and extensions remain platform-specific. A bit-string type such as WORD is not automatically interchangeable with an arithmetic UINT just because both occupy 16 bits in a given environment. Use explicit conversions when the design intends a semantic change.
Integer division truncates the fractional result
With integer operands and an integer destination, 7 / 2 commonly yields 3, not 3.5. A scaling expression such as Raw / 4095 * 100 can yield zero for nearly the entire input range if the first division truncates before multiplication. Options include:
- deliberately promote the calculation to a floating type;
- multiply before dividing after proving the wider intermediate cannot overflow; or
- use a reviewed scaling function with explicit raw/engineering ranges and validity.
Each option has a different overflow and rounding surface. Raw * 100 / 4095 preserves more integer resolution but requires an intermediate wide enough for Raw * 100. Adding 0.5 before converting a positive float to an integer is not a universal rounding function, especially for negative values. Use the target's documented conversion or rounding operator and test positive/negative half-way cases.
Guard divide by zero and invalid domains before execution
IF Denominator <> 0.0 THEN
Quotient := Numerator / Denominator;
QuotientValid := TRUE;
ELSE
QuotientValid := FALSE;
DivideByZeroCount := DivideByZeroCount + 1;
END_IF;
The guard must use a comparison appropriate to the numeric contract. For a computed floating denominator, “not exactly zero” may still allow an impractically tiny divisor and an enormous result; use a documented minimum magnitude when the physical/model requirement supports one. Square-root and logarithm functions need their own domain checks. CODESYS provides project check functions such as the documented CheckDivInt family when included, but portable application logic should still state the required failure behavior.
Detect overflow before it becomes a plausible value
For signed addition, prove that operands cannot exceed the destination range, use a wider intermediate, or compare against safe limits before calculation. For multiplication, divide the safe limit by the magnitude of one operand where that method is valid, or calculate in a proven wider type. Do not rely on a wrapped result looking obviously wrong; 32,000 + 1,000 stored in a 16-bit signed result can become a plausible negative number that propagates into an index or setpoint.
Platform response varies. Rockwell exposes documented math status and fault behavior by instruction/controller context. CODESYS-derived targets can insert checking POUs. Siemens behavior depends on instruction, type, CPU and diagnostic context. Record the actual result, flag/fault and application response for the exact target instead of asserting one universal PLC rule.
Vendor dialect notes: translate the contract, not just the syntax
| Concern | Rockwell Logix | Siemens S7-1200/1500 | CODESYS / Schneider / TwinCAT family |
|---|---|---|---|
| common array base | tag with declared dimension; zero-based indexing is typical | ARRAY[low..high] OF type in supported data declarations |
ARRAY[low..high] OF type; explicit non-zero lower bounds supported |
| dynamic index | tag/expression subscript | symbolic indirect indexing and array components | integer expression subscript |
| bound discovery | SIZE reports a documented dimension size |
LOWER_BOUND/UPPER_BOUND where supported |
operators available for documented fixed/variable forms by product/version |
| out-of-range behavior | documented major-fault cases including type 4 code 20 | runtime access/diagnostic behavior depends on CPU and instruction | implicit CheckBounds behavior is project/runtime specific |
| bulk copy | COP, synchronous CPS |
MOVE_BLK, UMOVE_BLK, variant form where supported |
typed assignment, library function or bounded loop |
| fill/search/file math | FLL, FSC, FAL, AVE, SIZE by language/mode |
appropriate move, compare, math and array instructions | loops/functions; exact libraries vary |
| distributing work | FAL/FSC documented modes or application state machine | application/task design and available instructions | application state machine/task design |
| portability trap | destination-element Length and raw byte transfer |
count/variant/optimized data and interruptibility | compiler-version bounds/check functions and implicit conversion |
Rockwell Logix
Use current online help for the exact controller generation. FAL and FSC have modes that can operate all elements, a numerical quantity per scan, or incrementally under documented triggers. Their control structure is persistent state; retrigger, .POS, completion and error behavior belong in the requirement. A raw COP is not a type-aware recipe converter. Rockwell also documents that an out-of-range subscript can major fault, so validate operator/HMI/message-supplied indexes before the referenced instruction is evaluated.
Siemens STEP 7
Prefer symbolic array access over raw pointers where it meets the requirement. Siemens' current indirect-addressing documentation explicitly warns that runtime-calculated addresses can access wrong values or overwrite memory. Use LOWER_BOUND and UPPER_BOUND only where supported by the selected CPU/tool/instruction context. PEEK, POKE and variant operations expand the possible data surface; they require a stronger type, area and bounds contract than ordinary symbolic indexing.
CODESYS, Schneider Machine Expert and Beckhoff TwinCAT
These environments share substantial IEC-style syntax but are not one interchangeable runtime. Current CODESYS documentation shows variable-length ARRAY[*] parameters with LOWER_BOUND and UPPER_BOUND. Current Schneider documentation describes implicit boundary checks and shows a default check that clamps an invalid index; that is an example implementation, not permission to accept silent substitution. Beckhoff recommends consistent named array/loop bounds and documents CheckBounds options. Review the generated check POU, compiler settings and deployed target rather than assuming an IDE-family default.
Production pattern: staged recipe array and ring buffer
A recipe array should separate the editable candidate from the active settings. Each recipe needs identity, version, value limits and validation status. Equipment logic should not read a half-edited recipe merely because an HMI can write its fields.
| Recipe state | Writer | Reader | Required transition |
|---|---|---|---|
| stored library | controlled recipe service | selection function | integrity/version acceptable |
| staging | HMI or authorized import through one interface | validator only | every field, unit and permission checked |
| validated staging | validator | commit owner | product identity and machine state permit commit |
| active | commit owner only | sequence/equipment logic | complete atomic/synchronized assignment as supported |
| applied acknowledgement | equipment logic | HMI/batch system | active version accepted at a defined safe point |
For a circular event buffer, maintain WriteIndex, Count, Sequence and overflow policy explicitly. After writing one complete event, advance the index and wrap it only inside the bounds:
IF NewEvent THEN
IF (WriteIndex >= cFirstEvent) AND (WriteIndex <= cLastEvent) THEN
aEvents[WriteIndex] := PendingEvent;
aEvents[WriteIndex].Sequence := NextSequence;
IF WriteIndex = cLastEvent THEN
WriteIndex := cFirstEvent;
ELSE
WriteIndex := WriteIndex + 1;
END_IF;
IF StoredCount < cEventCapacity THEN
StoredCount := StoredCount + 1;
ELSE
BufferOverrun := TRUE; (* oldest event is being replaced *)
END_IF;
ELSE
BufferIndexFault := TRUE;
END_IF;
END_IF;
This is not a high-speed event recorder or a safety log. Define whether overwriting the oldest item is acceptable, how a reader obtains a coherent event, what happens during upload, and whether retained memory/endurance meet the requirement. A sequence number helps detect replacement or missed events; it does not authenticate the record.
Troubleshoot arrays and math by the first divergent boundary
Do not start by changing indexes online or clearing the destination array. Preserve the fault record, task state and input values first. A reset can remove the evidence that distinguishes an invalid external request from a loop defect.
| Symptom | First evidence to capture | Likely boundary | Controlled next check |
|---|---|---|---|
| controller/program faults on access | exact fault type/code, routine/instruction, index expression and values | out-of-range or overflowed subscript | calculate the expression offline and test low/high guards on a bench |
| wrong element changes | requested index, mapped equipment ID, source/destination extents | index mapping or copy length | place distinctive values and sentinels in every element |
| last element never updates | declaration bounds and loop terminal value | off-by-one loop | trace first and final iteration with execution counters |
| adjacent structure field changes | copy source, destination, base tag and length units | raw block overwrite | compare byte layout and use a same-type isolated destination |
| average is too low | valid flags, accumulator type, operation order and valid count | integer truncation or invalid-value policy | replay a small hand-calculated dataset |
| result changes during calculation | input version, task writers, I/O/communication updates | incoherent snapshot | stage a completed dataset and repeat under simultaneous load |
| scan time spikes | element count, instruction mode, data type, called functions and task monitor | unbounded/all-at-once work | force maximum configured size in a representative test |
| divide or domain fault | numerator, denominator, operation, conversion and runtime diagnostics | missing invalid-domain guard | test zero, near-zero, negative and maximum cases as applicable |
| recipe appears partially applied | staging/active versions, writer cross-reference and commit handshake | multiple writers or non-coherent copy | write unique values to every field and interrupt at controlled points |
| ring buffer loses events | write/read indexes, count, sequence gaps and overrun flag | capacity or reader/writer ownership | inject exactly capacity, capacity+1 and simultaneous read/write cases |
Cross-reference every writer, including bulk operations
A leaf member may change even when it has no obvious assignment. Include array-indexed assignments, COP/CPS, FLL, FAL, block moves, structure assignments, HMI writes, messages, produced/consumed data, OPC UA access and initialization logic. On platforms with raw memory access or pointers, a normal tag cross-reference may not resolve every dynamic write; document that limitation and use controlled watchpoints/traces where available.
Use non-symmetric test patterns
Zeros and repeated values hide mapping defects. Populate test arrays with distinctive values such as 101, 202, 303 or byte patterns that do not look the same when swapped. Put known sentinel values immediately before and after the intended destination when the platform and safe test environment allow it. Then prove:
- the correct first and final elements changed;
- the number of changed elements matches the requirement;
- adjacent fields stayed unchanged;
- source values remained unchanged where required;
- the consumer accepted only the completed version; and
- invalid/partial data carried an explicit quality state.
Twelve acceptance tests for an array/data routine
| Test | Stimulus | Required recorded result |
|---|---|---|
| 1 | lower valid index | first element selected once; no neighbor changes |
| 2 | upper valid index | final element selected once; no fault |
| 3 | one below lower | access prevented and diagnostic set |
| 4 | one above upper | access prevented and diagnostic set |
| 5 | empty/no-valid dataset | no division, result invalid, count zero |
| 6 | all-positive and all-negative datasets | minimum/maximum initialize from real samples, not zero |
| 7 | maximum credible arithmetic values | no silent overflow; documented fault/substitute policy occurs |
| 8 | fractional and negative conversions | documented rounding/truncation matches target behavior |
| 9 | full copy/fill length with sentinels | exact intended extent changes; adjacent data remains intact |
| 10 | source changes during processing | snapshot/version policy produces coherent result or explicit abort |
| 11 | maximum configured array under simultaneous normal load | task execution and application deadline remain within limits |
| 12 | power/mode/download and recipe change | initialization, retention, busy/done and commit states recover deterministically |
Record CPU/catalog number, firmware, engineering-tool/compiler version, task configuration, array/type declarations, optimized/standard layout settings where relevant, test values, expected result, observed result, execution time and fault logs. The acceptance record should let a maintainer reproduce the decision without the original programmer.
PLC array, data and math answer map
| Question | Concise answer | Boundary to retain |
|---|---|---|
| What is an array in a PLC? | An indexed collection of same-type elements with declared bounds. | Bounds, ownership and update coherence are project-specific. |
How many elements are in ARRAY[0..7]? |
Eight; both lower and upper bounds are inclusive. | Some vendors use a dimension count rather than declaration syntax. |
| How do I stop an array index fault? | Validate the calculated index before any array reference executes. | Runtime fault/clamp behavior differs by platform. |
| Should a PLC array start at 0 or 1? | Use the explicit project convention and declared bounds consistently. | Rockwell arrays are commonly zero-based; IEC tools can support other lower bounds. |
| How do I loop through an array? | Use declared/named bounds or supported bound operators and measure worst-case execution. | A FOR loop normally completes in the current invocation. |
| How do I average PLC array values? | Sum accepted values in a suitable accumulator, count them, guard zero count and expose result validity. | Invalid-value and snapshot policy must be defined. |
| What is the difference between move and copy? | Move commonly transfers one typed value; copy/block operations transfer a specified extent. | Conversion, length units and byte layout are vendor-specific. |
| What is Rockwell COP length? | The documented Length is a number of destination elements for a contiguous byte copy. |
Element type determines the byte count. |
| What is the difference between COP and CPS? | Both copy; CPS provides documented synchronous protection against participating data changes during the copy. | It affects scheduling and is not automatically best for every size. |
| Why does PLC integer division lose decimals? | Integer result types truncate the fractional part. | Promotion and conversion rules vary by expression/platform. |
| Can arrays increase PLC scan time? | Yes; work grows with elements, operations, data types and calls. | Measure maximum configured work under representative load. |
| Are PLC arrays safe for recipes? | Yes when staging, validation, commit, version, access and machine-state rules are explicit. | A writable array alone is not a safe recipe transaction. |
Frequently asked questions
What is a PLC array?
A PLC array groups multiple elements of the same declared type under one name and selects them by an index. Arrays are useful for channels, recipes, steps, alarms, samples and buffers. The declaration defines bounds and element type; the application must additionally define meaning, writer, quality, update and failure behavior.
How do you declare an array in Structured Text?
IEC-style tools commonly use syntax such as aValues : ARRAY[0..7] OF REAL;. CODESYS, Schneider and TwinCAT document that form, including explicit lower and upper limits. Exact support for initialization, variable-length parameters, bounds operators and derived types depends on the product and compiler version.
Is ARRAY[0..10] ten elements?
No. It contains eleven elements because both endpoints are included. Its valid indexes run from 0 through 10. Calculate the element count as upper - lower + 1, and test the two boundary indexes explicitly.
What happens when a PLC array index is out of range?
It depends on the controller and runtime. Rockwell documents major-fault cases for out-of-range Logix subscripts. CODESYS-family projects can use implicit check functions whose response may clamp, report or raise an exception. Siemens warns that runtime indirect addressing can access wrong data. Prevent the invalid reference in application logic and verify the target response.
Should I clamp an invalid PLC array index?
Only when the requirement explicitly accepts substituting the nearest element and the substitution is diagnosed. For recipes, device selection or commands, clamping can apply valid data to the wrong target. Rejecting the request, holding a defined safe application state and exposing a diagnostic is usually clearer.
How do I copy an array in a PLC?
Use a target-supported typed assignment, bounded loop or documented block-copy instruction. Prove the source and destination types, complete extents, length units, overlap, concurrency and task-time behavior. A raw byte copy does not perform semantic type conversion.
What do Rockwell COP and CPS do?
Both copy contiguous bytes from a source to a destination, with length expressed in destination elements in the current documentation. CPS prevents participating data from changing until the copy completes in its supported context. Validate controller generation, operands and scheduling impact before choosing either.
How do I prevent divide-by-zero in PLC math?
Test the denominator before the division and define an invalid-result path. For floating calculations, consider whether a very small nonzero divisor is also invalid for the physical requirement. Record a diagnostic and do not let an old destination look like a new valid result.
Why does my PLC average return a whole number?
The operands or destination may be integers, so the fractional result is truncated or converted. Promote deliberately to REAL/LREAL or use a documented fixed-point method, then verify rounding, range, target execution time and negative values.
Can a FOR loop cause a PLC watchdog fault?
Yes. A loop usually completes all iterations in the current invocation, and maximum work can exceed the task/watchdog budget. Keep bounds finite and configuration-limited, avoid uncontrolled WHILE conditions, measure the worst case, and split work over scans only with explicit progress and coherence state.
How do I make a multi-scan array calculation coherent?
Capture a producer version or create a supported snapshot before processing. If the source changes, restart, abort or continue only according to a written policy. Publish results with their own completed version and validity so consumers never treat partial accumulators as complete.
Can a PLC array or math routine be a safety function?
Ordinary array access and math code do not become safety-rated because they run in a PLC. A safety function requires the approved safety controller, permitted instructions and data types, certified architecture where applicable, fault response, timing analysis and validation against the risk assessment and relevant standards.
Primary sources, review record and limitations
Reviewed August 30, 2026. Use the documentation revision that matches the exact controller, firmware, engineering tool and compiler.
- IEC 61131-3:2025 — programmable-controller programming-language syntax and semantics
- CODESYS ARRAY data type — fixed/variable dimensions and
LOWER_BOUND/UPPER_BOUND - CODESYS
CheckDivIntfamily — project hooks for division checking - Rockwell Studio 5000 array/file miscellaneous instruction family
- Rockwell COP and CPS official instruction reference — length, bytes, extents and consistency
- Rockwell FAL official instruction reference — modes and indexed array work
- Rockwell Index Through Arrays — dynamic subscripts and out-of-range fault boundary
- Rockwell Logix 5000 I/O and Tag Data manual — array organization and subscript behavior
- Rockwell Logix 5000 General Instructions manual — array/file and math status reference
- Siemens STEP 7 V20 basics of indirect addressing — runtime address risk and supported forms
- Siemens S7-1200 V20 array instructions — bounds, move, fill and array operation families
- Schneider EcoStruxure Machine Expert arrays — declarations, bounds and implicit checks
- Schneider standard data types — integer ranges and conversion warning
- Beckhoff TwinCAT arrays — fixed-length declaration, indexing and guarded example
- Beckhoff TwinCAT array programming conventions — common bound constants
The six figures are original conceptual editorial illustrations generated for this guide. They are not vendor screenshots, memory-layout specifications, instruction symbols, timing guarantees or safety designs. Product and company names belong to their respective owners. This independent guide is not an IEC, Rockwell Automation, Siemens, CODESYS, Schneider Electric or Beckhoff publication.
This page does not authorize online edits, forced indexes, raw-memory writes, recipe changes, downloading to a running controller, clearing a fault without diagnosis, bypassing an interlock or altering a safety function. Only qualified and authorized personnel following the site risk assessment, hazardous-energy procedure, approved backup/change/rollback process, exact product manuals and validated safety lifecycle should modify an installed control system.
PLC Programming IO Editorial Team
Industrial automation education, references, and software testing
The PLC Programming IO Editorial Team publishes sourced industrial-automation education and documents how material is reviewed, tested, and corrected. A team byline means the publisher is responsible for the page; it does not represent a fictional person or imply an engineering licence.
Coverage:
- • PLC programming concepts and examples
- • Vendor software tutorials and comparisons
- • SCADA, HMI, protocols, and instrumentation
- • Training, careers, and reference material
Review standard:
- • Prefer primary and official sources
- • Record software versions when material
- • Separate tested facts from estimates
- • Publish material corrections
Important scope note
This site provides education, not project-specific engineering approval. Safety, code, and compliance decisions require a qualified person with access to the actual machine and jurisdiction.