Siemens S7-300 FB41 CONT_C PID: Multi-Loop OB35 Setup

David Krause16 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

Overview

When implementing closed-loop control on a Siemens SIMATIC S7-300 or S7-400 PLC, the standard library block FB41 "CONT_C" (Continuous Controller) provides a fully featured PID controller suited to driving analog actuators such as pneumatic or motorized control valves. The block integrates anti-windup, derivative delay, deadband, output limiting, and a bumpless manual/auto switchover, all packaged as a single instance-DB-backed FB that drops into any S7 program.

This reference documents the canonical pattern for calling three independent PID loops — one per control valve — from a single cyclic interrupt organization block (OB35). It addresses the most common field questions on instance DB allocation, FC wrapper construction, OB35 cycle time, process variable / setpoint scaling, and the integrator behavior that looks like a bug but is normal controller dynamics. The same pattern applies to FB42 "CONT_S" (step controller for motor-actuated valves) and FB43 "PULSEGEN" (pulse-width modulation for three-point-step outputs) — only the block number and the LMN routing change.

Scope: This article targets STEP 7 V5.5 / STEP 7 Professional on S7-300 and S7-400 CPUs. For S7-1200 and S7-1500, the equivalent functionality is provided by the PID_Compact (V1.x / V2.x / V3.x) technology object and the OB is implicit. The CONT_C blocks do not run on S7-1200/1500.

Prerequisites

  • STEP 7 V5.5 (or STEP 7 Professional in the TIA Portal V13+) with the Standard Library "PID Control Blocks" installed and visible in the library navigator under Standard Library → PID Control Blocks.
  • SIMATIC S7-300 CPU such as 6ES7314-6CG03-0AB0 (CPU 314C-2 PN/DP) with firmware ≥ V2.6, or S7-400 CPU such as 6ES7416-3ES06-0AB0 (CPU 416-3 PN/DP) with firmware ≥ V6.0.
  • Analog input module for PV feedback — e.g., 6ES7331-7KF02-0AB0 (SM 331 AI 8×12 bit) or 6ES7331-7NF10-0AB0 (AI 8× HART) — and analog output module for the valve command, e.g., 6ES7332-5HD01-0AB0 (SM 332 AO 4×12 bit) or 6ES7332-8TF01-0AB0 (AO 8× HART).
  • OB35 not already consumed by a higher-priority application OB. OB10 (time-of-day) and the error OBs (OB82, OB86, OB121, OB122) coexist with OB35 without conflict.
  • Process variable scaled to a REAL in the range 0.0–100.0 % (or any consistent engineering range) before being wired to the FB41 input.

Block Diagram and Signal Flow

The three PID loops share the same FB type, the same wrapper FC, and the same call OB. They differ only in the instance DB number and the I/O addresses bound to that DB.

OB35 Cyclic 100 ms FC100 3 networks FB41 / DB41 Loop 1 — Valve 1 FB41 / DB42 Loop 2 — Valve 2 FB41 / DB43 Loop 3 — Valve 3 AO 0 (Valve 1) PIW256 / PQW256 AO 1 (Valve 2) PIW258 / PQW258

Figure 1 — OB35 → FC100 → 3 × FB41 (instance DBs 41/42/43) → analog outputs

FB41 CONT_C Parameter Reference

The block has 29 input pins and 6 output pins. The most important are tabulated below; refer to the STEP 7 online help for the full set.

