WinCC C-Script: Writing Tags via Button Press to I/O Fields

David Krause11 min read
HMI ProgrammingSiemensTutorial / 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

Writing a value from a button event into the tag configured on a WinCC I/O field is one of the most common HMI scripting tasks. Engineers frequently reach for SetOutputValueDouble() as a first attempt because the function name suggests "set the output of the I/O field." When the runtime is exercised the on-screen value of the I/O field does change momentarily, but the underlying process tag does not update, and the value disappears or reverts as soon as the runtime refresh cycle re-reads the tag. This behavior is not a bug; it is the documented semantics of SetOutputValueDouble in the WinCC C API: the function modifies the local display buffer of the screen object, not the connected tag.

Three reliable approaches are available in WinCC V7 (and equivalent TIA Portal / WinCC Professional screens):

  1. A Direct Connection on the button mouse event, transferring a constant or expression value to the tag.
  2. A C-Script that calls SetTagDouble(), SetTagWord(), SetTagBit(), or SetTagRaw() against the tag name.
  3. A C-Script that calls GetLink() first to retrieve the tag name configured on the I/O field, then writes that tag. This is the only variant that survives dynamic tag changes or faceplate use, where the I/O field's link target can be the faceplate tag prefix (e.g., .U) and the real tag is resolved at runtime.

This reference covers the C-Script mechanism with GetLink, the more compact SetTag... shortcut, and the script-free Direct Connection, plus faceplate handling and the LINKINFO structure that protects the script against non-tag links (e.g., a dynamic dialog).

Prerequisites

  • WinCC V7.x runtime or TIA Portal WinCC Professional / WinCC Unified runtime, with the project in RT (runtime) active state for verification.
  • An HMI tag already created (e.g., ProcessValue) of the data type matching the I/O field configuration (Double, Word, Bit).
  • An I/O field screen object configured with Properties > Output/Input > Output Value linked to the target tag via drag-and-drop, tag dialog, or dynamic dialog.
  • A button screen object whose event is wired to the script (typically Mouse > Press or Click).
  • For faceplate scenarios: a faceplate instance opened with a configured tag prefix.
Note: The behavior described here applies to WinCC V7 graphics runtime and WinCC Professional in TIA Portal V16 onward. In WinCC Unified, the scripting model uses JavaScript instead of C and the link-resolution helpers have different names; see IO field (RT Unified) - WinCC Unified for current Unified syntax.

Why SetOutputValueDouble Fails to Update the Tag

The Set... family of functions in the WinCC API addresses the screen object's local output buffer, not the tag acquisition layer. When a tag is configured on the I/O field's Output Value dynamic, the runtime refresh logic re-reads the tag at every update cycle and overwrites whatever SetOutputValueDouble placed into the buffer. The visible flash of "1" that appears in the I/O field is the local write itself; the next acquisition cycle restores the process value. To permanently drive the connected tag, you must address the tag directly via the SetTag... group of standard functions or via GetLink + SetTag....

Solution 1: Direct Connection (No Script)

The simplest and most robust path does not require any C code. Direct Connection evaluates at the moment of the button event and writes the configured source to the configured target atomically.

  1. Open the button's Properties > Events in the Graphics Designer.
  2. Select the trigger event (typically Mouse click or Press).
  3. Right-click the action list and choose Direct Connection.
  4. Set Source = Constant, value = 1 (or the desired value/type cast).
  5. Set Target = the tag used by the I/O field, or drag the I/O field object over the target area and pick Output Value.
  6. Compile and download; the runtime will write the constant to the tag on every press.

Direct Connection also tolerates expression sources (e.g., ProcessValue + 1) and supports all elementary tag types. When the target is the I/O field's Output Value dynamic, the runtime uses the configured link so the same tag is updated regardless of whether the I/O field points at a tag, an indirect tag, or a faceplate tag prefix.

Solution 2: C-Script with SetTagDouble / SetTagBit

When the tag name is fixed and known at compile time, a single SetTag... call is the shortest path. In the C editor, use the Internal functions > tag > set node and pick the function matching the tag's elementary type. If the tag is a real (Double), select SetTagDouble; if a Boolean bit, select SetTagBit; if a Word, SetTagWord. Each returns an BOOL success flag.

// Basic tag write on button press (fixed tag name)
if (GetTagDouble("ProcessValue") != 1.0)
{
    SetTagDouble("ProcessValue", 1.0);
}

