Building an S7-300 Recipe Engine for 48-Function Exhaust Dyeing

David Krause16 min read
S7-300SiemensTechnical 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

1. System Architecture Overview

The exhaust dyeing machine requires a flexible batch control system where the operator can compose an arbitrary sequence of process steps on the HMI before pressing start. Each step contains an arbitrary selection of unit operations drawn from a library of 48 function blocks, and every function selected for a given step must execute simultaneously. A single physical action (a function) can appear in multiple steps with different parameter sets, so the architecture must treat the 48 FBs as a library of types rather than a fixed list of 48 singletons.

The reference architecture decomposes the controller into four cooperating layers:

  1. Hardware abstraction FBs (FB100-FB147) – 48 reusable unit-operation blocks covering heating, cooling, water filling, drain, circulation, chemical dosing, color dosing, agitation, pH dosing, sampling, rinsing, and similar exhaust-dyeing primitives. Each is parameterised at call time, never hard-wired.
  2. Recipe DB (DB200) – a structured data block holding an array of N steps, each step containing an array of (FB-number, instance-pointer, parameter-set) tuples.
  3. Batch engine FB (FB200) – the sequencer. It walks the recipe array, raises the "StepActive" flag for the current step, dispatches calls to every FB whose index is listed in the current step, evaluates a per-step done-condition, and advances to the next step.
  4. HMI panel (WinCC flexible / TIA WinCC) – provides the recipe editor screen, the live run screen, the fault display, and the Continue button used after an interruption.

For an S7-300 with this scope, a CPU 315-2 PN/DP (6ES7315-2EH14-0AB0) with a 16 KB work memory is the minimum; a CPU 317-2 PN/DP (6ES7317-2EK14-0AB0) with 1 MB work memory is recommended if all 48 instance DBs are to be held in load memory. Programming can be done in STEP 7 V5.5 SP2 or in TIA Portal V16 with the S7-300 Add-On package.

2. S7-300 Hardware Selection and I/O Allocation

An exhaust dyeing machine typically uses the following I/O classes. Quantities below are reasonable for a 200-500 kg machine; scale them to your installation.

Signal class Typical count Module example Order number
Digital inputs (24 V DC) 64 SM 321 DI32x24VDC 6ES7321-1BL00-0AA0
Digital outputs (24 V DC, 0.5 A) 32 SM 322 DO32x24VDC/0.5A 6ES7322-1BL00-0AA0
Relay outputs (valves, heaters) 16 SM 322 DO16x230VAC/2A RLY 6ES7322-1HH01-0AA0
Analog inputs (PT100 / 4-20 mA) 8 SM 331 AI8xTC/4xRTD 6ES7331-7PF11-0AB0
Analog outputs (VFD speed, control valves) 4 SM 332 AO4x12Bit 6ES7332-5HD01-0AB0

Wire RTD temperature sensors (bath, jacket, dosing tank) into the AI8 module. Drive VFDs for the circulation pump and the agitator from the AO module. Hard-wire the steam valve, drain valve, and inlet valves to relay outputs sized for inductive loads; for inductive DC valves add a free-wheeling diode (1N4007) directly at the coil.

3. Function Block Library (48 Unit Operations)

Each of the 48 unit operations is implemented as an FB with a uniform interface. This uniformity is the single most important architectural decision: the batch engine can dispatch any function by index without case-by-case glue logic.

Standard FB interface (SCL):

FUNCTION_BLOCK FB_Template
VAR_INPUT
  iEnable   : BOOL;   // 1 = function is allowed to run
  iStart    : BOOL;   // 1 = operator/HMI issues run command
  iAbort    : BOOL;   // 1 = emergency or recipe abort
  iParams   : ARRAY[1..16] OF REAL; // parameter slot
  iSetpoint : REAL;   // primary setpoint (e.g. temperature, level)\i>
END_VAR
VAR_OUTPUT
  oRunning  : BOOL;
  oDone     : BOOL;   // pulses for one cycle on completion
  oError    : BOOL;
  oErrorId  : WORD;
  oActual   : REAL;
END_VAR
VAR
  State     : INT;    // 0=Idle,1=Ramp,2=Hold,3=Done,99=Fault
  TonHold   : TON;    // holds for iParams[2] seconds
  TonRamp   : TON;
