engineering

PLC Programming Best Practices

SPS-Programmierung Best Practices: Strukturierte Programmierung nach IEC 61131-3, Namenskonventionen, Fehlerbehandlung und wartbare Code-Strukturen für Siemens TIA Portal.

David Prybisch
15 min read
PLC Programming Best Practices

1.Introduction

PLC programming is more than just stringing logic blocks together. In over 10 years as an automation engineer, I've seen countless PLC projects – many with typical problems:

  • Unreadable variable names like M0.0, DB1.DBX0.0, or %MW100
  • Inconsistent programming language mix – LAD, FBD, STL, and SCL wildly mixed in the same project
  • Missing documentation – the original programmer left the company long ago

In this article, I share the most important best practices for maintainable PLC programs with Siemens TIA Portal.

2.Why Best Practices in PLC Programming?

2.1.The Problem: Legacy Code

Typical scenario from my practice:

A machine builder calls: "Our plant is down. Can you help?"

On-site disillusionment:

  • Variable names like M0.0, DB1.DBX0.0, %MW100 – nobody knows what they mean
  • LAD, FBD, STL, and SCL wildly mixed in the same project
  • No comments, no structure
  • Original programmer left the company years ago

Troubleshooting takes unnecessarily long because nobody can navigate the code.

2.2.The Solution: Professional Programming

Structured, maintainable PLC programs bring clear advantages:

  • Faster troubleshooting through descriptive variable names
  • Shorter onboarding time for new programmers
  • Fewer machine downtimes through better error handling

3.Best Practice 1: Consistently Use IEC 61131-3 Standards

IEC 61131-3 is the international standard for PLC programming and mandatory for CE-certified machines.

3.1.The 5 Programming Languages Overview

LanguageUse CaseAdvantagesDisadvantages
SCL (Structured Control Language)Complex algorithms, calculationsClear, readable, type-safeNot for hard real-time
LAD (Ladder Diagram)Simple logic, electrician-friendlyIntuitive for electriciansUnclear with > 50 rungs
FBD (Function Block Diagram)Control, data flow logicGraphically understandableHard to maintain with large programs
ST (Structured Text)Like SCL, IEC 61131-3 standardCompact, fastSteep learning curve
SFC (Sequential Function Chart)Sequential control (GRAFCET)State machines visualizedOverhead for simple logic

3.2.Structured Programming: The Pyramid

PLC program structure: Hierarchical block architecture from OB1 through FC_SystemControl to function blocks and data blocks

OB1 (Main)
  └── FC_SystemControl (Operating modes)
       ├── FB_PackMLStateMachine (Sequential control)
       │    ├── FB_MotorControl (Component)
       │    ├── FB_ValveControl (Component)
       │    └── FB_ConveyorControl (Component)
       └── FC_AlarmHandler (Utility)

Rule: Each level has exactly ONE clear responsibility.

4.Best Practice 2: Establish Naming Conventions

4.1.The Problem: Variable Name Chaos

Negative example (from practice):

// ❌ BAD
M0.0    // What is this?
DB1.DBX0.0   // Which motor?
%MW100  // Temperature? Pressure?

Positive example:

// ✅ GOOD
bMotorPumpMainRun : BOOL;  // Main pump motor running
iTemperatureTank1 : INT;   // Tank 1 temperature in °C
rPressureSetpoint : REAL;  // Pressure setpoint in bar

4.2.Standardized Prefixes (Hungarian Notation)

PrefixData TypeExample
bBOOLbMotorRun, bAlarmActive
iINTiCounter, iTemperature
rREALrSpeed, rPressure
sSTRINGsAlarmText, sRecipeName
dtDATE_TIMEdtStartTime, dtLastMaintenance
tTIMEtDelayStart, tCycleTime
udiUDINTudiPartCounter, udiTotalProduction

4.3.Inputs/Outputs with Prefix

// Inputs: ix (Input X)
ixMotorFeedbackRun : BOOL AT %I0.0;  // "x" = Bool
iwTemperatureSensor : WORD AT %IW0;   // "w" = Word

// Outputs: qx (Output X)
qxMotorStart : BOOL AT %Q0.0;
qwValvePosition : WORD AT %QW0;

Consistency is king: Decide on ONE convention with your team and document it.

5.Best Practice 3: Use Function Blocks vs. Functions Correctly

5.1.Function Blocks (FB) - Stateful

When to use?

  • Components with state (motor, valve, conveyor)
  • Alarm handlers
  • State machines

Example: Motor Control

FUNCTION_BLOCK FB_MotorControl
VAR_INPUT
  bStart : BOOL;        // Start command
  bStop : BOOL;         // Stop command
  bFeedback : BOOL;     // Motor running feedback
