Siemens S7-300 UDT to FB: Resolving Invalid Input Assignment

David Krause13 min read
S7-300SiemensTutorial / 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 programming a Siemens SIMATIC S7-300 (or S7-400) with STEP 7 V5.x, a common failure mode is calling a Function Block (FB) and trying to pass a User-Defined Type (UDT) directly at the block's formal parameter. The editor reports that the input is invalid, even though the data type is visible in the FB's interface declaration. The root cause is structural: a UDT in STEP 7 is a type definition, not a variable instance. The FB's IN, OUT, or IN_OUT parameter expects an actual storage location that holds the structured data, not the type template itself.

The fix is to create a Data Block (DB) declared with the UDT, then connect that DB to the FB input. This article walks through the exact declaration sequence in STEP 7 V5.x for S7-300/S7-400, contrasts it with the TIA Portal workflow for S7-1200/S7-1500, and provides a verification matrix and a field-tested troubleshooting checklist.

Prerequisites

  • STEP 7 V5.x (V5.4, V5.5, or V5.6) for S7-300/S7-400, or STEP 7 Basic/Professional V14 SP1 or later for S7-1200/S7-1500. The TIA Portal UDT model differs from the classic model and is covered separately.
  • An existing FB (for example, FB1 "Valve_Control") with at least one formal parameter declared as a UDT (for example, UDT0).
  • A defined UDT (for example, UDT0) containing the data structure that the FB expects.
  • A SIMATIC S7-300 CPU (for example, CPU 315-2 PN/DP, 6ES7 315-2EH14-0AB0) or comparable station configured in the SIMATIC Manager project.

Root Cause of the Invalid Input Error

In STEP 7, the editor performs strict type checking when wiring actual operands to formal parameters at an FB call. The formal parameter MyStruct : UDT0 inside the FB declares which type the instance must be, but at the call site the user must supply a variable that occupies memory matching that type. A UDT is a template; the compiler cannot generate a stack of bytes from a type name alone. The error message is essentially: "UDT0 is a TYPE, not a data word or data block where I can read or write."

The correct operand class for passing structured data to an FB is a DB (Data Block) instance, a local static variable of a higher-level FB, or an IN/OUT area. Typing the bare UDT name at the call site, as in the original symptom, is the most common beginner mistake in STEP 7 V5.x projects.

Critical distinction: A UDT is a data type (analogous to a struct in C). A DB declared of that UDT is the instance (the storage object). The FB input expects the instance, never the type.

Solution: STEP 7 V5.x Workflow (S7-300/S7-400)

The corrected procedure replaces the UDT name at the FB call with the name of a DB that has been declared as an instance of that UDT.

Step 1 — Confirm the FB Interface Declaration

Open FB1 "Valve_Control" in the LAD/FBD/STL editor and verify that the interface has the expected UDT parameter. A correct declaration in the IN, OUT, or IN_OUT section looks like the following:

// FB1 "Valve_Control" — IN section
VAR_INPUT
  Enable        : BOOL;          // Enable command
  ValveData     : UDT0;          // Structured valve data
END_VAR

// FB1 "Valve_Control" — IN_OUT section (preferred for bidirectional)
VAR_IN_OUT
  ValveData     : UDT0;          // Same UDT, read/write
END_VAR

// FB1 "Valve_Control" — STAT section
VAR
  LocalValve    : UDT0;          // Internal static instance
END_VAR

For shared structured data, declare the UDT in the IN_OUT section so the FB can both read and modify the same memory the caller owns. For read-only data, INPUT with UDT0 is correct; the compiler copies the input into the FB's instance-DB on every call.

Step 2 — Create an Instance DB of the UDT

In the SIMATIC Manager project tree, expand S7 Program > Blocks, right-click, and select Insert New Object > Data Block. The DB wizard offers two relevant choices:

  1. Select "Data block of a user-defined type (UDT)".
  2. Assign the DB number (for example, DB10) and symbolic name (for example, Valve_DB).
  3. From the dropdown, pick the same UDT that the FB expects (for example, UDT0).
  4. Click OK. STEP 7 generates DB10 "Valve_DB" with the full UDT structure visible in the declaration view.

