TwinCAT 3 · Track 14 · one machine, many products

Recipes & production data

Real lines almost never make one thing. The same donut machine has to run strawberry rings at 9am, choc-sprinkles at noon and sugar minis after lunch, and the operator changing it over is not a programmer. A recipe is how you let them: a named set of setpoints you swap into the running task without touching a line of code. Pick a product below and watch the machine reconfigure itself.

📋 data, not code👷 operator-changeable🔁 repeatable batches
01 · The big idea

Keep the code, swap the data, get a different product

The sequence never changes: advance the belt, glaze the donut, box them. What changes between products is just numbers: glaze colour, dwell time, belt speed and box count. Recipes pull those numbers out of the code and into a working struct the task reads from. Click a recipe and the line keeps running exactly the same program; only the loaded setpoints change, and the machine follows them.

RUN · 1 ms task · production
✓ ACTIVE
Recipe set · pick a product to load
Active setpoints · what the task reads this cycle
⤷ fbDonutStation( Setpoints := Active ): the FB only ever sees Active. Loading a recipe copies one row of the set into it; the FB never knows which product it is.

👀 Notice what did not happen: no download, no recompile, no engineer. The yellow setpoints flashed, the belt re-timed and the glaze changed colour in a 0.3 s changeover. Recipes exist to put that gap between "change a product" and "change the program".

02 · Anatomy of a recipe

A type, an active copy, and a set

There are three pieces. First comes a type, a STRUCT that names every parameter a product needs. Then one active instance the running code reads. Then a set: many filled-in copies, one per product, stored on the controller. Loading a recipe means copying the chosen row of the set into the active instance.

// 1 · the TYPE — every product is shaped like this
TYPE ST_DonutRecipe :
STRUCT
    sGlazeType     : STRING;   // 'strawberry'
    nConveyorSpeed : INT;      // mm/s
    tGlazeDwell    : TIME;     // pour time
    rFryTemp       : REAL;     // °C setpoint
    bSprinkles     : BOOL;
    nBoxCount      : INT;      // donuts per box
END_STRUCT
END_TYPE
// 2 · the ACTIVE copy the task actually reads
VAR_GLOBAL
    Active : ST_DonutRecipe;        // loaded set
END_VAR

// 3 · the SET — many products, stored on disk
//    (a file/array of ST_DonutRecipe rows)

// loading is just a copy:
Active := Set[ nChosenProduct ];

// from here the sequence is product-blind:
IF bDonutAtStation THEN
    tDwell( IN:=TRUE, PT:=Active.tGlazeDwell );
END_IF

The stored set is a table: rows are products and columns are the struct fields. An operator sees exactly this on the HMI. Click any row to load it (it drives the live line above too):

ProductsGlazeTypeSpeedtGlazeDwellrFryTempbSprinklesnBoxCount
03 · The machine picks the recipe and proves it

The tooling selects the recipe; the barcode confirms it

On a real mixed-reference line, an operator picking from a list is the exception. The machine usually selects its own recipe from a hardware signal, and then it refuses to run until a second, independent source agrees. Learn this pattern cold, because you will find it all over automotive, pharma and food plants: tool ID in → barcode confirm → cycle-start.

🔧 Channel 1: the tooling tells the machine. Each reference has its own physical fixture / gripper / mould. When it's clamped in, it presents a coded tool ID: hardwired ID pins, an RFID tag, or an EtherCAT coupler the bus enumerates. The PLC reads nToolID and auto-loads that reference's recipe. The machine knows what it is set up to make without anyone typing anything.

🔎 Channel 2: the barcode proves the part. Knowing the setup isn't enough; the part in front of the tool could be the wrong one. A BCR (barcode reader) scans the incoming part and the PLC compares sScannedRef against the loaded recipe. If they agree, cycle-start is enabled. If they disagree, the part is rejected and an alarm raised, before anything is processed.

Try it. Clamp a tool (it auto-loads its recipe and drives the live line up in §01), present a part to the reader, then hit Scan. Feed it the wrong box on purpose and watch the machine stop itself.

