Optional INOUT Variant Parameters in TIA Portal V14 FB/FC Design

David Krause11 min read
SiemensTechnical ReferenceTIA Portal
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

Optional INOUT Variant Parameters in TIA Portal V14 FB/FC Design

Modular function block design in TIA Portal V14 SP1 Update 4 on S7-1200/S7-1500 controllers benefits from a VARIANT INOUT interface that can remain unassigned when the calling instance has nothing to pass. This reference covers the mechanism that makes a Variant INOUT truly optional: the "Hide if no parameter is assigned" interface attribute, the predefined actual parameter (Vordefinierter Aktualparameter), and the runtime validation helpers (IS_ARRAY, TypeOfElements, CountOfElements, MOVE_BLK_VARIANT) that make the resulting FB safe to drop into any number of machine variants from a single library element.

Platform scope. Variant INOUT, MOVE_BLK_VARIANT, and the optional-parameter attributes documented here are supported on S7-1200 (firmware ≥ V4.2 for the Variant data type) and S7-1500. The classic S7-300/S7-400 family does not provide the VARIANT pointer type and cannot use this pattern; for those controllers, use ANY-based FBs from the legacy STEP 7 palette instead.

1. Problem Statement and Architectural Goal

A typical machine library contains a single HMI faceplate that drives groups of identical actuators: pneumatic cylinders grouped into Array[1..15] of "cylinder", Array[1..31] of "cylinder", and so on. The natural way to expose those groups to a generic faceplate is to pass a pointer to the relevant slice and let the block index into it. VARIANT is the appropriate type for such a pointer: it carries type information at runtime, supports arrays of UDT (PLC data type), and can be set to NULL when the caller has no group to expose.

The catch is that the TIA Portal editor, by default, expects every INOUT declared on an FB to be wired to an actual parameter. The block instance turns red and the call cannot be compiled unless all nine group INOUTs are tied to something concrete, even if the machine in question only has two groups wired. The remedy is to make the INOUT optional using two complementary mechanisms:

  • Visibility attribute: "Hide if no parameter is assigned" (section 2.7.3 of the S7-1200/S7-1500 programming manual). When no actual parameter is wired, the editor omits the slot in the call.
  • Predefined actual parameter (Vordefinierter Aktualparameter): a default operand, typically a temp or static tag of the same Variant type, that the block falls back to when nothing is wired. The block then sees a non-NULL but harmless value at runtime.

2. Prerequisites

Item Requirement
Engineering tool SIMATIC STEP 7 Basic / Professional V14 SP1 Update 4 or later (V15, V16, V17, V18, V19 retain the same attributes)
Controller family S7-1200 (CPU firmware ≥ V4.2) or S7-1500 / ET 200SP CPU
Block language SCL (recommended for Variant handling) or LAD/FBD with the Variant operators
Library type Master-copy / type FB so changes propagate to all instances; stored in a project library or global library
HMI target Comfort Panel, WinCC Runtime Advanced, or WinCC Professional with faceplate/tag interface

Reference: SIMATIC S7-1200 Manual Collection — Function Block (FB).

3. Declaring the Optional Variant INOUT

Create a new FB (e.g., FB_Lib_CylinderDispatcher) and add an INOUT section. Declare one VARIANT per group. For a dispatcher that fans out to nine groups:

FUNCTION_BLOCK "FB_Lib_CylinderDispatcher"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      i_SelGroup   : INT;          // 0..8 -> group index from HMI
      i_SelIndex   : INT;          // element index within the chosen group
      i_Command    : BOOL;         // extend / retract pulse
   END_VAR

   VAR_OUTPUT
      o_Status     : WORD;
      o_Element    : "cylinder";   // mirror of the selected element
   END_VAR

   VAR_INOUT
      grp1  : VARIANT;             // Array[1..15] of "cylinder"
      grp2  : VARIANT;             // Array[1..31] of "cylinder"
      grp3  : VARIANT;
      grp4  : VARIANT;
      grp5  : VARIANT;
      grp6  : VARIANT;
      grp7  : VARIANT;
      grp8  : VARIANT;
      grp9  : VARIANT;
   END_VAR

   VAR
      stat_dummy : VARIANT;        // predefined actual parameter
   END_VAR