END_VAR
BEGIN
  IF iAbort THEN State := 99; oError := TRUE; oErrorId := 16#F001; RETURN; END_IF;
  IF NOT iEnable THEN State := 0; oRunning := FALSE; oDone := FALSE; RETURN; END_IF;
  CASE State OF
    0:  // Idle
        oRunning := FALSE; oDone := FALSE;
        IF iStart THEN State := 1; END_IF;
    1:  // Ramp / approach setpoint
        oRunning := TRUE;
        IF Abs(oActual - iSetpoint) < iParams[1] THEN State := 2; END_IF;
    2:  // Hold
        TonHold(IN := TRUE, PT := REAL_TO_TIME(iParams[2] * 1000));
        IF TonHold.Q THEN TonHold(IN := FALSE); State := 3; END_IF;
    3:  // Done
        oRunning := FALSE; oDone := TRUE;
        IF NOT iStart THEN State := 0; oDone := FALSE; END_IF;
  END_CASE;
END_FUNCTION_BLOCK

Derive the 48 production FBs by inheritance: each one keeps the same input/output shape and only changes the body. Examples for an exhaust dyeing machine:

FB # Name Drives Done condition
FB100 HeatJacket Steam valve Q1, PT100 bath Bath ≥ iSetpoint for iParams[2] s
FB101 CoolJacket Cooling water valve Q2 Bath ≤ iSetpoint for iParams[2] s
FB102 FillWater Inlet valve Q3, flow meter FI1 Total volume ≥ iSetpoint (L)
FB103 DrainTank Drain valve Q4, level switch LSH LSH = empty for 5 s
FB104 Circulate Circulation pump P1, VFD speed Hold for iParams[2] s
FB105 Agitate Agitator VFD Hold for iParams[2] s
FB106-FB115 DoseChemical 1..10 Peristaltic pump, contact meter Delivered volume ≥ iSetpoint
FB116-FB135 DoseDye 1..20 Dye pump, flow integrator Delivered volume ≥ iSetpoint
FB136 HoldTemp Jacket control loop Hold for iParams[2] s within ±iParams[1] °C
FB137 SampleValve Sample valve Q5 Hold for iParams[2] s
FB138 Rinse Inlet + drain (sequenced) iParams[1] cycles done
FB139 pHControl Acid/base dosing pumps pH within ±0.1 of setpoint for 30 s
FB140-FB147 Custom / spare Reserved User-defined

Error codes common to all FBs: 16#F001 = aborted, 16#F002 = sensor break, 16#F003 = over-temperature, 16#F004 = timeout, 16#F005 = external interlock open. The engine collects oErrorId in a centralized fault DB (DB210) for HMI display.

4. Recipe Data Structure

DB200 holds the recipe. Keep it structured so the HMI can browse and edit it symbolically. Use a UDT for the step record so every step has the same shape.

TYPE UDT_Step :
STRUCT
  StepNo          : INT;                    // 1..MAX_STEP
  FnCount         : INT;                    // how many FBs in this step
  FnIndex         : ARRAY[1..16] OF INT;    // FB number 100..147
  FnSetpoint      : ARRAY[1..16] OF REAL;   // primary setpoint
  FnParams        : ARRAY[1..16,1..4] OF REAL; // parameter slot
  StepDoneCond    : INT;                    // 0=AND, 1=OR, 2=ANY (majority)
  MaxStepTime_s   : INT;                    // 0 = no timeout
END_STRUCT
END_TYPE
DATA_BLOCK DB200  // Recipe
  STRUCT
    RecipeId      : STRING[32];
    NSteps        : INT;                    // active step count
    Step          : ARRAY[1..64] OF UDT_Step;
    RecipeCRC     : DWORD;                  // integrity check
  END_STRUCT
BEGIN
  // initial values here if loading from a default
END_DATA_BLOCK

Capacity planning: With NSteps = 64, FnCount = 16, the array size is 64 × (1 + 1 + 16 + 16 + 64 + 1 + 1) × (2+2+2+4+4+2+2) = 64 × 86 × 16 ≈ 88 KB. A CPU 317 with 1 MB work memory absorbs this easily; a CPU 315 will require a slimmed-down NSteps of 32.

5. Batch Sequence Engine (FB200)

The engine is the heart of the system. It runs in OB1 (free cycle) or OB35 (100 ms cyclic interrupt for a deterministic tick). It uses a small state machine:

  • S0 = Idle (recipe loaded, not started)
  • S1 = Running step n
  • S2 = Step n done, dwell for iParams[1] s
  • S3 = All steps done
  • S9 = Hold (pause)
  • S99 = Fault
FUNCTION_BLOCK FB200_BatchEngine
VAR_INPUT
  iStart   : BOOL;
  iStop    : BOOL;
  iReset   : BOOL;
