Siemens S7-1200 PID Control in SCL: Tuning and Scaling Guide

David Krause14 min read
PID ControlSiemensTutorial / 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

Siemens S7-1200 PID Control in SCL: Tuning and Scaling Guide

This reference covers the implementation of a discrete PID controller in SCL for an S7-1200 or S7-1500 CPU, including scaling of the 0-27648 analog I/O range to engineering units (psi) and the use of the Astrom-Hogglund relay auto-tuning method to derive Kp, Ki, Kd for pneumatic valve control. It also documents the NORM_X and SCALE_X conversion instructions, the cyclic interrupt OB configuration, the use of the built-in PID_Compact instruction as an alternative, and a troubleshooting matrix for the most common drift and oscillation symptoms reported on field commissioning.

1. System Overview and Signal Chain

A typical pressure-control loop on an S7-1200 consists of the following signal path:

  1. Process variable transducer — 4-20 mA pressure transmitter, output mapped to the S7 analog-input word 0-27648.
  2. Analog input module — SM 1231 (e.g. 6ES7231-4HD32) configured for 4-wire current, 0-20 mA or 4-20 mA. The module returns INT 0-27648 with 0 = 4 mA (or 0 mA depending on configuration) and 27648 = 20 mA.
  3. PID algorithm — runs in a cyclic interrupt OB (OB30-OB38) so that the sample time Ts is deterministic. The CPU 1214C supports OB30-OB38 with phase offset; the CPU 1215C/1217C extend the available OBs.
  4. Analog output module — SM 1232 (e.g. 6ES7232-4HB32) or onboard AQ of the CPU, driving 4-20 mA to a current-to-pressure (I/P) transducer.
  5. I/P transducer — converts 4-20 mA to 3-15 psi pneumatic output that positions the control valve actuator.

The mapping from the S7-1200 analog word to engineering units follows the Siemens convention used by every SM 1231/1232 and by the signal boards:

Current (mA) AI/AQ word I/P output (psi) Valve position
4.000 0 3.0 Near closed
8.000 6912 6.0 ~25%
12.000 13824 9.0 ~50%
16.000 20736 12.0 ~75%
20.000 27648 15.0 ~100%
Important convention check. The S7-1200 analog modules use the bipolar representation 0-27648 for unipolar 0-20 mA / 4-20 mA. Confirm whether your specific AI module is configured for 0-20 mA or 4-20 mA in the device configuration; the engineering range changes accordingly. In a 4-20 mA configuration, 4 mA maps to 0 in the integer word and 20 mA maps to 27648.

2. Prerequisites

  • STEP 7 (TIA Portal) V16 or later; PID_Compact V2.3 or later requires TIA V16+. Earlier TIA versions ship PID_Compact V2.0/2.1/2.2.
  • S7-1200 CPU firmware V4.2 or later (for the cyclic interrupt OB30+ blocks to be available) — see Siemens Industry Online Support for the firmware update matrix.
  • SM 1231 AI module, 4-20 mA, configured in the device view.
  • SM 1232 AQ module or onboard AQ of the CPU, 4-20 mA.
  • I/P transducer with 3-15 psi output and 4-20 mA input.
  • Pneumatic actuator and a regulating valve with a known Cv characteristic.

3. Scaling with NORM_X and SCALE_X

The NORM_X and SCALE_X instructions are the canonical Siemens way to linearize an integer analog word to a REAL engineering value. They are documented in the TIA Portal help under Basic Instructions > Conversion Operations.

  • NORM_X(MIN, MAX, VALUE) returns a REAL normalized to the closed interval [0.0, 1.0]. VALUE is any integer or REAL.
  • SCALE_X(MIN, MAX, VALUE) takes a normalized REAL in [0.0, 1.0] and scales it to the closed interval [MIN, MAX].

The two functions can be chained in either order:

  • AI to engineering unit: NORM_X then multiply by span, or SCALE_X from [0.0, 1.0] into [eng_min, eng_max].
  • Engineering unit to AQ: NORM_X into [0.0, 1.0], then SCALE_X into [0, 27648].

