Calculating a 5-Second Moving Average in STEP 7 S7-300

David Krause19 min read
S7-300SiemensTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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:

  1. FC90 shift register + chained summation FB — the classic STEP 7 V5.x pattern when the project already owns FC90 instances
  2. OB35 cyclic interrupt with an indexed ring buffer and a running sum — lowest scan-time jitter, scales to 500+ samples without measurable CPU load
  3. PT1-style exponential smoothing — single REAL of state, ideal for very long windows where an exact arithmetic mean is not required
  4. TIA Portal V18 / V19 implementation using the IEC COUNT, ADD, and DIV blocks 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
OB35 cycle-time floor varies by CPU. Older S7-300 CPUs such as the CPU 312 IFM (6ES7312-5AC02-0AB0) only accept 10–60000 ms for OB35. If you configure 50 ms on a CPU that bottoms out at 10 ms, the value is silently rounded up to the next legal value, and your 5-second window will contain 100 samples (at 50 ms effective) or 500 samples (at 10 ms effective) instead of the 100 you expected. Always verify the effective cycle time in HW Config → CPU Properties → Cyclic Interrupts before commissioning.

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:

  1. Open the S7 project in STEP 7 Manager and double-click Hardware to launch HW Config.
  2. Open the CPU object and select the Cyclic Interrupts tab.
  3. Set OB35 priority to a value above OB1 (priority 12 by default; raise to 13 if PID_SCP runs in OB35 as well).
  4. Set the Execution interval to the desired T_s in milliseconds.
  5. Download the hardware configuration. The CPU will start OB35 on the next STOP→RUN transition.
OB35 phase offset is rarely useful for a 5-second average, but if you run multiple cyclic OBs and need deterministic staggering, set the Phase offset on the second OB to a non-zero multiple of 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:

  1. A trigger pulse once per sample period T_s — typically the OB35 cycle flag or a clock-bit generated from a timer.
  2. A summation loop that walks the shift array and adds every REAL slot into an accumulator.
  3. A divider that divides the accumulator by N to 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:

  1. The oldest sample is subtracted from the running sum.
  2. The new sample overwrites that slot.
  3. The index is incremented modulo N.
  4. 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.

Ring buffer of N = 50 REAL samples (5 s at 100 ms OB35) arr[0]idx=0 arr[1] arr[2] arr[3] arr[4] arr[48] arr[49] → wrap to arr[0] OB35 (100 ms) cycle: sum := sum − arr[idx] + i_raw arr[idx] := i_raw idx := (idx + 1) MOD 50 o_avg := sum / 50.0

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:

  1. L PIW 304 — read the analog input word (12-bit + sign, 0..27648 for ±10 V / 4..20 mA)
  2. FC105 SCALE — convert to engineering units (REAL)
  3. FB100 — compute the 5-second mean
  4. L "avg_flow_5s" — feed the averaged value to the PID controller or to the HMI tag
Do not average the raw PIW value (an INT in the range 0..27648) directly. A moving average on a 12-bit integer gives step-quantised output, and the FC90 shift-width BIT / BYTE / WORD options corrupt REALs. Always scale first and average after.

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:

  1. 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.6 within one time-constant (~5 s for Method 3, exactly at sample N for 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.
  2. 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 that N is correct.
  3. 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.
For regulatory or emission-reporting applications, do not use Method 3 (exponential smoothing). The regulator expects the arithmetic mean of the last 5 s of samples, not a weighted PT1 response. Use Method 1, 2, or 4 and archive both 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. FC90 is identical in behaviour and interface to its V5.x counterpart.
  • The shift-width field that was a BYTE constant B#16#04 in V5.x becomes an INT constant 4 in 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 200 with SRCBLK := "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.

Back to blog