END_VAR
VAR_OUTPUT
  oStepNo    : INT;
  oState     : INT;
  oFaultId   : WORD;
  oAllDone   : BOOL;
END_VAR
VAR
  State     : INT;
  CurStep   : INT;
  TmrDwell  : TON;
  TmrStep   : TON;
  TmrGlobal : TON;
  i         : INT;
  ActiveMask: ARRAY[1..16] OF BOOL;   // which FBs in current step are active
END_VAR
BEGIN
  // Global abort
  IF iStop THEN State := 0; CurStep := 0; oAllDone := FALSE; END_IF;
  IF iReset THEN State := 0; CurStep := 0; oAllDone := FALSE; oFaultId := 0; RETURN; END_IF;

  CASE State OF
    0:  // Idle
        CurStep := 0; oStepNo := 0; oAllDone := FALSE;
        IF iStart AND DB200.NSteps > 0 THEN
          State := 1; CurStep := 1; ActiveMask := FALSE;
        END_IF;

    1:  // Run current step
        oStepNo := CurStep;
        // Mark each FB listed in this step as enabled
        FOR i := 1 TO DB200.Step[CurStep].FnCount DO
          ActiveMask[i] := TRUE;
        END_FOR;

        // Per-step timeout
        IF DB200.Step[CurStep].MaxStepTime_s > 0 THEN
          TmrStep(IN := TRUE, PT := REAL_TO_TIME(DB200.Step[CurStep].MaxStepTime_s * 1000));
          IF TmrStep.Q THEN State := 99; oFaultId := 16#F004; TmrStep(IN := FALSE); END_IF;
        END_IF;

        // Evaluate done condition (AND/OR/ANY)
        IF StepComplete(DB200.Step[CurStep], ActiveMask) THEN
          TmrStep(IN := FALSE);
          State := 2;
          FOR i := 1 TO 16 DO ActiveMask[i] := FALSE; END_FOR;
        END_IF;

    2:  // Dwell between steps
        TmrDwell(IN := TRUE, PT := REAL_TO_TIME(DB200.Step[CurStep].FnParams[1,1] * 1000));
        IF TmrDwell.Q THEN
          TmrDwell(IN := FALSE);
          IF CurStep >= DB200.NSteps THEN
            State := 3;
          ELSE
            CurStep := CurStep + 1;
            State := 1;
          END_IF;
        END_IF;

    3:  // All done
        oAllDone := TRUE;
        IF NOT iStart THEN State := 0; oAllDone := FALSE; END_IF;

    99: // Fault
        // stays here until iReset
        ;
  END_CASE;
END_FUNCTION_BLOCK

The dispatcher StepComplete(...) checks every FB instance whose index is listed in the step. The mapping of FB number to instance DB is held in DB220 (a 48-element array of INT containing instance-DB numbers). When the engine wants to call FB n in step k, it loads DB[n] via OPN DB[InstanceNo] and calls the FB with the parameter set from DB200.Step[k].FnParams. The cleanest way in STEP 7 V5.5 is to use indirect addressing with a CASE that has 48 arms; in TIA Portal you can use a generic FB_MultiInstance pattern with AT-variables.

6. Simultaneous Execution and Function Re-use

Simultaneous execution is handled by the dispatcher calling every listed FB every scan within OB1. Because OB1 runs faster than the slowest process time constant (typical 100 ms), all 48 functions see iEnable = TRUE on the same cycle and their state machines run in lockstep.

Function re-use across steps is solved with a one-to-one map between FB number and FB instance DB. A physical operation (e.g. "heat jacket") is one FB (FB100) with one instance DB (DB100). When the same FB number appears in step 1 and step 5 with different setpoints, the engine simply re-calls FB100 with the new parameters; FB100's state machine and oDone pulse are reset only on a rising edge of iStart, which the engine issues at the start of each step via a per-FB one-shot. This is what makes the source's requirement "function used in step 1 can be used again in step 5" achievable without duplicating the FB.

// One-shot pattern in SCL (inside engine, per FB per step transition)
IF newStep_started THEN
  FB100_DB.iStart := TRUE;     // rising edge -> FB state machine leaves Idle
ELSE
  FB100_DB.iStart := FALSE;
END_IF;

7. Resumable Execution and State Persistence

