Detecting Unconnected FB Input Parameters in TIA Portal (S7-1200 / S7-1500)
1. Problem Statement and Engineering Context
In SIMATIC S7-1200 and S7-1500 programming with TIA Portal, an FB (Function Block) is a reusable code unit with its own instance DB. Each instance call site may wire the input parameters differently. A common engineering requirement is to write FB code that branches on whether a specific input terminal has been wired (connected to a real tag or literal) at the call site, or left open (left as the unconnected default placeholder). Classic use cases include:- Selector FBs that default to a different formula when an override input is absent.
- Calibration routines that apply a correction only when the calibration tag is wired.
- Optional interlocks that bypass a permissive check when its source is intentionally left open during commissioning.
- Generic utility FBs (math, scaling, statistics) that want to behave differently when an operand is missing versus when the operand is legitimately zero.
REAL, INT, BOOL) because the moment the FB is compiled the parameter has a data type and a default value, and the call site provides a value (or the default zero). The PLC does not expose a "connectedness" bit to SCL in the same way that some other PLCs do for inline coil parameters.
The solution is to choose an FB input parameter data type that can represent an unconnected state. Three workable approaches exist for the S7-1200/1500 family:
- REF_TO <Type> – a typed reference, with NULL meaning "unconnected". Supported on S7-1500 (firmware V2.0 and later). Not supported on S7-1200.
-
VARIANT – a typeless pointer whose
TypeCodeattribute can be inspected for the VOID type. Supported on S7-1500; on S7-1200 the VARIANT type is available but more limited. -
Sentinel default value – declare a default that the process is guaranteed never to produce (for example
-1.0for a process range of 0…100, or16#7FFF_FFFFfor an unsigned 16-bit field). Test the parameter against the sentinel. Universal across all S7 platforms.
2. Prerequisites
| Item | Requirement | Notes |
|---|---|---|
| Engineering tool | STEP 7 (TIA Portal) V15.1 or later | V17 / V18 recommended for current firmware. TIA Portal version overview |
| Controller (REF_TO method) | S7-1500 CPU, FW V2.0 or later | REF_TO is not available on S7-1200. S7-1500 system manual |
| Controller (VARIANT method) | S7-1200 (any FW) or S7-1500 (FW V2.0+) | S7-1200 supports VARIANT but limited to standard types and structures (not multi-instance). S7-1200 system manual |
| Language | SCL (Structured Control Language) | REF_TO and VARIANT dereference are only meaningful in SCL, not LAD/FBD |
| Online connection | Online & diagnostics or watch table | Needed to verify the bit pattern at runtime |
3. Method 1 — REF_TO with NULL Check (S7-1500 only)
3.1 How REF_TO behaves
A REF_TO is a typed pointer to a data area. When the input pin is left unconnected on the call site, the FB sees a NULL pointer (16#0000_0000_0000_0000). When the pin is connected, the FB receives the address of the operand. Inside the FB you test the pointer against NULL before dereferencing; dereferencing a NULL pointer causes the CPU to enter STOP with an OB121 / OB122 area-length error.
3.2 FB declaration block
Open the FB in TIA Portal, switch to the SCL editor, and declare the interface as follows:FUNCTION_BLOCK "FB_SumSelector_Ref"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
refA : REF_TO REAL; // optional contribution A
refB : REF_TO REAL; // required contribution B
refC : REF_TO REAL; // required contribution C
END_VAR
VAR_OUTPUT
sumResult : REAL; // computed result
bAConnected : BOOL; // diagnostic: A wired?
diagCode : INT; // 0=OK, 1=B missing, 2=C missing, 3=B+C missing
END_VAR
VAR
rB : REAL;
rC : REAL;
END_VAR
BEGIN
// Guard required operands before dereference
IF refB = NULL THEN
rB := 0.0;
diagCode := diagCode OR 1;
ELSE
rB := refB^;
END_IF;
IF refC = NULL THEN
rC := 0.0;
diagCode := diagCode OR 2;
ELSE
rC := refC^;
END_IF;
// Optional contribution A
IF refA <> NULL THEN
sumResult := refA^ + rB + rC;
bAConnected := TRUE;
ELSE
sumResult := rB + rC * 2.0;
bAConnected := FALSE;
END_IF;
END_VAR
END_FUNCTION_BLOCK
3.3 Call site example
In OB1 (or any cyclic OB), call the FB once with all inputs wired and once withrefA left open:
// Instance 1 — A wired, B and C wired
"iDB_Sum1"(refA := "DB_Process".scalingA,
refB := "DB_Process".scalingB,
refC := "DB_Process".scalingC);
// Instance 2 — A left open, B and C wired
"iDB_Sum2"(// refA omitted intentionally
refB := "DB_Calc".factorB,
refC := "DB_Calc".factorC);
3.4 Diagnostics
Set up a watch table with the two instance DBs. Right-click → Monitor/Modify. ThebAConnected tag in iDB_Sum1 reads TRUE and in iDB_Sum2 reads FALSE. The diagCode field indicates which required inputs were not supplied.
4. Method 2 — VARIANT Type Inspection (S7-1200 / S7-1500)
4.1 VARIANT semantics
A VARIANT can point to any elementary or structured data area, or to nothing. When the input pin is unconnected, the variant is initialised withTypeCode = 16#0000 (VOID) and the IS_NULL test returns TRUE. When wired, the TypeCode reflects the actual data type (e.g. 16#04 for INT, 16#05 for DINT, 16#08 for REAL). The VariantGet instruction is used to dereference and copy the actual value into a typed work variable.
4.2 FB declaration block
FUNCTION_BLOCK "FB_SumSelector_Var"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
pParA : VARIANT; // optional contribution A
pParB : VARIANT; // required contribution B
pParC : VARIANT; // required contribution C
END_VAR
VAR_OUTPUT
sumResult : REAL;
bAConnected : BOOL;
typeA : INT; // TypeCode for A (diagnostic)
typeB : INT; // TypeCode for B
typeC : INT; // TypeCode for C
bOk : BOOL; // overall validity
END_VAR
VAR_TEMP
rA : REAL;
rB : REAL;
rC : REAL;
wStatusA : WORD;
wStatusB : WORD;
wStatusC : WORD;
END_VAR
BEGIN
bOk := TRUE;
// ----- Optional A -----
typeA := pParA.TypeCode;
IF pParA.TypeCode = 16#0000 THEN
bAConnected := FALSE;
ELSE
// Try to read the variant as REAL
wStatusA := VariantGet(src := pParA, dst := rA);
IF wStatusA = 0 THEN
bAConnected := TRUE;
ELSE
// Connected but not REAL — degrade gracefully
rA := 0.0;
bAConnected := FALSE;
END_IF;
END_IF;
// ----- Required B -----
typeB := pParB.TypeCode;
IF pParB.TypeCode = 16#0000 THEN
rB := 0.0;
bOk := FALSE;
ELSE
wStatusB := VariantGet(src := pParB, dst := rB);
IF wStatusB <> 0 THEN
rB := 0.0;
bOk := FALSE;
END_IF;
END_IF;
// ----- Required C -----
typeC := pParC.TypeCode;
IF pParC.TypeCode = 16#0000 THEN
rC := 0.0;
bOk := FALSE;
ELSE
wStatusC := VariantGet(src := pParC, dst := rC);
IF wStatusC <> 0 THEN
rC := 0.0;
bOk := FALSE;
END_IF;
END_IF;
// ----- Computation -----
IF bAConnected THEN
sumResult := rA + rB + rC;
ELSE
sumResult := rB + rC * 2.0;
END_IF;
END_VAR
END_FUNCTION_BLOCK
4.3 TypeCode reference table
| TypeCode (hex) | S7 data type | Length (bytes) |
|---|---|---|
| 16#0000 | VOID (unconnected) | 0 |
| 16#0001 | BOOL | 1 |
| 16#0002 | BYTE | 1 |
| 16#0003 | CHAR | 1 |
| 16#0004 | WORD | 2 |
| 16#0005 | INT | 2 |
| 16#0006 | DWORD | 4 |
| 16#0007 | DINT | 4 |
| 16#0008 | REAL | 4 |
| 16#0009 | DATE | 2 |
| 16#000A | TOD (Time_of_Day) | 4 |
| 16#000B | TIME | 4 |
| 16#000C | S5TIME | 2 |
| 16#000F | DATE_AND_TIME (DT) | 8 |
| 16#0010 | STRING | n+2 |
16#0000 (VOID) only when the parameter is truly unconnected on the call site. If a literal or tag is connected — even one whose value is zero — the TypeCode will reflect the connected type. This is the correct semantic for "wired or not wired" detection.
4.4 Call site example
"iDB_SumVar"(pParA := "DB_Process".scalingA, // wired
pParB := "DB_Process".scalingB, // wired
pParC := "DB_Process".scalingC); // wired
"iDB_SumVar_2"(// pParA omitted → TypeCode stays 16#0000
pParB := "DB_Calc".factorB,
pParC := "DB_Calc".factorC);
5. Method 3 — Sentinel Default Value (Universal)
5.1 Concept
Pick a value that the process can never produce and declare it as the input default. Inside the FB, compare the actual value against that sentinel. If equal, treat the parameter as unconnected. Common sentinel values:| Parameter type | Recommended sentinel | Process range |
|---|---|---|
| REAL (scaling, %) | -1.0 (or -3.4E38 = -REAL#MIN) | 0…100 |
| REAL (temperature °C) | -1000.0 | -200…+1200 |
| INT (count, index) | -1 | 0…32767 |
| DINT (encoder pulse) | 16#7FFF_FFFF | 0…2^31-1 |
| WORD (bitmask) | 16#FFFF | 0…16#FFFE |
| TIME (duration) | T#0ms (or no sentinel — use absent enable) | process-specific |
5.2 FB declaration block
FUNCTION_BLOCK "FB_SumSelector_Sentinel"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
rParA : REAL := -1.0; // -1.0 = sentinel "not connected"
rParB : REAL; // required
rParC : REAL; // required
END_VAR
VAR_OUTPUT
sumResult : REAL;
bAConnected : BOOL;
END_VAR
BEGIN
IF rParA = -1.0 THEN
bAConnected := FALSE;
sumResult := rParB + rParC * 2.0;
ELSE
bAConnected := TRUE;
sumResult := rParA + rParB + rParC;
END_IF;
END_VAR
END_FUNCTION_BLOCK
5.3 Why this works for every S7-1200 / S7-1500
Because the comparison is done on a normal typed value, the FB compiles for every CPU family and firmware revision, including older S7-300/400 retrofits in TIA Portal. There is no special instruction, no library, and no language restriction beyond basic SCL comparison.5.4 Risks and mitigations
- Process collision: if the real process can ever produce the sentinel value, the FB will mis-classify it as "unconnected". Mitigate by documenting the sentinel in the FB header comment and by checking the sentinel against the process engineering limits at design time.
-
Implicit zero on unconnected pin: if a user does not wire a parameter and the editor does not preserve the explicit default (some earlier TIA Portal versions reset to literal zero in the call site), the sentinel is lost. Mitigate by always wiring the default literal explicitly at the call site:
rParA := -1.0. -
Multiple defaults in one FB: use distinct sentinels for each optional input to avoid ambiguity (e.g.
-1.0for A,-2.0for B).
6. Method Comparison Matrix
| Attribute | REF_TO | VARIANT | Sentinel |
|---|---|---|---|
| CPU support | S7-1500 only | S7-1200 + S7-1500 | All SIMATIC S7 |
| Firmware minimum | V2.0 | V4.0 (S7-1200), V2.0 (S7-1500) | Any |
| Language | SCL only | SCL only | SCL / LAD / FBD |
| Compile cost | Low (typed pointer) | Higher (variant handling) | Lowest |
| Runtime cost | NULL compare + deref | TypeCode read + VariantGet | Single equality compare |
| Distinguishes zero from unconnected | Yes | Yes | No (only if zero ≠ sentinel) |
| Library required | No | Yes — "VariantGet" / "VariantPut" | No |
| NULL deref risk | High — STOP if unguarded | Handled by ENO/Status | None |
| Visible in LAD/FBD | No (pin hidden) | No (pin shown as ??) | Yes |
| Best for | High-performance S7-1500 libraries | Generic utility FBs across families | Cost-sensitive / cross-platform projects |
7. Step-by-Step Implementation — VARIANT Method on TIA Portal V18
The walk-through uses the VARIANT method because it works on both S7-1200 and S7-1500.Step 1 — Create the FB
- In the project tree, right-click Program blocks → Add new block → Function block.
- Name:
FB_SumSelector_Var, language: SCL, number: auto. Click Add. - Open the FB, switch the interface view if not already open, and enter the declarations from section 4.2.
Step 2 — Compile
- Press F7 or right-click → Compile.
- If a syntax error is shown for
VariantGet, the "VariantGet" instruction must be resolved. Open the right-hand Instructions pane → Basic Instructions → Move operations → drag VARIANT_TO or the system block VariantGet into the editor. The compiler will then create the required F-system block automatically. - Compile again until the status reads "0 errors, 0 warnings".
Step 3 — Call the FB twice from OB1
- Open OB1 (SCL) and add the call patterns from section 4.4.
- Create the data blocks
DB_ProcessandDB_Calcif they do not exist, and declare the referenced REAL tags. - Compile OB1.
Step 4 — Download and go online
- Right-click the S7-1500 device → Download to device → select PG/PC interface → search → load.
- Open Watch and force tables → add a new watch table. Drag both instance DBs into the table along with the two source data blocks.
- Click Monitor all (glasses icon).
Step 5 — Verify the detection
- For instance 1 (all wired), expect
bAConnected = TRUEandsumResult = A+B+C. - For instance 2 (A omitted), expect
bAConnected = FALSE,typeA = 0(VOID), andsumResult = B + 2·C. - Modify
DB_Process.scalingAonline to confirm the path is re-evaluated on every cycle.
8. Edge Cases and Field-Proven Pitfalls
8.1 Unconnected parameter to a non-VARIANT FB
If the input is declared as a standardREAL and the pin is left unconnected at the call site, TIA Portal inserts the default value of the input. With the standard default 0.0 this means the FB cannot distinguish "user wired 0.0" from "user did not wire anything". The only remedy is to change the input type to REF_TO, VARIANT, or to give the input a non-zero default value explicitly at the FB declaration.
8.2 Connection via AT construct or POKE
Some legacy code wires an FB input via anAT overlay on a larger DB, or via POKE (S7-1500 only). These produce a valid connected address. REF_TO and VARIANT see the parameter as connected. If you need to detect the absence of a memory area, you must validate the contents, not the connection status.
8.3 Multi-instance FBs and parameters
When an FB is called as a multi-instance inside another FB, REF_TO and VARIANT work identically. There is no special handling. The REF_TO input must be assigned to an addressable tag — it cannot be assigned to a constant literal (LAD/FBD do not let you drag a constant to a REF_TO pin; SCL must be used for the assignment).8.4 S7-1200 firmware limits on VARIANT
The S7-1200 supports VARIANT but the VariantGet instruction is implemented in the CPU firmware rather than as a system block. Some early firmware versions (V4.0) do not support VARIANT inside FBs called from cyclic OBs — check the S7-1200 system manual, section on data types for the exact firmware list. On S7-1500, all variants are supported as of FW V2.0.8.5 Stop on NULL dereference (REF_TO)
Dereferencing a NULL REF_TO without the guardIF refX = NULL produces an area-length error. The CPU enters STOP and writes an OB121 / OB122 diagnostic entry. Always test for NULL before the ^ operator in production code. The diagnostic buffer entry for an unguarded NULL dereference is:
Event ID : 16#2522 (OB not found for programming error)
Event ID : 16#3582 (DB area length error)
Event ID : 16#39xx (Area length error during read/write)
Priority OB : OB 121 (Programming error)
Reaction : CPU goes to STOP unless OB 121 is loaded
8.6 VariantGet returning non-zero status
If the variant points to a BOOL but the destination is a REAL, VariantGet returns a non-zeroSTATUS word. The snippet in section 4.2 captures the status into wStatusA/B/C and sets bOk := FALSE for the diagnostic. The TIA Portal help on VariantGet enumerates the STATUS codes — the most common are:
| STATUS (hex) | Meaning |
|---|---|
| 16#0000 | No error |
| 16#0080 | Type mismatch between source VARIANT and destination |
| 16#8101 | Pointer is NULL |
| 16#8130 | Source is a literal (no addressable source — FB parameters are addressable, but literals are handled differently) |
| 16#8150 | Source is a STRING longer than the destination |
9. Diagnostics and Online Verification Procedure
9.1 Force table test
- Create a watch table containing the two instance DBs and the source data blocks.
- Set
DB_Process.scalingA = 12.5,DB_Process.scalingB = 3.0,DB_Process.scalingC = 7.0. - Confirm
iDB_SumVar.sumResult = 22.5(instance 1, A wired). - Confirm
iDB_SumVar_2.sumResult = 3.0 + 2*7.0 = 17.0(instance 2, A not wired). - Set
DB_Process.scalingA = 0.0and confirm the result is still 22.5 — proving that a wired zero is treated as "connected" and not as "absent".
9.2 Cross-check with the diagnostic buffer
- Online → Diagnostics → Diagnostic buffer.
- Confirm no Area length error events have been logged. If they have, the REF_TO guard in section 3.2 is missing or wrong.
9.3 LED and HMI confirmation
- Wire
iDB_SumVar.bAConnectedto an HMI tag named StatusA. - Use a WinCC Unified / Comfort Panel symbol to display the boolean state. Operators can confirm visually that the A operand is active or bypassed.
- This is also the right way to expose the diagnostic in an alarm log: HMI Alarm → trigger on
bAConnected = FALSEwith the text "Optional A operand bypassed".
10. Frequently Asked Questions
Does REF_TO work on S7-1200?
No. REF_TO is only available on S7-1500 CPUs from firmware V2.0 onward. On S7-1200 you must use VARIANT (with the limits described in section 4.4) or the sentinel default value method.
Can I detect an unconnected parameter with a normal REAL or INT input?
No. A normal typed input always carries a value (the editor's default literal, typically 0). Only REF_TO, VARIANT, or a sentinel default value can preserve the distinction between "wired" and "unconnected".
What is the exact value of an unconnected VARIANT?
The TypeCode is 16#0000 (VOID), and the IS_NULL operator returns TRUE. You can also use the operator = to compare the variant against the literal NULL.
What happens if I dereference a NULL REF_TO inside the FB?
The CPU enters STOP with a programming error (OB 121) and a diagnostic-buffer entry such as 16#3582. Always check the REF_TO for NULL before using the ^ dereference operator.
Is the sentinel method reliable for safety-relevant code?
Use it with care. The process must be guaranteed never to produce the sentinel value, and the sentinel must be declared explicitly at every call site. For SIL-rated FBs prefer REF_TO on S7-1500 or VARIANT with explicit OK-state diagnostics, because they expose "absent" as a structural property rather than as a value that could collide with a process reading.