PCS 7 Variable Preset Timer: Temperature-Based Shutdown FB

David Krause16 min read
HMI ProgrammingSiemensTutorial / How-to
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

PCS 7 Variable Preset Timer: Temperature-Based Shutdown FB

Siemens PCS 7 does not ship a stock function block that lets the operator change a timer's preset value (PV) while the timer is already running. That is a real problem for any application where the shutdown delay is a function of a live process variable such as temperature: as the process variable changes, the demand on the timer changes, and the system must re-target PV mid-flight without losing the already-accumulated elapsed time. The IEC 61131-3 timer blocks TP, TON, TOF, TONR and the legacy S5 instanced timers S_PULSE, S_ODT, S_ODTS, S_OFFDT, S_PEXT all latch the PV on the active edge. Subsequent writes to the PV input are ignored until the timer is re-started. This article documents a working pattern using a custom SCL function block (FB_VarDelay) that wraps the system tick counter SFC64 (TIME_TCK) and is fully compatible with PCS 7 CFC and the Advanced Process Library (APL).

Scope. The pattern below is valid for PCS 7 V8.x and V9.x running on AS 400 (S7-400) and AS 410 (S7-1500) automation stations, programmed in STEP 7 V5.6 or TIA Portal V16+ with the PCS 7 add-on. All block interfaces use standard IEC 61131-3 data types.

Problem Details: Variable PV With Mid-Run Re-Arming

The scenario described in the source is a temperature-driven shutdown timer for a piece of process equipment. The user has a temperature transmitter (analog input scaled to engineering units) and wants the delay time before opening the shutdown valve or stopping the motor to be calculated from a formula such as:

  • Linear inverse: T_delay = max(0, K1 - K2 · (T - T_ref))
  • Exponential decay: T_delay = K · e^(-α·(T - T_ref))
  • Hyperbolic: T_delay = K3 / (T - T_ref + ε) with ε a small positive constant to avoid divide-by-zero

The intent is that the delay shrinks as the temperature rises. The shutdown command is issued once (rising edge), but if the equipment continues to heat up after the command is issued, the user wants the new, shorter delay to take effect immediately, not to wait for the next start. Conversely, if the temperature drops back below the threshold, the user wants the timer to extend, not fire spuriously.

Three behaviors are required:

  1. Re-arm the comparison against the new PV without losing the already-accumulated elapsed time.
  2. If the new PV is shorter than the current elapsed value, the timer must fire immediately (i.e., re-trigger from the formula).
  3. If the new PV is longer, the timer must extend seamlessly with no glitch on the output.

None of the stock IEC or S5 timers in PCS 7 can do this.

Root Cause: How PV Latching Works in S7 Timers

IEC 61131-3 defines a timer abstractly as having inputs IN and PT and outputs Q and ET, with vendor-specific implementation. For Siemens IEC_TON in the STEP 7 Standard Library, the relevant semantics are:

Block Type PV Latched? Mid-Run PV Change Effective?
IEC_TP Pulse On IN rising edge No
IEC_TON On-delay On IN rising edge No
IEC_TOFF Off-delay On IN falling edge No
IEC_TONR Retentive on-delay On IN rising edge No
S_PULSE (FB 100) Pulse On IN rising edge No
S_ODT (FB 101) On-delay On IN rising edge No
S_ODTS (FB 102) Latched on-delay On IN rising edge No
S_OFFDT (FB 103) Off-delay On IN falling edge No
S_PEXT (FB 104) Extended pulse On IN rising edge No

The standard explanation is in the STEP 7 / TIA Portal documentation set for the Siemens Industry Online Support portal. Look for the "SIMATIC S7 Standard Functions" and "S7-1500/ET 200MP System Functions" reference manuals. Both list the same behavior: the PV input is sampled on the active edge and held in an internal word; re-writing the input word while the timer is running has no effect on the in-flight comparison.

The IEC 61131-3 standard is intentionally vendor-agnostic on this point. Section 6.4.3 of IEC 61131-3:2013 defines the timer state machine but does not mandate that PT be re-evaluated each cycle. Siemens has chosen the latched implementation for all of the blocks above, so the only escape hatch is a custom block.

Solution Architecture: FB_VarDelay Custom Function Block

