Overview: 5-Second Moving Average on S7-300 / S7-400
A 5-second moving average is one of the most common signal-conditioning primitives in STEP 7 programming. It is requested whenever a noisy analog input — a flow transmitter on a pulsating pump, a pressure transducer near a reciprocating compressor, a weighing signal on a vibratory feeder — must be stabilized before it is logged, displayed, or fed into a closed-loop controller. STEP 7 V5.x does not ship a dedicated "average" function block in the standard library, which is why the question of how to compute a moving average recurs in nearly every S7-300 and S7-400 project. The block most engineers reach for first is FC90, the standard-library shift register, but FC90 only shifts values through a buffer; it never adds, divides, or outputs a mean. The arithmetic mean has to be assembled by a summation FB or by indexing a ring buffer inside a cyclic interrupt OB.
This reference covers four field-proven implementations and the engineering trade-offs between them:
- FC90 shift register + chained summation FB — the classic STEP 7 V5.x pattern when the project already owns FC90 instances
- OB35 cyclic interrupt with an indexed ring buffer and a running sum — lowest scan-time jitter, scales to 500+ samples without measurable CPU load
- PT1-style exponential smoothing — single REAL of state, ideal for very long windows where an exact arithmetic mean is not required
-
TIA Portal V18 / V19 implementation using the IEC
COUNT,ADD, andDIVblocks from the global library, following the canonical pattern in Siemens Support entry 1021364
Choose based on three criteria: whether the process is fast or slow, whether the application needs the exact arithmetic mean or only a smoothed signal, and whether you are maintaining legacy STEP 7 V5.7 code or migrating to TIA Portal.
Prerequisites: Software, Catalog Numbers, and CPU Constraints
| Item | Recommended Version / Catalog Number | Notes |
|---|---|---|
| STEP 7 (Classic) | V5.7 SP2 or later | Required to reference current CPU firmware; older V5.5 projects open read-only |
| STEP 7 Professional (TIA Portal) | V18 Update 3 / V19 Update 2 | Required only if Method 4 is used |
| S7-300 CPU family | CPU 314, CPU 315-2 PN/DP (6ES7315-2EH14-0AB0), CPU 319-3 PN/DP (6ES7319-3AW00-0AB0) | All accept OB35 with 1 ms minimum cycle |
| S7-400 CPU family | CPU 412 / 414 / 416 / 417 | OB35 default 100 ms, range 1–60000 ms |
| Standard library block FC90 | Standard Library → S7 Programmable Functions → FC90 (shift register) | Shipped with every STEP 7 install; no license required |
| Standard library block FC105 | Standard Library → TI-S7 Converting Blocks → FC105 (SCALE) | Scales the analog raw word into engineering units before averaging |
| Optional SCL compiler | S7-SCL V5.7 SP2 (or TIA Portal SCL) | Used to write the averaging FB in high-level text |
| Optional: STL source | Built into every STEP 7 install | The reference FB100 in this document is written in STL for portability |
Sampling Time and Window Size Selection
The 5-second window has two degrees of freedom: the sample period T_s and the buffer length N. They are linked by
T_window = T_s × N
For a 5-second average the most common choices are:
| T_s (OB35 cycle) | N (samples) | Scan memory (REAL × 4 bytes) | Typical application |
|---|---|---|---|
| 100 ms (OB35 default) | 50 | 200 bytes | Slow process signals — level, temperature, pressure |
| 200 ms | 25 | 100 bytes | Very slow signals; reduces CPU load on saturated S7-314 CPUs |
| 500 ms | 10 | 40 bytes | Hourly logs, energy totals, tank strapping |
| 50 ms | 100 | 400 bytes | Fast flow / vibration pre-filter before PID_SCP |
| 20 ms | 250 | 1000 bytes | Reserved for high-speed CPUs (CPU 319, CPU 416 / 417) |
The OB35 default of 100 ms with N = 50 is the most common combination on S7-300 because it fits within the smallest work-memory envelope and still gives a smooth result for the majority of analog inputs. On S7-400, OB35 can be reconfigured to any multiple of the basic 1 ms clock up to 60 s via HW Config → CPU Properties → Cyclic Interrupts.
OB35 Cycle Configuration in HW Config
Configure OB35 once per CPU and never change it without re-validating N. The procedure:
- Open the S7 project in STEP 7 Manager and double-click Hardware to launch HW Config.
- Open the CPU object and select the Cyclic Interrupts tab.
- Set OB35 priority to a value above OB1 (priority 12 by default; raise to 13 if PID_SCP runs in OB35 as well).
- Set the Execution interval to the desired
T_sin milliseconds. - Download the hardware configuration. The CPU will start OB35 on the next STOP→RUN transition.
T_s. Without a phase offset, OB32 / OB35 / OB38 may all queue in the same millisecond, which can momentarily double the OB-load and trigger a cycle-time diagnostic event on a CPU 314.
Method 1 — FC90 Shift Register with Summation Chain
FC90 is the standard-library shift-register block (catalog name Shift Register). It shifts a value of width 1, 8, 16, or 32 bits through an ANY-pointer array each time its EN input receives a rising edge. It does not compute a sum or a mean — its only output is the value shifted out of the last slot of the array.
To convert FC90 into an averaging primitive, three additional pieces of logic are required:
- A trigger pulse once per sample period
T_s— typically the OB35 cycle flag or a clock-bit generated from a timer. - A summation loop that walks the shift array and adds every REAL slot into an accumulator.
- A divider that divides the accumulator by
Nto produce the mean.
Trigger generation
The simplest trigger is the OB35 cycle itself: every time OB35 fires, call FC90 once. There is no need for an external comparator, clock-bit, or timer.
Summation with chained ADD_R
The STEP 7 standard library only provides ADD_R, which accepts exactly two REAL operands. To sum N samples you have two options:
| Option | Approach | Pros | Cons |
|---|---|---|---|
| Chain ADD_R blocks | 50 cascaded ADD_R calls in a single FB | Easy to read, no index logic, ladder-diagram compatible | 50 additions of pure arithmetic per cycle; not scalable beyond ~100 samples |
| Indexed loop with indirect addressing | A single FB walks AR1 from sample[0] to sample[N-1] and accumulates in a local REAL | Linear CPU cost; same FB works for any N | Requires comfort with STL |
FC90 pitfalls encountered in practice
- Edge-triggered shift. FC90 shifts only when EN transitions from 0 to 1. If you call FC90 from OB1 with EN tied to a clock-bit that is already 1, the shift fires exactly once and then stops. Always call FC90 from a cyclic OB (OB35 / OB32 / OB38) so the EN is re-armed each cycle.
-
Shift width must match the data type. The S7 data type REAL is 32 bits. Use FC90's DWORD mode (shift width
B#16#04) so the IEEE-754 bit pattern is preserved end-to-end. - Static vs global array. The shift array must be declared STATIC inside an FB-instance DB, not as a global DB, because the ANY pointer into a global DB can collide with other FB instances during runtime re-mapping.
Method 2 — OB35 Cyclic Interrupt with Indexed Ring Buffer
This is the canonical production pattern on S7-300. A single FB owns a STATIC array of REAL samples, an index, and a running sum. Each time OB35 fires:
- The oldest sample is subtracted from the running sum.
- The new sample overwrites that slot.
- The index is incremented modulo
N. - The mean is computed as
sum / N.
Because the running sum is maintained incrementally, only one subtraction and one addition per cycle are required, regardless of N. This makes the method scale to windows of 500+ samples without measurable CPU load on a CPU 315-2 PN/DP.
Method 3 — PT1-Style Exponential Smoothing
If an exact arithmetic mean is not required — for example, when the moving average is used as a low-pass filter on the input of a PID controller — exponential smoothing gives a much smaller memory footprint and a smoother frequency response:
y(k) = α · x(k) + (1 − α) · y(k−1)
where
α = T_s / (T_window + T_s)
For T_s = 100 ms and T_window = 5 s the resulting α is approximately 0.0196. Only one REAL of state is required.
The downside is that y(k) is not a true mean; it is a weighted average that gives more weight to recent samples. For most slow process loops this difference is invisible, but if your application requires the average of exactly the last 5 seconds of data — for example, a regulatory emission report — stick with Methods 1 or 2.
| Step response target | α (100 ms cycle) | State memory | Equivalent arithmetic mean |
|---|---|---|---|
| 5 s window | 0.0196 | 1 REAL | Yes (within ~0.5 % steady-state error) |
| 30 s window | 0.0033 | 1 REAL | Yes |
| 5 min window | ~0.00033 | 1 REAL | Yes |
Method 4 — TIA Portal Alternative Using IEC Blocks
In TIA Portal V18 / V19, the standard "average value" calculation pattern published in Siemens Support entry 1021364 uses the following blocks from the global IEC library:
-
COUNT— counts samples up to a configurable maximum -
ADD— sums REAL values across a tag array -
DIV— REAL division
Siemens' reference example computes the average of five values (5.0 + 3.0 + 1.0 + 7.0 + 14.0) / 5 = 6.0; the same pattern scales linearly to 50 or 500 samples. TIA Portal's SCL compiler makes the summation loop trivial:
#sum := 0.0;
FOR #i := 0 TO #N - 1 DO
#sum := #sum + #samples[#i];
END_FOR;
#avg := #sum / INT_TO_REAL(#N);
For migration from STEP 7 V5.x to TIA Portal, the FC90-based project can be lifted into TIA Portal V18 as-is: the S7 Programmable Functions library is still installed, and FC90 behaves identically. The shift-width selection (BIT / BYTE / WORD / DWORD) maps directly to the new input field in the FC90 instance dialog. New code should still prefer the IEC pattern from entry 1021364 because it is portable across CPU families (S7-300, S7-400, S7-1200, S7-1500) and survives any future library change.
Method Comparison: CPU Load, Memory, and Latency
| Method | CPU load per cycle | State memory | Sample memory | Latency | Best for |
|---|---|---|---|---|---|
| 1 — FC90 + chained ADD_R | ~2N additions + 1 division | 0 bytes (uses FC90 ANY) | N × 4 bytes in FC90 array | 1 sample | Legacy projects that already own FC90 |
| 2 — OB35 + ring buffer | 2 additions + 1 subtraction + 1 division | 8 bytes (sum + idx) | N × 4 bytes in FB instance DB | 1 sample | New STEP 7 V5.x projects; windows up to 500 |
| 3 — PT1 exponential smoothing | 1 multiply + 1 addition + 1 subtract | 4 bytes (y(k−1)) | 0 bytes | ~5 s for α = 0.02 | PID pre-filters, very long windows |
| 4 — TIA IEC COUNT/ADD/DIV | ~N additions + 1 division | 8 bytes | N × 4 bytes | 1 sample | New TIA Portal projects |
Method 2 is the best general-purpose choice on STEP 7 V5.x. Method 3 wins whenever the window is longer than ~50 s because the memory savings dominate. Method 1 is reserved for projects with an existing FC90 dependency. Method 4 is the only choice on TIA Portal S7-1200 / S7-1500 if you want a portable IEC implementation.
Working Code: FB100 in STL
The following FB100 ("FB_AVG_5S") implements Method 2 in STL. It is intended for STEP 7 V5.7 and assumes OB35 is configured for a 100 ms cycle, giving N = 50 samples over 5 seconds. The complete declaration and code section are shown so the FB can be pasted directly into a STEP 7 source file.
FUNCTION_BLOCK FB100
TITLE = '5-Second Moving Average (OB35-driven)'
VERSION : '1.0'
VAR_INPUT
i_raw : REAL; // scaled input, e.g. 0.0..100.0 after FC105
i_reset : BOOL; // rising-edge clear of buffer and sum
END_VAR
VAR_OUTPUT
o_avg : REAL; // arithmetic mean of the last 5 s
o_full : BOOL; // TRUE once the buffer holds N valid samples
END_VAR
VAR
arr : ARRAY[0..49] OF REAL; // ring buffer
sum : REAL; // running sum of arr
idx : INT; // next write slot, 0..49
cnt : INT; // sample counter up to N
N : INT := 50; // window length
iv_reset : BOOL; // edge memory for i_reset
END_VAR
BEGIN
// ---- reset (rising edge) ----
IF i_reset AND NOT iv_reset THEN
idx := 0;
cnt := 0;
sum := 0.0;
END_IF;
iv_reset := i_reset;
// ---- incremental sum update ----
sum := sum - arr[idx] + i_raw;
arr[idx] := i_raw;
// ---- advance index modulo N ----
idx := idx + 1;
IF idx >= N THEN
idx := 0;
END_IF;
// ---- counter ----
IF cnt < N THEN
cnt := cnt + 1;
END_IF;
// ---- average output ----
IF cnt > 0 THEN
o_avg := sum / INT_TO_REAL(cnt); // partial-window average until N is reached
ELSE
o_avg := 0.0;
END_IF;
o_full := (cnt = N);
END_FUNCTION_BLOCK
Call the FB once from OB35 with:
CALL FB100, DB100
i_raw := "scale_flow".out_value // REAL from FC105
i_reset := "first_scan" // BOOL, TRUE on first OB35 cycle
o_avg := "avg_flow_5s" // REAL, writeable from HMI
o_full := "avg_flow_5s_ready"; // BOOL
The partial-window average (o_avg = sum / cnt) is intentional: while the buffer is filling up after a reset or first scan, the output reflects the mean of the samples actually present, which is the physically correct interpretation. Once o_full is TRUE the denominator locks to N.
Parameter Reference and Instance DB Layout
| Symbol | Type | Direction | Initial | Description |
|---|---|---|---|---|
| i_raw | REAL | IN | 0.0 | Scaled engineering value (after FC105 SCALE) |
| i_reset | BOOL | IN | FALSE | Edge-triggered clear of buffer and sum |
| o_avg | REAL | OUT | 0.0 | Current 5-second arithmetic mean |
| o_full | BOOL | OUT | FALSE | TRUE once the buffer holds N valid samples |
| arr[0..49] | ARRAY OF REAL | STAT | 0.0 | Ring buffer of samples (200 bytes) |
| sum | REAL | STAT | 0.0 | Running sum of arr |
| idx | INT | STAT | 0 | Next write index, modulo 50 |
| cnt | INT | STAT | 0 | Sample counter, saturates at N |
| N | INT | STAT | 50 | Window length; change here, not in code |
| iv_reset | BOOL | STAT | FALSE | Edge memory for i_reset |
The instance DB occupies 232 bytes (200 for the array + 32 for scalars). This is well within the work-memory limits of every S7-300 CPU and is regenerated cleanly by STEP 7 after a download because no initial values other than N := 50 are required.
Analog Input Pre-Processing Before Averaging
Feed FB100 with a value that has already been scaled into engineering units. The canonical pre-processing chain on an S7-300 SM 331 (6ES7331-7KF02-0AB0) is:
-
L PIW 304— read the analog input word (12-bit + sign, 0..27648 for ±10 V / 4..20 mA) -
FC105 SCALE— convert to engineering units (REAL) -
FB100— compute the 5-second mean -
L "avg_flow_5s"— feed the averaged value to the PID controller or to the HMI tag
Common Faults and Troubleshooting Matrix
| Symptom | Likely root cause | Diagnostic step | Fix |
|---|---|---|---|
| Output is constant equal to the most recent input | FC90 called with shift width = BIT (1 bit); the input is shifted as a single bit, not as a 32-bit REAL | Open the FC90 instance and check the WIDTH input | Set WIDTH := B#16#04 (DWORD = 32 bits) |
| Output equals input (no smoothing) | Comparator or trigger wired wrong; FC90 only shifts on a rising edge of EN, so a constant EN=TRUE fires once and stops | Monitor FC90.EN in OB35 online view | Generate a 1-cycle pulse from the OB35 priority class, or call FC90 directly from OB35 |
| Average drifts after long uptime | Floating-point round-off accumulates in the running sum; the value subtracted back is not bit-identical to the value originally added | Trigger an i_reset every 24 h from a clock DB |
Periodically reset and rebuild the buffer; alternatively use Kahan summation in SCL |
| CPU goes to SF with "OB35 cycle time exceeded" |
N is too large and the summation loop exceeds 100 ms |
Check the diagnostic buffer for OB35 cycle time | Reduce T_s to 200 ms, or move the summation into OB10 (time-of-day) at a lower priority |
| Sum overflows during commissioning | REAL accumulator overflows when N is small and inputs are large |
Monitor the STATIC sum in the VAT table |
Pre-scale inputs to a smaller engineering range (e.g., 0–10 instead of 0–1000) before averaging |
| Output flickers every 5 s with no transition | Oldest sample equals newest sample because the analog input is frozen | Check FC105 SCALE output against raw PIW | Replace the analog input module, shield the cable, or re-seat the front connector |
| o_avg jumps on the first cycle after reset | Partial-window average behaves like a step on the first valid sample because cnt = 1 gives o_avg = i_raw
|
Expected behaviour | If undesired, suppress o_full on the HMI until cnt = N
|
| Wrong mean on TIA Portal migration | FC90 instance imported from V5.x has the wrong ANY-pointer length | Open FC90 instance and re-enter the array length | Re-enter LEN := 50 for 50 REAL slots and recompile |
Verification and Commissioning Procedure
Before sign-off, validate the average FB with the following three-step commissioning procedure:
-
Step-response check. Inject a known step change (e.g., 0.0 → 50.0) at the input and confirm in the VAT table that the output reaches
50.0 × (1 − 1/e) ≈ 31.6within one time-constant (~5 s for Method 3, exactly at sampleNfor Methods 1 and 2). For Methods 1 and 2 the partial-window fill will produce a straight-line ramp from 0 to 50 over the first 5 s. -
Nyquist attenuation check. Inject a sine wave at the Nyquist limit (period = 2 ×
T_s) and confirm that the output amplitude is attenuated to roughly 0.6 of the input. This validates that the window is in fact 5 s and not 10 s, and thatNis correct. - Cycle-time sanity check. Force the OB35 cycle to 200 ms in the hardware configuration, restart the CPU, and confirm that the average step response now takes 10 s instead of 5 s. This is the cleanest way to catch mis-sized buffers in the field — a buffer of 50 samples with a 200 ms cycle gives a 10-second mean, not a 5-second mean, and this single test exposes the miscalculation immediately.
o_avg and the raw samples if the report template requires it.
Migration Considerations: STEP 7 V5.x to TIA Portal
When migrating an FC90-based averaging project from STEP 7 V5.7 to TIA Portal V18 or V19, observe the following:
- The S7 Programmable Functions library ships with every TIA Portal install.
FC90is identical in behaviour and interface to its V5.x counterpart. - The shift-width field that was a BYTE constant
B#16#04in V5.x becomes an INT constant4in TIA Portal. Migrating this value requires manual confirmation; do not rely on the auto-migrator. - The ANY pointer in V5.x becomes a VARIANT pointer in TIA Portal. The semantics are identical but the syntax differs: replace
SRCBLK := P#DB100.DBX0.0 BYTE 200withSRCBLK := "DB100".arr. - For new code, prefer the IEC averaging pattern from Siemens Support entry 1021364. It uses COUNT, ADD, and DIV from the global IEC library, is portable across S7-300 / S7-400 / S7-1200 / S7-1500, and survives any future FC90 deprecation.
Frequently Asked Questions
Why does FC90 not give me an average?
FC90 is a pure shift register: it moves each value one slot down an ANY-pointer array every time its EN input sees a rising edge. It never adds, never divides, and never outputs a mean. You must add a summation block (chained ADD_R calls or an indexed loop) and a division by N to produce the arithmetic mean. The canonical reference implementation is in Siemens Support entry 1021364.
Can I average 5 seconds of values with a single OB35 call?
No. OB35 only fires once per cycle (default 100 ms). To get a 5-second window you must accumulate 50 successive samples across 50 successive OB35 calls and then divide by 50. A single OB35 execution gives you one instantaneous sample, not a 5-second average.
What is the cheapest way in CPU time to average 5 seconds of data?
Method 2 — the incremental ring buffer with a running sum — uses one subtraction, one addition, and one division per cycle, independent of N. On a CPU 315-2 PN/DP it executes in under 50 microseconds per cycle even with N = 500. Method 3 (exponential smoothing) is even cheaper but is not an exact arithmetic mean.
My FC90 output looks stuck on the first value I ever sent in. What is wrong?
FC90 only shifts when EN transitions from 0 to 1. If you wire EN to a constant TRUE or to a signal that is already TRUE when OB35 starts, the shift fires exactly once and then never again. Generate a one-cycle pulse from the OB35 priority class or call FC90 directly from OB35 so the shift is re-armed every cycle.
Does STEP 7 TIA Portal still ship FC90, and which is the preferred IEC pattern?
Yes. The S7 Programmable Functions library is installed with every TIA Portal install from V13 upward, and FC90 behaves identically in V18 and V19. For new projects prefer the IEC averaging pattern documented in Siemens Support article 1021364, which uses COUNT/ADD/DIV and is portable across CPU families (S7-300, S7-400, S7-1200, S7-1500).
How do I size N for a process with a 1 ms PLC scan?
For a 5-second average at 1 ms you would need N = 5000 samples and 20 kB of scan memory — feasible only on an S7-400 CPU 416 / 417 with work memory above 4 MB. For S7-300 CPUs, raise the OB35 cycle to 100 ms (N = 50) or to 200 ms (N = 25). The averaging window is independent of OB1 scan time; only OB35 matters.