HMIx practical guides

Build a pie chart Custom Web Control for WinCC Unified

A pie chart makes it easy to compare the output of several production lines. Build one with HTML, CSS and JavaScript, add it to TIA Portal and try it with three internal tags.

HMIx team10 min readReviewed
Illustration of an industrial HMI showing a three-series pie chart with values 45, 35 and 20
AI illustration created for HMIx. The downloadable example uses the same three values, with a doughnut chart and a legend.

Guide environment: TIA Portal / WinCC Unified V21.1. Check the example’s import and operation on your target device.

01

What is a Custom Web Control?

A Custom Web Control, usually called a CWC, is a small web component you place inside a WinCC Unified screen. HTML defines its content, CSS defines its appearance and JavaScript makes it react. It is useful when a standard HMI object does not provide the presentation or interaction you need.

Think of a production dashboard, a table or a pie chart. The control exposes properties: named inputs such as ValueA. In TIA Portal, you link each property to an HMI tag; the CWC receives the values and updates the chart. It does not connect directly to the PLC on its own.

  • Choose a CWC for custom interactions, several coordinated values or a specialised layout.
  • For a single symbol that changes colour, position or fill level, a dynamic SVG is often the simpler choice.
  • For a reusable assembly of standard HMI objects, consider a faceplate.

HMIx offers hundreds of free HMI resources to download. Start with a control from the catalogue, or describe your idea to the HMIx AI generator and create a resource you can adapt to your project.

HMIx Pie Chart with five segments, a legend and a series tooltip
Pie Chart – Dashboard Widget, published on HMIx: a doughnut chart with five elements and configurable titles. Download it from its product page. Below, we build a three-input version to make the mechanism easy to follow. View this resource on HMIx

Siemens · Programming Custom Web Controls

02

Before you begin

Use a test project with a WinCC Unified device and an empty screen. You need TIA Portal with WinCC Unified Engineering and the matching Runtime or simulation components installed. A standard Comfort project and a Unified project are different targets.

  • For the first test, use Unified PC Runtime or the simulation supported by your Unified device.
  • Have a text editor and a ZIP utility available if you want to change the example. Visual Studio Code is convenient, but not required.
  • No PLC is needed: three internal HMI tags and three I/O fields provide the test values.

The browser preview checks the drawing. Only a test inside Unified verifies the WebCC connection and tag binding.

03

Build a three-value pie chart

Our pie chart has a hole in the middle: the total sits in the centre and each series appears in the legend. Its inputs are ValueA, ValueB and ValueC, initially 45, 35 and 20. These are amounts; they do not have to add up to 100. The control calculates each share of the total.

  1. Create the files

    Create manifest.json at the root and two folders: assets and control. Inside control, create index.html and code.js. Obtain Siemens' webcc.min.js from the official example linked below and place it in control too. Each file has a specific role; no web framework is needed.

  2. Define the three inputs

    In manifest.json, declare ValueA, ValueB and ValueC as numbers with defaults of 45, 35 and 20. The manifest also specifies the HMIx Pie Chart name, its identity and its entry file.

  3. Draw the chart and legend

    In control/index.html, add a ring with three segments, the total and the A, B and C legend. In code.js, calculate each share by dividing its value by the sum. When all three values are zero, display an empty ring. The example uses SVG inside HTML and needs no chart library.

  4. Connect it to Unified

    Load Siemens webcc.min.js unchanged. Start WebCC with the same three properties, read their values when connected and redraw whenever onPropertyChanged reports a change. This also displays the current data when the screen opens.

  5. Package the contents

    Place manifest.json, assets and control at the ZIP root, without an extra enclosing folder. Name the ZIP with the control's GUID in braces. If you edit our files, rebuild the ZIP with the same structure; changing the outer filename alone does not create a new control identity.

ValueA, ValueB and ValueC must match in the manifest and JavaScript. This guide’s download is an original example with three inputs; the catalogue Pie Chart uses Apache ECharts and five elements with Value and Title.

Download the pie chart example

Complete ZIP with three numeric inputs and WebCC. Code and structure checked; verify importing and connecting it in WinCC Unified V21.1.

View the example code

Optional: the download contains the complete example. You do not need to type this code to follow the test.

manifest.json

{
  "mver": "1.2.0",
  "control": {
    "identity": {
      "name": "HmixPieChart",
      "version": "1.0",
      "displayname": "HMIx Pie Chart",
      "type": "guid://8D821B43-4EF5-4BF1-A31D-931D283C7C04",
      "start": "./control/index.html"
    },
    "metadata": {
      "author": "HMIx",
      "description": "Educational read-only three-value pie chart"
    },
    "contracts": {
      "api": {
        "methods": {},
        "events": {},
        "properties": {
          "ValueA": { "type": "number", "default": 45 },
          "ValueB": { "type": "number", "default": 35 },
          "ValueC": { "type": "number", "default": 20 }
        }
      }
    },
    "types": {}
  }
}

