# README — S7-1500 SCL Sequencer (AUTO / INCR) with Pause vs Stop, Stop Categories, Skip/Disable, Safety-Locked Steps This project shows a Siemens S7-1500 step sequencer in SCL with: - AUTO mode (advance automatically) - INCR mode (advance only on operator NEXT pulse) - Pause vs Stop - Stop categories (immediate / end-of-step / end-of-cycle / no-stop) - Per-step UDT configuration (timeout + operator text) - Skip/disable steps (optional steps can be disabled) - Prevent disabling safety steps (PLC forces critical steps enabled) You can paste the TYPES into "PLC data types" and the FB code into an SCL FB. ------------------------------------------------------------ 1) Concepts / How it works ------------------------------------------------------------ 1. Step machine (FSM) - Variable "Step" defines the state: Step = 0 -> IDLE Step = 1..10 -> execute step logic - Each step has: - Actions (outputs to command) - Done condition (when it is allowed to advance) - Timeout (if not done within time -> Error) 2. Modes We use enum E_Mode: - MODE_STOP = 0 (not running, safe outputs) - MODE_AUTO = 1 (auto advance) - MODE_INCR = 2 (manual advance with NEXT) Mode selection priority: - If Enable=FALSE OR Cmd_Stop=TRUE OR ErrorActive=TRUE => MODE_STOP - Else if Mode_Auto=TRUE => MODE_AUTO - Else if Mode_Incr=TRUE => MODE_INCR - Else MODE_STOP 3. Pause vs Stop - STOP: - forces safe outputs (FALSE) - typically sets Step=0 (IDLE) - PAUSE: - freezes step progression and timer - keeps outputs frozen at last values (freeze outputs) Pause is implemented by: - PauseLatched toggled by Cmd_Pause edge - when pause becomes TRUE, capture current outputs into Frozen_* variables - while paused, outputs are forced to Frozen_* values 4. Stop categories (StopReq + StopCat) Enum E_StopCat: - STOPCAT_IMMEDIATE: stop instantly (Step=0 + safe outputs) - STOPCAT_END_OF_STEP: finish current step then stop - STOPCAT_END_OF_CYCLE: continue until the configured end-of-cycle step completes, then stop - STOPCAT_NO_STOP: info only (never stops) Stop requests are latched: - STOPCAT_IMMEDIATE / END_OF_STEP / END_OF_CYCLE -> latched in PLC - STOPCAT_NO_STOP -> not latched (only displayed as info) 5. Per-step configuration via UDT array Cfg[1..10] Each step stores: - Text: shown to operator - Timeout: step timeout - IsEndOfCycle: marks the “cycle end” step - ReqEnabled: HMI requested enable - Enabled: PLC effective enable used by sequencer - SafetyLocked: if TRUE, cannot be disabled - NextStep: optional jump target (0 means normal next) The PLC NEVER trusts ReqEnabled directly. PLC computes Enabled every scan: - if SafetyLocked => Enabled := TRUE (and optionally ReqEnabled forced TRUE too) - else Enabled := ReqEnabled 6. Skip/Disable steps Instead of Step := Step + 1, we compute "next enabled step": - If operator disables a step, sequence jumps over it - A helper FC_NextEnabledStep finds the next enabled step - In AUTO mode we wrap 10->1, in INCR mode we can end at 0 Optional routing: - If Cfg[Step].NextStep <> 0 then jump to that step (if enabled). ------------------------------------------------------------ 2) PLC Data Types (copy into "PLC data types") ------------------------------------------------------------ TYPE E_Mode : USINT ( MODE_STOP := 0, MODE_AUTO := 1, MODE_INCR := 2 ); END_TYPE TYPE E_StopCat : USINT ( STOPCAT_NONE := 0, STOPCAT_IMMEDIATE := 1, STOPCAT_END_OF_STEP := 2, STOPCAT_END_OF_CYCLE := 3, STOPCAT_NO_STOP := 4 ); END_TYPE TYPE UDT_StepCfg : STRUCT Text : STRING[80]; // Operator text for HMI Timeout : TIME; // Per-step timeout IsEndOfCycle : BOOL; // Marks end-of-cycle step (e.g., step 10) ReqEnabled : BOOL; // From HMI/DB (operator request) Enabled : BOOL; // PLC effective enable (used by sequencer) SafetyLocked : BOOL; // If TRUE, PLC forces Enabled := TRUE always NextStep : INT; // 0 = normal next enabled step, else jump to 1..10 END_STRUCT END_TYPE ------------------------------------------------------------ 3) Helper FC: Next enabled step (skip disabled steps) ------------------------------------------------------------ FUNCTION FC_NextEnabledStep : INT { S7_Optimized_Access := 'TRUE' } VAR_INPUT CurrentStep : INT; MaxStep : INT; // 10 Wrap : BOOL; // TRUE in AUTO (10->1), FALSE in INCR (end->0) Cfg : ARRAY[1..10] OF UDT_StepCfg; END_VAR VAR s : INT; i : INT; END_VAR BEGIN s := CurrentStep; FOR i := 1 TO MaxStep DO s := s + 1; IF s > MaxStep THEN IF Wrap THEN s := 1; ELSE FC_NextEnabledStep := 0; RETURN; END_IF; END_IF; IF Cfg[s].Enabled THEN FC_NextEnabledStep := s; RETURN; END_IF; END_FOR; FC_NextEnabledStep := 0; // none enabled END_FUNCTION ------------------------------------------------------------ 4) Main FB: Sequencer (AUTO/INCR + Pause/Stop + Stop categories) ------------------------------------------------------------ FUNCTION_BLOCK FB_Seq10_Adv { S7_Optimized_Access := 'TRUE' } VAR_INPUT Enable : BOOL; Mode_Auto : BOOL; Mode_Incr : BOOL; Cmd_Start : BOOL; Cmd_Stop : BOOL; // hard stop (safe outputs) Cmd_Pause : BOOL; // toggle pause (freeze) Cmd_ResetErr : BOOL; Cmd_Next : BOOL; // NEXT pulse in increment mode StopReq : BOOL; // stop request flag StopCat : E_StopCat; // stop request category // Example process inputs (replace with real ones) In_Ready : BOOL; In_ClampClosed : BOOL; In_ProcessDone : BOOL; In_EjectDone : BOOL; // Step config from DB/HMI/recipe Cfg : ARRAY[1..10] OF UDT_StepCfg; END_VAR VAR_OUTPUT Step : INT; // 0..10 Running : BOOL; Paused : BOOL; ErrorActive : BOOL; ErrorCode : WORD; HMI_StepText : STRING[80]; HMI_StopInfo : STRING[80]; // Example outputs (replace) Out_ClampClose : BOOL; Out_ProcessOn : BOOL; Out_Eject : BOOL; END_VAR VAR Mode : E_Mode; rStart : R_TRIG; rNext : R_TRIG; rReset : R_TRIG; rPause : R_TRIG; StepPrev : INT; StepEntry : BOOL; tStep : TON; AllowAdvance : BOOL; StopLatched : BOOL; StopLatchedCat : E_StopCat; PauseLatched : BOOL; Frozen_ClampClose: BOOL; Frozen_ProcessOn : BOOL; Frozen_Eject : BOOL; Cmd_ClampClose : BOOL; Cmd_ProcessOn : BOOL; Cmd_Eject : BOOL; i : INT; nxt : INT; END_VAR BEGIN // --------- edges ---------- rStart(CLK := Cmd_Start); rNext(CLK := Cmd_Next); rReset(CLK := Cmd_ResetErr); rPause(CLK := Cmd_Pause); // --------- sanitize step config (prevent disabling safety steps) ---------- // IMPORTANT: HMI should write ONLY ReqEnabled, not Enabled or SafetyLocked. FOR i := 1 TO 10 DO IF Cfg[i].SafetyLocked THEN Cfg[i].Enabled := TRUE; Cfg[i].ReqEnabled := TRUE; // reflect back to HMI (optional) ELSE Cfg[i].Enabled := Cfg[i].ReqEnabled; END_IF; END_FOR; // --------- reset error ---------- IF rReset.Q THEN ErrorActive := FALSE; ErrorCode := WORD#0; Step := 0; StopLatched := FALSE; StopLatchedCat := STOPCAT_NONE; PauseLatched := FALSE; END_IF; // --------- mode selection ---------- IF (NOT Enable) OR Cmd_Stop OR ErrorActive THEN Mode := MODE_STOP; ELSIF Mode_Auto THEN Mode := MODE_AUTO; ELSIF Mode_Incr THEN Mode := MODE_INCR; ELSE Mode := MODE_STOP; END_IF; // --------- start ---------- IF rStart.Q AND (NOT ErrorActive) THEN IF Step = 0 THEN // start at first enabled step IF Cfg[1].Enabled THEN Step := 1; ELSE Step := FC_NextEnabledStep(CurrentStep := 0, MaxStep := 10, Wrap := TRUE, Cfg := Cfg); END_IF; END_IF; END_IF; // --------- latch stop request (category-aware) ---------- IF StopReq THEN CASE StopCat OF STOPCAT_IMMEDIATE, STOPCAT_END_OF_STEP, STOPCAT_END_OF_CYCLE: StopLatched := TRUE; StopLatchedCat := StopCat; STOPCAT_NO_STOP: // info only; do not latch stop ELSE // none END_CASE; END_IF; // --------- pause toggle ---------- IF rPause.Q THEN PauseLatched := NOT PauseLatched; // capture outputs when entering pause IF PauseLatched THEN Frozen_ClampClose := Out_ClampClose; Frozen_ProcessOn := Out_ProcessOn; Frozen_Eject := Out_Eject; END_IF; END_IF; Paused := PauseLatched; // --------- running ---------- Running := (Mode <> MODE_STOP) AND (Step > 0) AND (NOT ErrorActive) AND (NOT Paused); // --------- step entry ---------- StepEntry := (Step <> StepPrev); StepPrev := Step; // --------- HMI step text ---------- IF (Step >= 1) AND (Step <= 10) THEN HMI_StepText := Cfg[Step].Text; ELSE HMI_StepText := 'IDLE'; END_IF; // --------- stop info ---------- HMI_StopInfo := ''; IF StopReq AND (StopCat = STOPCAT_NO_STOP) THEN HMI_StopInfo := 'INFO: stop request is NO_STOP (alarm/info only)'; ELSIF StopLatched THEN CASE StopLatchedCat OF STOPCAT_IMMEDIATE: HMI_StopInfo := 'Stop requested: IMMEDIATE'; STOPCAT_END_OF_STEP: HMI_StopInfo := 'Stop requested: END_OF_STEP'; STOPCAT_END_OF_CYCLE: HMI_StopInfo := 'Stop requested: END_OF_CYCLE'; ELSE HMI_StopInfo := 'Stop requested'; END_CASE; END_IF; // --------- if current step disabled -> jump immediately ---------- IF (Step >= 1) AND (Step <= 10) THEN IF NOT Cfg[Step].Enabled THEN Step := FC_NextEnabledStep(CurrentStep := Step-1, MaxStep := 10, Wrap := (Mode = MODE_AUTO), Cfg := Cfg); END_IF; END_IF; // --------- default commanded outputs ---------- Cmd_ClampClose := FALSE; Cmd_ProcessOn := FALSE; Cmd_Eject := FALSE; AllowAdvance := FALSE; // --------- step timer (does not run while paused) ---------- IF StepEntry THEN tStep(IN := FALSE, PT := Cfg[Step].Timeout); END_IF; IF (Step >= 1) AND (Step <= 10) THEN tStep(IN := Running, PT := Cfg[Step].Timeout); ELSE tStep(IN := FALSE, PT := T#0S); END_IF; // --------- step logic (demo steps, replace with your real logic) ---------- CASE Step OF 0: ; 1: // WAIT READY IF In_Ready THEN AllowAdvance := TRUE; END_IF; 2: // CLOSE CLAMP Cmd_ClampClose := TRUE; IF In_ClampClosed THEN AllowAdvance := TRUE; END_IF; 3: // PROCESS Cmd_ProcessOn := TRUE; IF In_ProcessDone THEN AllowAdvance := TRUE; END_IF; 4: // EJECT Cmd_Eject := TRUE; IF In_EjectDone THEN AllowAdvance := TRUE; END_IF; 5,6,7,8,9: // demo pass-through steps AllowAdvance := TRUE; 10: // END OF CYCLE AllowAdvance := TRUE; ELSE ErrorActive := TRUE; ErrorCode := WORD#16#9001; // invalid step END_CASE; // --------- timeout -> error ---------- IF tStep.Q AND Running THEN ErrorActive := TRUE; ErrorCode := WORD#16#8000 + WORD_TO_WORD(INT_TO_WORD(Step)); Mode := MODE_STOP; END_IF; // --------- stop category enforcement: immediate stop ---------- IF StopLatched AND (StopLatchedCat = STOPCAT_IMMEDIATE) THEN Step := 0; StopLatched := FALSE; StopLatchedCat := STOPCAT_NONE; PauseLatched := FALSE; END_IF; // --------- step advance ---------- IF (NOT ErrorActive) AND (NOT Paused) THEN // Evaluate "advance permission" depending on mode IF (Mode = MODE_AUTO AND AllowAdvance) OR (Mode = MODE_INCR AND AllowAdvance AND rNext.Q) THEN // Handle stop-after conditions IF StopLatched AND (StopLatchedCat = STOPCAT_END_OF_STEP) THEN Step := 0; StopLatched := FALSE; StopLatchedCat := STOPCAT_NONE; ELSIF StopLatched AND (StopLatchedCat = STOPCAT_END_OF_CYCLE) AND Cfg[Step].IsEndOfCycle THEN Step := 0; StopLatched := FALSE; StopLatchedCat := STOPCAT_NONE; ELSE // Compute next step: // 1) optional jump if NextStep configured IF (Step >= 1) AND (Step <= 10) AND (Cfg[Step].NextStep <> 0) THEN nxt := Cfg[Step].NextStep; IF (nxt >= 1) AND (nxt <= 10) AND Cfg[nxt].Enabled THEN Step := nxt; ELSE ErrorActive := TRUE; ErrorCode := WORD#16#9100; // invalid NextStep config END_IF; ELSE // 2) normal: next enabled step nxt := FC_NextEnabledStep( CurrentStep := Step, MaxStep := 10, Wrap := (Mode = MODE_AUTO), Cfg := Cfg ); Step := nxt; END_IF; END_IF; END_IF; END_IF; // --------- output policy: STOP vs PAUSE vs NORMAL ---------- IF (Mode = MODE_STOP) OR (Step = 0) OR ErrorActive THEN Out_ClampClose := FALSE; Out_ProcessOn := FALSE; Out_Eject := FALSE; ELSIF Paused THEN Out_ClampClose := Frozen_ClampClose; Out_ProcessOn := Frozen_ProcessOn; Out_Eject := Frozen_Eject; ELSE Out_ClampClose := Cmd_ClampClose; Out_ProcessOn := Cmd_ProcessOn; Out_Eject := Cmd_Eject; END_IF; END_FUNCTION_BLOCK ------------------------------------------------------------ 5) Recommended usage in OB1 ------------------------------------------------------------ - Create an instance DB for FB_Seq10_Adv. - Fill Cfg[1..10] with texts/timeouts and safety locks. Typical values: - Step 1: Text='Wait Ready', Timeout=T#10S, SafetyLocked=TRUE (if required) - Step 2: Text='Close Clamp', Timeout=T#5S, SafetyLocked=TRUE - Step 10: Text='Cycle End', Timeout=T#2S, IsEndOfCycle=TRUE Operator can only toggle: - Cfg[i].ReqEnabled for optional steps (SafetyLocked=FALSE) Run modes: - AUTO: Mode_Auto=TRUE - INCR: Mode_Incr=TRUE and use Cmd_Next pulses Stop: - Cmd_Stop -> immediate safe stop - StopReq+StopCat: - END_OF_CYCLE for "stop after part" - END_OF_STEP for "stop after step" - IMMEDIATE for urgent stop - NO_STOP for warnings/info Pause: - Cmd_Pause toggles pause/resume (outputs frozen + timer frozen) ------------------------------------------------------------ 6) What to customize next (common in production) ------------------------------------------------------------ 1) Split outputs into "Freeze on pause" and "Force safe on pause" groups. 2) Add per-step "StopAllowed" (some steps must never stop mid-step). 3) Add alarm integration with FIFO + timestamps (your previous alarm project). 4) Add step entry actions (run once on step change): - reset internal flags - start motion commands - log step start time 5) Add interlocks: - if safety chain open -> force STOPCAT_IMMEDIATE END OF README