The custom block is built around three principles:

  1. PV is an input that is re-read every scan. The comparison elapsed >= PV is done inside the block, not by a hardware timer.
  2. Elapsed time is computed from a stored start tick. It is not incremented per scan, so scan-time jitter does not accumulate and OB1 vs OB35 placement is irrelevant for accuracy.
  3. A retrigger input allows the user's formula block to re-arm the timer automatically if PV drops below the currently-elapsed value (auto-retrigger on demand change).

Inputs and outputs:

Direction Name Type Meaning
IN Start BOOL Rising edge begins timing
IN Reset BOOL Forces output low, clears state
IN PV_Time TIME Variable preset, re-read every cycle
IN AutoRetrig BOOL Re-arm when PV < elapsed
OUT Running BOOL Timer is active
OUT Done BOOL Elapsed ≥ PV_Time since last start
OUT Elapsed TIME Time accumulated in current run
OUT Remaining TIME Time left (0 if Done)
Timing diagram — variable PV, AutoRetrig = FALSE Start Running PV ↓ at t=200s: 60s → 20s Remaining jumps 60→20s Done PV ↑ at t=320s: 20s → 90s Remaining extends 20→90s Time →

SCL Implementation (STEP 7 V5.6 / TIA Portal)

The following source is valid SCL for both STEP 7 V5.6 (used with PCS 7 V8.x) and TIA Portal V16+ (used with PCS 7 V9.x / AS 410). Place the FB in the AS master data block program so it can be instantiated multiple times in CFC charts.

FUNCTION_BLOCK FB_VarDelay
{ S7_m_c := 'true' ; S7_tasklist := 'OB1' }
TITLE    = 'Variable Preset On-Delay'
VERSION  : '1.0'
AUTHOR   : 'PCS7-Eng'
FAMILY   : 'PCS7_Custom'

VAR_INPUT
    Start      : BOOL;            // Rising edge begins timing
    Reset      : BOOL;            // Forces output low, clears state
    PV_Time    : TIME;            // Variable preset, re-read every cycle
    AutoRetrig : BOOL := FALSE;   // Re-arm when PV drops below elapsed
END_VAR

VAR_OUTPUT
    Running   : BOOL;             // Timer is active
    Done      : BOOL;             // Elapsed >= PV_Time since last start
    Elapsed   : TIME;             // Time accumulated in current run
    Remaining : TIME;             // Time left until done (0 if Done)
END_VAR

VAR
    StartEdge   : BOOL;
    RunState    : BOOL;
    DoneState   : BOOL;
    tStartD     : DWORD;          // SFC64 value at last start
    tNowD       : DWORD;          // SFC64 value this scan
    tDiffD      : DWORD;          // unsigned delta, wraps modulo 2^32
    ElapsedMs   : DINT;           // elapsed in ms, signed
    PvMs        : DINT;           // PV in ms
    RemainingMs : DINT;
END_VAR

VAR_TEMP
    retVal : DWORD;
END_VAR

BEGIN
    // ---- 1) Reset has priority ----
    IF Reset THEN
        RunState  := FALSE;
        DoneState := FALSE;
        StartEdge := FALSE;
        ElapsedMs := 0;
    END_IF;

    // ---- 2) Read system tick (SFC64 / TIME_TCK) ----
    retVal := TIME_TCK();
    tNowD  := retVal;

    // ---- 3) Edge detection on Start ----
    IF Start AND NOT StartEdge THEN
        RunState  := TRUE;
        DoneState := FALSE;
        tStartD   := tNowD;
        ElapsedMs := 0;
        StartEdge := TRUE;
    ELSIF NOT Start THEN
        StartEdge := FALSE;
    END_IF;

    // ---- 4) Run logic: re-read PV every scan, recompute elapsed ----
    IF RunState THEN
        // Unsigned DWORD subtraction handles the 32-bit wrap correctly
        tDiffD := tNowD - tStartD;
        IF tDiffD > DWORD#16#7FFFFFFF THEN
            ElapsedMs := 2147483647;          // saturate to max DINT
        ELSE
            ElapsedMs := DWORD_TO_DINT(tDiffD);
        END_IF;

        PvMs := TIME_TO_DINT(PV_Time);

        IF ElapsedMs >= PvMs THEN
            DoneState := TRUE;
            RunState  := FALSE;
        ELSIF AutoRetrig AND (PvMs < ElapsedMs) THEN
            // PV dropped below current elapsed - re-arm
            tStartD   := tNowD;
            ElapsedMs := 0;
        END_IF;
    END_IF;

    // ---- 5) Outputs ----
    Running := RunState;
    Done    := DoneState;
    Elapsed := DINT_TO_TIME(ElapsedMs);

    IF RunState THEN
        RemainingMs := PvMs - ElapsedMs;
        IF RemainingMs < 0 THEN
            RemainingMs := 0;
        END_IF;
        Remaining := DINT_TO_TIME(RemainingMs);
    ELSE
        Remaining := T#0s;
    END_IF;