END_VAR

VAR_OUTPUT
  qxStart : BOOL;       // Motor start output
  bRunning : BOOL;      // Status: Motor running
  bAlarm : BOOL;        // Fault active
END_VAR

VAR
  tDelayStart : TON;    // Start delay
  tTimeoutFeedback : TON; // Feedback timeout
  bAlarmLatched : BOOL; // Latched fault
END_VAR

// Program logic here...
END_FUNCTION_BLOCK

Call:

// In OB1 or FC
dbMotorPumpMain(
  bStart := bSystemRun AND NOT bEmergencyStop,
  bStop := bSystemStop OR bEmergencyStop,
  bFeedback := ixMotorPumpMainFeedback
);
qxMotorPumpMainStart := dbMotorPumpMain.qxStart;

5.2.Functions (FC) - Stateless

When to use?

  • Calculations without state
  • Scaling
  • Mathematical operations
  • Utility functions

Example: Temperature Scaling

FUNCTION FC_ScaleTemperature : REAL
VAR_INPUT
  iwRawValue : INT;  // Raw sensor value (0-27648)
  rMinTemp : REAL;   // Minimum temperature (e.g., -50°C)
  rMaxTemp : REAL;   // Maximum temperature (e.g., +150°C)
END_VAR

// Scaling 0-27648 → rMinTemp to rMaxTemp
FC_ScaleTemperature := rMinTemp +
  (REAL#iwRawValue / 27648.0) * (rMaxTemp - rMinTemp);
END_FUNCTION

Call:

rTemperatureTank1 := FC_ScaleTemperature(
  iwRawValue := iwTempSensor1,
  rMinTemp := -50.0,
  rMaxTemp := 150.0
);

6.Best Practice 4: Error Handling According to NAMUR NE 107

NAMUR NE 107 is the industry standard for self-monitoring and diagnostics in process automation. In mechanical engineering, it has also become established.

6.1.The 4 States

NAMUR NE 107 diagnostic states: Normal Operation, Maintenance Required, Function Check, Failure

6.2.Implementation with FB_AlarmHandler

FUNCTION_BLOCK FB_AlarmHandler
VAR_INPUT
  bTrigger : BOOL;          // Alarm trigger
  eAlarmLevel : INT;        // 1=Info, 2=Warning, 3=Error, 4=Critical
  sAlarmText : STRING[80];  // Alarm text
END_VAR

VAR_OUTPUT
  bAlarmActive : BOOL;      // Alarm is active
  bAckRequired : BOOL;      // Acknowledgment required
END_VAR

VAR
  bAlarmLatched : BOOL;     // Latched alarm
  dtTimestamp : DATE_TIME;  // Alarm timestamp
END_VAR

// Logic: Latch alarm, set timestamp, acknowledge if needed
IF bTrigger AND NOT bAlarmLatched THEN
  bAlarmLatched := TRUE;
  dtTimestamp := CURRENT_TIMESTAMP();
  bAlarmActive := TRUE;

  // Logging to HMI/SCADA
  // ...
END_IF;

bAckRequired := bAlarmLatched AND eAlarmLevel >= 3;
END_FUNCTION_BLOCK

Best Practice: A central alarm manager collects all alarms and communicates them bundled to the HMI.

7.Best Practice 5: Version Control with Git

Yes, Git works for PLC code too! Since TIA Portal v18, integration has become significantly easier.

7.1.Setup: TIA Portal with Git

1. Activate Multiuser Engineering

TIA Portal → Project → Properties → Multiuser Engineering → Activate

2. Save project in Git-friendly format

# .gitignore for TIA Portal
*.ap18
*.zap18
__OPNB*
*.bak

3. Initialize Git repository

git init
git add .
git commit -m "Initial commit: TIA Portal project setup"

7.2.Branching Strategy for PLC Projects

main (Production code)
  ├── develop (Development)
  │    ├── feature/motor-control-update
  │    ├── feature/add-safety-logic
  │    └── bugfix/alarm-text-typo
  └── hotfix/critical-emergency-stop-bug

Workflow:

  1. Create feature branch: git checkout -b feature/new-conveyor-logic
  2. Commit changes: git commit -m "Add conveyor start delay 2s"
  3. Create pull request (GitHub, GitLab)
  4. Code review by second programmer
  5. Merge into develop
  6. Testing on development PLC
  7. Release branch → main

8.Best Practice 6: Comments and Documentation

8.1.Code Comments: The 3-Line Rule

Bad:

// ❌ Obvious, adds nothing
bMotorRun := TRUE;  // Start motor

Good:

// ✅ Explains WHY, not WHAT
// 2s delay due to pressure build-up in hydraulic circuit
// (See requirement REQ-HYD-012)
tDelayStart(IN := bStartRequest, PT := T#2S);
qxMotorStart := tDelayStart.Q;

8.2.Documentation Standards

For each FB/FC:

(*
  Name: FB_MotorControl
  Version: 1.2.0
  Author: David Prybisch
  Date: 2025-11-22

  Description:
    Standardized motor control with start delay,
    feedback monitoring and fault latching.

  Change History:
    v1.2.0 - 2025-11-22 - Added timeout monitoring
    v1.1.0 - 2025-10-15 - NAMUR alarm integration
    v1.0.0 - 2025-09-01 - Initial version
*)
FUNCTION_BLOCK FB_MotorControl
// ...
END_FUNCTION_BLOCK

UML Diagrams for System Architecture:

Use tools like PlantUML or Microsoft Visio to visualize overall architecture.

9.Best Practice 7: Testing and Simulation

9.1.Simulation with PLCSIM

Siemens PLCSIM Advanced allows testing PLC programs without physical hardware:

Workflow:

  1. Develop PLC program in TIA Portal
  2. Start PLCSIM Advanced and load project
  3. Set and check variables via watch table
  4. Systematically test sequential controls

Advantages:

  • Detect errors early – in the office instead of at the customer's site
  • New programmers can test safely
  • Validate changes before commissioning

10.Best Practice 8: Performance Optimization

10.1.Keep CPU Utilization in Check

Common performance killers:

  1. Too many timers/counters → Use arrays instead of individual variables
  2. String operations in real-time cycles → Move to lower-priority tasks
  3. Complex calculations in OB1 → Use cyclic interrupts (OB35)

Example: Array-based Timer Management

// ❌ BAD: 100 individual timers
VAR
  tMotor1 : TON;
  tMotor2 : TON;
  // ... 98 more timers
END_VAR

// ✅ GOOD: Array of timers
VAR
  atMotorTimers : ARRAY[1..100] OF TON;
END_VAR

// Call in loop
FOR i := 1 TO 100 DO
  atMotorTimers[i](IN := abMotorStart[i], PT := T#2S);
  aqxMotorStart[i] := atMotorTimers[i].Q;
END_FOR;

10.2.Memory Optimization with Multi-Instances

Problem: Each FB instance consumes its own DB (instance DB).

Solution: Multi-instances for recurring blocks.

// FB_ConveyorLine contains 10x FB_MotorControl as multi-instance
FUNCTION_BLOCK FB_ConveyorLine
VAR
  Motor1 : FB_MotorControl;  // Multi-instance
  Motor2 : FB_MotorControl;
  // ...
END_VAR

// Create only ONE instance of FB_ConveyorLine
dbConveyorLine : FB_ConveyorLine;  // Saves 90% memory vs. 10 separate FBs

11.Conclusion: Professional PLC Programming Pays Off

The investment in structured, maintainable PLC programs pays off multiple times:

  • Faster troubleshooting through descriptive variable names and clear structure
  • Shorter onboarding time for new programmers
  • Fewer machine downtimes through better error handling
  • Easier maintenance over the entire system lifetime

11.1.Checklist for Your Next PLC Project

  • Naming conventions agreed and documented with team
  • Hierarchical program structure planned (UML diagram)
  • IEC 61131-3 standards followed
  • Alarm management according to NAMUR NE 107 implemented
  • Git repository set up and .gitignore configured
  • Simulation with PLCSIM prepared
  • Code review process established
  • Documentation (FB header, system architecture) created

12.Infographic: PLC Best Practices at a Glance

PLC Programming Best Practices Infographic: Checklist for structured, maintainable PLC programs

13.Further Resources


About the Author: David Prybisch has been working as a PLC programmer for over 10 years. He supports machine builders with structured programming using Siemens TIA Portal, code reviews, and commissioning.

Tags

SPS-ProgrammierungTIA PortalIEC 61131-3SiemensBest PracticesCode QualityNAMUR NE 107SCLAutomatisierung

Related Articles

Automation in Luxembourg 2025: Trends Shaping the Greater Region
strategy
14 min

Automation in Luxembourg 2025: Trends Shaping the Greater Region

Automatisierungstrends 2025 in Luxemburg und der Großregion: Husky, Goodyear und IEE investieren Hunderte Millionen Euro, während der Fachkräftemangel (335.000 Arbeitskräfte bis 2040) die Automatisierung zur strategischen Notwendigkeit macht.

Read more: Automation in Luxembourg 2025: Trends Shaping the Greater Region