Pin Type Default Range Purpose
SP_INT REAL 0.0 -100.0 … 100.0 Setpoint in percent (after internal scaling).
PV_IN REAL 0.0 -100.0 … 100.0 Process value input, pre-scaled to percent.
PV_FAC REAL 1.0 any Multiplication factor applied to PV_IN before forming the error.
PV_OFF REAL 0.0 any Offset added to PV_IN × PV_FAC.
DEADB_W REAL 0.0 0.0 … 100.0 Deadband width; errors within ±DEADB_W are zeroed to suppress jitter.
GAIN REAL 2.0 any Proportional gain (unitless).
TI TIME T#20s ≥ CYCLE Integral action time (reset time). Set TI = T#0s to disable I.
TD TIME T#10s ≥ CYCLE Derivative action time. Set TD = T#0s to disable D.
TM_LAG TIME T#10s ≥ CYCLE/2 Derivative delay (low-pass on D-term); must be ≥ CYCLE/2.
CYCLE TIME T#1s ≥ 1 ms Sampling time of the controller. Must equal the OB35 call interval.
MAN REAL 0.0 LMN_LLM … LMN_HLM Manual manipulated value, used when MAN_ON = TRUE.
MAN_ON BOOL TRUE TRUE = manual mode, LMN tracks MAN. FALSE = automatic.
LMN_HLM REAL 100.0 any Manipulated value high limit (e.g., 100.0 %).
LMN_LLM REAL 0.0 any Manipulated value low limit (e.g., 0.0 %).
LMN REAL (out) LMN_LLM … LMN_HLM Manipulated value output in percent.
LMN_PER INT (out) 0 … 27648 Manipulated value scaled to 0–27648 for direct PQW write.
ER REAL (out) Effective error signal ER = SP_INT − PV_norm.

OB35 Configuration

OB35 is a cyclic interrupt organization block with priority class 12 and a default cycle time of 100 ms. The cycle time is configured in HW Config → CPU properties → "Cyclic Interrupts" tab by selecting the row "OB35" and entering the run-time in microseconds. The valid range is 1,000 µs to 60,000,000 µs (1 ms to 60 s).

For typical process control — flow, pressure, level, temperature — 100 ms is appropriate. For fast pressure or flow cascade inner loops, drop to 50 ms or 20 ms. For very slow temperature loops you can extend to 200 ms or 500 ms to reduce CPU load; the rule is that the cycle should be at least 5× faster than the dominant process time constant.