The operator requirement "if a fault or power failure occurs, HMI shows it and a Continue button resumes from where it stopped" is implemented by combining three mechanisms:

  1. Retentive data blocks. Declare DB200 (recipe) and DB210 (engine state) as retentive in the CPU's hardware configuration. The PLC will preserve them across power-down because the S7-300's retentive area is backed by the MMC for CPU 31xC and above. See the S7-300 CPU 31xC technological functions manual for the exact retention boundaries.
  2. Periodic checkpoint. Every 5 s, the engine writes a small struct (CurStep, State, per-FB state) to a retentive DB (DB230). The HMI "Continue" button is enabled only when DB230.CheckpointValid = TRUE.
  3. Resume from checkpoint. On Continue, the engine reads DB230 and seeds CurStep and the per-FB states, then transitions directly to S1 (Run step) with the same parameter set that was active at the moment of the fault.
// Checkpoint write in OB35 (100 ms cyclic) – executed every 50 cycles (5 s)
IF "clk_5s" THEN
  DB230.LastStep     := FB200_BatchEngine.CurStep;
  DB230.LastState    := FB200_BatchEngine.State;
  DB230.CheckpointValid := TRUE;
  DB230.CheckpointCRC := CRC32(DB230);
END_IF;

For an S7-300 without a buffered CPU, fit a MMC 512 KB (6ES7953-8LM20-0AA0) and enable all DBs as retentive; without retention, resume cannot survive a power-off.

8. HMI Integration with WinCC

Target a 10-inch Comfort Panel (TP1200 Comfort, 6AV2124-1MC01-0AX0) or a PC runtime. TIA WinCC V16 supports symbol-based tag binding, so the recipe DB and engine DB are accessible as plain PLC tags on the panel without manual pointer configuration.

Recommended screens:

  • Recipe Edit – a table view bound to DB200.Step[1..NSteps] with the FnIndex column displayed as a drop-down of the 48 FB numbers, FnSetpoint as a numeric field, and FnParams[1..4] as four numeric fields. Save the recipe by writing to DB200 only when the engine is in State 0.
  • Run – large numerals for oStepNo, oState, and a colour-coded bar showing ActiveMask[1..16] for the current step.
  • Alarms – use the standard WinCC alarm logging on the oErrorId word. Map codes 0xF001-0xF00F to text strings in the alarm text library.
  • Continue – a button enabled by the tag DB230.CheckpointValid and the permission tag Operator.Level ≥ 2. Its click event calls the FC FC_Resume in the PLC.
// FC_Resume (called by HMI Continue button)
IF DB230.CheckpointValid AND NOT FB200_BatchEngine.oAllDone THEN
  FB200_BatchEngine.CurStep := DB230.LastStep;
  FB200_BatchEngine.State   := DB230.LastState;
  DB230.CheckpointValid := FALSE;  // consume
END_IF;

For legacy STEP 7 V5.5 + WinCC flexible 2008 SP5 the screen design is the same; the only difference is the HMI tag import procedure, which uses the "Connections / Tags" editor rather than the TIA Portal device view. The WinCC flexible 2008 SP5 migration guide documents the import workflow.

9. Error Handling and Fault Display

Centralise every FB's oErrorId in DB210:

DATA_BLOCK DB210
  STRUCT
    FaultActive   : BOOL;
    FaultId       : WORD;
    FaultSourceFB : INT;       // 100..147
    FaultStep     : INT;
    FaultTimeStamp: DATE_AND_TIME;
    FaultMessage  : STRING[80];
  END_STRUCT
END_DATA_BLOCK

On every cycle, the engine scans DB210 and writes a single message to a panel alarm buffer. WinCC picks it up via the alarm log. The HMI's alarm screen must display FaultStep, FaultSourceFB, and the FaultTimeStamp so the operator can decide between manual reset and "Continue from where it stopped". The Continue button is the practical realization of the source's requirement that a power-failure or fault must be recoverable.

Safety note: for an exhaust dyeing machine the heating circuit must be interlocked to a high-temperature cut-out (e.g. PT100 at the bath bottom wired to a failsafe relay, not to the PLC). The PLC may command the steam valve, but the safety turn-off must be hard-wired. Refer to IEC 60204-1 and the OEM's safety integration manual for the specific implementation.

10. Commissioning and Verification

Commission in five passes:

  1. I/O check. With the PLC in Stop, force every output and verify the corresponding field device (valve opens, pump starts, VFD ramps). Verify every input by hand-actuating the sensor.
  2. FB single-step. With the engine disabled, set DB200.NSteps = 1, FnCount = 1, FnIndex[1] = 100. Cycle Start and confirm the bath reaches 60 °C and that oDone pulses.
  3. Library regression. For each of the 48 FBs, build a one-step recipe that exercises only that FB with a benign setpoint. Record pass/fail and the steady-state error. This regression catches parameter-mismatch bugs in the FBs before any customer recipe is loaded.
  4. Parallel execution. Build a recipe with one step containing FnCount = 6 (e.g. heat, fill, dose 1, dose 2, dose 3, agitate) and verify that all six FBs run concurrently and that the step only completes when the AND condition is satisfied.
  5. Resume test. With a long recipe running, open the door interlock (simulating a fault). Confirm the alarm appears on the HMI. Press Continue after the interlock is closed. Confirm execution resumes at the same step with the same parameters and that the time stamps in the production log are continuous across the fault.