Alternatively, the same DB can be created by hand in the declaration table:

// DB10 "Valve_DB" — declaration of an instance of UDT0
DATA_BLOCK "Valve_DB"
  TITLE   = 'Instance of UDT0'
{ S7_m_c := 'true' }
VERSION : 0.1
  STRUCT
    // --- The body of UDT0 appears here automatically ---
    ValveID     : INT;       // 0.0
    CmdOpen     : BOOL;      // 2.0
    CmdClose    : BOOL;      // 2.1
    PosFeedback : REAL;      // 4.0
    Alarms      : WORD;      // 8.0
  END_STRUCT;
END_DATA_BLOCK

Step 3 — Wire the DB to the FB Input

Open the calling block (for example, OB1 or another FB). Insert a call to FB1 "Valve_Control" and provide the instance DB when prompted (for example, DB100 "Valve_Control_DB"). Then wire the formal parameter ValveData to the actual operand "Valve_DB" (symbolic) or DB10 (absolute):

// OB1 / STL — calling FB1 with the UDT instance DB
CALL  FB1  , DB100
  Enable     := I 0.0
  ValveData  := "Valve_DB"      // symbolic; resolves to DB10
  // ... other parameters ...
  RetVal     := Q 4.0

The editor now accepts the assignment because DB10 is a real instance whose memory layout matches the UDT type expected by the FB.

Step 4 — Optional: Use the UDT Instance as IN_OUT

If the FB was declared with the same UDT in the IN_OUT section (the recommended pattern for structured data that the block modifies), the wiring is identical but the FB will read and write directly into DB10's memory area. No internal copy is made:

// FB1 interface with IN_OUT
VAR_IN_OUT
  ValveData : UDT0;       // passed by reference
END_VAR

// OB1 call
CALL  FB1  , DB100
  Enable     := I 0.0
  ValveData  := "Valve_DB"   // direct DB pointer, no copy
  RetVal     := Q 4.0
IN_OUT vs. INPUT with a UDT: IN_OUT passes a pointer to the caller's memory (efficient, FB can modify caller data). INPUT copies the structured data into the FB's instance DB on every call (safer, isolates data, but expensive for large UDTs). For valve, motor, and PID objects with frequent updates, IN_OUT is the conventional choice in STEP 7 V5.x.

Solution: TIA Portal Workflow (S7-1200/S7-1500)

On S7-1200 and S7-1500, the model is similar but with one important difference: TIA Portal introduces PLC data types (UDT) that can be instantiated either as a Global Data Block or as a tag in a different DB. The same principle applies — the FB expects an instance, not the type name. According to the Siemens support entry "Basics of PLC data types (UDT)" — STEP 7 Basic V14.0, ID 109742266, PLC data types serve as the basis for data exchange between multiple blocks through block interfaces.

Step 1 — Define a PLC Data Type

In the TIA Portal project tree, expand PLC_x > PLC data types and double-click Add new. Name it UDT_Valve and declare its members (for example, ValveID : Int, CmdOpen : Bool, PosFeedback : Real). The PLC data type editor in TIA Portal is the modern equivalent of the UDT editor in STEP 7 V5.x.

Step 2 — Declare the FB Interface Using the PLC Data Type

Open the FB (for example, FB1 "Valve_Control") in the TIA Portal block editor. In the block interface area, add a new Input, Output, or InOut parameter, name it ValveData, and set its data type to UDT_Valve from the dropdown. The portal will internally mark it as a structured type.

Step 3 — Create an Instance and Wire It

Either create a Global DB that contains a tag of type UDT_Valve, or instantiate the same data type as a multi-instance in the calling FB's Static section. At the call site, drag the global DB tag (for example, "Valve_DB".ValveData) onto the FB input. TIA Portal will accept the assignment because the global DB tag is a real instance of UDT_Valve.

// TIA Portal STL excerpt — calling FB1 from OB1
CALL  "Valve_Control" , "iDB_ValveControl"
  Enable     := %I0.0
  ValveData  := "Valve_DB".Data   // instance of UDT_Valve
  RetVal     := %Q4.0