Critical: The CYCLE input of FB41 must match the OB35 call interval exactly (e.g., CYCLE := T#100ms). The block integrates the I-term and computes the D-term with respect to CYCLE. A mismatch of 2× is a common cause of "the controller is sluggish" or "the controller is unstable" reports in the field.

OB35 is the preferred call location because its deterministic timing isolates the PID calculation from the OB1 scan-time jitter. Calling the FC from OB1 is technically possible but not recommended for closed-loop control, because OB1 cycle time can vary with the rest of the user program.

Three-Loop FC Wrapper Implementation

Step 1 — Create the instance DBs

  1. In SIMATIC Manager: Insert → S7 Block → Data Block.
  2. Name it DB41, set type to Instance DB, and pick FB41 from the Standard Library → PID Control Blocks.
  3. Repeat for DB42 and DB43 — all three reference the same FB absolute number 41.

The instance DB stores the static parameters (GAIN, TI, TD, CYCLE, LMN_HLM, LMN_LLM, etc.) and the internal state (integrator, derivative, manual value, last error). Each instance DB is independent; changing GAIN in DB41 does not affect DB42 or DB43.

Step 2 — Build the FC wrapper

Create FC100 (any unused FC number) with three networks, one call per loop. The structured text representation is shown below; the equivalent STL or LAD form is functionally identical.

// FC100 — Network 1: Loop 1
CALL "CONT_C" , "DB41"
      SP_INT   := MD100          // Setpoint 1 [%]
      PV_IN    := MD104          // Process value 1 [%]
      PV_FAC   := 1.0
      PV_OFF   := 0.0
      DEADB_W  := 0.5            // 0.5 % deadband
      GAIN     := 1.2
      TI       := T#120s
      TD       := T#0s
      TM_LAG   := T#10s
      CYCLE    := T#100ms
      MAN      := MD108
      MAN_ON   := M10.0           // TRUE = manual
      LMN_HLM  := 100.0
      LMN_LLM  := 0.0
      LMN      := MD112           // Captured for PQW write
      LMN_PER  := MW116
      ER       := MD120

// FC100 — Network 2: Loop 2 — identical, DB42, MD13x
// FC100 — Network 3: Loop 3 — identical, DB43, MD15x

The benefit of the FC wrapper is that OB35 itself contains exactly one statement: CALL FC100;. All three loops are guaranteed to execute in the same call of OB35, with the same timestamp, and in a deterministic order. If you later add a fourth loop, you append one more network to FC100 — OB35 is untouched.

Step 3 — Wire the analog output

Convert LMN (REAL, 0.0–100.0) to a peripheral integer for the analog output module. FB41 already provides LMN_PER in the 0–27648 range used by Siemens SM 332 modules. Simply assign it:

// In OB1 or FC100 trailing network
      L   MW116                    // LMN_PER for loop 1
      T   PQW 256                  // Slot 4, channel 0

For HART or current output, the conversion is implicit in the module. For voltage output, scale LMN_PER to 0–27648 → 0–10 V by setting the output range jumpers accordingly.

PV and SP Scaling — The PV_FAC Trap

The most common field mistake is mismatched scaling between SP_INT and PV_IN. FB41 normalizes the process variable to an internal range of -100.0 to +100.0 % using PV_FAC and PV_OFF:

PV_norm = PV_IN × PV_FAC + PV_OFF
ER      = SP_INT − PV_norm

The I and D terms operate on ER and assume the setpoint and the normalized PV share the same range. If the field PV is in 0–200 engineering units (e.g., 0–200 kPa gauge), there are two clean choices:

  • Recommended: Pre-scale the PV to 0.0–100.0 % (REAL) in user code, then set PV_FAC := 1.0 and PV_OFF := 0.0. Set SP_INT in the same 0.0–100.0 % range.
  • Alternative: Keep the PV in 0–200 and set PV_FAC := 0.5, PV_OFF := 0.0. The FB will then treat 0–200 as 0–100 % internally. SP_INT must also be entered in 0–200.
Field-proven pitfall: Setting PV_FAC := 2.0 with a PV that is already scaled 0–200 effectively double-counts the scaling and pushes the error ER to extreme values. The integrator saturates and LMN pegs at LMN_LLM (0 %) or LMN_HLM (100 %). This is not a controller bug — it is correct PID math on a misscaled input. Use PV_FAC := 1.0 when the PV is already in percent, and reserve PV_FAC for the case where PV is in raw engineering units and you want to keep the setpoint in the same units.

Always store the engineering-range bounds (e.g., 0 kPa / 200 kPa) in named constants and let your pre-scaling function block (FC or FB) compute the percent. This way the operator can be shown "50.0 %" while the controller continues to see 100 kPa internally — and you can switch ranges without changing PID tuning.

Auto vs Manual Mode Behavior

FB41 has two operating modes selected via MAN_ON:

  • MAN_ON = FALSE — Automatic. LMN is computed from ER through the full PID algorithm. The integrator (I-term) is updated every CYCLE.
  • MAN_ON = TRUE — Manual. LMN is forced to track the MAN input value directly. The I-term is internally frozen ("track") so that switching back to auto is bumpless — the controller does not produce a step on mode change.

It is normal for LMN to keep changing in auto mode even when PV appears constant, for three reasons:

  1. The I-term integrates any residual error (e.g., 0.1 % mismatch between SP and PV) until it saturates at LMN_LLM or LMN_HLM.
  2. Any noise on PV_IN creates a continuously varying ER that the controller tries to reject. The D-term will move LMN even faster on noisy PV.
  3. Process disturbances (load changes, upstream pressure drift) shift the operating point and the controller continuously re-trims LMN to maintain SP.

Confirmation that auto mode is working correctly requires: (a) the I-term settles within a reasonable bound (a few percent of the operating range), (b) LMN moves in the correct direction when SP is changed (increase SP → increase LMN for a direct-acting loop; decrease LMN for a reverse-acting loop — set GAIN negative to flip action), and (c) LMN returns to a steady value when SP equals PV.

PID Tuning Procedure (Ziegler–Nichols / Tyreus–Luyben)

  1. Open-loop check. With the controller in manual, drive the process to the desired operating point at LMN = 50 %. Wait for steady state.
  2. Initial auto settings. Switch to auto with GAIN := 0.5, TI := T#600s, TD := T#0s, DEADB_W := 0.0.
  3. Step test. Step the setpoint by 5 % and observe the response in the trend view of STEP 7 (curve recorder) or your HMI. Expect a slow, overdamped response at this gain.
  4. Find ultimate gain Ku. Increase GAIN by 50 % per step, returning to steady state after each step, until the loop oscillates with constant amplitude (sustained oscillation). Record the GAIN value as Ku and the oscillation period as Tu.
  5. Apply Ziegler–Nichols PI: GAIN := 0.45 × Ku, TI := 0.80 × Tu, TD := T#0s. Expect ~25 % overshoot on setpoint step.
  6. Apply Tyreus–Luyben PI (slower, less overshoot — preferred for process control): GAIN := 0.31 × Ku, TI := 2.2 × Tu, TD := T#0s.
  7. Add a small TD (≈ Tu/10) only if the loop must track fast setpoint changes; derivative amplifies measurement noise and is rarely needed on slow thermal or level loops.
  8. Fine-tune. After applying the calculated gains, repeat the step test and adjust GAIN by ±20 % and TI by ±30 % to taste. Record the final GAIN/TI/TD in the instance DB comments for future reference.
Closed-loop auto-tuning alternative: The PID Self-Tuner block FB58 (TCONT_CP) and the modular PID FB186 (PID_CP) include automatic controller optimization using the relay-feedback method, eliminating the manual Ku/Tu procedure. They are recommended for new projects where the conservative tuning of FB41 is too slow.

Verification Checklist

  • ☐ OB35 call interval matches CYCLE input on every FB41 instance (verify in HW Config and on each instance DB online).
  • ☐ All three instance DBs initialized with the same setpoint range and engineering units.
  • PV_FAC and PV_OFF consistent across the three loops. Use 1.0/0.0 if PV is already in percent.
  • MAN_ON := FALSE confirmed in each instance DB online before commissioning.
  • LMN_HLM and LMN_LLM set to the actuator stroke limits (0.0 and 100.0 for a 4–20 mA valve; or the actual engineering range for a positioner).
  • DEADB_W set slightly above the PV noise floor (e.g., 0.2–0.5 % for a clean 4–20 mA signal, 1.0 %+ for a noisy one) to suppress integrator wind-up on jitter.
  • ☐ Analog output wiring verified: PQW256 → terminal block → valve positioner. Disconnect actuator during initial auto tests; ramp LMN manually in 10 % steps and confirm stroke at the valve.
  • ☐ Loop response verified by 5 % step test in auto mode after manual trim. Documented overshoot, settling time, and steady-state error.
  • ☐ Bumpless transfer verified: switching from manual to auto at any LMN does not produce a step in valve position.

Troubleshooting Matrix

Symptom Likely Root Cause Diagnostic Fix
LMN pegs at 0 % or 100 % in auto PV_FAC / SP_INT scaling mismatch; SP and PV in different units Monitor ER (effective error) online — if it never crosses zero, scaling is wrong Set PV_FAC := 1.0 and pre-scale PV to percent in user code, or keep PV in engineering units and set PV_FAC = 100/span
Loop oscillates with growing amplitude GAIN too high, or CYCLE does not match OB35 interval Halve GAIN; if stable, it was gain. If still oscillating, check OB35 time vs. CYCLE input Reduce GAIN, increase TI, or set TD := T#0s; align CYCLE with OB35
Step on mode change manual→auto Integrator not tracked (I-term frozen) when in manual Look at LMN just before and after the switch FB41 supports bumpless transfer natively; ensure MAN_ON is wired, not left as default TRUE
LMN does not change when SP changes in auto Loop is reverse-acting with positive GAIN, or actuator saturated Check sign of (SP − PV) and sign of LMN change Set GAIN negative to reverse action, or check LMN_HLM / LMN_LLM
Output jitters in manual mode Noisy PV_IN feeding back into LMN_PER via derivative Disable TD; if jitter stops, D-term is the cause Set TD := T#0s, increase DEADB_W, or add external signal conditioning
OB35 not being called OB35 disabled in HW Config, or overwritten by online edit Use SFC6 / SFC24 to check OB start info, or add a counter in OB35 and watch it increment in VAT Enable OB35 in HW Config; re-download the project
One of three loops behaves differently from the others Wrong instance DB referenced, or DB parameters not initialized Open each instance DB online and verify GAIN, TI, LMN_HLM, LMN_LLM Re-initialize the instance DB from the source FB; check for online edits that did not get downloaded
CPU goes to STOP with SF (System Fault) OB35 not loaded but configured, or wrong FB version Check diagnostic buffer with SFC13 / STEP 7 online diagnostics Reload OB35 and the Standard Library; ensure FB41 version matches CPU firmware
Setpoint written by HMI has no effect SP_INT is wired to a memory bit overwritten elsewhere, or HMI tag length wrong Monitor SP_INT online; if it changes then resets, the bit is being clobbered Use a separate data block for HMI-tagged values, never the instance DB directly

Field-Proven Notes

  • FB41 vs. FB186 (PID_CP): FB186 is the modular successor to FB41 and includes a self-tuner (relay feedback), gain scheduling, and zone-based setpoint weighting. For new S7-300/400 projects with motor-actuated or fast processes, prefer FB186. FB41 remains the workhorse for thermal, level, and flow loops where the conservative tuning is acceptable.
  • Hot-swap of instance DB parameters: GAIN, TI, TD, LMN_HLM, LMN_LLM, DEADB_W, PV_FAC, PV_OFF can be written online from the HMI during commissioning, but always with the operator on-site. The integrator (INT_HOLD / I_ITVAL) is internal and should not be tampered with.
  • Cascade control: For cascade loops (e.g., flow inner, temperature outer), call the inner PID in OB35 with a 50 ms cycle, and the outer PID in OB35 with a 500 ms cycle. Use two different OB3x slots (e.g., OB32 at 500 ms) for the outer loop so that the inner loop runs ten times for every outer-loop update.
  • Direct vs reverse action: Heating loops are typically reverse-acting — increase LMN to increase temperature, so GAIN is positive. Cooling loops are direct-acting — decrease LMN (i.e., open a cooling valve) to increase temperature, so GAIN is negative. Pressure and flow can be either; verify by stepping LMN manually and watching the PV response.
  • Save/Restore of instance DBs: The instance DB can be saved with the project and restored via the HMI. Use SFC82 (Read from SDB) and SFC84 (Write to SDB) carefully — they affect the entire SDB, not individual DBs.

FAQ

Do I need three separate instance DBs for three PID loops with FB41?

Yes. Each independent PID loop requires its own instance DB (e.g., DB41, DB42, DB43), all referencing the same FB41 absolute number 41. The instance DB stores the controller state — integrator, derivative, manual value — which must be unique per loop.

Why call the FB41 in OB35 instead of OB1?

OB35 is a cyclic interrupt OB with a deterministic, configurable time base (default 100 ms). Calling the PID there isolates the calculation from OB1 scan-time jitter, which can vary with the rest of the user program. The FB41 CYCLE input must be set to the same value as the OB35 call interval.

My LMN pegs at 0 % or 100 % in auto mode — what is wrong?

Almost always a scaling mismatch between SP_INT and PV_IN. If your PV is already in 0–100 %, set PV_FAC := 1.0 and PV_OFF := 0.0. If your PV is in 0–200 engineering units, set PV_FAC := 0.5 and enter the setpoint in the same 0–200 range, or pre-scale the PV to 0–100 % in user code and keep PV_FAC := 1.0.

Should I use FB41 or FB186 / PID_CP for a new project?

For S7-300/400, FB186 (PID_CP) is the modular successor with built-in self-tuning and is preferred for new motor-actuated or fast-response loops. FB41 (CONT_C) remains the right choice for simple thermal, level, and flow loops where conservative tuning is acceptable. For S7-1200/1500, use the PID_Compact technology object; FB41 does not run on those CPUs.

Why does the manipulated value keep changing in auto mode even when PV is constant?

That is correct PID behaviour. The I-term integrates any residual error, the D-term reacts to noise on PV, and process disturbances continuously shift the operating point. Confirm the loop is working by checking that LMN returns to a steady value when SP equals PV, and that the I-term settles within a few percent of the operating range.

Back to blog