HMI Programming Guide: Screens, Alarms, and Tag Mapping That Operators Actually Trust

A practical, vendor-neutral HMI programming guide covering screen hierarchy, ISA-101 layout, alarm management, and tag mapping between the PLC and the operator panel.

HMI programming is what turns a working PLC program into a system an operator can actually run. The PLC closes the loops. The HMI is where people see what is happening, acknowledge alarms, change setpoints and decide whether to keep producing or shut down. A good HMI prevents accidents, and a bad one causes them.

This guide covers what HMI programmers do day to day: the ISA-101 screen hierarchy and colors, navigation, tag mapping between the PLC and the panel, alarms, trends, and the security and testing that a production system needs. It also shows the Structured Text on the PLC side that keeps the screens clean to bind and easy to maintain.

What HMI programming covers

An HMI (Human Machine Interface) is the screen an operator uses to work with a machine or process: a panel-mounted touchscreen, a thin client, or a PC running visualization software. HMI programming is the engineering work that produces it:

  • Screen design: layout, navigation, colors, animation logic.
  • Tag mapping: connecting on-screen objects to PLC variables.
  • Alarm configuration: priorities, messages, acknowledgment behavior.
  • Security: user roles, permissions, audit trails.
  • Recipes and data logging: production parameters and historical trends.
  • Testing and commissioning: checking every interaction against the real PLC.
  • The graphics are the visible part, but on a real project they are usually 30–40% of the work. The rest is tag architecture, alarm rationalization and integration testing. The architecture you settle in the first week decides whether the HMI still serves operators in ten years or gets torn out and replaced.

    HMI, SCADA and DCS

    SystemScopeTypical Software
    HMIOne machine or skid, local panelSiemens WinCC Comfort, Rockwell FactoryTalk View ME, Pro-face GP-Pro EX
    SCADAPlant-wide supervision, multiple PLCs, historizationIgnition, FactoryTalk View SE, Wonderware/AVEVA, WinCC Unified
    DCSTightly integrated control + visualization for continuous processEmerson DeltaV, Honeywell Experion, ABB 800xA
    The techniques in this guide apply to all three. The differences are mostly scale, redundancy, and how tightly the visualization layer is coupled to the controller.

    The ISA-101 four-level screen hierarchy

    ISA-101 (Human Machine Interfaces for Process Automation Systems) is built on one principle: the HMI should show the process state, not the process equipment. Its four-level display hierarchy has become the de facto standard for industrial HMIs:

    LevelNamePurposeUpdate Rate
    L1OverviewEntire plant/area at a glance5–10s
    L2Process AreaOne unit operation with key KPIs1–5s
    L3DetailSingle equipment item, all parameters500 ms to 1 s
    L4DiagnosticTrends, tuning, maintenance dataOn demand
    Level 1 is one screen with the whole process on it. Supervisors use it to see whether anything is wrong, so it has no detailed controls. Level 2 has one screen per major unit (a reactor, a packaging line, a CIP skid) showing all the equipment in that unit with its main process values and links deeper. Level 3 is the faceplate for one piece of equipment, such as a pump, a valve or a PID loop, and it is where the operator starts and stops things and changes setpoints. Level 4 holds raw I/O, communication status and drive parameters for maintenance, not operators.

    Operators should spend 80% of their time on L1 and L2. If they keep drilling into L3 and L4, the design has failed. The usual rookie mistake is the opposite: everything goes on Level 2 and there is no overview, so during an upset the operators lose situational awareness because they cannot see the whole plant on one screen.

    Navigation: any parameter in three clicks

    An operator should reach any parameter in the plant in three clicks or fewer:

  • On the L1 overview, select an area.
  • On the L2 area view, select the equipment.
  • The L3 faceplate popup shows every parameter and lets the operator change it.
  • The overview only works if each area tile shows the worst state inside it. Let the PLC work that out, so the HMI only animates a number:

    TYPE HMI_AreaStatus :
    STRUCT
        AreaName         : STRING[20];
        AreaState        : INT;         // 0=Off, 1=Running, 2=Warning, 3=Alarm
        ActiveAlarmCount : INT;
        ProductionRate   : REAL;
        EquipmentOnline  : INT;
        EquipmentTotal   : INT;
    END_STRUCT;
    END_TYPE

    PROGRAM HMI_Overview VAR Areas : ARRAY[1..8] OF HMI_AreaStatus; END_VAR

    // Aggregate area status — worst-case alarm state bubbles up Areas[1].AreaName := 'Raw Materials'; Areas[1].ActiveAlarmCount := AlarmMgr.CountByArea(1);

    IF Areas[1].ActiveAlarmCount > 0 THEN Areas[1].AreaState := 3; // Red — has active alarms ELSIF AreaWarnings[1] > 0 THEN Areas[1].AreaState := 2; // Yellow — warnings present ELSIF AreaRunning[1] THEN Areas[1].AreaState := 1; // Green — running normally ELSE Areas[1].AreaState := 0; // Gray — idle END_IF;

    Active alarms make the area red, warnings make it yellow, running makes it green, and otherwise it stays gray.

    Colors: gray for normal, color for abnormal

    Walk into a control room built before 2015 and you often see screens packed with 3D pipe graphics, rainbow color schemes and hundreds of data points in a single view. Operators watch these for 12-hour shifts and miss critical alarms because the important information is buried in visual noise. New screens still tend to look dramatic, with black backgrounds, bright green pipes and blinking red lights. ISA-101 deliberately rejects that style. Its recommended palette:

  • Background: light or medium gray (#D0D0D0 to #E5E5E5). Not white, not black.
  • Equipment and pipes: medium gray, darker than the background, with no process color coding.
  • Process values: black text on the gray background.
  • Abnormal conditions only: yellow (low priority alarm), orange (medium), red (high), magenta (critical).
  • When everything is colorful, nothing stands out. With color reserved for abnormal conditions, a single yellow indicator on an otherwise gray screen draws the operator's eye at once. This is called the gray-field approach. If you use green for running equipment, use it sparingly. This approach grew out of the Abnormal Situation Management Consortium's research on operator response, and it is the basis of ISA-101's guidance against traditional "Christmas tree" screens.

    Decide the color in the PLC and pass the HMI a state number, so every motor on every screen follows the same rule:

    // PLC-side: prepare color state for HMI binding
    // 0=Gray(Normal), 1=Green(Running), 2=Yellow(Warning), 3=Red(Alarm)
    TYPE HMI_ColorState : INT; END_TYPE

    FUNCTION FC_GetMotorColorState : INT VAR_INPUT Running : BOOL; Warning : BOOL; Alarm : BOOL; END_VAR

    IF Alarm THEN FC_GetMotorColorState := 3; // Red ELSIF Warning THEN FC_GetMotorColorState := 2; // Yellow ELSIF Running THEN FC_GetMotorColorState := 1; // Green ELSE FC_GetMotorColorState := 0; // Gray — normal stopped state END_IF;

    Tag mapping: connecting the HMI to the PLC

    Every dynamic element on a screen (a numeric value, an animated pump, a button, an alarm) is bound to a tag in the PLC. How you map those tags decides more than anything else whether the HMI project stays maintainable, and tag management is the biggest maintenance headache in HMI work.

    Rules for HMI tags

  • Use symbolic tags only. Bind to Pump_101.Run or Tank_201.Level_PV, never to %I0.3 or N7:42. If the I/O moves, the screens still work.
  • Keep one source of truth. Define the tags in the PLC and import them into the HMI. Do not retype them: typos in tag names are one of the most common commissioning bugs.
  • Keep data types consistent: REAL for analog values, BOOL for digital, INT or an enum for states. Mixing types between PLC and HMI causes silent rounding or truncation.
  • Group by equipment. Use UDTs or structures (Pump_101.Run, Pump_101.Fault, Pump_101.Hours) so the HMI faceplate binds to the structure once.
  • Put the engineering unit in the tag. Store the unit (degC, bar, %) as a tag property so the HMI can display it without hardcoding.
  • Example: a pump faceplate

    PLC UDT: PumpControl
        Run            : BOOL    // operator command
        Running        : BOOL    // confirmed feedback
        Fault          : BOOL    // any fault active
        FaultCode      : INT     // 0 = none, 1 = overload, 2 = no flow
        RunHours       : REAL    // accumulated run time
        InterlockOK    : BOOL    // safe to start

    HMI faceplate bindings: Start button → Pump_101.Run (write BOOL) Status lamp → Pump_101.Running (animate fill color) Fault banner → Pump_101.Fault (visible when TRUE) Fault message → Pump_101.FaultCode (lookup table → string) Hours display → Pump_101.RunHours (numeric, 1 decimal) Start enable → Pump_101.InterlockOK (button greyed out when FALSE)

    The start button is greyed out whenever the interlock is not satisfied. That one design choice prevents dozens of "I pressed start and it did not work" support calls. Note also that the operator command (Run) and the confirmed feedback (Running) are separate tags; keep them separate whatever naming you use.

    HMI data blocks in Structured Text

    Instead of exposing hundreds of individual tags, structure the PLC data into one HMI-ready block per equipment type:

    TYPE HMI_MotorFaceplate :
    STRUCT
        // Status (PLC → HMI, read-only)
        STS_Running      : BOOL;
        STS_Ready        : BOOL;
        STS_Faulted      : BOOL;
        STS_Local        : BOOL;        // Local/Remote mode
        STS_SpeedPct     : REAL;
        STS_CurrentAmps  : REAL;
        STS_RunHours     : DINT;
        STS_ColorState   : INT;         // For HMI element coloring

    // Commands (HMI → PLC, write) CMD_Start : BOOL; CMD_Stop : BOOL; CMD_Reset : BOOL; CMD_SpeedSP : REAL;

    // Alarms (PLC → HMI) ALM_Overload : BOOL; ALM_CommFault : BOOL; ALM_OverTemp : BOOL; END_STRUCT; END_TYPE

    TYPE HMI_ValveFaceplate : STRUCT STS_Open : BOOL; STS_Closed : BOOL; STS_Transitioning: BOOL; STS_Faulted : BOOL; STS_Position : REAL; // 0–100% for modulating valves STS_ColorState : INT;

    CMD_Open : BOOL; CMD_Close : BOOL; CMD_PositionSP : REAL;

    ALM_FailToOpen : BOOL; ALM_FailToClose : BOOL; ALM_TravelTime : BOOL; END_STRUCT; END_TYPE

    The prefixes give every tag a clear direction. STS_ (status) flows from PLC to HMI, CMD_ (command) flows from HMI to PLC, and ALM_ carries alarms to the HMI. Each piece of equipment has one tag group that is easy to find. You design one motor popup and reuse it for every motor, and commissioning is a matter of copying the structure and mapping the I/O.

    Instantiate the block for each physical device and map it to and from the control logic:

    // Instantiate for each physical motor
    PROGRAM HMI_DataExchange
    VAR
        Motor_Pump101    : HMI_MotorFaceplate;
        Motor_Pump102    : HMI_MotorFaceplate;
        Motor_Agitator   : HMI_MotorFaceplate;
        Valve_HV101      : HMI_ValveFaceplate;
        Valve_CV201      : HMI_ValveFaceplate;
    END_VAR

    // Map from control logic to HMI faceplate Motor_Pump101.STS_Running := Pump101_FB.Running; Motor_Pump101.STS_SpeedPct := Pump101_FB.ActualSpeed; Motor_Pump101.STS_Faulted := Pump101_FB.Faulted; Motor_Pump101.STS_ColorState := FC_GetMotorColorState( Running := Pump101_FB.Running, Warning := Pump101_FB.OverloadWarning, Alarm := Pump101_FB.Faulted );

    // Map HMI commands back to control Pump101_FB.CMD_Start := Motor_Pump101.CMD_Start; Pump101_FB.CMD_Stop := Motor_Pump101.CMD_Stop; Motor_Pump101.CMD_Start := FALSE; // Auto-reset one-shot commands Motor_Pump101.CMD_Stop := FALSE;

    The last two lines reset the start and stop commands after the control logic has read them, so a button press acts as a one-shot command.

    Alarms and ISA-18.2

    Alarms are the most safety-critical part of an HMI. The relevant standard is ISA-18.2 / IEC 62682, Management of Alarm Systems for the Process Industries, complemented by EEMUA 191 in Europe. A common scheme uses four priorities, and the HMI must make the priority obvious at a glance:

    PriorityColorOperator Response TimeTypical Use
    LowYellow< 30 minTrend deviation, advisory
    MediumOrange< 10 minProcess value out of range
    HighRed< 3 minSafety-related, production impact
    CriticalMagenta + flashImmediateShutdown trigger, safety system trip

    Alarm rationalization checklist

    Before you add an alarm to the HMI, check it:

  • Does it require an operator action? If not, it is an event, not an alarm.
  • Is there a deadband? Without one, a value oscillating around the setpoint will chatter dozens of alarms per minute.
  • Is there an on-delay? A transient spike should not trigger an alarm.
  • Is the priority correct? Most "high priority" alarms in legacy systems are really low priority. Be ruthless.
  • Can the operator do anything about it in the time available? If not, it belongs in the maintenance log, not the alarm summary.
  • A well-rationalized system averages fewer than 1 alarm per 10 minutes per operator in steady state. If your alarm list is full, the answer is rationalization, not bigger screens.

    Flashing only the critical alarm

    Flashing is reserved for critical alarms that nobody has acknowledged yet. The PLC can drive it with a 0.5 s on, 0.5 s off toggle:

    TYPE HMI_AlarmDisplay :
    STRUCT
        AlarmID      : INT;
        Message      : STRING[80];
        Priority     : INT;       // 1=Critical, 2=High, 3=Medium, 4=Low
        Timestamp    : STRING[20];
        State        : INT;       // 0=Normal, 1=Active, 2=Acknowledged, 3=Active+Ack
        AreaID       : INT;
        FlashEnable  : BOOL;      // Only for critical
    END_STRUCT;
    END_TYPE

    FUNCTION_BLOCK FB_HMI_AlarmBanner VAR TopAlarms : ARRAY[1..5] OF HMI_AlarmDisplay; TotalActive : INT; HighestPriority : INT; FlashToggle : BOOL; tmrFlash : TON; END_VAR

    // Flash timer for critical alarms: 0.5s on, 0.5s off tmrFlash(IN := NOT tmrFlash.Q, PT := T#500MS); IF tmrFlash.Q THEN FlashToggle := NOT FlashToggle; tmrFlash(IN := FALSE); END_IF;

    // Set flash enable only on critical unacknowledged alarms TopAlarms[1].FlashEnable := (TopAlarms[1].Priority = 1) AND (TopAlarms[1].State = 1) AND FlashToggle;

    Animation that shows state

    HMI animation should tell the operator the state of the equipment. It should not draw attention for its own sake.

  • Pumps and motors: the fill color changes (gray → green) when running. Optionally, the impeller rotates slowly when on. Nothing spins when off.
  • Valves: color or fill for open, closed and transitioning. Show the commanded state and the feedback state separately if they can disagree.
  • Pipes: stay gray. Do not animate flow with moving dashes; it is visual noise and creates motion sickness on large screens.
  • Tanks: a vertical bar fill proportional to the level tag, with a color band for the normal range. Change the color only outside it.
  • Trends: always put the setpoint, the process variable and the alarm limits on the same chart.
  • Preparing trend data in the PLC

    Trends are the most useful diagnostic tool on an HMI. Beyond PV, SP and the alarm limits, let the PLC pre-calculate the derived values: deviation from setpoint, rate of change, and the rolling minimum, maximum and average over the last 5 minutes.

    TYPE HMI_TrendData :
    STRUCT
        ProcessValue    : REAL;
        Setpoint        : REAL;
        ControlOutput   : REAL;    // PID output %
        DeviationPct    : REAL;    // How far from SP
        RateOfChange    : REAL;    // Units per minute
        AvgLast5Min     : REAL;
        MinLast5Min     : REAL;
        MaxLast5Min     : REAL;
    END_STRUCT;
    END_TYPE

    FUNCTION_BLOCK FB_TrendPrep VAR_INPUT PV : REAL; SP : REAL; CO : REAL; END_VAR VAR_OUTPUT TrendOut : HMI_TrendData; END_VAR VAR PrevPV : REAL; SampleBuffer : ARRAY[1..300] OF REAL; // 5 min at 1s samples SampleIndex : INT := 1; SampleCount : INT := 0; i : INT; Sum : REAL; END_VAR

    TrendOut.ProcessValue := PV; TrendOut.Setpoint := SP; TrendOut.ControlOutput := CO;

    // Deviation IF ABS(SP) > 0.001 THEN TrendOut.DeviationPct := ((PV - SP) / SP) * 100.0; ELSE TrendOut.DeviationPct := 0.0; END_IF;

    // Rate of change (units per minute, assuming 1s scan) TrendOut.RateOfChange := (PV - PrevPV) * 60.0; PrevPV := PV;

    // Rolling buffer for min/max/avg SampleBuffer[SampleIndex] := PV; SampleIndex := SampleIndex + 1; IF SampleIndex > 300 THEN SampleIndex := 1; END_IF; IF SampleCount < 300 THEN SampleCount := SampleCount + 1; END_IF;

    // Calculate statistics Sum := 0.0; TrendOut.MinLast5Min := 99999.0; TrendOut.MaxLast5Min := -99999.0;

    FOR i := 1 TO SampleCount DO Sum := Sum + SampleBuffer[i]; IF SampleBuffer[i] < TrendOut.MinLast5Min THEN TrendOut.MinLast5Min := SampleBuffer[i]; END_IF; IF SampleBuffer[i] > TrendOut.MaxLast5Min THEN TrendOut.MaxLast5Min := SampleBuffer[i]; END_IF; END_FOR;

    // Guard the average: first scan SampleCount = 0, and dividing by zero // is a runtime fault on every real PLC. Don't ship code that relies on // "the buffer will be full by then" — it won't be on cold start, restart, // or after a download. IF SampleCount > 0 THEN TrendOut.AvgLast5Min := Sum / INT_TO_REAL(SampleCount); ELSE TrendOut.AvgLast5Min := PV; // No history yet — current value is the best estimate END_IF;

    > Guard every division. Controllers handle an unguarded Sum / 0 differently, depending on the platform and the data type: an arithmetic error, a logged fault, an exception that stops the task, or a REAL result of infinity that then spreads into every calculation using it. None of these is what the operator should see on a trend. The TryPLC engine raises a runtime fault and halts the scan, so the editor flags this on the first cycle instead of returning 0 like a calculator. Treat every divisor in your code the way the ABS(SP) > 0.001 guard handles SP a few lines up: guard it, or prove from the surrounding logic that it can never be zero.

    Data for different panel sizes

    HMIs run on everything from 24" control room monitors to 7" panel PCs. Give the PLC data two detail levels: a compact tile for small panels and overview screens, and a full record for detail screens.

    // Compact data for small panels / overview tiles
    TYPE HMI_EquipmentTile :
    STRUCT
        TagName      : STRING[12];
        Status       : INT;        // 0=Off, 1=Run, 2=Warn, 3=Alarm
        MainValue    : REAL;       // One primary KPI
        MainUnit     : STRING[6];  // Engineering unit
    END_STRUCT;
    END_TYPE

    // Full data for detail screens TYPE HMI_EquipmentDetail : STRUCT Tile : HMI_EquipmentTile; // Includes compact data SecondaryVals: ARRAY[1..6] OF REAL; SecondaryLbls: ARRAY[1..6] OF STRING[16]; Trend : HMI_TrendData; Alarms : ARRAY[1..5] OF HMI_AlarmDisplay; CmdAvailable : ARRAY[1..8] OF BOOL; // Which commands are valid now END_STRUCT; END_TYPE

    CmdAvailable tells the HMI which commands are valid right now, the same idea as greying out the pump's start button when its interlock is not satisfied.

    Security and user roles

    Modern HMIs support role-based access control. A typical setup:

  • Operator: start, stop, acknowledge alarms, change setpoints within configured ranges.
  • Supervisor: change setpoint ranges, override interlocks (with audit trail), reset run hours.
  • Engineer / Maintenance: access diagnostic screens, force I/O, modify alarm limits.
  • Administrator: create users, change roles, modify screens.
  • Log every privileged action with the username, timestamp, old value and new value. This is mandatory for FDA 21 CFR Part 11 in pharma, and increasingly expected in food and beverage and in energy.

    Testing your HMI before commissioning

  • Force every tag the screen reads from the PLC. Check that the on-screen value updates and the units are correct.
  • Trigger every alarm. Confirm the message, priority, color and acknowledgment behavior.
  • Test every button with the interlocks both satisfied and not satisfied.
  • Walk through the navigation from Level 1 to Level 4 and back. There should be no dead ends.
  • Simulate a power loss mid-operation. The HMI should reconnect cleanly and show the current PLC state, not a stale snapshot.
  • Have an operator try it. They will find usability problems you never will.
  • The squint test

    ISA-101 practitioners use a quick check: squint at the screen from 6 feet away. If you cannot tell at once which area has a problem, the design needs work. In normal operation the screen should look mostly gray, calm and boring, which is correct. A single alarm should make one area pop with color. With several alarms, the priorities should be visibly ranked, with critical standing out above high.

    If the squint shows a rainbow of colors during normal operation, no clear problem area, or decorative elements competing with the process data, redesign. Design the screens for the worst moment of the operator's shift: calm during normal running, and instantly readable when something goes wrong.

    A practical learning path

    If you are new to HMI programming, work through these in order:

  • Pick one platform (WinCC, FactoryTalk View or Ignition) and build a simple two-screen project, an overview and a pump faceplate, against a simulated PLC.
  • Add alarms with proper priorities and deadbands.
  • Implement user roles and audit logging.
  • Refactor to ISA-101 colors and notice how much calmer the screen feels.
  • Build a Level 1 overview for a multi-unit process.
  • Add trends and a recipe management page.
  • You can practice the PLC side (the tag structures, the interlocks, the alarm conditions) in the TryPLC editor without a physical HMI panel. Once that side is solid, the HMI work is mostly screen design and binding.

    Frequently asked questions

    What is HMI programming?

    HMI programming is the work of designing the screens, alarms, navigation, and tag mappings that let an operator monitor and control a machine or process from a Human Machine Interface panel. It is not just drawing graphics — it covers screen hierarchy, alarm priorities, security, animation logic, and how every on-screen element is bound to a PLC tag.

    Which HMI software should I learn first?

    For Siemens shops, learn TIA Portal WinCC (Comfort or Unified). For Rockwell, learn FactoryTalk View ME and SE. For vendor-neutral work, Ignition by Inductive Automation is the most portable skill because it is used across many industries. The underlying concepts — screen hierarchy, alarm classes, tag bindings — transfer between all of them.

    What is the difference between HMI and SCADA?

    An HMI is the local operator interface for a single machine or skid, usually a panel on the equipment. SCADA (Supervisory Control and Data Acquisition) is a plant-wide system that aggregates data from many PLCs and HMIs, handles historization, and provides dashboards for supervisors. SCADA software like Ignition or FactoryTalk View SE can also act as an HMI, which is why the terms are sometimes used interchangeably.

    What is ISA-101 and why does it matter for HMI design?

    ISA-101 is the international standard for Human Machine Interfaces in process automation. It defines screen hierarchies (Level 1 overview, Level 2 unit, Level 3 detail, Level 4 diagnostic), a muted color palette that reserves bright colors for abnormal conditions, and consistent alarm representation. Following ISA-101 dramatically reduces operator error and is increasingly required by clients in oil and gas, pharma, and food and beverage.

    How should I map PLC tags to HMI tags?

    Use a single source of truth — a tag database in the PLC with descriptive names (Pump_101_Run, Tank_201_Level_PV) and import them into the HMI rather than retyping. Group tags by area or unit, keep data types consistent (REAL for analog, BOOL for digital, INT for states), and never bind an HMI element directly to a physical I/O address — always go through a symbolic tag so wiring changes do not break screens.

    How many alarms should appear on an HMI screen at once?

    Per the EEMUA 191 and ISA-18.2 guidelines, a well-designed system averages fewer than 1 alarm per 10 minutes per operator in steady state, and no more than 10 alarms in the first 10 minutes of a major upset. If your screens routinely show dozens of active alarms, the problem is alarm rationalization, not screen design — you need to remove nuisance alarms, set proper priorities, and add deadbands before adding more graphics.

    What is the three-click rule for HMI navigation?

    An operator should reach any parameter in the plant in three clicks or fewer: select the area on the Level 1 overview, select the equipment on the Level 2 area view, then see and change its parameters on the Level 3 faceplate.

    How can I quickly check whether an HMI screen design works?

    Use the squint test: squint at the screen from about 6 feet away. In normal operation it should look mostly gray and calm. If you cannot tell at once which area has a problem when an alarm is active, the design needs work.