1 Tooling clamped in the machine
2 Part presented to the BCR
· present a part ·
3 PLC verdict · cycle interlock
Awaiting scan…
Clamp a tool and present a part, then scan.
// the real pattern: select by tool, then VERIFY before you run
CASE nToolID OF                          // coded tool ID read at the inputs
    1: Active := Set.Strawberry;
    2: Active := Set.Choc;
    3: Active := Set.Sugar;
ELSE
    bToolFault := TRUE;  bCycleEnable := FALSE;   // no/unknown tool → inhibit
END_CASE

IF bScanDone THEN                          // the BCR finished a read
    IF sScannedRef = Active.sRef THEN
        bCycleEnable := TRUE;                  // ✓ both channels agree → run
    ELSE
        bRejectPart  := TRUE;  bAlarm := TRUE;  // ✗ wrong part for this tooling
    END_IF
END_IF

This is poka-yoke, or error-proofing. Two independent channels must name the same reference, or the machine won't cycle. The interlock has to handle every outcome in this table:

Tool IDBCR readPLC decisionWhat happens
valid · recipe loadedmatches Active.sRefCONFIRMEDbCycleEnable := TRUE: run the batch
valid · recipe loadeddifferent referenceMISMATCHbRejectPart + bAlarm: part ejected
valid · recipe loadedno-read / smudgedNO-READre-present / manual entry, never assume
none / unknownn/aNO TOOLbToolFault: cycle-start inhibited

Why bother, when an operator could just pick it? People mis-pick, and a wrong reference is expensive: scrapped product, a line stop, or a recall in regulated industries. Two-channel verification takes the human out of the critical decision and logs the scanned barcode against the batch, so you get traceability without extra work. Selection becomes automatic, and being right is checked by two independent channels.

04 · Where recipes live in TwinCAT

On the controller, not in the code

Recipes have to outlive any single download, so they are stored as files on the target, edited from the HMI, and loaded at runtime. TwinCAT gives you the Recipe Manager object and its RecipeManagement library for exactly this: register the variables you want managed, then save and load named recipes that persist on disk. (On TwinCAT HMI you'd use its built-in recipe management instead: the same idea with a nicer front end.)

👷
Operator · HMI
Picks "Choc Sprinkle" from a list, or edits a value and hits Save. No IDE, no login as engineer.
→
🗂️
Recipe files on disk
RecipeManagement stores each named recipe as a file in the runtime's file directory on the controller (the File path on the Recipe Manager's Storage tab), where it survives power-off. TwinCAT creates these files from the project's recipes when you download to the controller.
→
⚙️
Active struct → task
Load copies the chosen recipe into Active. The 1 ms task reads it on the next cycle and the machine follows without a stop or a download.

⚠️ Recipe ≠ PERSISTENT. PERSISTENT/RETAIN keep the machine's one current state alive across a reboot, as a single snapshot. A recipe set is a library of many named products you load on demand. They do different jobs, and people mix them up constantly.

✅ Validate on load. A recipe is just data, and data can be wrong. Range-check every field as you copy it into Active (a 600 °C fryer setpoint should be refused, not fried). Recipes move responsibility to the operator, so the code must guard the limits.

05 · Why & when to use recipes

The payoff is changeover, traceability & trust

Reach for recipes as soon as a machine makes more than one variant, or as soon as the same numbers get typed in twice. They buy you four things.

⏱️
Changeover in seconds, by the operator
Switching products becomes "pick from a list" instead of "call the programmer and download new code". A 20-minute, error-prone changeover shrinks to one tap, and you get that production time back on every switch.
0.3 s
🎯
Every batch is identical
The strawberry ring made today uses byte-for-byte the same setpoints as the one made next month. No more "I think the temp was about 180": the recipe is the spec, repeated exactly.
repeatable
🧾
Traceability for free
Log which recipe ran which batch and you can answer "what settings made lot #4471?", which food, pharma and any audited process require. The recipe name is your paper trail.
audit-ready
🛡️
One code base to maintain
Fix a bug in the glaze sequence once and every product inherits the fix. With copy-pasted per-product programs you'd fix it twelve times and miss one. A new product adds a row of data, while every duplicated program drifts on its own.
×1
06 · Check yourself

Two that catch people out

1 · Your donut line must make three products and the operators change it over themselves. What's the right structure?

2 · A colleague says "we already have PERSISTENT variables, so we don't need recipe management." Are they right?