May 20, 2026

Clean Code for PLCs: Best Practices for Writing Readable and Maintainable SCL

The principles of clean code—established in software engineering decades ago—apply equally to PLC programming.

 

Title: Clean Code Principles Framework - Description: Clean Code Principles Framework

 

The diagram above illustrates how multiple clean code principles converge to produce readable, maintainable code that delivers long-term value. As SCL adoption grows and PLC projects become more complex, the importance of writing maintainable, readable code cannot be overstated. A PLC program that runs correctly today but is incomprehensible to the next engineer who maintains it creates technical debt and increases the risk of costly errors. This article explores best practices for writing clean, professional SCL code that stands the test of time.

 

The Cost of Messy Code

Before diving into best practices, it is worth understanding why clean code matters in industrial automation. A poorly written PLC program might function correctly initially, but as systems evolve and requirements change, maintenance becomes increasingly difficult. Engineers spend more time deciphering existing code than writing new code. Bugs are harder to identify and fix. Testing becomes unreliable. The cost of these inefficiencies multiplies over the lifetime of a system, often exceeding the initial development cost many times over.

 

In safety-critical applications, messy code introduces additional risks. If an engineer cannot quickly understand how a system works, they cannot confidently make changes or troubleshoot issues. This can lead to safety incidents and regulatory compliance problems.

 

Naming Conventions: The Foundation of Clarity

Clear, descriptive names are the foundation of readable code. Variable, function, and block names should immediately convey their purpose. Avoid cryptic abbreviations or single-letter variables (except in mathematical contexts where conventions are well-established).

 

Poor Example:

VAR

    x, y, z : REAL;

    t1, t2 : INT;

    f : BOOL;

END_VAR

 

Good Example:

VAR

    motor_speed_rpm : REAL;

    motor_temperature_celsius : REAL;

    pressure_bar : REAL;

    timer_delay_seconds : INT;

    timer_elapsed_seconds : INT;

    motor_fault_detected : BOOL;

END_VAR

 

Adopt a consistent naming convention across your organization. Common approaches include:

 

        Snake_case: motor_speed_rpm, temperature_sensor_input

        camelCase: motorSpeedRpm, temperatureSensorInput

        Hungarian notation: fMotorFault, iMotorSpeed (where prefix indicates type)

 

The specific convention matters less than consistency. Choose one and apply it uniformly across all projects.

 

Comments: Explain the Why, Not the What

Comments should explain the reasoning behind code, not simply restate what the code does. Code that is well-written and properly named largely explains itself. Comments should address the "why"—the business logic, design decisions, and non-obvious implications.

 

Poor Comment:

// Increment counter

counter := counter + 1;

 

Good Comment:

// Increment counter to track number of bottles processed

// Reset occurs when daily production target is reached

counter := counter + 1;

 

Use comments to document assumptions, constraints, and potential pitfalls. Explain complex algorithms and non-obvious optimizations. Comment edge cases and error conditions.

 

Function Design: Single Responsibility Principle

Each function should have a single, well-defined responsibility. A function that does multiple things is harder to test, reuse, and maintain. Apply the Single Responsibility Principle (SRP) from software engineering to PLC code.

 

Poor Design:

FUNCTION_BLOCK ProcessData

    // Reads sensors, validates data, calculates statistics, and logs results

    // Does too many things

END_FUNCTION_BLOCK

 

Good Design:

FUNCTION_BLOCK ReadSensors

    // Responsibility: Read sensor inputs and perform basic validation

END_FUNCTION_BLOCK

 

FUNCTION_BLOCK CalculateStatistics

    // Responsibility: Calculate statistical metrics from validated data

END_FUNCTION_BLOCK

 

FUNCTION_BLOCK LogResults

    // Responsibility: Format and log results to persistent storage

END_FUNCTION_BLOCK

 

Smaller, focused functions are easier to test, understand, and reuse. They also facilitate code reuse across different projects.

 

DRY Principle: Don't Repeat Yourself

Duplicated code is a maintenance nightmare. When a bug is found in duplicated logic, it must be fixed in every location. When requirements change, all copies must be updated. Use functions and function blocks to eliminate duplication.

 

Poor (Duplicated Code):

