From 2cf2ab3814fa8db29e9258e53404057abd99647f Mon Sep 17 00:00:00 2001 From: Dejan Date: Tue, 24 Feb 2026 11:34:36 +0000 Subject: [PATCH] Add s7-1500/demo-advance-scl-contol.md --- s7-1500/demo-advance-scl-contol.md | 531 +++++++++++++++++++++++++++++ 1 file changed, 531 insertions(+) create mode 100644 s7-1500/demo-advance-scl-contol.md diff --git a/s7-1500/demo-advance-scl-contol.md b/s7-1500/demo-advance-scl-contol.md new file mode 100644 index 0000000..6f65725 --- /dev/null +++ b/s7-1500/demo-advance-scl-contol.md @@ -0,0 +1,531 @@ +# 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 \ No newline at end of file