Important behaviors:

  • Write semantics: SetTag... issues an asynchronous write by default. For time-critical sequences where the next action depends on the new value being read back, use SetTagDoubleWait, SetTagDoubleStateWait, or the synchronous variant described in the Inductive Automation training material covering WinCC V7 tag I/O.
  • Data type match: mismatched types trigger an implicit conversion by the runtime. Pass the value in the function's native type to avoid silent truncation or sign extension.
  • Permissions: tag writes are governed by the user administration in the runtime. If the active user lacks write authority for the configured tag, SetTag... returns FALSE with no error dialog.
Note: For tag I/O over standard WinCC channels the asynchronous SetTagDouble typically returns before the PLC has acknowledged; for cyclic verification do not rely on its return value alone. The Reading and Writing Tags training video illustrates blocking and synchronous write semantics.

Solution 3: C-Script with GetLink (Dynamic Tag Handling)

When the tag configured on the I/O field may change at runtime — for example, because the I/O field points at the faceplate tag prefix .U, an indirect tag structure, or any dynamic whose target is a tag rather than a dialog or script — use GetLink() to extract the actual tag name. GetLink walks the configured dynamic of a screen object property and returns its type plus the resolved tag name in a LINKINFO structure. The pattern below writes a value to whichever tag the I/O field is bound to, regardless of where that tag name originated.

#include "trigger.h"
LINKINFO plink;

// Read the link configured on IOField1 -> OutputValue
if (GetLink(lpszPictureName, "IOField1", "OutputValue", &plink))
{
    // Only proceed if the dynamic is a direct or indirect tag link
    if ((plink.LinkType > BUBRT_LT_VARIABLE_INDIRECT) ||
        (plink.LinkType < BUBRT_LT_VARIABLE_DIRECT))
    {
        return;
    }
    else
    {
        // Write 1 to the linked tag using the matching elementary function
        SetTagBit(plink.szLinkName, 1);
    }
}

Why the LinkType Guard

The Output Value dynamic of an I/O field can be wired several ways: a direct tag, an indirect tag structure, a dynamic dialog (limit/value table), an expression, or another script-driven dynamic. GetLink returns TRUE for any resolved dynamic; passing its szLinkName to SetTag... when the underlying dynamic is a dynamic dialog or an expression is meaningless and will return a runtime error or fail silently. The two-line guard restricts the write to the legitimate tag-link range:

Constant Value (typical) Meaning
BUBRT_LT_VARIABLE_DIRECT Defined in apdef.h Output Value is a direct tag reference
BUBRT_LT_VARIABLE_INDIRECT Greater than direct Output Value is an indirect tag (multiplexed)
Other LinkType values Below direct or above indirect Dynamic dialog, expression, script — skip

These constants are declared in the WinCC C header apdef.h, included transitively when trigger.h is pulled into the script. Refer to the WinCC V7 documentation set, Function Reference > Graphics > Link Functions, for the current enumerated values for your installed service pack; the symbolic names are stable across V7.x.

Faceplate Tag Handling

Faceplates invert the script problem. The I/O field inside the faceplate does not know which real tag it is bound to; the tag prefix .U (or any user-configured faceplate interface tag) is replaced by the instance name prefix at runtime. GetLink returns the resolved name including the instance prefix, which is exactly what SetTag... needs.

// Inside a faceplate button's Click event
LINKINFO plink;
if (GetLink(lpszPictureName, "IOField1", "OutputValue", &plink)
    && plink.LinkType == BUBRT_LT_VARIABLE_DIRECT)
{
    SetTagDouble(plink.szLinkName, 1.0);
}

If the I/O field is configured with the faceplate interface tag .U and the faceplate is instantiated as FP_Motor1, then GetLink returns FP_Motor1.U. Calling SetTagDouble("FP_Motor1.U", 1.0) writes the instance tag correctly. The .U prefix on its own, used inside the faceplate source script, would resolve against the faceplate's own prefix and fail.

Note: If the I/O field's Output Value is bound to an indirect tag structure (a Word tag whose bit pattern selects one of N tags), GetLink returns the index structure reference, not the resolved tag. Writing to that index structure changes which tag is read by the I/O field; you still need a separate indirect write to update the actual process tag the index points at. In that scenario, prefer Direct Connection over a C-Script.

LINKINFO Structure Reference

The LINKINFO structure is the return type of GetLink and GetLinkEx. Knowledge of its fields is required to handle every dynamic variant robustly.

Field Type Description
szLinkName char[260] Resolved name of the tag, expression text, or dialog name depending on LinkType
LinkType DWORD Enumerated dynamic type; compare against BUBRT_LT_VARIABLE_DIRECT / _INDIRECT
dwCycle DWORD Update cycle used by the dynamic, in ms
bLinkExist BOOL Whether a dynamic is configured on the queried property