Multi-Instance Behavior in TIA Portal

On S7-1500 with optimized block access (the default), passing a structured tag does not always generate a copy. InOut parameters pass a pointer; large Input parameters may be passed by reference if the block was compiled with the "Pass parameters by reference" option. This is an important behavior change from S7-300 and is documented in the STEP 7 (TIA Portal) help under "Block interface > Parameter types".

Verification Procedure

After wiring the UDT instance DB to the FB, run the following checks to confirm the assignment is valid and the data flow is correct.

  1. Compile the program (Project > Compile All in STEP 7 V5.x, or right-click the device > Compile in TIA Portal). The compiler should report zero errors related to the FB call.
  2. Open the FB in the editor and confirm the parameter still shows the UDT type and the symbolic connection to the DB.
  3. Download the blocks to the CPU and place the CPU in RUN.
  4. Monitor the DB online using Monitor/Modify on DB10 "Valve_DB". Write known values to a UDT member, then trigger the FB call and confirm that the FB reads those values (for an INPUT or IN_OUT) or writes back to them (for IN_OUT).
  5. Check the instance DB of the FB (for example, DB100). For an INPUT parameter, confirm the FB's instance-DB shadow copy of the UDT matches the source DB at the start of the call. For an IN_OUT parameter, confirm there is no copy — the FB reads/writes directly into the caller's DB.

Parameter Comparison Table

Formal Parameter Accepts UDT? Data Flow Memory Use Recommended Use
INPUT (UDT) Yes Caller → FB (one-way, copied into instance-DB) One extra copy in instance-DB Read-only structured config
OUTPUT (UDT) Yes FB → Caller (copied out of instance-DB at end of call) One extra copy in instance-DB Status return objects
IN_OUT (UDT) Yes Caller ⇄ FB (passed by reference, no copy) No copy — pointer to caller DB Bidirectional control objects (valve, motor, PID)
STAT (UDT) Yes Internal static instance Lives in instance-DB Internal state retention across calls
TEMP (UDT) Yes (limited) Stack-local for call duration Stack frame of OB/FB Scratch structured data

Troubleshooting Matrix

Symptom Likely Cause Resolution
"Invalid data type" when typing UDT name at FB input Typing the type (UDT) instead of an instance (DB) Create a DB declared as that UDT and use the DB at the call site
"Instance DB not found" for the FB itself FB has no assigned instance-DB At the call site, provide an instance DB (for example, DB100)
FB reads stale data despite correct wiring INPUT parameter copies on call — caller's DB updated after the call started Change the formal parameter to IN_OUT for direct access
Compiler error: UDT0 not defined in this scope UDT not downloaded to CPU, or wrong UDT number used Confirm the UDT is in the S7 program > Blocks folder and download it
TIA Portal: structured tag accepted but data is wrong Optimized block access strips symbolic addresses; wrong slice accessed Disable optimization on the DB or use full symbolic name ("DB".Tag.Member)
SF LED on CPU after download Mismatch between offline UDT version and online UDT version Recompile all blocks and download the entire program; the UDT must match the version that the FBs expect

Best Practices

  • One UDT, one purpose. Define a separate UDT for each logical object (for example, UDT_Valve, UDT_Motor, UDT_PID). Reusing a single UDT across many FBs leads to versioning pain when the structure changes.
  • Prefer IN_OUT for large structures. A UDT with 200 bytes costs 200 bytes of copy time on every call if declared as INPUT. IN_OUT avoids the copy.
  • Use symbolic addressing. The symbolic name "Valve_DB" survives renumbering; the absolute name DB10 does not. Configure the symbol table in STEP 7 V5.x or use the PLC tags in TIA Portal.
  • Version the UDT. When the UDT is modified (a new member is added), recompile every block that uses it. STEP 7 V5.x will mark the dependent blocks with a timestamp mismatch — re-download the entire program to avoid online/offline drift.
  • Document UDT members. Use the COMMENT field for every member in the UDT declaration. This is the only documentation that travels with the offline/online project.
  • Avoid ANY pointers as a workaround. Some legacy STEP 7 code uses ANY pointers or POINTER types to pass structures. This is fragile and unnecessary when a proper UDT instance is available — use the UDT.

