Overview
SIMATIC WinCC screens frequently need a single object whose color encodes a value carried by several PLC bits. A typical case is a process indicator where Bit 0 = running (green), Bit 1 = warning (yellow), Bit 2 = fault (red), and Bit 3 = maintenance (brown). The classic workaround — stacking three or four objects on top of each other and toggling their Display property — works, but inflates the picture with duplicates and forces the operator to maintain overlapping visibility logic.
The standard WinCC scripting toolbox — VBScript, ANSI-C, Dynamic Dialog, and the Status Display — provides four first-class techniques for collapsing a multi-bit input into a single object whose BackColor and ForeColor reflect the current state. This reference documents each technique end-to-end, defines the bit-to-color contract, gives runnable code for WinCC Professional (TIA Portal V16/V17) and WinCC V7.5 SP2, lists the supported color constants, and ends with a verification matrix plus a troubleshooting table that maps common runtime errors to root causes. For background on how WinCC encodes the 24-bit color space, see Color depth on Wikipedia.
Prerequisites
- SIMATIC WinCC V16 or V17 (WinCC Professional in TIA Portal) or SIMATIC WinCC V7.5 SP2. The VBScript
HMIRuntimeobject is identical across both code paths; the C-scriptapdefap.hdeclarations are valid on the WinCC V7.x side, while WinCC Professional uses VBScript as the only native scripting language. - An HMI tag of data type
DWORD,WORD, orBOOLarray that exposes the state bits from the controller. The HMI connection can be an S7-1200/S7-1500 over PROFINET, or a Softnet connection for a simulated PLC. - Engineering rights to edit the HMI screens in the Graphics Designer and to enable the Global Script Runtime under "Runtime settings > Scripts".
- Knowledge of the tag name and the bit ordering convention in the PLC: WinCC reads multi-bit tags the same way the PLC lays them out, with Bit 0 being the least significant bit.
- Reference: Siemens Industry Online Support — TIA Portal Help, "Working with scripts" chapter, for the official list of scripting object members.
- Reference: SIMATIC HMI product page for current runtime version and compatibility matrices.
0xFF0000 is therefore blue, not red. C-script color constants such as CO_RED abstract this so you do not have to remember the byte order at runtime.Bit-to-Color Mapping Architecture
All four techniques share a single mapping contract. Document the contract once in the project header so every script, dynamic dialog, and bitmap set references the same bit positions.
| Bit | Mask (hex) | State | BackColor | ForeColor | Priority |
|---|---|---|---|---|---|
| 0 | 0x01 | Running | 0x00FF00 (green) | 0x000000 (black) | Lowest |
| 1 | 0x02 | Standby | 0x00FFFF (yellow) | 0x000000 (black) | Low |
| 2 | 0x04 | Warning | 0x0000FF (red) | 0x00FFFF (yellow) | Medium |
| 3 | 0x08 | Fault | 0x0000FF (red) | 0xFFFFFF (white) | High |
| 4 | 0x10 | Maintenance | 0x004040 (brown) | 0x00FFFF (yellow) | Highest |
Priority matters. When more than one bit is set, the highest-priority state wins. The code examples below always test the highest-priority bit first and use Exit Sub / return to short-circuit the rest. This avoids the "blinking green over a red fault" symptom that engineers encounter when they write naive left-to-right assignments.
Method 1: C Script with GetTagBit (WinCC V7.x)
The C-script variant is the most compact and runs on every screen object. The header apdefap.h exposes GetTagBit, GetTagDWord, and the color constants CO_RED, CO_GREEN, CO_BLUE, CO_BLACK, CO_WHITE, CO_YELLOW, and so on. Wire the script to the BackColor property of the object; return value is interpreted as a BGR long.
#include "apdefap.h"
long _main(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
if (GetTagBit("Tag_Fault")) return CO_RED; // 0x0000FF
if (GetTagBit("Tag_Maintenance")) return CO_DK_GRAY; // 0x004040
if (GetTagBit("Tag_Warning")) return CO_YELLOW; // 0x00FFFF
if (GetTagBit("Tag_Standby")) return CO_CYAN; // 0xFFFF00
if (GetTagBit("Tag_Running")) return CO_GREEN; // 0x00FF00
return CO_BLACK;
}
Per-object wiring: Right-click the object → Properties → BackColor → Dynamic → C Action. The trigger field accepts either a tag name (cycles on every change) or a fixed cycle (default 2 s). For fastest response, set the trigger to the highest-priority tag with the property "On change". A separate trigger on ForeColor is normally unnecessary: the dynamic dialog trick below handles both colors from one trigger.
Validation in the compiler: Open the script in the C-Editor and press F7 ("Check Syntax"). The V7.x C-Editor flags missing parentheses, undeclared variables, and bad tag names. The script will not be downloaded to the RT until it compiles.
Method 2: VBScript with HMIRuntime (Cross-Version)
VBScript is the only scripting language that works identically in WinCC V7.x and WinCC Professional (TIA Portal V16/V17). Use it when the project will be migrated to TIA Portal later. The script reads a single DWORD alarm tag, masks the bits with the And operator, and assigns RGB triplet literals to the BackColor / ForeColor of the object passed in by the runtime.
Sub IO_Color_ByAlarmBits(Byval obj)
Dim tagName, val
tagName = obj.ObjectName & ".ALARM"
val = HMIRuntime.Tags(tagName).Read
If (val And 16) <> 0 Then ' Bit 4 - maintenance (highest priority)
obj.BackColor = RGB(64, 32, 0) ' brown
obj.ForeColor = vbYellow
Exit Sub
ElseIf (val And 8) <> 0 Then ' Bit 3 - fault
obj.BackColor = vbRed
obj.ForeColor = vbWhite
Exit Sub
ElseIf (val And 4) <> 0 Then ' Bit 2 - warning
obj.BackColor = vbYellow
obj.ForeColor = vbBlack
Exit Sub
ElseIf (val And 2) <> 0 Then ' Bit 1 - standby
obj.BackColor = vbCyan
obj.ForeColor = vbBlack
Exit Sub
ElseIf (val And 1) <> 0 Then ' Bit 0 - running
obj.BackColor = vbGreen
obj.ForeColor = vbBlack
Exit Sub
Else
obj.BackColor = vbBlack
obj.ForeColor = vbYellow
End If
End Sub
Wire the procedure through an event: object properties → Event → Click / Mouse Over, or trigger it from a tag-change event in the Scheduler. The script uses obj.ObjectName & ".ALARM" so each instance of the object reads its own alarm tag by convention — a pattern that scales to hundreds of indicators without parameterization.
For a panel-wide periodic update, register the Sub in the Global Script → Actions → Project Modules and schedule it on a 1-second cycle. In WinCC Professional the equivalent entry point is the "Scheduled tasks" folder of the HMI device.
HMIRuntime.Tags(tagName).Read in a single read per cycle, not per property. Two scripts on the same tag will each fire their own read; consolidate the mask test into one script and assign both BackColor and ForeColor inside the same call.Method 3: Dynamic Dialog with Bitwise Formula
Dynamic Dialog avoids custom code altogether by treating the bit pattern as a numeric value. Encode the multi-bit state as a sum of place values: Status = Bit0·1 + Bit1·10 + Bit2·100 + Bit3·1000. The status integer then maps directly to a discrete look-up table inside Dynamic Dialog.
| Bit0 | Bit1 | Bit2 | Bit3 | Status | BackColor (BGR hex) | Field Text |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0x000000 | Off |
| 1 | 0 | 0 | 0 | 1 | 0x00FF00 | Run |
| 0 | 1 | 0 | 0 | 10 | 0x00FFFF | Standby |
| 0 | 0 | 1 | 0 | 100 | 0x0000FF | Warn |
| 0 | 0 | 0 | 1 | 1000 | 0xFF0000 | Fault |
Open the object → Properties → BackColor → Dynamic Dialog. In the "Formula" field, type:
(Bit0_Tag) + (Bit1_Tag)*10 + (Bit2_Tag)*100 + (Bit3_Tag)*1000
Then define value ranges: 0 / 0..0 → black, 1 / 1..1 → green, 10 / 10..10 → yellow, 100 / 100..100 → red, 1000 / 1000..1000 → blue. Use the Range column rather than the Value column so the dialog accepts a 1-bit-wide window for each state.
Decimal place values (1, 10, 100, 1000) are chosen because they never collide when summed. Hex place values (0x1, 0x10, 0x100, 0x1000) are equivalent and easier to read alongside mask hex. Do not use binary place values (1, 2, 4, 8) for this technique — the place-value trick relies on unique digit positions, while the C/VB scripts rely on bitwise AND, which is what binary place values enable.
Method 4: Status Display with Bitmap Sets
The Status Display is the only built-in object that can change both color and graphics from a single tag value. Configure a Status Display, open Properties → Bitmaps, and add one entry per state. Set the "Status value" to the same integer used in the Dynamic Dialog approach. The runtime swaps the bitmap on every value change without a custom trigger.
- Drag a Status Display from the toolbox onto the screen.
- Right-click → Configure → Bitmaps. Add a row for each state and bind the desired PNG/BMP to it.
- Set "Process" to
Status, the integer tag computed from the bit pattern. - For each state, set the "Background color" of the bitmap row to the desired BGR value; WinCC tints the bitmap at runtime.
- Set the trigger cycle to "On change" to minimize RT load.
The Status Display is the most compact solution for control panels where the icon itself (a pump, a valve, a fan) carries the state. It does require one bitmap per state, but the runtime memory cost is small because the images are loaded once and swapped by handle.
Method 5: Compound (Custom) Object Layering
If the rest of the picture is built from compound objects, the cleanest way to add multi-bit coloring is to expose the Display property of the colored rectangle through the compound object's interface. Draw three identical rectangles, layer them on top of one another, mark the upper two as hidden by default, select all three, and choose "Create compound object" from the Graphics Designer menu.
- Open the compound object in the configuration dialog and expose the
Displayproperty of each internal rectangle to the public interface. - Bind each exposed property to its own tag trigger (Tag_Fault → red, Tag_Warning → yellow, Tag_Running → green).
- Save the compound object to the library; instances inherit the interface and can be re-bound to different tags per screen.
Compound objects centralize the look-and-feel in one place — change the fill color once, and every instance updates — but they cost an extra picture-level object and force every screen to use the same triggering tags. For panel-wide consistency, use the library; for screen-specific overrides, prefer Method 1 (C script) or Method 2 (VBScript).
WinCC Color Constant Reference
Use the named constants whenever possible. They abstract the BGR byte order and survive theme or version changes. The table below maps the most common constants in WinCC V7.x and WinCC Professional.
| Constant | BGR Long | RGB Equivalent | Typical Use |
|---|---|---|---|
| CO_BLACK | 0x000000 | (0, 0, 0) | Default background |
| CO_WHITE | 0xFFFFFF | (255, 255, 255) | Default foreground |
| CO_RED | 0x0000FF | (255, 0, 0) | Fault |
| CO_GREEN | 0x00FF00 | (0, 255, 0) | Running |
| CO_BLUE | 0xFF0000 | (0, 0, 255) | Information |
| CO_YELLOW | 0x00FFFF | (255, 255, 0) | Warning |
| CO_CYAN | 0xFFFF00 | (0, 255, 255) | Standby |
| CO_MAGENTA | 0xFF00FF | (255, 0, 255) | Manual / Override |
| CO_DK_GRAY | 0x404040 | (64, 64, 64) | Inactive / Disabled |
| CO_LT_GRAY | 0xC0C0C0 | (192, 192, 192) | Disabled text |
| CO_BROWN | 0x004040 | (64, 32, 0) | Maintenance |
In VBScript, the same colors are exposed as vbRed, vbGreen, vbBlue, vbYellow, vbBlack, vbWhite, vbCyan, vbMagenta. In WinCC Professional, RGB() returns the same BGR long the runtime expects, so RGB(255, 0, 0) paints red directly.
Verification and Commissioning
- Compile check. Open the C-Editor and press F7 for each script. VBScript has no separate compile step, but the WinCC script debugger (Alt+F11) flags syntax errors when you load the picture in the runtime simulator.
- Tag simulation. In WinCC V7.x, open "Tag Simulation" from the Tools menu and toggle each bit. In WinCC Professional, use the HMI Simulation table to write directly to the bit tags. Confirm the BackColor of the bound object changes within the configured trigger cycle.
-
Priority check. Set all four bits simultaneously. Verify the highest-priority color wins. If two colors flicker, the script is missing an
Exit Sub/returnshort-circuit. - Performance check. Open "Performance" in the RT diagnostic dialog. A typical 50-indicator screen with one C script per indicator should consume less than 5% CPU on a Comfort Panel. Above 15% CPU, consolidate the scripts into a single project-module Sub triggered on a tag-change event.
-
Color-blind check. Toggle the screen through every state and confirm the ForeColor / BackColor combination has at least a 4:1 luminance contrast for operators with deuteranopia. Yellow on green fails this test; switch the standby ForeColor to
vbBlack. - Load test. With all indicators cycling once per second, run the RT for 30 minutes and confirm no memory growth in the Windows Task Manager. A leaking script shows up as 1–2 MB of additional memory every cycle.
Troubleshooting Matrix
| Symptom | Likely Cause | Fix |
|---|---|---|
| Object stays black even when Bit 0 is set | Script is bound to ForeColor instead of BackColor; or the return value is interpreted as RGB instead of BGR |
Re-bind the trigger to BackColor and use CO_RED or a BGR literal, not an RGB constant |
| Two colors flicker on screen | Two independent scripts both assign to the same property, last-write-wins race | Consolidate into a single script that sets BackColor and ForeColor together |
| Color never changes on a Comfort Panel | Global Script Runtime is disabled in the RT settings | Open "Runtime settings > Scripts" and enable both C and VB runtimes |
| C script: "Tag not found" at runtime | Tag name is missing the connection prefix, or the tag is in a different channel | Open the tag in the tag management dialog and copy the full Connection::TagName form |
| VBScript: "Object required: HMIRuntime" | The Sub is invoked outside an event context (e.g. from a startup script before runtime is fully loaded) | Move the call into a "Tag-triggered" action with a valid tag or use the picture-open event |
| Dynamic Dialog: wrong color for one state | Range column overlaps with the next state; e.g. 1..1 defined twice |
Re-order the ranges from smallest to largest and check the "Value/Range" column types match |
| Status Display: bitmap is blank | The bitmap file is missing from the project folder or the path was absolute | Re-import the bitmap, which stores it under the project's GraCS directory with a relative path |
| Slow screen load (>5 s) on a Basic Panel | Too many individual script triggers; the panel executes them sequentially | Replace per-object C scripts with a single scheduled global action |
| Color correct in WinCC Professional, wrong in TIA Portal V17 | Project migrated to WinCC Unified, which uses a different API | Wrap the legacy code in a compatibility layer or port to the WinCC Unified GraphQL / JavaScript API |
Frequently Asked Questions
Why is my 0xFF0000 literal showing blue, not red?
WinCC encodes colors as a 24-bit BGR long. The literal 0xFF0000 sets the blue byte to 0xFF and is therefore blue. Use CO_RED, 0x0000FF, or RGB(255, 0, 0) in VBScript instead.
Which method is the fastest on a Comfort Panel?
Method 4 (Status Display with bitmaps) — the runtime swaps a bitmap handle and never re-evaluates a script. C scripts and VBScript are comparable in performance, but the Status Display wins when the same tag drives both color and graphic.
Can I drive 16 states with this technique?
Yes. The bit-pattern mapping scales to 4 bits (16 states) by extending the place-value formula to Bit0·1 + Bit1·10 + ... + Bit15·10^15, or by using a single integer tag with the state code computed in the PLC. C and VBScript mask the same way with one If per state.
Does the same code work in WinCC Unified?
No. WinCC Unified uses a different API based on JavaScript and the GraphQL event interface. The C/VBScript examples above target WinCC V7.x and WinCC Professional (TIA Portal V16/V17). For Unified, port the logic to a HMIRuntime.SupportedTags subscription in JavaScript.
How do I keep the script readable for the next engineer?
Document the bit-to-color contract once in the project header (see the table above) and reference it by name in every script. Use the CO_RED family of constants instead of hex literals, and place a single If Tag_Fault Then Exit Sub guard at the top of every VB Sub to short-circuit before lower-priority branches run.