For each of the nine INOUTs, open the interface properties and set the following two attributes:

  1. Visibility"Hide if no parameter is assigned". Without an actual parameter, the call no longer flags the slot red and the compiler accepts the instance.
  2. Predefined actual parameterstat_dummy (a static Variant tag declared in the FB's VAR section). stat_dummy is initialised to NULL by the system, so the runtime sees a defined, non-garbage pointer when nothing is wired.
Why both attributes? "Hide if no parameter is assigned" controls the editor and the compilation. The predefined actual parameter controls the runtime: the block never sees a truly uninitialised pointer, so defensive code is not required to handle the "no caller wired anything" case — the only test needed is "is the Variant NULL?".

4. Validating the Variant at Runtime

Inside the FB, every group is checked in the same order before being dereferenced. The validation pattern follows the four-step rule documented in the S7-1200/S7-1500 programming manual, section on Variant pointers:

  1. myVariant <> NULL — is there anything passed?
  2. IS_ARRAY(myVariant) — is it an array?
  3. TypeOfElements(myVariant) = TypeOf(#cylinder) — does the element type match the UDT?
  4. CountOfElements(myVariant) — is the index in range?

A typical validation ladder for a single group:

// Pseudo-code in SCL
IF grp1 <> NULL AND IS_ARRAY(grp1)
   AND TypeOfElements(grp1) = TypeOf(#cylinder)
   AND i_SelIndex >= 1 AND i_SelIndex <= CountOfElements(grp1)
THEN
   // safe to dereference
   MOVE_BLK_VARIANT(SRC := grp1, SRC_INDEX := i_SelIndex - 1,
                    DEST := o_Element, DEST_INDEX := 0, COUNT := 1);
ELSE
   o_Status.%X0 := TRUE;   // group not configured / out of range
END_IF;

Wrap the same four-step test into a helper FC (e.g., FC_VariantCheck) and call it nine times with IF ... CASE i_SelGroup OF 1: FC_VariantCheck(grp1, ...); 2: FC_VariantCheck(grp2, ...); ... END_CASE; to keep the dispatcher body compact. MOVE_BLK_VARIANT is the only block operator permitted to dereference a Variant for both read and write; conventional MOVE_BLK requires compile-time types and will not accept a Variant source or destination.

5. Designing the Library FB so it Compiles Cleanly

Two structural rules must be followed or the block will fail to build:

  1. All FB parameters live in the interface. No implicit global tags, no direct I/O access (%I, %Q) inside the FB body. Only VAR_INPUT, VAR_OUTPUT, VAR_INOUT, VAR_TEMP, VAR STATIC, and VAR CONSTANT are permitted. This rule is enforced by the SIMATIC S7-1200/S7-1500 programming style guide and is also why the predefined actual parameter lives in VAR rather than as a global tag.
  2. Variants are validated before use. A Variant that has not been checked for NULL, array structure, and element type can throw an access error at runtime the first time the dispatcher is exercised. Treat the four-step test as mandatory.

Reference: Siemens Industry Online Support — Multiple usage of identical FB within a single FC for the underlying style guidance on FC/FB parameter design.

6. Wiring the Dispatcher in OB1 / a Cyclic FB

When the type FB is instantiated as DB_Lib_Dispatcher in the user program, the call looks as follows in a machine that has only two groups wired:

"DB_Lib_Dispatcher"(
   i_SelGroup := "HMI".SelGroup,
   i_SelIndex := "HMI".SelIndex,
   i_Command  := "HMI".CmdPulse,
   grp1 := "DB_MM1".MM_1,            // Array[1..15] of "cylinder"
   grp2 := "DB_MM2".MM_2             // Array[1..31] of "cylinder"
   // grp3..grp9 are hidden by the visibility attribute
);

At runtime, the dispatcher reads i_SelGroup, picks the corresponding INOUT, and either dereferences it with MOVE_BLK_VARIANT or returns the configured group mask if the INOUT is hidden (i.e., the caller left it unassigned). The HMI faceplate can therefore target a dispatcher that is shared by every machine variant in the project, and the unused group slots silently disappear from the call.

7. HMI Faceplate Integration

For a WinCC Comfort or WinCC Professional faceplate, expose the dispatcher's inputs and outputs as tag interface entries:

Faceplate property Plumbed to HMI tag
SelGroup i_SelGroup Internal — written by faceplate logic
SelIndex i_SelIndex Internal
Command i_Command Internal — pulse on button press
Status o_Status Visible on faceplate for diagnostics
Element o_Element Multiplexed view of the selected cylinder

Because the dispatcher multiplexes o_Element, the faceplate needs only one set of IO fields (extended, retracted, sensor feedback, fault). Each press of the faceplate simply re-routes i_SelGroup/i_SelIndex; the dispatcher swaps the underlying slice without further HMI work.

8. Verification and Commissioning

Walk through the following checklist on the target CPU before declaring the library block production-ready:

  1. Compile with all groups unassigned. The block instance must compile. If the call still shows red slots, the visibility attribute was not applied; right-click the INOUT in the interface and re-assert "Hide if no parameter is assigned".
  2. Download and go online. Force i_SelGroup := 0 in the watch table; o_Status should report the "no group selected" mask. Force i_SelGroup := 3 while grp3 is unassigned; the dispatcher must return the "group not configured" flag and not throw an access error.
  3. Force a valid group/index combination. Verify the o_Element mirror matches the underlying array element. Toggle i_Command and confirm the source array was written back through MOVE_BLK_VARIANT.
  4. Boundary tests. Force i_SelIndex := 0 and i_SelIndex := CountOfElements(grp) + 1; the dispatcher must reject both and not corrupt adjacent memory.
  5. Cross-version regression. If the library is shared with projects on older TIA Portal versions (V13 SP1 lacks the "Hide if no parameter is assigned" attribute on INOUTs), the block must be forked, or the calling code must be prepared to wire placeholder Variants manually.

9. Troubleshooting Matrix

Symptom Likely cause Fix
Call shows red slots even though the group is not used Visibility attribute not set on the INOUT Open the INOUT properties, enable "Hide if no parameter is assigned"
Compiler error "INOUT must be assigned" Project is on TIA V13 SP1 or earlier; Variant INOUT cannot be hidden Upgrade project to V14 SP1 Update 4 or later, or wire a dummy Variant
Runtime access violation when group is unassigned Predefined actual parameter was left empty Define a static Variant (stat_dummy : VARIANT) and assign it as the predefined actual parameter for every optional INOUT
MOVE_BLK_VARIANT does not compile SCL block is optimised and the source Variant has not been validated Run the four-step validation (<>NULL, IS_ARRAY, TypeOfElements, CountOfElements) first
Always returns "group not configured" even with a wired group Element type does not match (UDT version mismatch between caller and library) Re-import the UDT "cylinder" into the master library and update all instances
Library block compiles in V15 but call shows red in V14 V14 SP1 Update 4 requires the same attribute; SP1 (without update 4) may not surface it in the editor Apply SP1 Update 4 or later; confirm the attribute persisted via right-click → Properties → Interfaces

10. Edge Cases and Field-Proven Caveats

Multi-instance FBs. If the dispatcher is itself a multi-instance inside another FB, the predefined actual parameter must live in the VAR section of the dispatcher, not in the parent's static data; TIA Portal disallows referencing the parent's VAR as a default operand of an INOUT.

Optimised block access. The S7_Optimized_Access := 'TRUE' attribute is required for Variants in S7-1500. S7-1200 firmware < V4.2 does not support optimised FBs with Variants — upgrade the CPU firmware before adopting this pattern.

Know-how protection. When the library block is know-how protected, the predefined actual parameter and visibility attributes are still visible in the interface — the protection only hides the body. Library consumers therefore see exactly the slots they have to wire.

Pointer lifetime. A Variant INOUT holds a reference to the actual operand; the referenced DB or memory area must not be deleted while the dispatcher is in use. If the HMI loads/unloads data blocks at runtime, gate the dispatcher's enable input on a "DB valid" bit to avoid dangling pointer faults.

Typeof for nested structs. TypeOfElements returns the element type of the outermost array. If a caller wraps the array in a struct (e.g., Array[1..15] of "cylinder" is a member of machineData), the Variant must point to the array member, not the parent struct, otherwise the type check will fail.

11. Standards and Documentation References

Variants, the four-step validation pattern, and MOVE_BLK_VARIANT are documented in the SIMATIC S7-1200/S7-1500 programming and operating manual collection. The visibility attribute "Hide if no parameter is assigned" is described in section 2.7.3 of the same collection, alongside the predefined actual parameter (called Vordefinierter Aktualparameter in the German UI). The architectural rule that an FB may only use interface-declared tags is enforced by the SIMATIC S7-1200/S7-1500 style guide for modular block design and is referenced in the Siemens Industry Online Support topic on multiple usage of identical FBs within a single FC.

Relevant official documentation:

FAQ

Can an INOUT Variant in TIA Portal V14 be left unassigned?

Yes. Open the INOUT properties in the FB interface, enable the visibility attribute "Hide if no parameter is assigned", and set a predefined actual parameter (e.g., a static VARIANT tag). The call then compiles without a red slot when nothing is wired, and the runtime sees a non-NULL but inert pointer.

Which CPU families support the optional Variant INOUT pattern?

S7-1200 with firmware V4.2 or later and the entire S7-1500 / ET 200SP CPU family. S7-300/S7-400 do not implement the VARIANT pointer type and cannot use this mechanism; legacy systems should fall back to ANY-based FBs in STEP 7 V5.x.

How do I safely dereference a Variant INOUT inside the FB?

Run the four-step validation — variant <> NULL, IS_ARRAY(variant), TypeOfElements(variant) = TypeOf(element), and a bounds check against CountOfElements(variant) — and then use MOVE_BLK_VARIANT to read or write. Standard MOVE_BLK requires a compile-time type and will not accept a Variant source or destination.

Why is the call still red after enabling "Hide if no parameter is assigned"?

Either the project is on a TIA Portal version older than V14 SP1 Update 4 (the attribute is editor-side and depends on the build), or the predefined actual parameter was not assigned. Both are required: visibility controls the editor; the predefined parameter guarantees a defined runtime value.

Can the dispatcher be reused across different machine variants from a single library FB?

Yes. Store the FB as a master copy in the project library or a global library, instance it once per machine, and wire only the groups that exist on that machine. Unused INOUTs hide themselves and the predefined actual parameter keeps the runtime safe, so a single library element scales from a two-group machine to a nine-group machine without code duplication.

Back to blog