The Siemens FAQ "How do you scale integer values in real numbers and vice versa for analog inputs and outputs in STEP 7 (TIA Portal)?" covers both directions with worked examples — see Siemens Industry Online Support, entry ID 39334504.

3.1 Read the pressure transmitter (AI to psi)

For a 4-20 mA pressure transmitter with 0-10 psi range, the integer word 0-27648 must become 0.0-10.0 psi:

// SCL — convert AI word to engineering unit
#PV_norm := NORM_X(MIN := 0,    MAX := 27648.0, VALUE := "AI_Pressure_RAW");
#PV_psi  := SCALE_X(MIN := 0.0, MAX := 10.0,    VALUE := #PV_norm);

Equivalently, since the inner NORM_X result is already in [0.0, 1.0], a single multiply works:

#PV_psi := NORM_X(MIN := 0, MAX := 27648.0, VALUE := "AI_Pressure_RAW") * 10.0;

3.2 Drive the I/P transducer (control % to AQ word)

For a controller output of 0.0-100.0 percent mapped to 4-20 mA:

// SCL — convert PID output percent to AQ integer
"AQ_Valve_INT" := REAL_TO_INT(
    SCALE_X(MIN := 0.0, MAX := 27648.0,
            VALUE := NORM_X(MIN := 0.0, MAX := 100.0,
                             VALUE := "DB_PID".CV_Percent))
);

Note that SCALE_X returns a REAL; cast to INT or DINT before assigning to the analog-output address. The SM 1232 ignores the bottom three bits, so values 0-27648 are valid even when the LSBs are zero.

Bipolar vs unipolar. If the SM 1232 is configured for -10 V to +10 V (bipolar), use the range -27648 to +27648. For 4-20 mA current output the range is always 0 to 27648. Mixing the ranges produces an offset that looks like drift — see Section 9.

4. Cyclic Interrupt OB Configuration

A discrete PID must run at a deterministic sample time. Use a cyclic interrupt OB (OB30-OB38) rather than the main OB1. The OB period sets the controller sample time Ts.

OB Default period Typical use
OB30 10 ms Fast pressure loops, valve positioning
OB31 20 ms Standard pressure / flow
OB32 100 ms Temperature, slow process
OB33 500 ms Level, very slow
OB34-OB38 1 s - 60 s Long-term regulation

Configure the OB30 properties in the device view of the CPU (Properties > Cyclic Interrupts). Set Phase Offset = 0 ms initially. Confirm that the OB30 is not overwritten by the same-numbered OB elsewhere in the program; S7 CPUs error out on duplicate OB numbers.

5. Discrete PID Implementation in SCL

The following SCL function block implements a positional PID with anti-windup and derivative-on-error. It runs once per OB30 tick and exposes both the engineering-unit output and the percent output.

FUNCTION_BLOCK "FB_DiscretePID"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      PV_psi        : REAL;   // Process variable in engineering units
      SP_psi        : REAL;   // Setpoint in engineering units
      Kp            : REAL;   // Proportional gain
      Ti_s          : REAL;   // Integral time (seconds), 0 = no I action
      Td_s          : REAL;   // Derivative time (seconds), 0 = no D action
      Ts_s          : REAL;   // Sample time (seconds)
      OutMin_Pct    : REAL;   // Output lower limit in %
      OutMax_Pct    : REAL;   // Output upper limit in %
      Enable        : BOOL;
   END_VAR

   VAR_OUTPUT
      CV_Percent    : REAL;   // 0-100 % controller output
      Error_psi     : REAL;   // SP - PV in engineering units
      TrackingActive: BOOL;
   END_VAR

   VAR
      IState        : REAL;   // Integrator state
      LastError     : REAL;   // For derivative-on-error
      LastPV        : REAL;   // For derivative-on-PV (alternative)
      DerivOnPV     : BOOL := FALSE;
   END_VAR

