Skip to main content

Integrating Artificial Intelligence (AI) into Programmable Logic Controller (PLC) programming is revolutionizing industrial automation by enhancing efficiency, adaptability, and predictive capabilities. Leveraging tools like the Python Scripting Engine in CODESYS and cloud-based AI services offers engineers innovative approaches to designing and managing PLC applications.

Python Scripting Engine in CODESYS

​The Python Script Engine in CODESYS is a powerful tool that enables automation of various development and project management tasks within the CODESYS environment. By integrating IronPython scripting, developers can streamline processes such as project creation, device configuration, and code generation, thereby enhancing efficiency and precision. ​

Key functionalities accessible through the Python Script Engine include:

  • System: Access to general CODESYS functionalities, such as exiting the application and handling the user interface.
  • Projects: Management of projects, including loading, creating, saving, and closing projects.
  • Online: Online functions like logging into devices and applications, managing access data, performing network scans, and gateway management.
  • LibraryManager: Management of library repositories, including viewing, installation, and removal of libraries.
  • DeviceRepository: Handling of device repositories, including import and export of device descriptions.

Scripts can be executed directly from the CODESYS Development System interface or via the command line, allowing for flexible integration of automation processes into existing workflows.

To facilitate the creation and editing of scripts, CODESYS offers a Python Editor plug-in. This tool provides features such as auto-completion, syntax highlighting, code structuring, and intelligent indentation, thereby increasing productivity during the development of automation solutions based on Python scripts.

By leveraging the Python Script Engine, developers can achieve a higher level of automation and accuracy in managing projects within CODESYS, leading to more efficient and reliable development processes.

For professionals in the field of industrial automation, precision and consistency are critical. The CODESYS Python Scripting Engine provides powerful capabilities to automate repetitive tasks such as adding devices, creating programs, importing libraries, or configuring projects — all with surgical precision.

The default path to the Script Engine stubs is:

C:\Program Files\CODESYS 3.5.xx.xx\CODESYS\ScriptLib\Stubs\scriptengine

This folder contains all the Python stubs required to interact with the CODESYS scripting API. These stubs act as an interface between your Python code and the internal CODESYS objects.

How to Use in VS Code

To enable features like code completion, syntax highlighting, and type hinting when writing CODESYS scripts in Visual Studio Code, you can do the following:

  1. Copy the scriptengine folder from the default path:

C:\Program Files\CODESYS 3.5.xx.xx\CODESYS\ScriptLib\Stubs\scriptengine

  1. Paste it into your Python project directory, for example:

my-codesys-scripts/

├── scriptengine/

├── my_script.py

This setup allows VS Code to recognize the CODESYS-specific modules, which helps with writing, testing, and debugging your automation scripts more efficiently — even outside of the CODESYS IDE.

Sample Programs in Python Scripting Engine for interacting with Codesys

Sample program: 

devId = None
devices = device_repository.get_all_devices("CODESYS Control Win V3")
for device in devices:
    devId = device.device_id
proj = projects.primary


# delete existing testdev device
existing = proj.find('testdev')
if len(existing) > 0:
    existing[0].remove()


# add device
proj.add('testdev', devId)
apps = proj.find('Application', True)
if len(apps) > 0:
    app = apps[0]
    tc = app.create_task_configuration()

What the Script Does

This script is used to:

  1. Fetch a device ID for CODESYS Control Win V3,
  2. Remove any existing device called testdev from the project,
  3. Add a new device using the fetched device ID,
  4. Create a Task Configuration for the application in the project.

Script Explained – Line by Line

devId = None

devices = device_repository.get_all_devices("CODESYS Control Win V3")

for device in devices:

    devId = device.device_id
  • Fetches all available devices from the Device Repository matching the type “CODESYS Control Win V3”.
  • Loops through the results and stores the device_id of the last matching device into the devId variable.

💡 In production scripts, you’d typically select a specific version or device instead of blindly taking the last one.

proj = projects.primary

Gets the currently opened CODESYS project — this is the project that the script will modify.

# delete existing testdev device

existing = proj.find('testdev')

if len(existing) > 0:

    existing[0].remove()
  • Searches the project for any device named testdev.
  • If found, it removes the existing device to avoid duplicates.
  • This makes the script idempotent — safe to run multiple times.
