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:
- Process variable transducer — 4-20 mA pressure transmitter, output mapped to the S7 analog-input word 0-27648.
- 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.
- 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.
- 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.
- 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% |
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.
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
- 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).
- 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.
- 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.
- 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;
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
- Wire the pressure transmitter; force 0 %, 50 %, 100 % of range with a calibrator and verify AI_Pressure_RAW reads 0, 13824, 27648.
- 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.
- Verify the pneumatic output of the I/P at those currents: 3, 9, 15 psi nominal.
- 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.
- Run relay auto-tuning (Section 6); record Ku, Tu, a, d.
- Apply Ziegler-Nichols conservative row; switch to auto; trend PV, SP, CV.
- Reduce Kp and increase Ti until overshoot is below 5 %; this is the operating point.
- 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.