Use szLinkName directly with any SetTag... variant. Use dwCycle as a hint for not re-writing faster than the configured acquisition interval if the same script is bound to multiple events.

Internal Functions Editor Walkthrough

When assembling the script through the Internal Functions dialog rather than hand-typing, the canonical path in WinCC V7 is:

  1. Open the C editor for the button event.
  2. Right-click and choose Internal functions > tag > set > wait.
  3. Pick SetTagDoubleWait (for a Double tag) or the matching SetTagXxxWait for your data type.
  4. Double-click the inserted call; fill in the Value column with the literal or expression to write.
  5. For Tag, either paste the literal name or pass plink.szLinkName when chaining off GetLink.

The wait variants block the script until the runtime confirms the write, useful for sequencing without polling. The plain Set variants return immediately and are faster on bulk operations.

Diagnostic and Verification Steps

  1. Tag Monitor: open the tag's online display in the Graphics Designer or via WinCC Explorer > Tag Management; click the button and confirm the value transitions to 1 and back to the process value when the PLC re-reads.
  2. Trace: add printf("LinkType=%u Name=%s\n", plink.LinkType, plink.szLinkName); at the end of the script to log resolved link details to the WinCC diagnostic file WinCC_Service_.log.
  3. GSC Runtime: use ApDiag.exe to confirm the script compiled and triggered; look for the event name, return code, and any error status.
  4. PLC ACK: if the tag is a PLC tag, verify the PLC-side operand reflects the new value using the PLC's online monitor — the asynchronous SetTag... path can momentarily desynchronize HMI and PLC under heavy runtime load.
  5. Multiple presses: exercise the button at a rate exceeding dwCycle to validate that no write is dropped.

Troubleshooting Matrix

Symptom Likely Cause Fix
I/O field flashes 1 and reverts SetOutputValueDouble used instead of SetTag... Replace with SetTagDouble or Direct Connection
Script returns FALSE from GetLink Object or property name misspelled Use the exact screen object name and "OutputValue" property string
GetLink succeeds but SetTag returns FALSE User lacks write authority on this tag Adjust user administration or run RT as administrator
Tag does not exist error after getlink Link dynamic is dialog/expression, not tag Add LinkType guard around SetTag call
Faceplate tag unchanged Script passes literal ".U" instead of resolved name Pass plink.szLinkName to SetTagDouble
Write succeeds but PLC sees old value Asynchronous write not yet flushed Use SetTagXxxWait or read-back polling
Debug output: LinkType=0 always Output Value dynamic is unconfigured Open I/O field properties, wire a tag or dynamic

Best Practices

  • Default to Direct Connection for fixed-value writes; it is the lowest-overhead option and avoids script compile cycles.
  • Reserve GetLink scripts for faceplates, indirect tags, and configurable reusable screen objects.
  • Always wrap GetLink with the LinkType guard so the script degrades to a no-op instead of an error when the dynamic changes shape in the future.
  • Pick the smallest SetTag... variant whose data type matches the tag — write-only-what-you-intend to avoid the silent type-conversion path.
  • For high-frequency sequences, use the ...Wait variant and prefer changing the tag's PLC update cycle over rushing the script to match.
  • Document szLinkName in the script header when the I/O field name or property is configurable to keep maintenance tractable across screen revisions.

Why does SetOutputValueDouble change the I/O field display but not the linked tag?

SetOutputValueDouble writes to the screen object's local output buffer, which the runtime refresh overwrites on the next acquisition cycle. Use SetTagDouble or Direct Connection to address the connected tag directly.

Which SetTag function should I use for an HMI real tag?

Use SetTagDouble for Double tags, SetTagWord for 16-bit integer tags, and SetTagBit for Boolean tags. For blocking writes that wait for runtime confirmation, use SetTagDoubleWait, SetTagWordWait, or SetTagBitWait.

How do I write to the tag associated with an I/O field inside a faceplate?

Call GetLink on the I/O field's OutputValue property to retrieve the resolved tag name (which includes the faceplate instance prefix). Pass plink.szLinkName to SetTagDouble. Do not pass ".U" directly — that resolves in the faceplate namespace, not the instance namespace.

What does the LinkType guard check for in the GetLink script?

It ensures the I/O field's OutputValue dynamic is a direct or indirect tag link, not a dynamic dialog, expression, or script-driven dynamic. Calling SetTag on a non-tag link returns FALSE at best and an access violation at worst.

When should I prefer Direct Connection over a C-Script?

Use Direct Connection when the source is a constant or a simple expression and the target tag is fixed or follows the I/O field link. It compiles to runtime data without a script cycle and survives re-import of graphics without script recompilation.

Back to blog