Can Python or C++ Control PLC Machines Deterministically?

Stefan Weidner10 min read
Other ManufacturerPLC HardwareTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

After separating deterministic machine control from user-mode analytics, the PLC owns the scan-critical sequence while Python or C++ handles only work whose latency and failure behavior the machine can tolerate. The deciding factor is not whether a computer can exchange data with a PLC. It is where the code executes, which runtime schedules it, and what happens when that path stalls.

Where does the control request originate?

Follow the packet. A local PLC task reads inputs, executes control logic, and updates outputs inside the controller runtime. Python or C++ can enter that path in three different places: as code supported by the PLC runtime, as a process on a PC-class controller beside the runtime, or as an application on an external computer communicating over a network.

Request origin Path to output Primary failure boundary Suitable role
PLC runtime task Runtime → local or configured I/O Task overrun, program fault, or controller fault Sequencing, interlocks, state machines, and time-bounded control
User-mode process on a PC-class controller Python/C++ process → local software interface → PLC task → I/O Process scheduling, interface failure, or stale exchanged data Algorithms, data transformation, and services outside the critical scan
External computer Application → network adapter → switches and cabling → controller service → PLC logic → I/O Any host, network, protocol, or controller-service failure OEE, traceability, recipe transfer, reporting, and operational data

A remote application can command almost any controller for which it has a compatible interface, but that does not make the application part of the deterministic PLC scan. Network transit, operating-system scheduling, protocol handling, and reconnect behavior add delay and jitter. Keep the final permissive and output decision in the PLC whenever delayed, duplicated, missing, or stale commands could create an unsafe or damaging state.

Can the target execute Python or C++?

Read the controller architecture and supported programming environment before writing code. A traditional controller may have processor speed measured in MHz and memory measured in KB, with a proprietary runtime and no general-purpose process environment. A PC-class controller may have GHz-class processing, GB-class memory, Windows or Linux, and a PLC runtime layered over the operating system. Only the second architecture inherently provides a place where ordinary user-mode applications might run.

Reading to take Outcome Decision
Manufacturer programming-language list Only IEC 61131-3 languages are listed Use Ladder, Function Block, Structured Text, or another listed language for deployed PLC logic. Move to the physical-path check only if an external application is still required.
Controller documentation lists a managed C/C++ extension C or C++ executes under defined runtime restrictions Check task context, memory-allocation limits, execution deadline, and debugging model before selecting it.
Controller exposes a general-purpose operating-system side A Python or C++ process can run beside the PLC runtime Treat the process and PLC as separate execution domains with an explicit data interface and stale-data handling.
No supported runtime or operating-system facility is documented The language cannot run locally through the normal engineering workflow Use an external host or translate the algorithm into a supported PLC language.

Beckhoff provides examples of both approaches. A TwinCAT installation can keep most control logic in Structured Text while starting a Python process with NT_StartProcess and exchanging values through ADS using pyads. Beckhoff also offers a C++ option identified as TC1300, with special limits including memory-allocation constraints. These capabilities do not establish support on every Beckhoff target; confirm the selected controller, runtime, and license in its documentation.

The Arduino Opta supports its IEC options and the Arduino C++ environment. The described toolchain provides C++11 rather than a newer language standard. Phoenix Contact PLCnext was identified as a platform to evaluate for Python and C++, but the installation described did not test it. Treat product-family names as search starting points, then verify the exact hardware and software combination.

What does the physical path show?

Layer one first. For an external host, prove the route before debugging Python syntax, C++ objects, PLC tags, or protocol services. Record the interfaces and link state at every hop; a successful application-level write cannot occur through a disconnected adapter, incorrect network segment, failed switch port, or unavailable controller interface.

  1. Identify the sending network adapter and read its configured address, subnet, and route.
  2. Trace the cable and switch path to the controller interface. Record link indication and error counters where the hardware exposes them.
  3. Read the controller interface address from its engineering configuration or diagnostic display.
  4. Test basic reachability with a method supported by the installation. A failed reachability test sends the investigation back to addressing, routing, link state, and filtering.
  5. If reachability passes, continue to the controller service and application interface. Reachability alone does not prove that ADS or another PLC service is configured or authorized.
Path setting Read from Mismatch effect
Host address and subnet Host adapter configuration The request leaves through the wrong interface or cannot reach the controller segment.
Controller address Controller configuration The application targets the wrong endpoint.
Route Host and controller routing tables Requests or replies stop between subnets.
Service and port Manufacturer runtime and protocol configuration The host is reachable, but the PLC communication endpoint does not accept the session.
Symbol or data identifier PLC project and exported interface The session opens, but reads or writes address the wrong object or fail resolution.

Does the communication path meet the control deadline?

Measure elapsed command time and variation at the PLC boundary. A PLC normally executes cyclically: it samples or maps inputs, runs logic, and updates outputs. Application code must return control within the task deadline. A blocking loop that waits indefinitely for an input conflicts with this model; implement the operation as a state machine that advances on successive scans.

Python running as an ordinary operating-system process is not a suitable default for deterministic control. Interpreter activity, operating-system scheduling, resource contention, and runtime pauses can interrupt execution at times unrelated to the PLC cycle. C++ can be deterministic only when the controller platform places it in a defined real-time task and constrains operations that could block or allocate unpredictably. The language name alone does not provide determinism.

