The PLC Program Nobody Could Read
We took over maintenance on a custom machine that another integrator had built. The PLC program was 1,200 rungs long. There were no comments. The tags were named “Sensor1”, “Motor2”, “Valve3”. There was no structure — one long line of ladder logic that started at the top and ended at the bottom. When a station malfunctioned, we had to scroll through 800 rungs to find the output that controlled the gripper. It took two days to diagnose a simple sensor failure that should have taken ten minutes.
PLC program structure for custom machines isn’t about being a neat freak. It’s about the next person who opens the program — which might be you six months later, or a customer’s maintenance technician who doesn’t know the machine. A program that works but nobody can read is a maintenance liability. This article is how I structure PLC and HMI code so that the person debugging it at 2 AM on a Saturday can find what they need.
Program Organization: Divide by Station, Not by Type
The worst PLC programs organize by function: “all inputs here, all outputs there, all logic in the middle.” That sounds logical. It’s terrible for debugging. When Station 3’s gripper isn’t working, you need to see Station 3’s input, output, and logic together — not jump between three rungs scattered across 500 lines.
The Station-Based Structure
Organize the PLC program by physical station or axis. Each station is its own code block (subroutine, program organization unit, or ladder section) that contains everything that station needs:
MAIN (overall machine state, cycle logic) ├── INFEED (sensor inputs, feed logic, eject) ├── STATION_1 (index, clamp, press, retract) ├── STATION_2 (pick, place, gripper control) ├── STATION_3 (vision trigger, reject divert) ├── OUTFEED (conveyor, queue sensor) └── UTILITIES (I/O mapping, alarms, comms)
When Station 2 fails, you open STATION_2. Everything for that station is in one block: the input sensors, the valve outputs, the motion commands, and the interlocks. You don’t hunt through the whole program.
Within Each Station: A Standard Template
Every station block follows the same template. When you’ve seen one station, you’ve seen them all:
- Input conditioning: Debounce sensor inputs, translate raw I/O into named signals.
- Safety interlocks: Is the station safe to run? (Guard closed, e-stop clear, part present)
- Sequence control: The step sequencer that moves the station through its cycle (idle → clamp → press → release → unclamp).
- Output logic: What drives the valves, motors, and lights based on the current step.
- Fault detection: Did the expected sensor trigger within the timeout? If not, alarm.
The template is the same for every station. The inputs and outputs change, but the structure doesn’t. A technician who learned the template on Station 1 can debug Station 5 without re-learning the logic.
Tag Naming: The Detail That Saves Hours
Tags are the variable names in the PLC. “Sensor1” is a tag. “Station2_PartPresent” is a name. The difference is whether the next person knows what it does without cross-referencing a drawing.
A Consistent Naming Convention
I use a structured tag name: Area_Device_Type_Description
| Tag | What It Is | Bad Alternative |
|---|---|---|
| Stn1_ClampCylinder_Extend | Valve output: extend the clamp on Station 1 | Output3 |
| Stn1_Clamp_Retracted | Input: clamp is fully retracted | Sensor7 |
| Stn2_Pick_VacuumOK | Input: vacuum present on gripper | Input15 |
| Infeed_Part_Ready | Input: part waiting at infeed | Prox4 |
| Sys_EStop_Active | Internal: e-stop circuit is open | Bit20 |
The tag name tells you where it is (Stn1, Infeed, Sys), what device it is (ClampCylinder, Vacuum, EStop), and what it does (Extend, Retracted, Active). You don’t need the I/O drawing to know what the tag does. This sounds obvious. It’s amazing how many programs skip it.
Sequence Control: Step-Based, Not Goto-Based
The most reliable way to control a machine cycle is a step sequencer. The machine is in one step at a time. Each step has an action (extend this cylinder, start this motion) and a transition (when this sensor triggers, move to the next step).
How the Sequencer Works
A simple sequencer:
- Step 0 (Idle): Wait for “cycle start” signal. All valves home.
- Step 1 (Clamp): Energize clamp extend valve. Wait for “clamp extended” sensor. Timeout if no sensor in 5 seconds → alarm.
- Step 2 (Press): Energize press valve. Wait for “press extended” sensor. Timeout → alarm.
- Step 3 (Release): Retract press. Wait for “press retracted.”
- Step 4 (Unclamp): Retract clamp. Wait for “clamp retracted.”
- Step 5 (Done): Signal “cycle complete.” Go to Step 0.
This is readable. Every step has a clear action and a clear exit condition. If the machine hangs in Step 2, you know the press isn’t reaching the extended sensor. You don’t have to trace through interlocking logic to find it.
Avoid “goto” logic where any rung can jump to any rung. It works on day one but becomes spaghetti by project three. The step sequencer forces a linear, traceable flow.
Timeout is mandatory. Every step that waits for a sensor has a timeout. If the sensor doesn’t trigger in 5 seconds, the machine alarms and stops. Without timeouts, a machine that loses a sensor just sits there forever — and nobody knows why. The timeout tells the operator what went wrong.
Fault Handling: The Operator’s Best Friend
A machine that stops without telling you why is worse than a machine that stops with an alarm code. Fault handling isn’t optional.
What a Good Fault Does
- Stops the cycle immediately (not at the end of the step).
- Lights an alarm on the HMI with a clear message: “Station 2: Clamp did not extend in 5 seconds.” Not “Error 4023.”
- Identifies the root cause as specifically as possible. “Part not detected at infeed” is more useful than “Input sensor fault.”
- Stops only the affected station if possible. Don’t stop the whole line because one gripper didn’t close — stop that station, keep the rest running until the queue clears.
- Logs the fault with a timestamp and the step number. The maintenance team reviews the fault log to find recurring issues.
Alarm Severity
Not all faults are equal. Categorize them:
| Severity | What It Means | Machine Response |
|---|---|---|
| Warning | Non-critical (low air pressure, filter dirty) | HMI message, keep running |
| Fault | Cycle cannot continue (sensor timeout, part missing) | Stop cycle, alarm, wait for reset |
| Critical | Safety-related (e-stop, guard open) | Safe stop, motion stops, requires safety reset |
HMI Design: The Operator’s Interface
The HMI isn’t a PLC programming exercise. It’s what the operator touches. A good HMI gets the operator running in three taps. A bad HMI requires navigating four screens to find a simple override.
The Screens That Matter
- Main screen: The machine in miniature. Show the current station state (idle/running/fault), the cycle count, and the major indicators (air pressure, e-stop, guards). This is what the operator sees 90% of the time.
- Fault screen: The active alarm, the history of recent faults, and a “reset” button. The operator should be able to clear a jam and press reset in two taps.
- Manual screen: Jog controls for each axis and valve. Used for setup and maintenance. Color-coded so the operator knows what they’re about to move.
- Setup / Recipe screen: Product selection, changeover parameters, and counters. The operator picks the product from a dropdown, not by entering numbers.
HMI Don’ts
- Don’t show raw PLC addresses. The operator doesn’t care that Output 3.7 is on. Show “Gripper: Open.”
- Don’t make the operator type in numeric values unless they need to. Buttons and dropdowns beat keyboard input.
- Don’t hide critical alarms on a secondary screen. If the machine is faulted, the main screen says so.
- Don’t use the HMI as a maintenance diagnostic tool. That’s what the PLC programmer’s laptop is for. The HMI is for the operator.
Documentation: The Code Is Not the Documentation
Well-commented code helps, but it isn’t enough. Every machine needs:
- I/O list: A table that maps every tag to its physical I/O address, device, and location. This is what the electrician uses to troubleshoot a wired sensor.
- Cycle description: A one-page document that describes the normal cycle step by step. What each station does, what the sensors look for, and what the normal sequence is.
- Fault list: A table of every alarm code, its meaning, and the recommended fix. The operator uses this to clear faults without calling the integrator.
This documentation takes a day to write. It saves a week of support calls over the machine’s life.
A PLC/HMI Program Checklist
- Is the program organized by station (not by I/O type)?
- Does every station follow the same template (inputs, interlocks, sequence, outputs, faults)?
- Do tags use a consistent naming convention (area_device_function)?
- Is every step sequencer step timed out?
- Do faults show a clear message (not an error number)?
- Does the HMI main screen show the machine state at a glance?
- Can the operator reset a fault from the main screen?
- Is there a manual mode for setup and maintenance?
- Is the I/O list, cycle description, and fault list documented?
- Can a non-programmer read the program and understand what it does?
The Bottom Line
Industrial automation control design isn’t about making the machine run. It’s about making it run in a way that the next person can diagnose, maintain, and modify. Organize by station, name tags like a human would, use a step sequencer with timeouts, and build an HMI that an operator can use without a manual. The PLC program that reads like a well-documented process — not like a tangle of contacts — is the one that doesn’t get replaced the first time the original integrator is unavailable.