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):
- A Direct Connection on the button mouse event, transferring a constant or expression value to the tag.
- A C-Script that calls
SetTagDouble(),SetTagWord(),SetTagBit(), orSetTagRaw()against the tag name. - 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.
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.
- Open the button's Properties > Events in the Graphics Designer.
- Select the trigger event (typically Mouse click or Press).
- Right-click the action list and choose Direct Connection.
- Set Source = Constant, value =
1(or the desired value/type cast). - 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.
- 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, useSetTagDoubleWait,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...returnsFALSEwith no error dialog.
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.
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:
- Open the C editor for the button event.
- Right-click and choose Internal functions > tag > set > wait.
- Pick SetTagDoubleWait (for a Double tag) or the matching
SetTagXxxWaitfor your data type. - Double-click the inserted call; fill in the Value column with the literal or expression to write.
- For Tag, either paste the literal name or pass
plink.szLinkNamewhen chaining offGetLink.
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
-
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
1and back to the process value when the PLC re-reads. -
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 fileWinCC_Service_..log - GSC Runtime: use ApDiag.exe to confirm the script compiled and triggered; look for the event name, return code, and any error status.
-
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. -
Multiple presses: exercise the button at a rate exceeding
dwCycleto 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
GetLinkscripts for faceplates, indirect tags, and configurable reusable screen objects. - Always wrap
GetLinkwith theLinkTypeguard 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
...Waitvariant and prefer changing the tag's PLC update cycle over rushing the script to match. - Document
szLinkNamein 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.