// In Module A

IF sensor_value > threshold THEN

    alarm_triggered := TRUE;

    log_event("High sensor value detected");

    send_notification("Alert: Sensor threshold exceeded");

END_IF;

 

// In Module B

IF another_sensor > threshold THEN

    alarm_triggered := TRUE;

    log_event("High sensor value detected");

    send_notification("Alert: Sensor threshold exceeded");

END_IF;

 

Good (Reusable Function):

FUNCTION TriggerAlarm

VAR_INPUT

    sensor_name : STRING;

    sensor_value : REAL;

END_VAR

    alarm_triggered := TRUE;

    log_event(CONCAT("High ", sensor_name, " value detected"));

    send_notification(CONCAT("Alert: ", sensor_name, " threshold exceeded"));

END_FUNCTION

 

// Usage in both modules

TriggerAlarm("sensor_value", sensor_value);

TriggerAlarm("another_sensor", another_sensor);

 

Modularity and Encapsulation

Organize code into logical modules, each with a clear interface. Use User Defined Types (UDTs) and function blocks to encapsulate related data and operations. This promotes code reuse and makes systems easier to understand.

 

Example: Encapsulated Motor Controller

TYPE MotorController

    speed_setpoint : REAL;

    current_speed : REAL;

    temperature : REAL;

    fault_detected : BOOL;

   

    FUNCTION_BLOCK MotorController

        // Initialize motor controller

    END_FUNCTION_BLOCK

   

    FUNCTION Start

        // Start motor with safety checks

    END_FUNCTION

   

    FUNCTION Stop

        // Stop motor gracefully

    END_FUNCTION

   

    FUNCTION UpdateSpeed

        // Update motor speed based on setpoint

    END_FUNCTION

END_TYPE

 

This encapsulation makes it clear what operations are available on a motor controller and ensures consistent behavior.

 

Error Handling and Defensive Programming

Write code that anticipates and handles errors gracefully. Use return codes or exceptions to signal error conditions. Validate inputs before processing them.

 

Example: Defensive Function

FUNCTION CalculateAverage

VAR_INPUT

    values : ARRAY[1..100] OF REAL;

    count : INT;

END_VAR

VAR_OUTPUT

    average : REAL;

    error : BOOL;

END_VAR

 

// Validate input

IF count <= 0 OR count > 100 THEN

    error := TRUE;

    average := 0.0;

    RETURN;

END_IF;

 

// Calculate average

VAR sum : REAL := 0.0;

FOR i := 1 TO count DO

    sum := sum + values[i];

END_FOR;

 

average := sum / count;

error := FALSE;

 

Code Organization and Structure

Organize your SCL projects with a clear directory structure and naming convention. Group related function blocks, functions, and data types together. Use meaningful section comments to delineate different parts of the code.

 

Example Project Structure:

Project/

├── GlobalData/

   ├── DataTypes.scl (UDTs and custom types)

   ├── Constants.scl (Project-wide constants)

   └── GlobalVariables.scl (Global variables)

├── MotorControl/

   ├── MotorController.scl (Motor control function block)

   └── MotorFunctions.scl (Helper functions)

├── Sensors/

   ├── SensorReader.scl (Sensor input handling)

   └── SensorCalibration.scl (Calibration functions)

└── Main/

    └── Main.scl (Main program logic)

 

Testing and Validation

Write code with testability in mind. Use function blocks that can be instantiated and tested independently. Create unit tests for critical functions. Document test cases and expected results.

 

Example: Testable Function

FUNCTION_BLOCK PIDController

    // Designed to be tested independently

    // Inputs and outputs are clearly defined

    // No dependencies on external systems

END_FUNCTION_BLOCK

 

// Test case

VAR

    pid : PIDController;

    setpoint, process_value : REAL;

    output : REAL;

END_VAR

 

// Test 1: Zero error should produce zero output

setpoint := 100.0;

process_value := 100.0;

pid(setpoint := setpoint, process_value := process_value);

output := pid.output;

// Assert output ≈ 0.0

 

Documentation

Maintain comprehensive documentation that explains the overall architecture, key algorithms, and design decisions. Document assumptions about hardware, communication protocols, and external systems. Keep documentation synchronized with code changes.

 