control/index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>HMIx Pie Chart</title>
    <style>
      * {
        box-sizing: border-box;
      }
      body {
        margin: 0;
        padding: 20px;
        background: #171e26;
        color: #f3f5f6;
        font:
          14px system-ui,
          sans-serif;
      }
      header {
        display: flex;
        justify-content: space-between;
        color: #bac5ce;
        font-size: 11px;
        letter-spacing: 0.14em;
      }
      main {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 24px;
        min-height: 240px;
      }
      svg {
        width: 55%;
        max-width: 300px;
        flex-shrink: 0;
      }
      ul {
        list-style: none;
        padding: 0;
        min-width: 110px;
      }
      li {
        display: grid;
        grid-template-columns: 10px 1fr auto;
        align-items: center;
        gap: 10px;
        padding: 14px 0;
        border-bottom: 1px solid #35404a;
      }
      .dot {
        width: 8px;
        height: 8px;
        border-radius: 50%;
      }
      small {
        display: block;
        margin-top: 4px;
        color: #bac5ce;
        font-variant-numeric: tabular-nums;
      }
      strong {
        font-variant-numeric: tabular-nums;
      }
      @media (max-width: 360px) {
        body {
          padding: 12px;
        }
        main {
          gap: 12px;
        }
      }
    </style>
  </head>
  <body>
    <header><span>HMIx</span><span>PIE CHART</span></header>
    <main>
      <svg viewBox="0 0 220 220" role="img" aria-labelledby="chart-title">
        <title id="chart-title">Distribution of A, B and C</title>
        <circle
          cx="110"
          cy="110"
          r="76"
          fill="none"
          stroke="#35404a"
          stroke-width="28"
        />
        <g transform="rotate(-90 110 110)" fill="none" stroke-width="28">
          <circle id="ValueA" cx="110" cy="110" r="76" stroke="#47d2c8" />
          <circle id="ValueB" cx="110" cy="110" r="76" stroke="#79a9ed" />
          <circle id="ValueC" cx="110" cy="110" r="76" stroke="#f5c16c" />
        </g>
        <text
          id="total"
          x="110"
          y="115"
          text-anchor="middle"
          fill="#f3f5f6"
          font-size="30"
          font-weight="600"
        >
          100
        </text>
        <text
          x="110"
          y="139"
          text-anchor="middle"
          fill="#bac5ce"
          font-size="10"
          letter-spacing="2"
        >
          A + B + C
        </text>
      </svg>
      <ul aria-label="Values and shares">
        <li>
          <i class="dot" style="background: #47d2c8"></i
          ><span>A<small id="ValueA-amount">45</small></span
          ><strong id="ValueA-percent">45%</strong>
        </li>
        <li>
          <i class="dot" style="background: #79a9ed"></i
          ><span>B<small id="ValueB-amount">35</small></span
          ><strong id="ValueB-percent">35%</strong>
        </li>
        <li>
          <i class="dot" style="background: #f5c16c"></i
          ><span>C<small id="ValueC-amount">20</small></span
          ><strong id="ValueC-percent">20%</strong>
        </li>
      </ul>
    </main>
    <script src="webcc.min.js"></script>
    <script src="code.js"></script>
  </body>
</html>

control/code.js

/* HMIx educational pie chart. Values are amounts, not required percentages. */
var values = { ValueA: 45, ValueB: 35, ValueC: 20 };
var keys = ['ValueA', 'ValueB', 'ValueC'];
var circumference = 2 * Math.PI * 76;

function drawChart() {
  // Normalize before adding to avoid overflow for very large finite values.
  var maximum = Math.max(values.ValueA, values.ValueB, values.ValueC, 1);
  var sum = keys.reduce(function (n, key) {
    return n + values[key] / maximum;
  }, 0);
  var offset = 0;
  keys.forEach(function (key) {
    var fraction = sum ? values[key] / maximum / sum : 0;
    var arc = document.getElementById(key);
    var length = fraction * circumference;
    arc.setAttribute('stroke-dasharray', length + ' ' + circumference);
    arc.setAttribute('stroke-dashoffset', String(-offset));
    arc.setAttribute('visibility', fraction ? 'visible' : 'hidden');
    document.getElementById(key + '-amount').textContent = String(values[key]);
    document.getElementById(key + '-percent').textContent =
      Math.round(fraction * 100) + '%';
    offset += length;
  });
  var total = values.ValueA + values.ValueB + values.ValueC;
  document.getElementById('total').textContent = isFinite(total)
    ? String(total)
    : '—';
}

function setValue(key, value) {
  if (keys.indexOf(key) === -1 || typeof value !== 'number' || !isFinite(value))
    return;
  values[key] = Math.max(0, value);
}