# add device

proj.add('testdev', devId)

Adds a new device to the project with the name testdev, using the device_id fetched earlier.

apps = proj.find('Application', True)

if len(apps) > 0:

    app = apps[0]

    tc = app.create_task_configuration()
  • Searches for the Application object in the project (recursive = True).
  • If found, it gets the first one and creates a Task Configuration within it.
  • Task configurations are needed to define cyclic tasks like MainTask or VISU_TASK.

Summary

This script:

  • Dynamically finds a CODESYS device,
  • Ensures the project has no duplicates,
  • Adds the new device cleanly,
  • Prepares the project with a new task configuration — ready for adding programs.

More sample scripts:

Add a device with a task configuration

# add device with a task configuration
proj.add('PLC', devId)
apps = proj.find('Application', True)
if len(apps) > 0:
    tc = apps[0].create_task_configuration()


# add task
task = tc.create_task('Task')
task.pous.add('PLC_PRG')


# proj = projects.open("C:\Users\Administrator\Documents\PL Przepompownia AI\Untitled1.project")


# devId = None
# devices = device_repository.get_all_devices("CODESYS Control Win V3")
# for device in devices:
#     devId = device.device_id
# proj = projects.primary


# # add device
# proj.add('PLC', devId)
# apps = proj.find('Application', True)
# if len(apps) > 0:
#     tc = apps[0].create_task_configuration()


# # add task
# task = tc.create_task('Task')
# task.pous.add('PLC_PRG')

Create a POU

proj = projects.primary # current project


found = proj.find("Application", True)
app = found[0]


# Create FB
mypou = app.create_pou("MyPou")


# Change declaration of the FB
implementation = mypou.textual_declaration.replace("""FUNCTION_BLOCK MyPou
VAR_INPUT
   iValue : INT;
END_VAR
VAR_OUTPUT
END_VAR
VAR
END_VAR""")


# Change implementation of the FB
mypou.textual_implementation.replace("""iValue := iValue + 1;""")


# Add method to FB
dosomething = mypou.create_method("DoSomething", "INT")


# Change declaration of the method
dosomething.textual_declaration.replace("""METHOD DoSomething : INT
VAR_INPUT
   iVal1 : INT;
   iVal2 : INT;
END_VAR""")


# Change implementation of the method
dosomething.textual_implementation.replace("""DoSomething := iVal1 + iVal2;""")


# # Find the pou and delete it
# found = app.find("MyPou")
# if found and len(found) == 1:
#    found[0].remove()
# else:
#    print("POU 'MyPou' was not found")

Create global and persistent variable list:

# Get the primary project or open a project
proj = projects.primary  # Use the currently open project


# Find the Application object
found = proj.find("Application", True)
app = found[0]


# Create a Global Variable List (GVL)
gvl = app.create_gvl("GVL_Process")


# Define the global variables in the GVL
gvl.textual_declaration.replace("""VAR_GLOBAL
    // System Configuration
    g_xSystemEnabled    : BOOL := TRUE;    // System enable flag
    g_xEmergencyStop    : BOOL := FALSE;   // Emergency stop status
    g_xMaintenanceMode  : BOOL := FALSE;   // Maintenance mode flag
   
    // Process Variables
    g_rTemperature      : REAL;            // Process temperature
    g_rPressure         : REAL;            // Process pressure
    g_rFlowRate         : REAL;            // Flow rate
    g_rLevel            : REAL;            // Tank level
   
    // Control Parameters
    g_rTempSetpoint     : REAL := 25.0;    // Temperature setpoint
    g_rPressSetpoint    : REAL := 2.5;     // Pressure setpoint
    g_rFlowSetpoint     : REAL := 100.0;   // Flow rate setpoint
    g_rLevelSetpoint    : REAL := 50.0;    // Level setpoint
   
    // Alarms
    g_xTempAlarm        : BOOL;            // Temperature alarm
    g_xPressAlarm       : BOOL;            // Pressure alarm
    g_xFlowAlarm        : BOOL;            // Flow rate alarm
    g_xLevelAlarm       : BOOL;            // Level alarm
   
    // Timers
    g_tCycleTime        : TIME := T#100MS; // Control cycle time
    g_tAlarmDelay       : TIME := T#5S;    // Alarm delay time
END_VAR""")


