What CNC machines—and an established part of the automation industry I should have paid more attention to—taught me about building more flexible machines
I have spent much of my career programming machines.
Some have been conventional PLC-controlled machines built around sequential operations. Others have been CNC-style systems where the machine programming and the part programming were very clearly separated.
For a long time, I treated those as two different types of automation.
I am beginning to think that was a mistake.
On many custom machines, particularly machines built around a PLC, it is common for the control program to contain not only how the machine works, but also the exact sequence required to manufacture the product the machine was originally designed to make.
Clamp the part.
Move to a position.
Turn on a process.
Wait for a condition.
Perform an operation.
Inspect.
Move somewhere else.
Repeat an operation.
Unclamp.
That can be a perfectly good way to build a machine, particularly when the sequence is fixed and unlikely to change.
But many machines do change.
The customer adds another product.
The process engineer discovers a better order of operations.
A second material needs to be processed differently.
An inspection operation needs to happen earlier.
A product no longer requires one of the original operations.
Or perhaps the machine was originally designed around one product and, five years later, the customer wants to use it for something that nobody anticipated when the machine was built.
I have been involved with machines where the physical capability was already there.
The axes could make the moves. The valves, clamps, sensors, heaters, lasers, inspection devices or other equipment already existed. Mechanically, the machine could do what was being asked of it.
The problem was that the product sequence was embedded in the machine program.
So what was fundamentally a process change became a machine-software change.
Someone had to open the controller project, understand an existing sequence, modify it, test it, and make sure the new path through the program did not create an unintended effect somewhere else.
The more products a machine accumulated, the more complicated this could become. I have worked with sequencers that eventually had to jump to different steps, skip sections, return to earlier operations or follow different paths depending on the selected product.
Eventually, simply understanding what the machine was supposed to do for one particular product could become difficult.
That experience has convinced me of something I now believe quite strongly:
Program the machine to know what it can do.
Program the product or process to tell the machine what it should do.
Those are two different programming responsibilities.
And I think we combine them far too often.
This Is Not a PLC Programming Language Argument
Before going any further, I want to make one distinction clear.
This is not an argument against PLCs.
It is definitely not an argument against ladder logic.
Modern PLC platforms give us several useful ways to program a machine. IEC 61131-3 defines Structured Text, Ladder Diagram, Function Block Diagram and Sequential Function Chart as part of its programming model, and those tools exist because different problems are best expressed in different ways. [1] (PLCopen)
I have always believed in using the programming method that fits the task.
A single PLC application may reasonably use ladder for discrete logic, Structured Text for calculations and data handling, function blocks for reusable equipment functions and SFC or another state-oriented structure for sequences.
That is not the issue I am trying to address.
My question comes before choosing the programming language:
Should the product-specific sequence be part of the machine application at all?
That is a very different question.
What CNC Machines Teach Us
My experience with CNC machines has been completely different.
A CNC machine is programmed to know how to perform a set of well-defined functions.
Move an axis.
Interpolate a line or arc.
Turn a spindle or process on.
Apply an offset.
Set a feed rate.
Change a tool.
Call a machine-specific function.
The machine builder programs those capabilities.
But the machine builder generally does not program the sequence required to manufacture every part that will ever run on the machine.
The part programmer does that.
The part program becomes an executable description of the product. It can contain coordinates, feeds, speeds, process commands, tool selections and the order in which the required operations are performed.
The RS274/G-code model is a good example of this separation. NIST’s RS274NGC work describes an interpreter that reads numerical-control code and converts that input into canonical machining functions. Input can come directly from a file, while the interpreter and machine control remain separate from the specific part program being executed. [2] (NIST)
That distinction is important.
We do not modify the CNC control software because tomorrow’s part has three holes instead of four.
We do not rewrite the machine controller because a feature has moved 10 mm.
We do not normally change the CNC because the customer wants to perform two machining operations in a different order.
The machine already knows how to drill, move, interpolate, and operate its spindle.
The part program says where, when, and with what parameters those capabilities are used.
Parts change constantly.
The underlying machine program does not have to.
The more I compare that architecture with some of the custom machines I have worked on, the more I wonder why we do not use the same philosophy more often for sequential manufacturing processes.
I Apparently Arrived Late to This Party
Researching this article has been a little humbling.
I originally came to this idea by comparing my experience programming CNC machines with my experience programming conventional sequential machines.
I thought the distinction was interesting:
Why are we comfortable separating the product program from the machine control on a CNC, yet often combine them on other types of manufacturing machines?
Then I started looking more closely at ISA-88.
It turns out a substantial part of the process-control industry has understood the underlying principle for a long time.
ISA-88 was first published in 1995 and established models for batch-control systems that deliberately distinguish equipment capabilities, control procedures and recipes. ISA describes the series as providing models for modular designs and efficient recipe management, while Part 2 specifically addresses the representation and exchange of recipes, equipment capabilities and control procedures. [3] (isa.org)
ISA’s own retrospective describes ISA-88 as an established automation design model built around modularity and reuse. [4] (isa.org)
I am not claiming that ISA-88 says every custom manufacturing machine should be programmed the way I am proposing.
It does not.
ISA-88 is a batch-control standard, and the kinds of machines I am discussing may be laser systems, assembly equipment, coating machines, inspection systems, deposition equipment or completely different process machines.
But the architectural similarity is hard to ignore.
ISA-88 separates reusable equipment behavior from product-specific recipe procedures.
I arrived at essentially the same idea by looking at CNC machines.
That tells me I am not inventing a new programming theory.
I am apparently late to the party.
And I suspect quite a few other machine builders and PLC programmers may be late to it too.
The concept I have been calling Recipe-Driven Machine Control is therefore not meant to be presented as another formal standard.
It is a way of applying an already-established architectural idea more broadly:
Build and validate the machine capabilities once. Put the product-specific order of those capabilities somewhere else.
A Recipe Can Be More Than a Table of Numbers
Recipes are already common in industrial machines.
But many machine recipes are primarily collections of parameters.
For example:
PRODUCT = 127
TEMPERATURE = 150
PRESSURE = 70
DWELL_TIME = 12
PROCESS_SPEED = 125
POWER = 750
That is useful.
It prevents us from hard-coding product values into controller logic.
But we have only separated the parameters from the program.
We have not separated the procedure.
Suppose Product 127 needs:
- Clamp the part.
- Move to Position A.
- Inspect Feature 1.
- Perform Process A.
- Move to Position B.
- Perform Process B.
- Inspect Feature 2.
- Unclamp.
Product 128 might use the exact same machine but require:
- Clamp.
- Move to Position B.
- Perform Process B.
- Move to Position A.
- Perform Process A.
- Skip the first inspection.
- Perform a different final inspection.
- Unclamp.
A traditional parameter recipe still leaves us with a problem.
Now we start adding recipe flags:
SKIP_INSPECTION_1 = TRUE
PROCESS_B_FIRST = TRUE
USE_FINAL_INSPECTION_TYPE_2 = TRUE
RETURN_TO_POSITION_A = TRUE
Then the fixed machine sequencer has to interpret those flags and find its way through the correct path.
At some point I think we should ask a simpler question:
Why not put the sequence itself in the recipe?
Make the Process Program the Recipe
Instead of thinking of the recipe as a table of numbers, imagine a machine with a controlled vocabulary of validated commands:
CLAMP_PART
VERIFY_PART
MOVE
SET_PROCESS
PROCESS_ON
PROCESS_OFF
INSPECT
WAIT
UNCLAMP_PART
Then the product file might look something like this:
PART = 127
REVISION = C
CLAMP_PART
MOVE X=125.0 Y=40.0
INSPECT FEATURE=1
SET_PROCESS POWER=750
PROCESS_ON
MOVE X=225.0 SPEED=120
PROCESS_OFF
MOVE X=250.0 Y=65.0
INSPECT FEATURE=3
UNCLAMP_PART
That one file now contains both the process parameters and the manufacturing procedure.
A second product might be:
PART = 128
REVISION = A
CLAMP_PART
MOVE X=250.0 Y=65.0
SET_PROCESS POWER=680
PROCESS_ON
MOVE X=190.0 SPEED=95
PROCESS_OFF
MOVE X=125.0 Y=40.0
INSPECT FEATURE=1
UNCLAMP_PART
The machine application did not change.
The machine capabilities did not change.
The product changed.
That feels like a much cleaner division of responsibility.
And it closely resembles the conceptual separation found in ISA-88, where procedural recipe elements call upon equipment capabilities rather than redefining the underlying equipment control for each product. ISA’s own training material distinguishes procedural elements, equipment entities and the phase logic that actually executes in the equipment. [5] (isa.org)
The Machine Functions Have to Be Real Functions
This architecture only works if the callable machine functions are properly engineered.
CLAMP_PART cannot simply be another name for:
Output_347 = TRUE
Otherwise, we have not really separated anything meaningful.
A reusable machine function should have a defined contract.
For example, CLAMP_PART might understand:
- Which machine states permit clamping.
- Whether a part is present.
- Whether pneumatic or hydraulic pressure is available.
- Which outputs must be controlled.
- Which sensors confirm successful clamping.
- How long the operation may take.
- What constitutes failure.
- Which alarm should be generated.
- What state the machine should enter after failure.
- How the operation can be reset or recovered.
From the process program’s point of view, however, all of that should be represented by something much simpler:
CLAMP_PART
The process program requests a machine capability.
The underlying machine software determines how to perform that capability correctly.
That is where modular programming becomes important.
The controller application can still contain sophisticated state handling, interlocks, diagnostics, communications, motion control, device control, and error recovery.
We are not removing machine intelligence.
We are organizing it.
We Should Validate Capabilities Instead of Rewriting Them
Another consequence I find attractive.
Once a machine function has been engineered and tested, we should be able to reuse it.
Suppose CLAMP_PART has already been validated.
It checks the required conditions.
It handles the device.
It confirms feedback.
It detects timeout.
It reports meaningful faults.
Why should Product B require another version of clamping logic?
It should not.
Product B should simply call CLAMP_PART.
The same idea can apply to:
LOAD_PART
MOVE_TO_PROCESS
START_VACUUM
WAIT_FOR_PRESSURE
START_LASER
INSPECT_FEATURE
CHANGE_TOOL
DISPENSE
HEAT
COOL
UNLOAD_PART
This is where the relationship to established modular-control thinking becomes particularly interesting. ISA-88 formalized reusable equipment and procedural elements decades ago, while modern automation practitioners continue to advocate abstracting recipe configuration from the PLC layer so that process personnel can adjust manufacturing behavior without continually modifying the reusable controller layer. [6] (Automation World)
Again, I am not arguing that every machine should literally implement ISA-88.
I am arguing that the separation it represents is worth learning from.
The Process Engineer Should Be Able to Read the Process
One of my biggest attractions to a text-based process program is simply readability.
I have worked with machines where figuring out the manufacturing sequence for one product required looking through controller code, step numbers, recipe parameters and conditional branches.
That makes the controller program carry two kinds of information simultaneously:
Machine implementation: how the equipment actually works.
Manufacturing intent: what this particular product requires.
Those are not the same thing.
A well-designed process file could make the manufacturing intent obvious.
Someone who understands the process could open:
PART_127_REV_C.PROC
and read from top to bottom what the machine is expected to do.
That person should not need to know which EtherCAT output drives the clamp valve.
They should not need to understand the servo tuning.
They should not need to understand how the vision system communicates.
They should not need to know how a timeout is implemented.
They need to understand the product.
That is a much better separation of expertise.
Why I Like Text Files
I strongly favor a text-based representation for the process program.
It does not have to be G-code.
It might be G-code.
It might be a purpose-built process language.
It might use descriptive commands rather than numeric M-codes.
The exact syntax is secondary.
The important point is that the process definition should be a straightforward, portable file.
That creates some useful possibilities.
The file can be:
- Read without opening the controller programming environment.
- Stored with the product documentation.
- Backed up independently from the machine application.
- Compared between revisions.
- Reviewed before release.
- Generated by another software tool.
- Simulated or checked before execution.
- Archived with production records.
- Managed in a version-control system.
- Examined by engineering software or AI tools.
I particularly like the possibility that the same file could serve three roles:
Executable process program.
Product recipe.
Human-readable process record.
That is much harder to achieve when the manufacturing procedure is distributed through controller logic and a separate recipe database.
But the Process Program Cannot Own Everything
Making the sequence easier to change does not mean making the machine uncontrolled.
Quite the opposite.
The process layer needs boundaries.
A production implementation would likely require things such as:
- Approved and active recipe revisions.
- User permissions.
- Parameter limits.
- Syntax checking.
- Allowed command checking.
- Compatibility checking between recipe and machine software.
- Audit history.
- Backup and restore.
- Simulation or dry-run capability.
- Controlled release procedures.
And some functions should simply never be available to the process program.
Safety should remain part of the engineered machine architecture.
Essential interlocks should remain part of the engineered machine architecture.
Device protection should remain part of the engineered machine architecture.
A recipe can request:
PROCESS_ON
but it should not have the authority to bypass the conditions that determine whether PROCESS_ON is permitted.
This separation is also consistent with ISA-88’s functional model, which treats recipe management, sequential control, regulatory control, and safety interlock systems as related but distinct control-system functions. [3] (isa.org)
ACS Motion Control Provides a Useful Example
Because much of my work involves motion-intensive and laser-processing machines, ACS Motion Control provides a particularly good example of how this architecture can be implemented.
ACS’s SPiiPlus platform supports G-code through GSP, including standard and user-definable G and M codes. ACS specifically describes user-defined codes as a way to create machine-specific functionality and cites laser configuration as an example. GSP can also be combined with ACSPL+ for additional machine functionality. [7] (ACS Motion Control)
More importantly for this discussion, ACS documentation explicitly states that a controller program buffer can contain ACSPL+, pure GSP code or a mixture of the two. Mixing them allows the application to coordinate I/O with motion, check machine conditions during G/M execution and activate controller-specific functions. [8] (ACS Motion Control)
That creates a natural architecture.
The GSP/process layer can contain the product program.
ACSPL+ can implement the underlying machine capabilities.
A custom M-code might request:
M120 ; Clamp Part
M200 ; Process Gas On
M300 ; Laser Process Enable
M400 ; Inspection Cycle
Or a future interface could expose those capabilities with more descriptive names.
Either way, the important distinction remains:
The process program says what needs to happen.
The machine software determines how the machine safely and correctly makes it happen.
ACS is only one implementation example.
This concept does not depend on ACS.
It does not depend on G-code.
It does not even depend on using a PLC.
It is the separation that matters.
Where I Think This Approach Makes the Most Sense
I would not add another programming layer just because it sounds architecturally elegant.
There has to be a reason.
A fixed-purpose machine that performs six operations in exactly the same order for its entire life may not gain enough from an editable process language to justify it.
Likewise, some automation systems are fundamentally event-driven rather than product-sequence-driven.
But I think this approach becomes increasingly attractive when:
- The machine will manufacture multiple products.
- The process is expected to evolve.
- Customers may add products years after installation.
- Product variants use different combinations of the same equipment functions.
- Process engineers regularly change both parameters and procedure.
- The same basic machine platform will be reused across several applications.
- Manufacturing intent should be independently reviewed, documented or version-controlled.
The simple test I keep coming back to is this:
If the machine already has all the physical capabilities required for a new product, should adding that product require changing the underlying machine-control application?
Sometimes the answer will legitimately be yes.
But I think we accept that answer far too quickly.
The Question I Wish I Had Asked Earlier
For most of my career, when I approached a sequential machine, one of the first questions was:
How should I program the sequence?
What states do I need?
What steps do I need?
How should I transition between them?
How should I handle the different products?
Those are still important questions.
But I now think there is one question that should come first:
Whose sequence is it?
Is this really the machine’s sequence?
Or is it the product’s process?
If it is the product’s process, maybe it belongs with the product.
That sounds obvious after you say it.
It was not always obvious to me.
Conclusion
The more I study this, the less I think this is really about PLC programming.
It certainly is not about Ladder versus Structured Text versus SFC.
Those are implementation tools.
Use whichever ones make the underlying machine software understandable, maintainable and appropriate for the task. Use several of them in the same project when that makes sense.
The bigger question comes before any of that:
What belongs in the machine program, and what belongs in the product program?
CNC machines have answered that question successfully for decades.
The machine builder programs the capabilities.
The user programs the part.
Parts change constantly.
The CNC does not.
The batch-process industry reached a related conclusion through equipment and recipe models long ago.
I am apparently late to that party.
But discovering that has made me more convinced of the idea, not less.
For flexible sequential machines, I think we should stop automatically assuming that the complete product procedure belongs inside the controller application.
Build the machine software around reusable, validated capabilities.
Give those capabilities defined inputs, outputs, permissives, status, diagnostics, timeout behavior, and recovery.
Then let a controlled, readable process program define how those capabilities are used for each product.
Program the machine to know what it can do.
Program the product or process to tell it what to do.
I think a lot more machines should be built that way.
References
[1] PLCopen — IEC 61131-3: Programming Languages. Overview of the standardized PLC programming languages, including Structured Text, Ladder Diagram, Function Block Diagram and Sequential Function Chart.
PLCopen — IEC 61131-3
[2] National Institute of Standards and Technology — The NIST RS274NGC Interpreter, Version 3. NISTIR 6556, describing RS274 numerical-control input, interpretation and canonical machining functions.
[3] International Society of Automation — ISA-88 Series of Standards / ISA88 Batch Control. Official ISA descriptions of batch-control architecture, recipes, equipment capabilities, control procedures and modular designs.
[4] Dennis Brandl, ISA InTech — “The ISA-88 Batch Control Standards.” Historical overview of ISA-88, its modular design model and industrial adoption.
**[5] International Society of Automation — Batch Control Using the ISA-88 Standards (IC40). ** ISA training overview covering equipment entities, procedural elements, phase logic, states and alternative controller architectures.
[6] Automation World — “The Power of Modular PLC Code.” Discussion of modular controller functions and abstraction of recipe configuration from the PLC layer.
[7] ACS Motion Control — “G-Code Programming.” Describes SPiiPlus G-code support, user-definable G/M codes, machine-specific functionality and integration with ACSPL+.
[8] ACS Motion Control Knowledge Center — “GSP and ACSPL+.” Documents combining GSP and ACSPL+ in a program buffer for synchronous I/O, condition checking and controller-specific machine functions.