drawChart();
if (typeof WebCC !== 'undefined') {
  WebCC.start(
    function (connected) {
      if (!connected) return;
      keys.forEach(function (key) {
        setValue(key, WebCC.Properties[key]);
      });
      drawChart();
      WebCC.onPropertyChanged.subscribe(function (change) {
        setValue(change.key, change.value);
        drawChart();
      });
    },
    {
      methods: {},
      events: [],
      properties: { ValueA: 45, ValueB: 35, ValueC: 20 },
    },
    ['HMI'],
    10000,
  );
}

Siemens · Manifest structureSiemens · WebCC and the Runtime interfaceSiemens · Example files and WebCC library

04

Add the ZIP to your TIA Portal project

  1. Find the project folder

    Open the folder containing your TIA Portal project. Inside UserFiles, create CustomControls if it does not exist.

  2. Copy the CWC package

    Put the importable {GUID}.zip in UserFiles\CustomControls. Keep this ZIP compressed. Do not copy only index.html or an outer download bundle.

  3. Refresh and place the control

    Open the Unified screen. In Tools → My controls, click Refresh and drag HMIx Pie Chart onto the screen. Allow room for the ring and its legend, for example 480 × 300 pixels.

  4. Check its interface

    Select the control and open Properties → Properties → Interface. Find ValueA, ValueB and ValueC and assign static values of 45, 35 and 20. Next, you will link them to tags.

Siemens example showing My controls, a gauge on a Unified screen and its Interface properties
Siemens engineering screenshot: My controls → screen → Interface. Siemens shows GaugeMeter; for our test, select HMIx Pie Chart and its ValueA, ValueB and ValueC properties. Reference · © Siemens AG · Licence and attribution

Siemens · Installing and using a CWC

05

Test it with three internal tags, without a PLC

  1. Create three internal tags

    In the HMI tag table, add ProductionA, ProductionB and ProductionC using a numeric type such as Real and no PLC connection.

  2. Link each input to its tag

    In the CWC interface, choose Tag for dynamization: ValueA → ProductionA, ValueB → ProductionB and ValueC → ProductionC. Keep Read only enabled; the I/O fields will change the data.

  3. Add three I/O fields

    Add an I/O field in input/output mode for each tag. Label them A, B and C. For this test, use amounts between 0 and 100 and set input limits where available.

  4. Compile and run

    Set this as the start screen, compile the HMI and fix any reported errors. Start the supported HMI simulation, or load and start your test project in Unified PC Runtime. Open the Runtime client and sign in with the test project's configured user if requested.

  5. Try two distributions and the empty state

    Enter 45, 35 and 20, confirming each entry. Then change A to 90: its segment grows and the total becomes 145. Finally, set all three to zero; the ring should be empty. Reopen the screen to check that it shows the current values.

Siemens screenshot of a CWC property bound to a tag with the Read-only option enabled
A property linked to a tag in the Siemens example. For our chart, replace GaugeValue / Motor_Speed with ValueA / ProductionA and repeat for B and C, keeping Read only enabled. Reference · © Siemens AG · Licence and attribution

First, see what should happen

Change A, B or C and watch the segments respond. The inputs are amounts; the chart calculates their percentages.

100Total
45 / 45%
35 / 35%
20 / 20%
Interactive explanation in this guide. This is not a WinCC Runtime session.

Your test is successful when…

45 / 35 / 20 gives a total of 100. With A = 90, the total is 145 and A accounts for about 62%. Three zeros leave an empty ring. Reopening the screen should show the current values.

Siemens · Installing and using a CWCSiemens · CWC engineering example and original screenshots

06

If something does not work

  • The control is missing: check UserFiles\CustomControls, the ZIP root, valid manifest JSON and the GUID filename, then refresh My controls.
  • The chart appears but does not change: check ValueA, ValueB and ValueC, their tags and the WebCC connection. Opening the HTML locally checks the appearance, not the Unified connection.
  • It works on PC but fails on a Unified Comfort Panel: keep dependencies inside the package. Siemens documents restrictions on external links and on loading external data with fetch or XMLHttpRequest.
  • A change is not visible: rebuild the ZIP, use TIA Portal's control update workflow, compile and reload the test project. Keep a backup of the working version.

Once this small test works, add one feature at a time. Validate the final control on the actual device before connecting it to a real process.

Siemens · CWC restrictions on Unified Comfort Panels

Frequently asked questions

Do I need to know JavaScript?

You can import and bind an existing CWC without writing JavaScript. To change its behaviour by hand, basic HTML, CSS and JavaScript help. HMIx AI can prepare the files; you still review and test the result in Unified.

Can I reuse the control in another project?

Copy its importable ZIP into the other project's UserFiles\CustomControls and configure that project's tags. Keep the package and its version together; do not assume it behaves like a versioned faceplate library type.

Does a working preview mean the control is ready?

It confirms appearance, not the full Runtime integration. Also test startup, property changes, the target device and the expected screen sizes.

Sources and further reading