END_FUNCTION_BLOCK

Notes on the SCL

  • TIME_TCK is SFC64 and must only be called in OB1, OB35, OB82 (diagnostic), OB121/OB122 handlers, or in CFC/SFC charts placed in OB1/OB35. Do not call it in OB100 (warm restart).
  • TIME_TCK returns a 32-bit tick. On SIMATIC S7-400 CPUs the tick period is 10 ms. On SIMATIC S7-1500 CPUs the tick period is 1 ms. This is documented in the "S7-300/400 Standard Functions" and "S7-1500 System Functions" reference manuals. The block treats the tick as milliseconds, which is correct for S7-1500 and off by 10× for S7-400; scale Elapsed accordingly or compute the period in a CPU-type conditional.
  • DINT_TO_TIME and TIME_TO_DINT are standard SCL conversion functions. They treat the DINT as signed milliseconds; safe for our range.
  • The attribute { S7_m_c := 'true' } declares the FB as multi-instance capable, which is required for use in PCS 7 CFC charts where the block is instantiated multiple times in one chart or in a chart family.
  • The attribute { S7_tasklist := 'OB1' } tells the SCL compiler the FB may be called in OB1, allowing more efficient code generation.

Temperature Formula and REAL-to-TIME Conversion

The PV input to FB_VarDelay is of type TIME (32-bit signed milliseconds), but the formula block typically operates in REAL (engineering units, e.g. seconds). A small FC bridges the two:

FUNCTION FC_RealSecToTime : TIME
VAR_INPUT
    Seconds : REAL;
END_VAR
VAR_TEMP
    ms : DINT;
END_VAR
BEGIN
    IF Seconds <= 0.0 THEN
        FC_RealSecToTime := T#0s;
    ELSIF Seconds > 2147483.0 THEN
        // T#24d20h31m23s647ms limit
        FC_RealSecToTime := DINT_TO_TIME(2147483647);
    ELSE
        ms := REAL_TO_DINT(Seconds * 1000.0);
        FC_RealSecToTime := DINT_TO_TIME(ms);
    END_IF;
END_FUNCTION

A reference formula block in SCL:

FUNCTION_BLOCK FB_DelayFormula
VAR_INPUT
    Temperature : REAL;   // degC
    T_ref       : REAL := 60.0;
    K1          : REAL := 120.0;   // seconds at T_ref
    K2          : REAL := 2.0;     // s/degC slope
    T_min       : REAL := 5.0;     // minimum clamp, seconds
END_VAR
VAR_OUTPUT
    DelayTime : TIME;
END_VAR
VAR
    raw : REAL;
END_VAR
BEGIN
    raw := K1 - K2 * (Temperature - T_ref);
    IF raw < T_min THEN
        raw := T_min;
    ELSIF raw > K1 THEN
        raw := K1;
    END_IF;
    DelayTime := FC_RealSecToTime(raw);
END_FUNCTION_BLOCK

For an exponential-decay formulation, replace the linear term with raw := K1 * EXP(-K2 * (Temperature - T_ref)). The EXP function is available in the SCL standard library under "Floating-point math functions".

CFC Wiring and Operator Display

In a PCS 7 CFC chart, the typical connection topology is:

CFC topology — temperature-driven variable-PV shutdown timer TEMP_PT AI 4..20mA FB_DelayFormula T → PV_Time FB_VarDelay PV_Time, Start, Reset MOT_STOP_EXE APL block SHUTDOWN_CMD BOOL RESET_OP FB_DelayFormula.DelayTime (TIME) → FB_VarDelay.PV_Time

The chart is compiled into the AS master data block. The OS faceplate for the timer block exposes the live values of PV_Time, Elapsed, Remaining, and Done. Standard PCS 7 faceplate techniques (WinCC faceplate designer, type FB_VarDelay as a "block icon with S7-m-c = true") allow the operator to see the timer counting down in real time. The faceplate can also expose an override toggle: a BOOL input wired to the formula block's bypass that forces Done := TRUE immediately, used for emergency shutdowns.