Documentation Checklist:

        System architecture and data flow

        Function and function block descriptions

        Variable definitions and their units

        Error codes and their meanings

        Safety considerations and interlocks

        Known limitations and future improvements

        Change history and version control information

 

Conclusion

Clean code is not a luxury in PLC programming—it is a necessity. As automation systems become more complex and the cost of downtime increases, the ability to quickly understand and modify code becomes critical. By following these best practices—clear naming, focused functions, comprehensive comments, modularity, error handling, and thorough documentation—engineers create code that is reliable, maintainable, and professional.

 

The investment in writing clean code pays dividends throughout the system's lifecycle. Bugs are easier to find and fix. New team members can understand the code quickly. Changes can be made confidently. In the long run, clean code reduces costs, improves reliability, and enables innovation. For any engineer serious about their craft, mastering these practices is essential.

 

References

[1] Clean Code: A Handbook of Agile Software Craftsmanship - Robert C. Martin

[2] Code Complete: A Practical Handbook of Software Construction - Steve McConnell

[3] Siemens TIA Portal Programming Guidelines - https://support.industry.siemens.com/cs/document/109742519

4-20 mA Standard: Fundamentals

The 4-20 milliampere (mA) current loop standard represents one of the most fundamental and widely adopted signal transmission methods in industrial automation and process control.

Professional Industrial Control Room with PLC Systems
Figure 1: Professional Industrial Control Room with PLC Systems and 4-20 mA Measurement Displays
4-20 mA Current Loop System
Figure 2: Complete 4-20 mA Current Loop System - From Transmitter to PLC Input


Established over seven decades ago, this analog signal standard continues to dominate modern industrial environments despite the proliferation of digital communication protocols. Understanding the 4-20 mA standard is essential for anyone working with programmable logic controllers (PLCs), sensors, transmitters, and industrial instrumentation.

The 4-20 mA standard defines a current-based analog signal transmission method where 4 mA represents the minimum (0%) signal level and 20 mA represents the maximum (100%) signal level. This seemingly simple concept has become the backbone of countless industrial applications, from temperature measurement and pressure monitoring to flow rate detection and level sensing. The widespread adoption of this standard across industries speaks to its reliability, simplicity, and effectiveness in transmitting analog signals over long distances with minimal signal degradation.

Historical Context and Evolution

The 4-20 mA standard emerged in the 1950s as a solution to the limitations of voltage-based analog signals. Early industrial systems relied on 0-10 volt signals, which proved susceptible to noise, voltage drops over long cable runs, and signal degradation. Engineers recognized that current-based signals offered superior noise immunity and could maintain signal integrity over extended distances without requiring expensive shielding or signal conditioning.
The choice of 4-20 mA specifically was deliberate and strategic. The 4 mA minimum value, rather than 0 mA, serves a critical purpose: it allows for detection of broken wires or disconnected sensors. If a wire breaks, the current drops to zero, which is easily distinguishable from the 4 mA minimum signal. This built-in diagnostics capability provided a significant safety advantage over voltage-based systems. The 20 mA maximum was chosen as a safe upper limit that wouldn't cause excessive power dissipation in the circuit while remaining practical for most industrial applications.

Technical Principles

The 4-20 mA current loop operates on the principle of constant current transmission. Unlike voltage signals that can vary due to impedance changes and cable resistance, current signals remain relatively constant throughout the transmission path. This fundamental difference makes current loops inherently more robust and reliable for industrial applications.
A typical 4-20 mA loop consists of several key components: a signal source (transmitter), a power supply, connecting wires, and a signal receiver (PLC input module). The transmitter generates a current proportional to the measured parameter, and this current flows through the circuit loop. The receiver measures the voltage drop across a precision resistor (typically 250 ohms) to determine the current value. The voltage drop is then converted to a digital representation for processing by the PLC.
The mathematical relationship is straightforward: if a 250-ohm resistor is used in the receiver circuit, a 4 mA current produces a 1-volt drop (4 mA × 250 Ω = 1 V), and a 20 mA current produces a 5-volt drop (20 mA × 250 Ω = 5 V). This 1-5 volt range at the receiver provides a clear, measurable signal that analog-to-digital converters can easily process.

