Overview
Ordinary differential equations (ODEs) describe the dynamic behavior of virtually every physical plant: motor speed, tank level, temperature loop, and current/voltage in an RLC network. In a Siemens PLC, the controller cannot integrate symbolically the way an engineer does with a Laplace transform on paper; the CPU must produce a discrete-time approximation of the continuous-time response. This article documents four production-tested SCL (Structured Control Language) function blocks that integrate first-order, second-order, and differentiator transfer functions in real time on S7-1200 and S7-1500 controllers running TIA Portal V16 or later.
Three blocks implement explicit Euler integration and one block implements the Tustin (bilinear) transform. Both techniques belong to the family of numerical methods for ordinary differential equations, the canonical reference for which is the Wiley text Numerical Methods for Ordinary Differential Equations. Each block was refined to set an explicit error flag whenever the user enters a coefficient that would cause division by zero, and to zero the integrators on the rising edge of a REST request.
Mathematical Background
All four function blocks solve variants of a single canonical form. The first-order plant
Y(s)/F(s) = 1 / (A·s + B)
is the generic form of a low-pass filter. The casual differentiator
Y(s)/F(s) = C·s / (A·s + B)
is the D-term of an ideal PID controller written in parallel form. The second-order system
Y(s)/F(s) = 1 / (A·s² + B·s + C)
describes a lightly damped mechanical resonance, an LC filter, or a positioning axis. By scaling A, B, and C, the same code covers critically damped, under-damped, and over-damped responses.
Euler Integration
Explicit (forward) Euler replaces the differential operator with the finite difference dy/dt ≈ (Y(k+1) − Y(k)) / T, giving the recurrence
Y(k+1) = Y(k) + T · F(k, Y(k))
with local truncation error O(T²) and global error O(T). It is conditionally stable: for the first-order plant with time constant τ, the block remains stable only if the sample time T < 2·τ. Engineers using Euler in production must therefore halve the controller's task time whenever they double the dominant time constant.
Tustin (Bilinear) Transform
The Tustin transform substitutes
s ≈ (2/T) · (z − 1) / (z + 1)
which maps the left-half s-plane onto the interior of the unit circle and yields unconditional stability for any positive T. For the first-order plant this gives the difference equation
(2B + C·T)·Y(k+1) = A·T·F(k+1) + A·T·F(k) + (C·T − 2B)·Y(k)
The mapping is exact at DC and is exact at half the Nyquist frequency, with smooth amplitude roll-off in between. Engineers typically use Tustin for plant models that will be simulated in the same scan as the controller, where stability over a wide T range outweighs the small extra arithmetic cost.
SCL Environment and Prerequisites
The blocks were developed against the SCL grammar that ships with:
- STEP 7 V5.6 SP2 (S7-300/400 target)
- STEP 7 in TIA Portal V16, V17, V18, V19 (S7-1200/1500 target)
The SCL language manual for S7-1500 is available on the Siemens support portal as "S7-1500/ET 200MP - Programming and Operating Manual - SCL"; the equivalent for S7-1200 is "S7-1200 Programmable Controller - System Manual". Both describe the TIME data type, the TIME_TO_DINT and DINT_TO_REAL conversion functions, and the REAL arithmetic library used by every block below.
| Item | Required | Notes |
|---|---|---|
| CPU | S7-1200 (FW ≥ 4.4) or S7-1500 (FW ≥ 2.0) | REAL math supported on both |
| Engineering | TIA Portal V16 or later | Same SCL source compiles on V15.1 with no changes |
| OB | Cyclic OB (e.g. OB1 or OB35) | Call at deterministic interval matching INTERVAL/I_Ts |
| DBs | One instance DB per FB call | Holds YOLD, FOLD, X1, X2 statics |
Y in the same engineering units as F. A transfer function with gain 1/(B/A) maps a step in F of magnitude A·ΔF/B to a steady-state change of ΔF·A/B in Y. Always confirm the sign of B matches the expected sign of the steady-state response before commissioning.First-Order System via Euler (FB1)
FB1 solves Y/F = 1 / (A·s + B). The integrator is updated every INTERVAL, defaults to 1 s, and may be re-tasked down to 10 ms on S7-1500 and 100 ms on S7-1200. The block uses YOLD as the static integrator state, applies an explicit Euler step, then writes the new value back into the static for the next scan.
FUNCTION_BLOCK FB1
TITLE = 'ODE_EULER'
// Y 1
// -- = ----------
// F A*S + B
VERSION: '2.0'
AUTHOR: HD
NAME: first
FAMILY: FORUM_E
VAR_INPUT
F : REAL;
A : REAL;
B : REAL;
INTERVAL : TIME := T#1S;
END_VAR
VAR_IN_OUT
REST : BOOL := FALSE;
END_VAR
VAR_OUTPUT
Y : REAL;
error : BOOL;
END_VAR
VAR
YNEW : REAL := 0.0;
YOLD : REAL := 0.0;
K : REAL := 0.0;
DELTA : REAL := 0.0;
T_INTERNAL: REAL := 1.0;
END_VAR
BEGIN
T_INTERNAL := DINT_TO_REAL(TIME_TO_DINT(INTERVAL)) / 1000.0;
IF REST = 1 THEN
YOLD := 0;
YNEW := 0;
Y := 0;
OK := FALSE;
error := 1;
ELSIF A = 0 THEN
YOLD := 0;
YNEW := 0;
Y := 0;
OK := FALSE;
error := 1;
ELSE
DELTA := (F - (B * YOLD)) / A;
YNEW := YOLD + (DELTA * T_INTERNAL);
YOLD := YNEW;
Y := YNEW;
OK := TRUE;
error := 0;
END_IF;
END_FUNCTION_BLOCK
Calling Convention
- Instantiate the FB in a cyclic OB with a unique instance DB.
- Drive
Fwith the plant input (REAL in engineering units). - Set
Ato the time-constant τ andBto the static gain denominator coefficient. - Set
INTERVALequal to the OB cycle or the desired update period; the conversionTIME_TO_DINT(...)/1000.0produces seconds in REAL. - Pulse
RESTfor one cycle to clear the integrator; the block holdserror=1for that cycle.
Casual Differentiator via Euler (FB2)
FB2 implements Y/F = C·s / (A·s + B), the derivative branch of an ideal PID controller. The same time constant A/B filters the raw derivative to keep it realizable, and C sets the high-frequency gain. Because the block contains an explicit derivative on F, it must use FOLD to approximate dF/dt over the same interval.
FUNCTION_BLOCK FB2
TITLE = 'ODE_EULER'
// Y C*S
// -- = ------
// F A*S + B
VERSION: '2.0'
AUTHOR: HD
NAME: DIFF
FAMILY: FORUM_E
VAR_INPUT
F : REAL;
A : REAL := 1.0;
B : REAL := 1.0;
C : REAL := 1.0;
INTERVAL : TIME := T#1S;
END_VAR
VAR_IN_OUT
REST : BOOL := FALSE;
END_VAR
VAR_OUTPUT
Y : REAL;
error : BOOL;
END_VAR
VAR
FOLD : REAL := 0.0;
YNEW : REAL := 0.0;
YOLD : REAL := 0.0;
DELTA : REAL := 0.0;
T_INTERNAL: REAL := 1.0;
END_VAR
BEGIN
T_INTERNAL := DINT_TO_REAL(TIME_TO_DINT(INTERVAL)) / 1000.0;
IF REST = 1 THEN
YOLD := 0;
YNEW := 0;
Y := 0;
OK := FALSE;
error := 1;
ELSIF A = 0 THEN
YOLD := 0;
YNEW := 0;
Y := 0;
OK := FALSE;
error := 1;
ELSE
DELTA := ((C * (F - FOLD)) / T_INTERNAL - (B * YOLD)) / A;
YNEW := YOLD + (DELTA * T_INTERNAL);
YOLD := YNEW;
FOLD := F;
Y := YNEW;
OK := TRUE;
error := 0;
END_IF;
END_FUNCTION_BLOCK
-B/A. Always pre-filter the input with a first-order lag in the real process measurement (PT1 in CFC, or FB1 in this library) before the differentiator, and clamp Y in the caller to limit the kick on setpoint changes.Second-Order System via Euler (FB3)
FB3 implements Y/F = 1 / (A·s² + B·s + C) by writing the system as two coupled first-order states X1 = Y and X2 = Ẏ, then integrating each with explicit Euler. This is the standard phase-variable decomposition used in control theory textbooks.
FUNCTION_BLOCK FB3
TITLE = 'ODE_EULER'
// Y 1
// -- = -------------
// F A*S^2 + B*S + C
VERSION: '2.0'
AUTHOR: HD
NAME: SECOND
FAMILY: FORUM_E
VAR_INPUT
F : REAL;
A : REAL := 1.0;
B : REAL := 1.0;
C : REAL := 1.0;
INTERVAL : TIME := T#1S;
END_VAR
VAR_IN_OUT
REST : BOOL := FALSE;
END_VAR
VAR_OUTPUT
Y : REAL;
error : BOOL;
END_VAR
VAR
X1 : REAL := 0.0;
X2 : REAL := 0.0;
X1OLD : REAL := 0.0;
X2OLD : REAL := 0.0;
DELTA1 : REAL := 0.0;
DELTA2 : REAL := 0.0;
T_INTERNAL: REAL := 1.0;
END_VAR
BEGIN
T_INTERNAL := DINT_TO_REAL(TIME_TO_DINT(INTERVAL)) / 1000.0;
IF REST = 1 THEN
X1 := 0.0;
X2 := 0.0;
X1OLD := 0.0;
X2OLD := 0.0;
Y := 0;
OK := FALSE;
error := 1;
ELSIF A = 0 THEN
X1 := 0.0;
X2 := 0.0;
X1OLD := 0.0;
X2OLD := 0.0;
Y := 0;
OK := FALSE;
error := 1;
ELSE
DELTA2 := (F - (C * X1OLD) - (B * X2OLD)) / A;
X2 := X2OLD + (DELTA2 * T_INTERNAL);
DELTA1 := X2;
X1 := X1OLD + (DELTA1 * T_INTERNAL);
X2OLD := X2;
X1OLD := X1;
Y := X1;
OK := TRUE;
error := 0;
END_IF;
END_FUNCTION_BLOCK
Stability Caveat
For a second-order plant A·s² + B·s + C, the explicit Euler update matrix has eigenvalues that leave the unit circle when T > 2·A/B. Always satisfy T < A·ωn·2 / B (where ωn = sqrt(C/A)) and run the block from a fast OB (OB35 at 100 ms or OB1 at 10 ms on S7-1500) when the natural frequency approaches 1 Hz.
First-Order Process via Tustin (FOP)
The Tustin implementation avoids the conditional-stability problem of Euler at the cost of a small amount of algebra at every cycle. The block stores the previous input U0 and output Y0, then computes Y1 from the bilinear form of the transfer function Y/U = A / (B·s + C).
FUNCTION_BLOCK FOP
TITLE = 'First Order Process'
// Y A
// -- = ----------
// U B*s + C
//
// Use Tustin method to approximate
// 2 z-1
// s = ---*----------
// T z+1
//
// Y A*T*z + A*T
// -- = ----------------------------------
// U (2B + C*T)*z + (C*T - 2B)
//
// (2B + C*T)*y(k+1) = A*T*u(k+1) + A*T*u(k) + (C*T - 2B)*y(k)
//
VERSION: '0.0'
AUTHOR: ZLiang
NAME: FOP
FAMILY: PROCESS
VAR_INPUT
I_U : REAL;
I_A : REAL;
I_B : REAL;
I_C : REAL := 1.0;
I_Ts : TIME := T#100MS;
END_VAR
VAR_IN_OUT
IQ_RST: BOOL := FALSE;
END_VAR
VAR_OUTPUT
Q_Y : REAL;
Q_ERR : BOOL;
END_VAR
VAR
Y0 : REAL := 0.0;
Y1 : REAL := 0.0;
U0 : REAL := 0.0;
Ts : REAL;
END_VAR
BEGIN
Ts := DINT_TO_REAL(TIME_TO_DINT(I_Ts)) / 1000.0;
IF IQ_RST THEN
Y0 := 0.0;
Y1 := 0.0;
U0 := 0.0;
Q_ERR := FALSE;
IQ_RST:= FALSE;
ELSIF (I_B < 0.005) AND (I_B > -0.005) THEN
Q_ERR := TRUE;
Y0 := 0.0;
Y1 := 0.0;
ELSE
Y1 := ((I_A * Ts * I_U) + (I_A * Ts * U0) +
((2.0 * I_B) - (I_C * Ts)) * Y0) /
((2.0 * I_B) + (I_C * Ts));
U0 := I_U;
Y0 := Y1;
Q_ERR := FALSE;
END_IF;
Q_Y := Y1;
END_FUNCTION_BLOCK
Why Tustin over Euler for Plant Models
The Tustin form matches the plant exactly at DC, so the steady-state gain of the simulated plant is always 1.0 (for A/C = 1) regardless of sample time. When the same scan computes the controller and the plant, Tustin avoids the drift that explicit Euler introduces into long-running simulations, particularly for integrator-like plants with B ≈ 0 (the block guards that case with the |I_B| < 0.005 check and sets Q_ERR).
Method Comparison
| Criterion | Euler (FB1/FB2/FB3) | Tustin (FOP) |
|---|---|---|
| Stability | Conditional; T < 2·τ or 2·A/B | Unconditional for stable continuous plant |
| DC accuracy | Drifts with T | Exact |
| Arithmetic cost | 1 add, 1 multiply (per state) | 4 multiplies, 1 divide, 1 add |
| Order support | 1st and 2nd order, plus differentiator | 1st order only in this library |
| Use case | Real-time control on fast OB | Plant simulation alongside controller |
Stability and Accuracy Analysis
For the first-order plant A·s + B, explicit Euler produces the closed-form iteration
Y(k+1) = (1 − B·T/A)·Y(k) + (T/A)·F(k)
which is bounded-input bounded-output (BIBO) stable iff |1 − B·T/A| < 1, i.e. T < 2·A/B. Engineers tuning a PID loop that includes this block should keep the cycle time below 50 % of the dominant plant time constant to leave a comfortable stability margin.
The Tustin block's iteration is
Y(k+1) = [(C·T − 2B)/(2B + C·T)]·Y(k) + [A·T/(2B + C·T)]·(F(k+1) + F(k))
with pole at (2B − C·T)/(2B + C·T), which is strictly inside the unit circle for all positive B, C, T. Amplitude error at ω = ωn is 0 dB; phase error at ω = ωn is below 5° for T < 0.2·τ.
Commissioning and Verification
- Compile and download the four FBs into the PLC project. Each FB creates its own instance DB on the first call.
-
Open the instance DB online and confirm the
YOLD,X1OLD,X2OLD,FOLD,Y0, andU0statics initialize to 0.0. Any non-zero starting condition produces a step transient on the first scan. -
Apply a step on F and trace
Ywith a 1 kHz sampling trace (S7-1500 supports this in the Online & Diagnostics view). Verify the steady-state value matches the analyticalY(∞) = F·A/B(first order) orY(∞) = F·A/C(second order) within 1 %. - Halve the OB period and confirm the simulated step response is unchanged. With Euler, halving the period reduces the error by 2×; with Tustin, the response is essentially identical to four significant figures.
-
Force A = 0 on FB1, FB2, or FB3 and confirm the block asserts
error = 1and clampsY = 0without throwing an OB priority-class exception. The PLC remains in RUN even with a forced zero coefficient. -
Force I_B near 0 on FOP and confirm
Q_ERRis raised. ResetIQ_RSTto clear.
error / Q_ERR outputs as safety interlocks in the calling logic and never use the simulated Y as a sole input to a safety-rated function.Integration Patterns in a TIA Portal Project
Engineers typically wire the four blocks into a CFC chart or a sequence of SCL calls in OB35. A recommended layout for a position-loop simulation:
- FB1 simulates the first-order current loop, with
A = τ_i,B = 1, andFdriven by the controller output. - FB3 simulates the second-order mechanical plant, with
A = J,B = B_friction,C = K_spring, andF= current-loop output. - FB2 forms the D-term of a parallel PID, with
A = τ_d + 0.05to clamp derivative noise andC = K_d. - FOP replicates the same first-order plant using Tustin, sitting in parallel with FB1 to validate the simulation against the analytical response during commissioning.
Error Handling Matrix
| Block | Trigger | Reaction | Recovery |
|---|---|---|---|
| FB1, FB2, FB3 | REST = 1 | Y=0, error=1, OK=0 | Drop REST; next scan recomputes |
| FB1, FB2, FB3 | A = 0 | Y=0, error=1, OK=0 | Re-enter non-zero A and pulse REST |
| FOP | IQ_RST = 1 | Y0=Y1=U0=0, error cleared | Automatic; bit self-resets |
| FOP | |I_B| < 0.005 | Y0=Y1=0, Q_ERR=1 | Re-enter I_B; block resumes |
| All | INTERVAL ≤ 0 | Division by zero on T_INTERNAL | Guard INTERVAL ≥ T#10MS at the call site |
Frequently Asked Questions
Which S7 CPUs support these SCL function blocks?
All S7-1200 CPUs with firmware ≥ 4.4 and all S7-1500 CPUs with firmware ≥ 2.0 support the REAL math, TIME conversions, and static instance DBs used in the blocks. The same source compiles unchanged on S7-300/400 under STEP 7 V5.6 with the legacy SCL editor.
How do I pick a sample time for the Euler blocks?
For a first-order plant with time constant τ, set INTERVAL ≤ τ/5 (target 1 % steady-state accuracy) and always below 2·τ to keep the explicit Euler iteration BIBO stable. For the second-order block, the rule is INTERVAL ≤ 2·A/B with the additional constraint INTERVAL < 0.5/ωn where ωn = sqrt(C/A).
Why does FB2 amplify noise and what is the fix?
The casual differentiator rolls off at 20 dB/decade above the pole at −B/A but amplifies anything above that frequency unbounded. The standard fix is to set A ≥ τ_d + 0.05 s so the pole is at low frequency, and to clamp Y to a maximum derivative kick (e.g. ±10 % of the input span per scan) in the calling PID block.
What is the difference between FB1 and FOP if both solve a first-order plant?
FB1 uses explicit Euler and is appropriate for real-time control because the arithmetic is minimal. FOP uses the Tustin bilinear transform and is appropriate for plant simulation alongside the controller because it is unconditionally stable and matches the plant gain exactly at DC. Use FB1 in the hot path and FOP in the soft path during commissioning for cross-check.
How do I avoid the OB1 cycle-time drift from contaminating INTERVAL?
Call all four blocks from a hardware-timed OB (OB30–OB38 configurable from 0.5 ms to 60 s on S7-1500) and read the actual elapsed time with the system clock RD_SYS_T if you need adaptive step control. Never call them from OB1 with a long cycle when the process is fast — the variable interval breaks the stability analysis and produces aliased responses.
Can I cascade these blocks to build higher-order plants?
Yes. Two FB1 instances in series produce a critically damped second-order response when the time constants differ by a factor of 4 or more. For lightly damped second-order behavior, use FB3 directly. For a fourth-order plant, instantiate FB3 twice in series and feed the output of the first into the input of the second; both blocks share the same INTERVAL.