Verification and Commissioning Tests

Five tests are mandatory after wiring. Run them in S7-PLCSIM (PCS 7 simulation) or against the live AS with the chart in online test mode.

  1. Static PV test. Force PV_Time = T#10s, pulse Start = TRUE. Elapsed should increment at the tick rate, and Done should fire at 10 s ± 1 tick.
  2. PV-down test (AutoRetrig = TRUE). Set PV_Time = T#60s, start, wait 30 s, then drop PV_Time = T#5s. The block should re-arm: Remaining drops to ~5 s and Done fires ~5 s later (total elapsed ≈ 35 s).
  3. PV-down test (AutoRetrig = FALSE). Same conditions, but AutoRetrig = FALSE. Remaining drops to 5 s and Done fires at ~35 s without re-arming, exactly as the latched timer would, except the elapsed time is preserved.
  4. PV-up test. Set PV_Time = T#20s, start, wait 5 s, then raise PV_Time = T#90s. Remaining jumps from 15 s to 85 s, Done fires ~85 s later. No glitch on the Done output during the change.
  5. Reset test. Assert Reset = TRUE while Running = TRUE. All outputs go low, Elapsed = T#0s, Done falls, no spurious re-fire on the next scan.
  6. Wrap test (S7-1500 only). In S7-PLCSIM, use the SFC64 test API to force tStartD near 0xFFFFFFFE. After one tick, tDiff should equal 2 (not a huge negative number); the unsigned subtraction must handle the wrap.
  7. Scan-time independence test. Place the FB call in OB1 (typical 100 ms cycle) and OB35 (1 s cycle). Both must give the same Done timing to within ±1 tick of the tick period.

The online test is available in the CFC editor (right-click → "Test mode") or via the S7-PLCSIM "Sequence" view, both of which are documented in the PCS 7 commissioning manual on the Siemens Industry Online Support portal.

Edge Cases and Safety Patterns

  1. Sensor break / negative temperature. If Temperature < T_ref, the formula block should clamp the output to T#0s or to a configured minimum. The shutdown command will then be honored immediately on the next scan, which is the correct fail-safe behavior.
  2. PV = 0. Done fires on the same scan that Start is recognized. The code sets RunState := FALSE and DoneState := TRUE in the same pass, which is a one-shot behavior. This is intentional and is the correct interpretation of a zero-delay demand.
  3. PV update faster than tick. If the upstream formula block updates PV_Time every 10 ms and the user's logic checks every 1 ms on S7-1500, there is no race. The comparison is elapsed >= PV (not equality), and the block cannot "miss" a transient demand.
  4. Operator override. Add a bypass BOOL that forces Done := TRUE and Running := FALSE without going through the timer. The formula block still updates PV_Time, but the override wins. Wire it to the PCS 7 mode selector in the faceplate.
  5. Mode handling. When the operator puts the unit in "Maintenance" or "Out of Service", gate Start from issuing a shutdown by AND-ing with the PCS 7 MODE_IN input from the APL operator block.
  6. Multi-instance discipline. Always declare the FB as multi-instance (S7_m_c := 'true') and place instance DBs in the AS master data block. Avoid DB-of-FB patterns that fragment the S7 memory model and complicate online modifications.
  7. AS 410 redundancy. On a fault-tolerant AS 410 H system, SFC64 returns the local CPU's tick. Both H-CPUs are synchronized to within ~10 ms, so a hot-standby failover during a run will cause at most one tick of jitter on Elapsed. This is acceptable for process-grade shutdown timing; for sub-tick precision, use a separate high-resolution clock from an external timer module.
  8. Force / simulate. PCS 7 force tables and "simulate variable" CFC functions bypass the formula block. When the operator simulates a temperature value, the simulation must update PV_Time in lockstep; otherwise the user can observe Remaining jumping in unexpected ways. Test force-mode operation explicitly.

Performance and Scan-Time Considerations