For documentation, export the PLC project to a read-only TIA Portal file and the HMI project to a panel image; both go into the machine's technical file as required by the Machinery Directive 2006/42/EC and the operator's quality system.

11. Performance and Memory Budget

For the recommended CPU 317-2 PN/DP with 1 MB work memory:

Block Approx. footprint
48 FBs (avg. 1.5 KB code each) 72 KB
48 instance DBs ~25 KB
DB200 Recipe (64 steps × 16 fns) ~90 KB
DB210 Faults + DB220 Dispatcher + DB230 Checkpoint ~12 KB
FCs and the engine FB200 ~20 KB
HMI tag database (TP1200) ~5 MB on the panel (no PLC cost)

OB1 cycle time stays under 30 ms on a CPU 317-2 PN/DP with this design. Watch the cycle-time monitor in HW Config; if it climbs above 100 ms, move the dispatch loop into OB35 and reduce the per-scan work in OB1 to engine + HMI only.

12. Common Pitfalls and Field-Notes

Symptom Likely cause Fix
Function runs but never sets oDone Done condition depends on a sensor wired to a different AI channel than the FB reads Cross-check the AI assignment in HW Config against the FB's PEW address
Step never advances even though all FBs are done AND/OR/ANY miscoded in StepComplete() Insert a watch on ActiveMask[1..16] in the watch table
Continue button greyed out after power fail DB230 not declared retentive Mark DB230 retentive in PLC properties; verify by pulling the MMC and re-inserting
HMI shows the same recipe after editing Recipe screen writes to a mirror tag instead of DB200 Bind the table to the symbol "DB200.Step" directly, not a copy
Two FBs controlling the same output (e.g. both steam and auxiliary heater call Q1) Two physical FBs were assigned the same hardware output by mistake Use the dispatcher map DB220 to enforce one output per FB
VFD speed oscillates when batch runs Agitator and circulator both write to the same AO Reserve one AO per FB; document in the recipe schema

Field-proven tip: always include a 4-7-9 second "drain & rinse" final step in the recipe schema. The exhaust dyeing chemistry produces loose dye after the bath; without that final step the next batch starts with contaminated water and fails the first color-dosing tolerance check.

How many steps can a single recipe contain on an S7-300 with this design?

A CPU 317-2 PN/DP with 1 MB work memory can hold 64 steps of up to 16 parallel functions each in DB200. A CPU 315-2 PN/DP (16 KB work memory) is limited to roughly 24 steps. Resize UDT_Step arrays in DB200 to match the chosen CPU's memory budget.

Can a function that runs in step 1 also be used in step 7 with a different setpoint?

Yes. Each FB has exactly one instance DB (e.g. FB100 HeatJacket uses DB100). The engine calls FB100 with the parameter set that is stored in the current step's row of DB200, so the same physical function is re-used with a new setpoint in every step where its index is listed.

How is the Continue button implemented after a fault or power failure?

The engine writes a checkpoint (current step, state, per-FB states) into retentive DB230 every 5 s. On power restoration or fault recovery, the HMI Continue button reads the checkpoint and re-seeds the engine. The CPU's retentive area plus the MMC backup make the checkpoint survive a power-down; without MMC-backed retentive DBs, resume is not possible.

What is the recommended STEP 7 / TIA Portal version for this architecture?

STEP 7 V5.5 SP2 with WinCC flexible 2008 SP5 (legacy path) or TIA Portal V16 with the S7-300 Add-On (recommended). Use TIA V18 if you need symbolic consistency between the PLC and the Comfort Panel; older TIA versions do not support the S7-300 Add-On cleanly.

Why use FB numbers 100-147 for the library?

Numbering above FB100 keeps the user space clear of the system FBs (FB0-FB39) and leaves FB0-FB99 free for application utility blocks such as FB200 (the engine) and FB1-FB9 (common helpers). The 48-license range 100-147 is a Siemens convention for non-system, application-level FBs and avoids collision with the firmware-installed PID, motion, and technology FBs.

Back to blog