Reference: Where UDTs Live in the Project

In STEP 7 V5.x, UDTs are managed under S7 Program > Blocks > System Data only for system blocks; user-defined UDTs are created at the S7 Program > Blocks level alongside FBs, FCs, and DBs. The UDT editor opens a declaration table identical in layout to a DB, except the generated object is a TYPE, not an instance. Numbering for UDTs is independent of DB numbering — a project can have UDT0 through UDT65535 without conflict.

In TIA Portal, the equivalent is a PLC data type under PLC_x > PLC data types. The PLC data type is consumed by FB interfaces, DB tag declarations, and FC/FB local tags. As noted in Siemens Support entry 109742266, PLC data types are the S7-1200/S7-1500 mechanism for implementing structured data exchange between multiple blocks through their interfaces.

Field-Notes: Common Pitfalls and Corrections

Pitfall 1 — Typing the UDT number instead of the DB name. The classic mistake. The FB call expects a data operand (a DB, a local static, or a fully qualified structured tag). Supplying the UDT number itself is a category error.

Pitfall 2 — Declaring the UDT inside a function (FC). UDTs are project-global type definitions; they cannot be declared inside an FC's local stack. Declare the UDT at the program level, then instantiate it in a DB.

Pitfall 3 — Forgetting the instance-DB for the calling FB. An FB needs its own instance-DB to retain STAT variables across calls. The compiler will catch this and prompt for a DB number at the call site.

Pitfall 4 — Modifying the UDT but not re-downloading all dependents. Adding a new member to the UDT silently changes the byte layout of every DB and FB that uses it. Always recompile and download the entire S7 program after UDT changes; partial downloads can cause online/offline interface mismatches and an SF (system fault) LED.

Pitfall 5 — Using the same UDT name in different programs. A UDT is unique within a CPU's program. If two FBs in the same project need different versions of "Valve data," define two UDTs (for example, UDT_ValveV1 and UDT_ValveV2) instead of mutating one.

Compatibility Notes

The UDT model in STEP 7 V5.x (S7-300/S7-400) is fully supported and remains the canonical workflow for these legacy CPUs. On S7-1500 with TIA Portal V14 or later, UDTs are replaced by PLC data types at the same logical layer; existing STEP 7 V5.x UDTs can be migrated via the TIA Portal migration tool. S7-1200 supports PLC data types from firmware V4.0 onward; earlier firmware versions accept only elementary data types in FB interfaces.

Why does typing the UDT name at the FB input fail with "invalid data type"?

A UDT in STEP 7 is a type definition, not a variable. The FB input expects a memory instance (a DB, a static local, or a structured tag). Create a DB declared as that UDT (for example, DB10 of UDT0) and assign the DB to the FB input instead of the UDT name.

How do I create a Data Block from a UDT in STEP 7 V5.x?

Right-click S7 Program > Blocks, choose Insert New Object > Data Block, select "Data block of a user-defined type (UDT)", pick the UDT number from the dropdown, assign a DB number and symbolic name, and confirm. STEP 7 generates the DB with the full UDT structure.

Should the FB parameter be declared as INPUT or IN_OUT for a UDT?

Use IN_OUT for large UDTs that the FB modifies (valve, motor, PID objects) — it avoids copying the structure on every call. Use INPUT only for small, read-only configuration structures where isolating the caller's data is more important than performance.

What is the TIA Portal equivalent of a STEP 7 V5.x UDT?

It is the PLC data type, created under PLC_x > PLC data types. Per Siemens Support ID 109742266, PLC data types implement data exchange between blocks through block interfaces on S7-1200 and S7-1500.

What happens if I modify a UDT after other blocks are already using it?

The byte layout of every dependent DB and FB instance changes. Recompile the entire S7 program, download all blocks, and verify online against offline. Partial downloads after a UDT modification typically result in an SF LED and online/offline interface mismatch errors.

Back to blog