Timing reading If within limit If outside limit
PLC task execution time versus configured deadline or watchdog The local algorithm fits its assigned task under the measured test load. Reduce work, divide it across approved tasks, or move noncritical computation outside the control task.
Age of data received from Python/C++ The PLC may consume it under an explicit freshness rule. Reject it, hold the defined fallback, and raise a communication status.
End-to-end request and acknowledgement time The path may serve the application if the machine tolerance exceeds the measured worst case. Move the time-critical decision into the PLC or a supported real-time component.
Reconnect and process-restart behavior The PLC retains authority and recovers without an unintended transition. Redesign ownership, startup state, and heartbeat handling before commissioning.

Do not decide from an average response time. Capture the longest observed transaction during representative CPU, network, I/O, and application load, then compare it with the machine requirement and the configured task diagnostics. Read the actual deadline and watchdog from the project; no universal value applies.

Which architecture matches the function?

Start with the consequence of lateness. A command that stops being valid when it is one cycle late belongs in the deterministic control domain. A calculation that can arrive later, be rejected when stale, or be repeated safely can cross into a Python or external C++ service.

Function Preferred execution domain Reason
Machine sequence, interlock, permissive, or immediate output decision PLC language or supported real-time extension The task scheduler, watchdog, and I/O update model govern execution.
Specialized computation not convenient in Structured Text Supported C++ real-time component, or user-mode service with PLC-side validation The correct choice depends on its deadline and permitted failure behavior.
OEE, traceability, reporting, and operational data External Python/C++ application These functions normally tolerate networked data exchange and do not need direct output ownership.
Custom robotics or CNC kinematic object Manufacturer-supported C++ extension C++ can implement the specialized object while conventional PLC logic invokes it.
Logic generation from a specification Offline Python tooling The script creates engineering artifacts rather than controlling live outputs.
Algorithm prototyping Python model followed by a controlled Structured Text translation Python accelerates iteration, while deployed code remains in the PLC environment.

Offline generation is a separate use case. One Rockwell Studio 5000 workflow used Python to generate approximately 90% of ladder logic from a software design specification through L5X-style routine files. Generated logic still requires review, import validation, compilation, and functional testing. Translation from Python to Structured Text also requires explicit checks for overflow, data truncation, type width, signedness, and different numeric behavior.

How should the resolving architecture be implemented?

  1. Classify each function by maximum tolerated latency, stale-data behavior, and consequence of host or network loss.
  2. Place interlocks, state transitions, command qualification, and final output authority in the PLC task.
  3. Select a local real-time C++ extension only when the exact controller and runtime document support for it. Read the permitted task contexts and memory rules before coding.
  4. For a PC-class controller, run Python or non-real-time C++ in the operating-system domain and expose only a narrow data contract to the PLC. The Beckhoff pattern can use NT_StartProcess to launch Python and ADS through pyads for exchange.
  5. Define command validity in PLC logic. Include a command value, an update indicator such as a sequence change, a status, and a freshness decision based on the PLC's own time base.
  6. Make startup conservative. The PLC must not treat default memory, a restarted process, or an old retained value as a new command.
  7. Keep communication operations nonblocking from the PLC task's perspective. Advance connection, request, response, timeout, and recovery as states across scans.
  8. Expose process health and data age to diagnostics so maintenance personnel can separate a stopped script, broken route, rejected symbol, and stale calculation.

Keep the maintenance boundary visible. Ladder, Function Block, and Structured Text are commonly selected because online monitoring exposes machine state in the same environment used for troubleshooting. When C++ or Python is necessary, leave the operating sequence and fault response readable in the PLC project rather than hiding machine behavior behind an opaque service.

How is the resolving branch verified?

  1. Compile the PLC project and any supported C++ component with no unresolved interface or type errors.
  2. Confirm the host address, controller address, route, configured service, and requested data identifiers against the running configuration.
  3. Observe the PLC task execution diagnostics at idle and under representative load. Compare the maximum reading with the configured task deadline or watchdog.
  4. Log the age and sequence of every external result at the PLC boundary. Verify that repeated or out-of-order results cannot retrigger a command.
  5. Disconnect the network, stop the user-mode process, and restart it. Confirm that the PLC detects stale data, applies the defined fallback, and does not issue an unintended output transition.
  6. Restore communication and verify that recovery requires a fresh, valid exchange rather than reuse of the last value.
  7. Exercise numeric boundaries in both environments, including overflow and truncation cases introduced by Python-to-Structured-Text translation or C++ type conversion.
  8. Run the machine through each permitted state while monitoring the PLC's command qualification, state transition, output, task time, and external-process status.

FAQ

How do I run Python with a Beckhoff PLC?

Keep the scan-critical logic in TwinCAT, launch the Python process with NT_StartProcess, and exchange defined values through ADS using pyads. Add PLC-side freshness, startup, and process-loss handling before allowing the data to influence a command.

How do I decide whether C++ belongs inside the PLC?

Check whether the exact target offers a supported real-time C++ facility such as Beckhoff TC1300, then read its task and memory-allocation restrictions. If the code executes as an ordinary operating-system process, treat it as an external service rather than deterministic PLC logic.

How do I use Python without controlling outputs directly?

Use it for OEE, traceability, reporting, operational data, offline L5X generation, or algorithm prototyping. Pass results through a narrow interface and let the PLC validate freshness, range, machine state, and command permission.

How do I verify that Python or C++ control is safe to release?

Measure maximum PLC task time and end-to-end data age under representative load, then stop the process and disconnect the network. The final verification is restoring the path and confirming that the PLC accepts only a fresh exchange without an unintended state or output transition.

Back to blog