Advantages Over Alternative Standards

The 4-20 mA standard offers numerous advantages that explain its continued dominance in industrial settings. First, current-based signals demonstrate superior noise immunity compared to voltage signals. Industrial environments contain numerous sources of electromagnetic interference, including motor drives, welding equipment, and high-voltage power lines. Current signals, flowing through a complete loop, are far less susceptible to this interference than voltage signals referenced to ground.
Second, the 4-20 mA standard enables long-distance transmission without signal degradation. Cable resistance, which severely impacts voltage signals, has minimal effect on current signals. A 4-20 mA signal can reliably travel several kilometers through standard industrial cabling without requiring signal conditioning or amplification. This capability makes it ideal for distributed control systems where sensors are located far from the central control facility.
Third, the standard provides inherent diagnostics. The 4 mA minimum value allows systems to distinguish between a valid zero-level signal and a broken wire or disconnected transmitter. This feature has prevented countless industrial accidents and equipment failures by alerting operators to communication problems.
Fourth, 4-20 mA systems require relatively simple and inexpensive hardware. The signal conditioning circuitry is straightforward, and compatible transmitters and receivers are widely available from numerous manufacturers at competitive prices. This standardization has created a mature, well-established ecosystem of compatible equipment.

Industry Standards and Compliance

The 4-20 mA standard is formally defined in several international standards, most notably ISA SP50 (Instrumentation, Systems, and Automation Society) and IEC 60381-1 (International Electrotechnical Commission). These standards specify the electrical characteristics, signal ranges, accuracy requirements, and safety considerations for 4-20 mA systems.
Compliance with these standards ensures interoperability between equipment from different manufacturers. A temperature transmitter from one vendor will work reliably with a PLC input module from another vendor, provided both comply with the standard. This interoperability has been crucial to the standard's success and widespread adoption.
The standards also define performance characteristics such as accuracy, repeatability, and response time. Typical industrial-grade 4-20 mA transmitters maintain accuracy within ±0.5% of the full scale, with response times in the range of 100-500 milliseconds depending on the application.

Applications in Modern Industry

Today, 4-20 mA signals are ubiquitous in industrial automation. Temperature sensors, pressure transmitters, flow meters, level sensors, and pH analyzers all commonly use 4-20 mA output. In manufacturing facilities, these signals connect to PLCs that monitor and control processes. In utilities, they transmit data from remote monitoring stations to central control centers. In chemical plants, they provide critical measurements for process control and safety systems.
The standard's flexibility allows it to represent any measurable parameter. A pressure transmitter might output 4 mA for 0 PSI and 20 mA for 100 PSI. A temperature transmitter might output 4 mA for -50°C and 20 mA for 150°C. The transmitter's internal calibration determines the relationship between the measured parameter and the output current.

Coexistence with Digital Standards

While digital communication protocols like Profibus, Modbus, and Ethernet have gained significant market share, 4-20 mA systems remain prevalent and continue to be installed in new facilities. Many modern industrial systems use hybrid approaches, combining 4-20 mA signals for critical measurements with digital protocols for secondary data and diagnostics. This coexistence reflects the proven reliability of the 4-20 mA standard and the practical advantages it offers for certain applications.

Conclusion

The 4-20 mA standard represents a remarkable achievement in industrial standardization. Developed over seventy years ago, it continues to serve as the primary analog signal transmission method in industrial automation. Its advantages—noise immunity, long-distance capability, inherent diagnostics, and simplicity—ensure its continued relevance in modern industrial environments. For PLC programmers and automation engineers, thorough understanding of 4-20 mA principles, calculations, and applications remains essential knowledge. As industries evolve and adopt new technologies, the 4-20 mA standard will likely remain a foundational element of industrial control systems for decades to come.

Key Takeaways

4-20 mA represents a current-based analog signal standard with 4 mA = 0% and 20 mA = 100%
The 4 mA minimum enables detection of broken wires and disconnected sensors
Current signals offer superior noise immunity and long-distance transmission capability
The standard is formally defined in ISA SP50 and IEC 60381-1
4-20 mA systems remain widely used despite the emergence of digital protocols
Understanding this standard is essential for modern automation engineers