# Create a persistent Global Variable List
persistent_gvl = app.create_persistentvars("GVL_Settings")


# Define the persistent global variables
persistent_gvl.textual_declaration.replace("""VAR_GLOBAL PERSISTENT
    // System Settings
    p_xAutoStartEnabled : BOOL := TRUE;    // Auto-start on power up
    p_tStartupDelay     : TIME := T#30S;   // Startup delay time
   
    // Control Parameters
    p_rTempKp           : REAL := 1.5;     // Temperature controller P gain
    p_rTempKi           : REAL := 0.2;     // Temperature controller I gain
    p_rTempKd           : REAL := 0.1;     // Temperature controller D gain
   
    p_rPressKp          : REAL := 2.0;     // Pressure controller P gain
    p_rPressKi          : REAL := 0.3;     // Pressure controller I gain
    p_rPressKd          : REAL := 0.05;    // Pressure controller D gain
   
    p_rFlowKp           : REAL := 1.0;     // Flow controller P gain
    p_rFlowKi           : REAL := 0.15;    // Flow controller I gain
    p_rFlowKd           : REAL := 0.0;     // Flow controller D gain
   
    p_rLevelKp          : REAL := 3.0;     // Level controller P gain
        p_rLevelKi          : REAL := 0.25;    // Level controller I gain
    p_rLevelKd          : REAL := 0.1;     // Level controller D gain
   
    // Alarm Thresholds
    p_rTempHighLimit    : REAL := 80.0;    // Temperature high limit
    p_rTempLowLimit     : REAL := 5.0;     // Temperature low limit
    p_rPressHighLimit   : REAL := 5.0;     // Pressure high limit
    p_rPressLowLimit    : REAL := 0.5;     // Pressure low limit
    p_rFlowHighLimit    : REAL := 200.0;   // Flow high limit
    p_rFlowLowLimit     : REAL := 10.0;    // Flow low limit
    p_rLevelHighLimit   : REAL := 90.0;    // Level high limit
    p_rLevelLowLimit    : REAL := 10.0;    // Level low limit
END_VAR""")


# Create a constant Global Variable List
const_gvl = app.create_gvl("GVL_Constants")


# Define the constant global variables
const_gvl.textual_declaration.replace("""VAR_GLOBAL CONSTANT
    // System Constants
    c_iMaxDevices       : INT := 10;       // Maximum number of devices
    c_iMaxAlarms        : INT := 100;      // Maximum number of alarms
   
    // Physical Constants
    c_rGravity          : REAL := 9.81;    // Gravity acceleration (m/s²)
    c_rWaterDensity     : REAL := 1000.0;  // Water density (kg/m³)
    c_rAirDensity       : REAL := 1.225;   // Air density at sea level (kg/m³)
   
    // Conversion Factors
    c_rPa_To_Bar        : REAL := 0.00001; // Pascal to Bar conversion
    c_rBar_To_PSI       : REAL := 14.5038; // Bar to PSI conversion
    c_rLiter_To_M3      : REAL := 0.001;   // Liter to cubic meter conversion
   
    // Time Constants
    c_tMaxRunTime       : TIME := T#24H;   // Maximum continuous run time
    c_tMaintenanceInterval : TIME := T#720H; // Maintenance interval (30 days)
END_VAR""")


print("Global Variable Lists created successfully!")

Simulation mode:

# set device to simulation mode - this only check Simulation mark in the online tab
devs = proj.find('testdev') # type here project name up to Application
    devs[0].set_simulation_mode(True)


# alternatively check the sim is enabled beforehand and turn it on if not yet enabled


devs = proj.find('testdev')
if len(devs) > 0:
   # check if sim is enabled
   cur_sim_mode = devs[0].get_simulation_mode()
   print(cur_sim_mode)
   # enable sim conditionally
   if not cur_sim_mode:
      devs[0].set_simulation_mode(True)

If you want to fully discover the potential of the Python Scripting Engine in CODESYS and understand how to streamline process automation with insights from Claude Sonnet, be sure to check out Part 2 of the article. There, you’ll learn how to apply the acquired knowledge in practice and harness the advanced capabilities of this tool.

Author

Matthew Kurantowicz

Author Matthew Kurantowicz

More posts by Matthew Kurantowicz