Running Average Filter for S7-1200 and S7-1500 Analog Inputs
Pressure transducers installed on hydraulic manifolds, pneumatic networks, and process piping rarely deliver a perfectly stable 4–20 mA or 0–10 V signal. Quantization noise, mechanical vibration, pump ripple, and electrical interference combine to produce a fluctuating reading that the controller must smooth before any closed-loop action is taken. A field engineer measuring a 0–10 bar transducer may see ±0.25 bar of jitter where the process can tolerate only ±0.1 bar. The standard fix is a running average filter built on a fixed-size ring buffer, sampled by a cyclic interrupt OB, and exposed to the application as a single normalized REAL value.
This reference covers the complete implementation path on Siemens S7-1200 and S7-1500 controllers using TIA Portal V17 or later. It includes OB time-base differences, OSCAT FT_AVG usage, custom SCL ring-buffer code, alternative filter topologies (median and exponential moving average), commissioning steps, and a troubleshooting matrix.
1. Problem Definition and Filter Selection
A typical 13-bit analog input on the SM 1231 or SM 531 module delivers a step size of roughly 4.88 µA at 4–20 mA, or 2.44 mV at 0–10 V. After scaling in the PLC, that translates to:
| Transducer Range | Step Size (13-bit) | Step Size (16-bit R) |
|---|---|---|
| 0–10 bar | 0.0012 bar | 0.00015 bar |
| 0–100 bar | 0.012 bar | 0.0015 bar |
| 0–600 bar | 0.073 bar | 0.009 bar |
Quantization is rarely the dominant noise source. The dominant noise is low-frequency mechanical and process noise (pump pulsation at 10–25 Hz, regulator hunting, water hammer). To attenuate that noise without adding 100 ms of dead time, a running average over a sliding window of N samples is the simplest viable solution.
When to choose a running average over EMA
- Running average: equal weight on every sample in the window. Best when the window length matches a known noise period (e.g., one pump cycle).
- Exponential moving average (EMA): recent samples weighted higher. Best when the noise spectrum is broadband and you want minimal code footprint.
- Median filter: rejects impulse spikes (e.g., water hammer). Best when the signal is contaminated by single-sample transients.
2. Prerequisites
| Item | Specification |
|---|---|
| Controller | SIMATIC S7-1200 (CPU 1211C/1212C/1214C/1215C/1217C, firmware V4.2 or later) or SIMATIC S7-1500 (CPU 1511-1 PN through 1518-4 PN/DP, firmware V2.0 or later) |
| Engineering software | STEP 7 Basic/Professional V17 (or V18/V19 with backward compatibility) |
| Analog input module | SM 1231 (S7-1200) or SM 531 (S7-1500), 8 AI or 16 AI variant |
| Pressure transducer | 4–20 mA two-wire or 0–10 V three-wire, output range scaled in the PLC |
| Optional library | OSCAT BASIC library for S7-1200/S7-1500 (FT_AVG, INC1, DELAY_ function blocks) |
Download the S7-1200 system manual, S7-1500 system manual, and TIA Portal programming reference from the Siemens support portal. Confirm the firmware version on your CPU under Online > Accessible devices > CPU > Diagnostics before writing OB time bases.
3. Time Base Difference: S7-1200 vs S7-1500
This is the single most common source of errors when porting code between the two families:
| Platform | Cyclic OB time unit | Example: 100 ms |
|---|---|---|
| S7-1200 (OB30–OB38) | Milliseconds (ms), integer input | Phase time = 100 |
| S7-1500 (OB30–OB38) | Microseconds (µs), integer input | Phase time = 100000 |
Configure the OB phase time in the project tree under Program blocks > OB30 > Properties > General > Cycle time. Entering 100 on an S7-1500 would yield a 100 µs OB — 1000× faster than intended and likely to overload the cyclic task.
4. Siemens Built-in Analog Input Filtering
Before writing any SCL, evaluate the hardware-side filter on the analog module. SM 1231 and SM 531 modules offer a configurable smoothing factor under Device configuration > Properties > Inputs > Channel > Smoothing:
| Smoothing level | Equivalent samples | Typical use |
|---|---|---|
| None | 1 | Fastest response, raw signal |
| Weak | 4 | Light noise, fast PID loops |
| Medium | 16 | General process monitoring |
| Strong | 32 | Slowly changing pressures, tank level |
This is a hardware-configured moving average inside the module firmware. It is the lowest-overhead option and is sufficient for many applications. Switch to software filtering only when:
- The required window size is not offered in the module's discrete steps.
- You need the filter to survive a module hot-swap with identical parameters.
- You need access to both the raw and filtered signal in parallel.
5. Ring Buffer Architecture
The running average is implemented on top of a ring buffer (circular queue) of fixed size N. Every sample period, the oldest value is overwritten with the newest reading. The arithmetic mean is recomputed and made available to the application.
Two implementation strategies are common on S7-1200/S7-1500:
- Sum-then-subtract: maintain a running SUM. Add the new sample, subtract the oldest sample, divide by N. O(1) per update.
- Full re-sum: iterate over all N slots every update. O(N) per update, simpler to debug.
For N ≤ 64 and 10 ms sample period, both strategies fit comfortably inside the cyclic OB budget on a CPU 1214C. The sum-then-subtract approach is recommended because it avoids accumulator drift from floating-point round-off over long runtimes.
6. OSCAT FT_AVG Implementation
The OSCAT BASIC library (open-source, available for S7-1200 and S7-1500) provides a pre-built running average block called FT_AVG that wraps a ring buffer with internal sum management. The block depends on two helper FBs:
-
INC1— modulo increment with wraparound. -
DELAY_— sample-delay line used as the underlying ring buffer.
6.1 Block interface
| Input/Output | Name | Type | Description |
|---|---|---|---|
| INPUT | IN | REAL | New sample to insert into the window |
| INPUT | N | INT | Window length (number of samples) |
| INPUT | RST | BOOL | Reset the buffer (clears all slots and sum) |
| OUTPUT | OUT | REAL | Running average |
| OUTPUT | VALID | BOOL | TRUE once the window is full |
6.2 Program structure
Call FT_AVG from a cyclic OB at the sample period you require:
// OB30 — 10 ms cyclic interrupt (S7-1200: phase = 10; S7-1500: phase = 10000)
// Sample the scaled pressure and feed it to the running average
"FT_AVG_DB"(IN := "Pressure_scaled",
N := 10, // 10 samples × 10 ms = 100 ms window
RST := FALSE,
OUT => "Pressure_avg",
VALID => "Pressure_avg_valid");
Initialize the instance DB on first scan using a one-shot in OB100 (warm restart) or OB101 (hot restart). With this configuration, the controller delivers a fresh average every 10 ms, the window represents the last 100 ms of pressure data, and the VALID output goes TRUE after the first 10 calls (100 ms).
7. Custom SCL Implementation (No External Library)
If the OSCAT library is not approved for the project, build the running average directly in SCL. The block below runs on both S7-1200 (firmware V4.2+) and S7-1500 with identical code.
FUNCTION_BLOCK "FB_RunningAvg"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
InputValue : REAL; // Latest sample (scaled engineering units)
WindowSize : INT; // Number of samples (1..64)
Reset : BOOL; // TRUE clears the buffer
END_VAR
VAR_OUTPUT
Average : REAL; // Current running average
BufferFull : BOOL; // TRUE after WindowSize samples
SampleCnt : INT; // Number of samples seen since reset
END_VAR
VAR
Buffer : ARRAY[1..64] OF REAL;
WriteIdx : INT; // Next slot to overwrite (1-based)
Sum : LREAL; // Long-real accumulator, avoids float drift
END_VAR
VAR_TEMP
i : INT;
END_VAR
BEGIN
IF Reset OR (WindowSize < 1) THEN
Sum := 0.0;
WriteIdx := 1;
SampleCnt := 0;
BufferFull := FALSE;
FOR i := 1 TO 64 DO Buffer[i] := 0.0; END_FOR;
Average := 0.0;
RETURN;
END_IF;
// Subtract the value about to be overwritten, then add the new sample
Sum := Sum - Buffer[WriteIdx] + InputValue;
Buffer[WriteIdx] := InputValue;
// Advance write index with wraparound
WriteIdx := WriteIdx + 1;
IF WriteIdx > WindowSize THEN
WriteIdx := 1;
BufferFull := TRUE;
END_IF;
// Track total samples for VALID semantics
IF SampleCnt < WindowSize THEN
SampleCnt := SampleCnt + 1;
END_IF;
Average := REAL(Sum / WindowSize);
END_FUNCTION_BLOCK
7.1 Why LREAL for the sum
S7-1200 REAL (32-bit IEEE 754) provides roughly 7 significant decimal digits. With a 100-bar transducer and a 64-sample window, the running sum reaches 6400 bar; small values added to that sum can lose precision. LREAL (64-bit) gives ~15 digits and is supported on both families. Use LREAL internally, cast to REAL only at the output.
7.2 Calling the FB from a cyclic OB
// OB30 — 10 ms sampling
"iDB_RunningAvg"(InputValue := "Pressure_scaled",
WindowSize := 10,
Reset := FALSE,
Average => "Pressure_avg",
BufferFull => "Pressure_avg_valid",
SampleCnt => "Pressure_sample_count");
8. Alternative Filter: Exponential Moving Average
For applications that need a tunable time constant without managing a buffer, EMA is the cheapest option. The single-line update rule is:
y[n] = α·x[n] + (1 − α)·y[n−1]
where α ∈ (0, 1] is the smoothing factor. Equivalent window length is approximately N = 2/α − 1. To match a 100 ms / 10 ms window (N = 10), use α = 0.18.
FUNCTION_BLOCK "FB_EMA"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
InputValue : REAL;
Alpha : REAL; // 0.0..1.0
Reset : BOOL;
END_VAR
VAR_OUTPUT
Average : REAL;
END_VAR
VAR
Prev : REAL;
Init : BOOL;
END_VAR
BEGIN
IF Reset OR NOT Init THEN
Prev := InputValue;
Init := TRUE;
Average := InputValue;
RETURN;
END_IF;
Prev := Alpha * InputValue + (1.0 - Alpha) * Prev;
Average := Prev;
END_FUNCTION_BLOCK
EMA reacts faster to real setpoint changes than a sliding window of equal effective length, but it never fully forgets old samples. Use it when RAM is constrained or when the desired window length is non-integer.
9. Alternative Filter: Median-of-5
Median filters reject outliers (single-sample transients from ESD events, valve slam, water hammer). A median-of-5 sorts five consecutive samples and outputs the third. This is recommended for outdoor hydraulic systems with intermittent electrical noise.
// Inside a 10 ms cyclic OB
// SortBuffer[1..5], latest sample in SortBuffer[5]
FOR i := 4 DOWNTO 1 DO
SortBuffer[i+1] := SortBuffer[i];
END_FOR;
SortBuffer[1] := InputValue;
// Insertion sort (5 elements, negligible cost)
FOR i := 2 TO 5 DO
key := SortBuffer[i];
j := i - 1;
WHILE (j >= 1) AND (SortBuffer[j] > key) DO
SortBuffer[j+1] := SortBuffer[j];
j := j - 1;
END_WHILE;
SortBuffer[j+1] := key;
END_FOR;
MedianOut := SortBuffer[3];
For many applications, the optimal solution is median-of-5 followed by running average of 4 — that combination rejects impulses and smooths pump pulsation.
10. Step-by-Step Commissioning
- Wire and scale the transducer. Connect the pressure transmitter to a free SM 1231/SM 531 channel. Verify the scaling in the technology object or in your FC_Scale block: 4 mA → 0 bar, 20 mA → 10 bar.
- Create the cyclic OB. Insert a new Program block > OB > Cyclic interrupt (OB30). Set the phase time to 10 (S7-1200) or 10000 (S7-1500).
- Add the filter FB. Insert Program block > FB, paste the SCL from section 7, compile.
- Create an instance DB. Right-click the FB, select Generate instance DB.
- Call the FB from OB30. Wire the scaled pressure input and set WindowSize = 10.
-
Initialize on restart. In OB100, set
iDB_RunningAvg.Reset := TRUEon the first scan so the buffer starts clean. - Download and go online. Add the filter tags to a watch table. Force the input to a known static pressure and observe the average converge.
-
Verify VALID. Confirm
Pressure_avg_validgoes TRUE within 100 ms of startup. - Tune window size. Increase N until output jitter is below the application threshold (e.g., 0.1 bar). Decrease N if the average lags actual process changes too much.
11. Verification and Diagnostics
Add the following tags to a watch table or HMI trend:
| Tag | Type | Expected behavior |
|---|---|---|
| Pressure_scaled | REAL | Raw scaled pressure, noisy |
| Pressure_avg | REAL | Smoothed value, ±0.1 bar jitter on a 0–10 bar transducer |
| Pressure_avg_valid | BOOL | TRUE after 100 ms; FALSE only during reset |
| Pressure_sample_count | INT | Rises 0 → 10 within first 100 ms, then steady |
| OB30_execution_us | DINT | OB30 run time; should stay well below 10000 µs |
| OB30_overflow_count | DINT | Non-zero indicates the OB is missing its phase time |
To check OB30 run time programmatically, read OB30_PREV_CYCLE (microseconds) inside the OB. If it exceeds the configured phase time, the cyclic task is being pre-empted — typically by a higher-priority OB or by long execution in OB1.
12. Troubleshooting Matrix
| Symptom | Likely cause | Remediation |
|---|---|---|
| Average never changes | Input wired to wrong tag; instance DB not refreshed | Re-check the call in OB30, confirm the input is non-zero in the watch table |
| Average stays at 0.0 | Reset held TRUE by latching logic | Reset the reset coil; verify OB100 one-shot logic |
| VALID never goes TRUE | WindowSize = 0 or negative | Clamp WindowSize to 1..64 at the call site |
| Jitter still ±0.25 bar | WindowSize too small or sample period too long | Increase N to 25 or sample at 5 ms |
| Average lags real changes by > 1 s | Window too large | Reduce N or switch to EMA with α ≈ 0.2 |
| OB30 cycle time exceeded warning | Filter FB called from OB1 instead of OB30, or N too large | Move call to OB30; verify with OB30_PREV_CYCLE |
| Drift in average over hours | REAL accumulator losing precision | Use LREAL internally as shown in section 7 |
| Output spikes during pump start | Mechanical transient wider than window | Add median-of-5 stage ahead of the average |
| Different result on S7-1200 vs S7-1500 | OB phase time unit mismatch | S7-1200 uses ms, S7-1500 uses µs — fix in OB properties |
| Filter jumps on cold restart | Buffer not initialized | Drive RST in OB100 on first scan |
13. Performance and Resource Budget
On a CPU 1214C (firmware V4.4), the running average FB with N = 32 executes in roughly 80 µs of OB30 time, leaving > 99 % of the 10 000 µs phase budget available for other cyclic work. On a CPU 1516-3 PN/DP, the same block executes in ~5 µs.
| Window N | Approx. memory (instance DB) | Approx. CPU 1214C OB30 time |
|---|---|---|
| 10 | 328 bytes | 30 µs |
| 32 | 920 bytes | 80 µs |
| 64 | 1.7 KB | 150 µs |
For applications that need N > 256, consider whether the time constant justifies the buffer size, or switch to EMA.
14. Integration with PID and Control Loops
If the averaged pressure feeds a PID compact block (PID_Compact for S7-1200, PID_Compact for S7-1500), feed Pressure_avg into the Setpoint and Input parameters. Do not feed the raw noisy signal — the integral term will accumulate quantization noise and produce integrator windup.
Set the PID sampling time to a multiple of the filter window. With a 100 ms filter, a 100 ms PID cycle is acceptable; 1 s is preferred for slow pressure loops. The PID_Compact block's input scaling and process value limits apply downstream of the filter.
15. Field-Proven Tips
- Always scale the analog input first, then filter. Filtering raw counts introduces non-linearities when the scale curve is non-linear (e.g., square-root for flow).
- Keep the cyclic OB priority low (OB30, priority 7) and let OB1 run the main sequence. Do not nest filter calls inside FB calls triggered by hardware interrupts unless you can prove the timing budget.
- Document the WindowSize, sample period, and OB number on the HMI faceplate so maintenance staff can identify which filter is in service.
- For safety-relevant signals (SIL 2/3), the running average is non-deterministic in response time. Use a separate, certified filter path.
- On TIA Portal V18 and later, you can use the Trace function to record both the raw and filtered signals simultaneously for tuning.
16. Reference Material
Verify the OB configuration and time-base semantics against the official Siemens documentation:
- Siemens Industry Online Support — search for "S7-1200 System Manual" and "S7-1500 System Manual" for the current edition.
- STEP 7 TIA Portal Programming and Operating Manual — covers cyclic OB configuration.
- SM 1231 Analog Input Module Device Manual — smoothing factor settings.
- SM 531 Analog Input Module Device Manual — S7-1500 channel configuration.
- OSCAT BASIC library — open-source library for S7-1200/S7-1500, includes FT_AVG, INC1, DELAY_.
FAQ
What OB time value should I enter for a 10 ms cyclic interrupt on S7-1200 versus S7-1500?
On S7-1200, set the cyclic OB phase time to 10 (milliseconds). On S7-1500, set it to 10000 (microseconds). Entering 10 on an S7-1500 would call the OB every 10 µs and likely fault the CPU.
How do I stop my 0.25 bar pressure fluctuation without slowing down the control loop?
Use a running average with a window length matched to the dominant noise period. For 100 ms of pump pulsation, sample every 10 ms and average 10 samples (N=10). This delivers a fresh average every 10 ms while cutting jitter by a factor of √10.
Should I use the OSCAT FT_AVG block or a custom SCL implementation?
Use FT_AVG if OSCAT is already approved in your project; it is tested and widely deployed. Use the custom SCL from this article when OSCAT is not permitted or when you need an LREAL accumulator to avoid drift over very long runtimes.
Why does my averaged pressure drift upward over hours of operation?
You are accumulating the running sum in a 32-bit REAL. With large transducer ranges and N above ~32, the precision loss becomes visible. Switch the internal accumulator to LREAL and convert back to REAL at the FB output.
Can the SM 1231 hardware smoothing replace the SCL running average?
For most pressure applications, yes — set the channel smoothing to "Medium" or "Strong" in the device configuration and no software filter is needed. Use a software filter when you need a non-standard window size, when you need to access both raw and filtered values, or when module hot-swap must preserve filter behavior.