The FB_VarDelay block is intentionally lean:

  • TIME_TCK is a system call that takes ~5–20 µs of CPU time per invocation on AS 410. Calling it once per FB is fine. Avoid calling it multiple times per scan in the same FB to keep determinism.
  • The block uses ~150 bytes of work memory and ~50 bytes of load memory per instance, comparable to IEC_TONR (FB 4) in the standard library.
  • The SCL compiler optimizes the DWORD subtraction and conditional into a single SUB DWORD plus a comparison, so the wrap-around case does not require a branch in the common path.
  • On an AS 410 (CPU 410-5H), FB_VarDelay can be instantiated 200+ times in a single chart with no measurable scan-time impact.
  • If the block is placed in OB35 (1 s cyclic interrupt), it is called once per second and Elapsed resolution is 1 s. For sub-second timing, place it in OB1 (or in OB32, OB33, OB34 with appropriate cycle time). PCS 7 normally places all chart logic in OB1.

Alternative Implementations Compared

Approach Pros Cons
FB_VarDelay (recommended) Variable PV, runtime-safe, scan-time independent, retrace New block to validate, requires SCL
Counter-based FC (CTU + clock bit) Uses only stock blocks PV changes are not smoothly tracked, clock-bit granularity (e.g. 1 s) limits resolution
IEC_TONR with PV re-write No custom code Documented to ignore mid-run PV changes on S7-300/400/1500
Time-of-day comparison (SFC1 + arithmetic) Familiar pattern Slower, more code, no advantage over SFC64
OB35 + T_ADD / T_SUB + DONE check Clean time arithmetic Relies on OB35 cycle; loses correctness if OB35 cycle changes
Self-resetting timer pair Uses stock TP/TOF Re-start logic must be custom, race conditions at PV=0

The FB_VarDelay pattern is preferred because it is the only approach that is mathematically guaranteed to handle a variable PV across all combinations of scan time, PV update rate, and wrap-around.

Why PLC Timing Differs from Microcontroller Timing

It is worth contrasting this with microcontroller implementations for context. On an Arduino, the delay() function is a blocking call that pauses the loop for a specified number of milliseconds. There is no concept of a "running timer" because the entire CPU is halted. The next iteration of loop() can change the ms argument and the new value takes effect on the next call. In a PLC, the cycle is non-blocking and the timer must coexist with scan logic, so PV changes are observable only when the cyclic OB re-reads them. This is why IEC 61131-3 has a dedicated timer state machine and why the standard leaves PV-latching semantics to the vendor.

The same reasoning applies to other PLC platforms: Allen-Bradley TON and TOF instructions in RSLogix 5000 / Studio 5000 also latch the preset at enable; the only way to get a variable-PV on-delay is a custom AOI (Add-On Instruction) using the GSV instruction on the WallClockTime object, which is functionally identical to the TIME_TCK approach described here.

References for further study (all official manufacturer or standards pages):

FAQ

Can I use a stock IEC_TON block with a variable PV input in PCS 7?

No. The IEC_TON block in STEP 7 / TIA Portal latches PV on the rising edge of IN. Subsequent writes to PV do not affect the in-flight run. Use the FB_VarDelay shown in this article, or an equivalent custom block, in CFC.

What is the resolution of the variable delay timer?

On AS 410 (S7-1500 CPUs used in PCS 7 V9), SFC64 returns ticks at the system clock period — 1 ms on S7-1500. On AS 400 (S7-400 CPUs used in PCS 7 V8), the tick period is typically 10 ms. For 1 ms resolution on a S7-400 system, switch to AS 410 or use a hardware counter module.

How do I handle a temperature formula that produces negative or zero delays?

Clamp the formula output at zero (or at a configured minimum like 5 s) in the calculation block. A zero delay should fire the shutdown immediately; negative delays are nonsensical. The FB_VarDelay code already handles PV = 0 correctly as a one-shot.

Can I trigger the timer multiple times in the same run?

Yes. The AutoRetrig input restarts the timer when PV drops below the current elapsed value. If you want explicit manual re-trigger, pulse the Start input again; the rising edge re-arms the block and resets Elapsed to zero, regardless of AutoRetrig.

Does FB_VarDelay need a special instance DB layout for PCS 7?

Use the default instance DB and add the block to your AS master data block. The S7_m_c := 'true' attribute makes the FB multi-instance capable, which is required for use in CFC charts that instantiate the block multiple times in one chart or in a chart family.

How does this behave across a CPU failover in an H-system?

SFC64 on a fault-tolerant AS 410 H returns the local CPU's tick, which is synchronized with the standby CPU to within ~10 ms. A hot-standby failover during a run will cause at most one tick of jitter on Elapsed. This is acceptable for process-grade shutdown timing; for sub-tick precision, use a separate high-resolution clock from an external timer module.

Back to blog