A PLC array is one variable that holds several elements of the same data type, and each element is reached through an index that the program can change at runtime. For a drive with ten speed presets, you store the presets in Velo[1] through Velo[10]. The speed reference then becomes Velo[indice], and you change speed by writing a value from 1 to 10 into indice. You never add another IF branch.
How does an index value turn into a memory location?
Follow the request from the instruction to the data. The instruction Velo[indice] asks the runtime for an element. The runtime takes the array's base address, subtracts the declared lower bound from indice, and multiplies the result by the element size. That gives the element's position within one contiguous block. It works only because every element has the same type and size, so no element needs its own name. The index is an ordinary integer variable, so a HMI entry, a sequence step, a counter, or a loop variable can set it.
| Access form | Who sets the target | When it resolves | Typical use |
|---|---|---|---|
Velo[3] |
Programmer, fixed at compile | Compile time | Same as a named tag |
Velo[indice] |
Program logic or operator | Every scan | Preset selection, recipes |
Array[i] inside FOR |
Loop counter | Each loop pass, same scan | Sums, averages, searches |
Check: open the array in the online watch table and confirm the element count and lower bound match the declaration. Some platforms start at 0, while an ARRAY[1..10] declaration starts at 1.
How should the speed-preset table be declared so operator numbering matches the index?
Declare the bounds to match the numbering the operator already uses. If the HMI shows presets 1 to 10, declare 1 to 10. Then nobody has to add or subtract 1 anywhere in the program.
Load the ten presets as initial values or from the HMI. If the presets must survive a power cycle, mark them retentive. An array that is not retentive can reload zeros or initial values on a restart, and the drive then receives an unexpected reference.
Check: after a power cycle, read Velo[1] and Velo[10] online and confirm they still hold the configured presets.
How does the selected preset reach the drive without an out-of-range fault?
The index is the weak point in the path. If indice holds 0 or 11 while the bounds are 1..10, the address calculation points outside the block. Depending on the platform, the controller then faults, clamps the value, or reads or writes whatever memory lies next to the array. Clamp the request before it becomes an index:
indice := LIMIT(1, SpeedSelect, 10);
DriveSpeedRef := Velo[indice];
- Write
SpeedSelectfrom the HMI or the sequence logic, never directly intoindice. - Clamp it with
LIMIT, or reject out-of-range values and raise an operator message. - Move
Velo[indice]to the drive reference tag that the fieldbus or analog output scales and sends.
SpeedSelect written |
indice after clamp |
Element sent to drive |
|---|---|---|
| 1 | 1 | Velo[1] |
| 7 | 7 | Velo[7] |
| 0 | 1 | Velo[1] |
| 11 | 10 | Velo[10] |
Check: force SpeedSelect to each value in the table and confirm that DriveSpeedRef and the drive's own reference display follow it.
What does "walking the array with a loop" actually execute?
A FOR loop sets its counter to each value in the range in turn and runs the body once per value, all within the same PLC scan. Using the counter as the index means one line of code processes every element:
With Array[1] := 10, Array[2] := 20 and Array[3] := 30, the passes run as follows:
| Pass | i |
Operation |
Suma after pass |
|---|---|---|---|
| 1 | 1 | 0 + 10 | 10 |
| 2 | 2 | 10 + 20 | 30 |
| 3 | 3 | 30 + 30 | 60 |
The Suma := 0; line before the loop is mandatory in a PLC. The program runs every scan and Suma keeps its value between scans. Without the reset, the result grows by 60 on every scan instead of staying at 60. Also keep the loop bounds inside the declared array bounds. A loop running to 4 over a 1..3 array causes the same out-of-range access as a bad selector.
Check: monitor Suma across several scans. It must hold steady at 60, not climb.
How is a running average built from stored samples?
Averaging combines both access types. A trigger writes each new sample to the next slot with index := index + 1. A loop then sums the stored samples, and the program divides by the number of samples. Wrapping the write index turns the array into a ring buffer, so the average always covers the most recent samples.
Three details decide whether the code works:
- The trigger must be a one-shot. A level signal writes a sample on every scan and fills the whole buffer within ten scans.
- Divide by
Count, not by 10. Otherwise the average reads low until the buffer has filled once. - The
Count > 0guard prevents a division by zero on the first scan after startup.
Check: inject a constant value on AnalogIn. Avg must equal that value from the first sample onward, and Idx must go back to 1 after 10.
Which other loops earn their place, and how is the full chain proven?
Any job that applies the same operation to many same-type values fits an array plus a loop:
| Pattern | Array content | Loop action | Recurring pitfall |
|---|---|---|---|
| Alarm summary | BOOL alarm flags | Set AnyAlarm if any element is TRUE; record the first index |
Not clearing AnyAlarm before the loop |
| Min/max search | Measured values | Compare each element with the running min/max | Seeding min with 0 instead of element 1 |
| Recipe table | Arrays of setpoint structures | Copy the selected recipe to the active setpoints | Unclamped recipe number |
| Part tracking | One element per station | Shift elements down one slot per conveyor step | Shifting in the wrong direction overwrites data |
Every pass of every loop adds to scan time, because the whole loop runs before the scan finishes. Read the controller's maximum cycle time from its diagnostics after you add large loops, and compare it with the configured watchdog.
End-to-end check:
- Step
SpeedSelectthrough 0, 1, 5, 10 and 11. Confirm thatindiceclamps, thatDriveSpeedRefmatches the expectedVeloelement, and that the drive's reference display matches. - Run the summing loop with known values and confirm
Sumaholds 60 across consecutive scans. - Feed a constant into the averaging buffer and confirm that
Avgequals it before, during and after the first wrap ofIdx. - Record the maximum cycle time with all loops active and confirm it stays below the watchdog setting with margin.
FAQ
Why does my FOR loop sum keep increasing every PLC scan?
The accumulator keeps its value between scans, so each scan adds the full total again. Set it to 0 on the line immediately before the FOR statement, for example Suma := 0;.
Why does the PLC fault when my array index changes?
The index has gone outside the declared bounds, such as 0 or 11 for an ARRAY[1..10]. Clamp it with LIMIT(1, value, 10) before you use it, and read the diagnostic buffer to see which instruction faulted.
Why does my moving average read low right after startup?
The code divides by the full array size while only a few slots hold samples. Keep a sample count, capped at the array size, and divide by that count, with a guard for zero.
Why does a large FOR loop cause a cycle time or watchdog fault?
The whole loop runs inside one scan, so its execution time adds directly to the cycle. Shorten the loop, spread the work over several scans with a persistent index, or move the loop to a slower task. Then check the maximum cycle time against the watchdog.
How do I select a VFD speed preset from an array in Structured Text?
Store the presets in ARRAY[1..10] OF REAL, clamp the selector to 1-10, and assign DriveSpeedRef := Velo[indice];. Verify it by stepping the selector and comparing the result with the drive's reference display.