BEGIN
    IF NOT #Enable THEN
        #CV_Percent := 0.0;
        #TrackingActive := TRUE;
        RETURN;
    END_IF;

    #TrackingActive := FALSE;
    #Error_psi := #SP_psi - #PV_psi;

    // Proportional
    #CV_Percent := #Kp * #Error_psi;

    // Integral with conditional integration (anti-windup)
    IF #Ti_s > 0.0 THEN
        #IState := #IState + (#Kp / #Ti_s) * #Error_psi * #Ts_s;

        // Clamp integrator when output saturates AND error pushes further into saturation
        IF (#CV_Percent + #IState) > #OutMax_Pct
           AND #Error_psi > 0.0 THEN
            #IState := #OutMax_Pct - #CV_Percent;
        END_IF;
        IF (#CV_Percent + #IState) < #OutMin_Pct
           AND #Error_psi < 0.0 THEN
            #IState := #OutMin_Pct - #CV_Percent;
        END_IF;
        #CV_Percent := #CV_Percent + #IState;
    END_IF;

    // Derivative on PV (avoids derivative kick on setpoint change)
    IF #Td_s > 0.0 THEN
        IF #DerivOnPV THEN
            #CV_Percent := #CV_Percent
                          - (#Kp * #Td_s / #Ts_s) * (#PV_psi - #LastPV);
            #LastPV := #PV_psi;
        ELSE
            #CV_Percent := #CV_Percent
                          + (#Kp * #Td_s / #Ts_s) * (#Error_psi - #LastError);
            #LastError := #Error_psi;
        END_IF;
    END_IF;

    // Output clamp
    IF #CV_Percent > #OutMax_Pct THEN #CV_Percent := #OutMax_Pct; END_IF;
    IF #CV_Percent < #OutMin_Pct THEN #CV_Percent := #OutMin_Pct; END_IF;
END_FUNCTION_BLOCK

Key implementation notes drawn from the TIA Portal help and from Eurotherm's PID tuning primer:

  • Always use derivative-on-PV for valve control. Derivative-on-error causes a spike (derivative kick) on every setpoint change.
  • Use conditional integration instead of plain clamping of the integrator. Clamping alone freezes the integrator during saturation but does not discharge it, which produces windup when the setpoint finally drops.
  • Run the FB exactly once per OB30 tick; do not call it from OB1 as well.

6. Relay / Astrom-Hogglund Auto-Tuning Method

The relay feedback method (Astrom and Hogglund, 1984) identifies the ultimate gain Ku and ultimate period Tu of the process without driving it into sustained oscillation with a P-only controller. The process is forced to oscillate by switching the controller output between two levels (+d and -d) around the setpoint, and the resulting limit cycle is measured.

6.1 Procedure

  1. Place the loop in manual. Force the controller output to a value that brings the PV close to the desired operating point (typically 50 % of range).
  2. Switch to automatic with a relay output of amplitude d (e.g. ±5 % of CV span). The hysteresis ε prevents chattering from noise and is typically 2-3 × the PV noise band.
  3. Wait for a clean limit cycle (usually 3-5 cycles). Record the peak amplitude a of the PV oscillation around the setpoint, and the period Tu between zero crossings of the same sign.
  4. Compute Ku:
Ku = (4 * d) / (PI * sqrt(a^2 - eps^2));
Tu = measured period of oscillation (seconds);

If hysteresis ε is small compared to a, the simplified form is Ku ≈ 4d / (π · a). See the PID controller article for the derivation.

6.2 Ziegler-Nichols Tuning Rules

Controller Kp Ti (s) Td (s)
P 0.50 · Ku — —
PI 0.45 · Ku Tu / 1.2 —
PID 0.60 · Ku Tu / 2.0 Tu / 8.0
Some overshoot 0.33 · Ku Tu / 2.0 Tu / 3.0
No overshoot 0.20 · Ku Tu / 2.0 Tu / 3.0

For a pneumatic valve on a pressure loop, the conservative "no overshoot" row is normally the correct starting point. The Tyreus-Luyben rules (Kp = 0.45·Ku, Ti = 2.2·Tu, Td = Tu/6.3) give a more damped response that is usually preferred for process control.

7. Output Scaling to 0-27648

The relationship between the PID output CV_Percent (0-100 %) and the AQ integer word is linear in a 4-20 mA configuration:

AQ_word = CV_Percent / 100 * 27648

Use SCALE_X so that the result is properly clamped to the integer range even when CV_Percent drifts slightly outside 0-100 % due to floating-point rounding:

// In OB30, after FB_DiscretePID runs:
"DB_Valve".AQ_Valve_INT := REAL_TO_INT(
    SCALE_X(MIN := 0.0, MAX := 27648.0,
            VALUE := NORM_X(MIN := 0.0, MAX := 100.0,
                             VALUE := "DB_PID".CV_Percent))
);
"AQ_Valve" := "DB_Valve".AQ_Valve_INT;
REAL_TO_INT truncation. TIA Portal's REAL_TO_INT truncates (rounds toward zero). For valve control the difference between truncated and rounded is usually irrelevant, but for a closed loop that hits the rail exactly you can use ROUND (IEC 61131-3 standard function) instead.

8. PID_Compact as the Built-in Alternative

Siemens ships PID_Compact (instruction number 2711500) for S7-1200 and PID_Compact / PID_3Steps / PID_Temp for S7-1500. They are documented in the TIA Portal help under Technology > PID Control and in the function manual S7-1200 Programmable Controller — Function Manual (entry ID 109751826). The instruction provides:

  • Automatic output scaling to the 0-27648 analog word.
  • Anti-windup and setpoint ramp built in.
  • Tuning modes Pretuning and Fine tuning that perform the relay identification automatically.
  • Configuration DB with hundreds of parameters (Retain.Cfg.LoadBackUp, Retain.Cfg.SetpointLimitation, etc.).

For new code, prefer PID_Compact and use the built-in pretuning rather than writing a discrete PID by hand. The hand-written discrete PID is justified only when:

  • The sample time must be below 10 ms (e.g. fast pneumatic servo valves that close at < 50 ms).
  • Custom control structures are required (cascade, override, gain scheduling) that PID_Compact does not expose.
  • Migration of legacy STL/SCL code.

9. Troubleshooting Matrix

Symptom Likely root cause Diagnostic Fix
Output climbs to 100 % and stays Integrator windup due to persistent saturation Monitor DB_PID.IState in a watch table Add conditional integration; reduce Ti or limit OutMax_Pct
Output drifts up or down with constant parameters Sample time Ts is shorter than OB period, or PV scaling offset Compare FB_DiscretePID call count per second to OB30 period Ensure FB runs exactly once per OB30; verify AI scaling; check AI channel for live-zero (4 mA)
Sustained oscillation at one period Gain too high or Ti too short Halve Kp; if oscillation stops, Kp is on the edge of instability Reduce Kp to 0.5 × current; double Ti
Valve chatters near setpoint Derivative action on measurement noise Trace PV_psi on a trend; check for > 0.5 % noise Filter PV with a first-order lag (PT1) of 1-2 × Ts; or set Td = 0
Setpoint change causes large spike Derivative on error enabled Check DerivOnPV flag Switch to derivative-on-PV (DerivOnPV := TRUE)
PV reads -1.5 psi or +10 % off Wrong AI range (bipolar vs unipolar) Read AI configuration in device view Set AI to 4-20 mA unipolar and re-download hardware configuration
AQ never moves even though CV_Percent changes AQ address wrong, or AQ module not configured for current output Force AQ with watch table; check device configuration of SM 1232 Set SM 1232 channel type to "Current" 4-20 mA; recompile hardware
"PID Compact error during runtime" (hex 80B0 / 80B1) PID_Compact instance DB not downloaded, or background OB missing Check online > diagnostics; look for OB priority Re-download blocks; ensure PID_Compact is called from a cyclic OB with priority > 1
PID works in simulation but not on plant I/P transducer wired reversed, or air supply pressure low Measure mA at I/P terminals; check supply > 20 psi above max output Reverse polarity; verify instrument air

10. Verification and Commissioning Procedure

  1. Wire the pressure transmitter; force 0 %, 50 %, 100 % of range with a calibrator and verify AI_Pressure_RAW reads 0, 13824, 27648.
  2. Force AQ_Valve_INT = 0, 13824, 27648 in a watch table; verify with a multimeter that the I/P input reads 4, 12, 20 mA.
  3. Verify the pneumatic output of the I/P at those currents: 3, 9, 15 psi nominal.
  4. Set Kp = 0, Ti = 9999, Td = 0; place the loop in manual; step CV_Percent to 25 %, 50 %, 75 % and record PV behavior. Confirm time constant and dead time by inspection.
  5. Run relay auto-tuning (Section 6); record Ku, Tu, a, d.
  6. Apply Ziegler-Nichols conservative row; switch to auto; trend PV, SP, CV.
  7. Reduce Kp and increase Ti until overshoot is below 5 %; this is the operating point.
  8. Disable test mode; switch to production setpoint; record final Kp, Ti, Td in the tuning logbook.

11. Field-Notes Summary

  • Confirm the analog module range (0-20 mA vs 4-20 mA, vs -10 to +10 V) before writing any SCALE_X block. An offset of 1 mA on the AI looks like windup; an offset on the AQ looks like bias.
  • For the Astrom-Hogglund relay method, start with d = 5 % of the CV span and ε = 2-3 × the PV noise band. If the process will not oscillate, increase d to 10 %.
  • Derivative action on PV, not on error, for valve control.
  • Conditional integration is preferable to clamp-only anti-windup.
  • For loops that must run faster than 10 ms, hand-written SCL is justified. For everything else, PID_Compact is faster, safer, and shipped with pretuning.
  • Always confirm the I/P transducer air supply pressure is at least 20 psi above the maximum required output signal pressure.

FAQ

How do I scale a 0-27648 analog input to 0-10 psi in TIA Portal?

Use NORM_X to convert the integer word to a normalized REAL in [0.0, 1.0], then either multiply by 10.0 or feed the normalized value into SCALE_X with MIN = 0.0 and MAX = 10.0. Both NORM_X and SCALE_X are documented under Basic Instructions > Conversion Operations in the TIA Portal help.

Why does my hand-written PID output drift up or down with the same parameters?

Three common causes on an S7-1200: (1) the FB is called more or less than once per OB30 tick, so the effective Ts differs from the value used in the formula; (2) the integrator state IState is not retained across CPU STOP/RUN transitions and accumulates bias; (3) the AI range is configured for 0-20 mA but the transmitter is 4-20 mA, producing a 1.38 psi offset. Verify with a watch table and check the AI channel configuration in the device view.

Should I use PID_Compact or write my own discrete PID in SCL?

Use PID_Compact for sample times ≥ 10 ms and for any loop that does not require custom structures (cascade, override, gain scheduling). PID_Compact ships with Pretuning and Fine tuning that implement the relay identification automatically. Write a discrete PID in SCL only when the sample time must be faster than 10 ms or when a non-standard control structure is required.

How do I derive Kp, Ti, Td from the relay auto-tuning test?

Compute Ku = 4d / (π · √(a² - ε²)) and Tu = period of the resulting limit cycle, where d is the relay output amplitude, a is the peak PV amplitude around setpoint, and ε is the relay hysteresis. Apply the Ziegler-Nichols conservative row for valves: Kp = 0.20 · Ku, Ti = Tu / 2.0, Td = Tu / 3.0. The Eurotherm tuning primer documents the manual method as a fallback.

What is the difference between NORM_X and SCALE_X, and in which order should I call them?

NORM_X takes a value with known physical bounds and returns a REAL in [0.0, 1.0]. SCALE_X takes a REAL in [0.0, 1.0] and returns a value in a new physical range. To convert an integer analog input to engineering units, call NORM_X first, then either multiply by the span or chain into SCALE_X. To convert an engineering-unit PID output to an AQ integer, the reverse chain applies. The TIA Portal help documents both functions under Basic Instructions > Conversion Operations.

Back to blog