Overview and Engineering Context
Backing up PLC software is the easy part. Proving that the backup you took last Tuesday still matches the program actually running on the controller this Friday is the hard part. In Siemens SIMATIC S7-300 and S7-400 systems, there is no single "modified since last backup" flag exposed on the front of the CPU. The information exists inside the system, however, in the System Status List (SSL) and in the diagnostic buffer. The job of a program-change indicator is to read that information at runtime, latch it into a bit that an electrician or technician can see on a panel light or HMI, and then clear that bit only when a deliberate reset action is performed.
This article documents the SFC51-based pattern used for S7-300 controllers. The same architecture applies to S7-400, but the SSL IDs and parameter widths are different and must be retuned. The pattern also adapts cleanly to S7-1200 and S7-1500 using the modern RD_SINFO, RDSYSST, and Trace-based change logs, but those platforms are out of scope here.
Prerequisites
- STEP 7 V5.x (or compatible) installed and licensed for the target CPU family.
- CPU firmware compatible with SFC51 (standard on every S7-300 from 314 onward; on S7-400 SFC51 is supported but with different SSL list boundaries).
- A free digital output or HMI tag wired as the Program-Modified indicator.
- A momentary pushbutton wired to a digital input for the 5-second reset action.
- The Siemens Application Note FAQ entry 51577288 "Detecting program changes in S7-300/400" and the attached 51577287_PROG_CHECK_en.pdf.
SFC51 Fundamentals: Reading the System Status List
SFC51 (RDSYSST - Read System Status List) is the CPU firmware function block that lets user code read partial SSL snapshots. The block signature in STEP 7:
CALL "RDSYSST" // SFC51
REQ := TRUE // Start the read
SZL_ID := W#16#0019 // SSL list selector
INDEX := W#16#0000 // Sub-index within the list
RET_VAL := MW100 // Return code
BUSY := M101.0 // Operation in progress
SZL_HEADER := DB20.DBX0.0 // 32-byte header (length, count)
DR := P#DB21.DBX0.0 BYTE 240 // Destination area for the SSL record
SFC51 is asynchronous. The first call with REQ=TRUE starts the read; subsequent calls must keep REQ=TRUE until BUSY=FALSE and then evaluate RET_VAL. A non-zero return in RET_VAL on a non-busy call is the error code (e.g. W#16#80A1, W#16#80B1, W#16#80C3, W#16#80C4) - full mapping is in the STEP 7 online help under SFC51.
Key SSL IDs for Change Detection
| SSL_ID (hex) | Index | Length (bytes) | Contents | Useful for change detect? |
|---|---|---|---|---|
| W#16#0011 | W#16#0001 | 34 | Module identification of CPU (order number, firmware, serial) | Yes - serial and checksum fields shift on block download |
| W#16#0111 | W#16#0000 | 18 | Module identification compact | Yes |
| W#16#0019 | W#16#0000 | 14 | CPU status (RUN/STOP, force, HALT cause) | Yes - bit "stop caused by program change" toggles |
| W#16#0019 | W#16#0003 | 14 | CPU status with last RUN-to-STOP transition | Yes - second transition reason word |
| W#16#0019 | W#16#0004 | 14 | CPU status including diagnostic buffer overflow | Yes |
| W#16#0019 | W#16#0005 | 14 | CPU status including communication error count | Indirectly |
| W#16#0131 | W#16#0001 | 36 | Component identification with hardware serial | Yes - hardware serial forms a stable identity baseline |
| W#16#0094 | W#16#0000 | 16 | Time-stamp information, last PLC time change | Yes - programmer usually sets time during download |
| W#16#0124 | W#16#0000 | 20 | Diagnostic buffer header with write pointer | Yes - buffer write pointer advances on every event |
| W#16#0F31 | W#16#0000 | variable | Diagnostic buffer entries (newest first) | Yes - "Download of user program" entry ID |
The full catalog of SSLs is in the STEP 7 Online Help under "System Status Lists (SSL)" and in the S7-300 CPU 31xC and 31x: System Manual. Note that not all SZL-IDs are valid on all CPUs: the list is firmware-dependent. Always test the read with a temporary program before committing the design.
FB26: The Program-Status Block
The block published under Siemens FAQ 51577288 is FB26 (sometimes labeled "CPU_STATUS_FB" or similar). Its job is to periodically poll several SSLs, extract the bits that matter, and expose them on a small data block interface so OB1 (or OB35) can use them as boolean conditions.
FB26 Interface
| Parameter | Direction | Type | Description |
|---|---|---|---|
| SZL_ID_1 | INPUT | WORD | First SSL to read (default W#16#0019) |
| SZL_IDX_1 | INPUT | WORD | Index of first SSL (default W#16#0000) |
| SZL_ID_2 | INPUT | WORD | Second SSL (default W#16#0111) |
| SZL_IDX_2 | INPUT | WORD | Index of second SSL |
| CYCLE_TIME | INPUT | TIME | Polling interval (T#500ms typical) |
| CPU_RUN | OUTPUT | BOOL | 1 = CPU is in RUN |
| CPU_STOP | OUTPUT | BOOL | 1 = CPU is in STOP |
| CPU_HALT | OUTPUT | BOOL | 1 = HALT cause active |
| PROG_MODIFIED | OUTPUT | BOOL | 1 = program change detected since last reset |
| HARDWARE_MOD | OUTPUT | BOOL | 1 = hardware configuration change detected |
| FORCE_ACTIVE | OUTPUT | BOOL | 1 = force function active |
| LED_RUN | OUTPUT | BOOL | 1 = RUN LED on |
| LED_STOP | OUTPUT | BOOL | 1 = STOP LED on |
| LED_FAULT | OUTPUT | BOOL | 1 = SF / BF / error LED on |
| KEY_SWITCH | OUTPUT | BYTE | Mode switch position (0=unknown, 1=RUN, 2=STOP, 3=RUN-P) |
| ERROR | OUTPUT | BOOL | 1 = last SFC51 call returned an error |
| STATUS | OUTPUT | WORD | Last RET_VAL from SFC51 |
Internally, FB26 maintains a static-instance data block that holds the previous SSL contents. On each cycle, it reads the current SSL, XORs the relevant fields with the snapshot, and sets PROG_MODIFIED if any monitored field differs.
Step-by-Step: Building the Program-Modified Indicator
Step 1 - Import the FB26 source
- Download the FAQ attachment from entry 51577288.
- Open your STEP 7 project and select File > Retrieve to restore the attached archive.
- Drag the FB26 source into the Blocks container of your S7 program.
- Right-click FB26 > Check Block Consistency and compile.
Step 2 - Create the instance DB
- Right-click FB26 > Insert SDB / Instance DB.
- Assign DB number (e.g. DB260) and name "Prog_Change_DB".
- Open DB260 and confirm the instance interface matches the table above.
Step 3 - Call FB26 from OB1 (or OB35 for cyclic)
// OB1 STL - call FB26 every scan
CALL "Program_Status" , "Prog_Change_DB"
SZL_ID_1 := W#16#0019
SZL_IDX_1 := W#16#0000
SZL_ID_2 := W#16#0111
SZL_IDX_2 := W#16#0000
CYCLE_TIME:= T#500MS
CPU_RUN := M200.0
CPU_STOP := M200.1
CPU_HALT := M200.2
PROG_MODIFIED := M201.0 // <-- the bit you want
HARDWARE_MOD := M201.1
FORCE_ACTIVE := M201.2
LED_RUN := M202.0
LED_STOP := M202.1
LED_FAULT := M202.2
KEY_SWITCH:= MB203
ERROR := M204.0
STATUS := MW205
If you call from OB35 (100 ms default), debounce the rising edge of PROG_MODIFIED with a one-shot to avoid latching on a single glitch during a long-running SFC51 read.
Step 4 - Latch the change bit
The raw PROG_MODIFIED from FB26 toggles whenever the SSL differs from the snapshot at FB26 startup. If you call FB26 cold on every CPU restart, the snapshot rebuilds from whatever program is now resident and the bit will not latch a real change. Two design options solve this:
- Remanent snapshot DB. Store the captured SSL bytes in a remanent DB (set Retain = true on the DB properties) so the comparison survives a power cycle.
-
Two-stage latching. In OB1, write the bit into a remanent flag, e.g.
M201.0, and only clear it through the 5-second reset. FB26's internal flag becomes a "raw change detected this scan" pulse.
// Two-stage latch (OB1 STL)
AN M 207.0 // Reset_PB edge
A DB260.DBX...".PROG_MODIFIED // raw change
S M 208.0 // Program_Changed_Latched (remanent)
A M 207.1 // Reset_PB pressed >= 5s
R M 208.0
Step 5 - Reset pushbutton with 5-second hold
// Reset logic using IEC timer (OB1 STL)
A I 0.0 // Reset pushbutton NC/NO contact
AN M 207.0 // one-shot edge memory
L S5T#5S // 5 second preset
SD T 50 // pulse timer, on-delay
NOP 0
NOP 0
NOP 0
A T 50
= M 207.1 // PB held >= 5s
= M 207.0 // raw PB state for next cycle edge
Alternative: use a counter that increments each OB1 cycle the button is held, and resets when released. When the counter reaches the value corresponding to 5 s at the OB1 scan rate (e.g. 250 cycles at 20 ms), set the reset flag. This avoids tying up an S7 timer resource.
Step 6 - Wire the indicator output
Drive any free digital output with M208.0. Most plants use a yellow or amber pilot lamp on the cabinet door labeled "PROGRAM CHANGED - BACKUP REQUIRED". For HMI visibility, expose M208.0 and the reset button on a WinCC flexible / TIA Portal faceplate.
S7-400 Adaptation
SFC51 is available on S7-400 as well, but with caveats:
- SSL_ID W#16#0019 is supported on S7-400 with an extended data record length (28 bytes vs. 14 on S7-300). Allocate a larger destination buffer.
- SSL_ID W#16#0111 is not valid on every S7-400 CPU. Use W#16#0011 with INDEX=1 instead, which is the canonical module identification list on S7-400.
- FB26 as published is targeted at S7-300. The internal data record lengths and the offset of individual status bits differ. Recompile from the F-SCL source provided in the FAQ and re-validate against the S7-400 System and Operating Manual before deploying.
- On S7-400H (redundant), call FB26 only from the active CPU; the standby CPU returns W#16#80C3 in RET_VAL when SSLs are queried outside its leadership.
Verification and Commissioning
-
Baseline test. Power the CPU, wait for RUN, confirm
M208.0 = 0. -
Trigger test. Connect STEP 7, perform PLC > Download User Program to Memory Card with a single bit toggled in OB1. Confirm
M208.0latches ON. -
Reset test. Hold the reset pushbutton for 5 s. Confirm
M208.0returns to OFF. -
Power-cycle test. Power off the PLC and power back on. Confirm
M208.0remains OFF (snapshot DB is remanent) but the LED indicators still report correct CPU state. - Diagnostic buffer check. In STEP 7, open PLC > Diagnostic/Setting > Diagnostic Buffer. The event "Download of user program" with event ID W#16#4502 should be the topmost entry after each download.
Troubleshooting Matrix
| Symptom | RET_VAL from SFC51 | Likely cause | Corrective action |
|---|---|---|---|
PROG_MODIFIED never asserts even after a download |
0 (success) | FB26 instance DB not remanent; snapshot rebuilt every restart | Set Retain = true on the instance DB; ensure DB is loaded to retentive area |
| FB26 ERROR = 1 immediately | W#16#80A1 | SZL_ID not supported on this CPU/firmware | Replace SZL_ID with the documented fallback for the CPU order number |
| FB26 ERROR = 1 after a few hours | W#16#80B1 | Index out of range for the chosen SZL_ID | Reduce INDEX to the maximum allowed value listed in STEP 7 online help |
PROG_MODIFIED asserts on every cold start |
0 | SSL byte used as the fingerprint is part of the cold-start banner and varies run-to-run | Mask the bytes that vary on cold start; compare only the user-program checksum field |
PROG_MODIFIED flaps during download |
0 then W#16#80C3 | SSL becomes momentarily unreadable during STOP-to-RUN transition | Debounce the raw flag with a 2-second on-delay before latching |
| FB26 ERROR = 1 only on S7-400H standby | W#16#80C3 | SSL query on non-leader CPU | Route FB26 call via the active CPU only, or accept the error and use OB1 cycle for filtering |
| Reset pushbutton does not clear the bit | N/A | PB wired NC but code expects NO (or vice versa) | Verify polarity with online monitor; or use both edges (positive and negative) to set/reset |
| Bit latches but cabinet lamp does not light | N/A | Output mapped to wrong address or DO module is in diagnostic interrupt | Check HW Config online view; check module diagnostics buffer |
Program Change Backup Workflow
The indicator is only as useful as the procedure around it. Recommended workflow per cell:
- Maintenance confirms the yellow "Program Changed" lamp is ON.
- Technician opens STEP 7, archives the project to the versioned backup path (e.g.
\\fileserv\PLC\Line_07\YYYY-MM-DD.zip). - Technician performs an offline block comparison: Blocks > Compare Blocks (Online > Offline). Any delta must be reviewed and signed off by the engineer of record.
- Technician presses the cabinet-door reset button for 5 s; the yellow lamp extinguishes.
- The release of the reset is logged in the CMMS ticket.
If the workflow is supervised from an HMI / SCADA layer, replicate the bit onto the supervisory tag using OPC / OPC UA so that the change event can be time-stamped and archived alongside other production events.
Adjacent Use Case: Drive-Side Change Indication
The same engineering pattern (latch a "modified" bit, expose on the cabinet, reset with a held button) applies to variable-frequency drives. On AutomationDirect DURApulse GS20(X) drives, the PLC-Quick-Start demo in the official DURApulse GS20(X) VFD Quick Start Part 2 video walks through the drive-side state indicator concept: the drive remembers its power-on state separately from the PLC, and a parameter write from the keypad or digital input changes that state. If you are wiring an end-to-line change-detection scheme (PLC + drive), mirror the PLC change bit onto a drive parameter read (e.g. GS20 parameter 00-04 = "Drive status") so the cabinet lamp can reflect changes on either side.
FAQ
Which SSL_ID is the most reliable single indicator of a program change on an S7-300 CPU?
SSL_W#16#0011 with INDEX=1 (module identification, 34 bytes) is the most stable and firmware-independent. The user-program CRC embedded in this record changes whenever blocks are downloaded, but the order number and serial numbers remain constant. Compare only the CRC fields to avoid false positives.
Does FB26 also work for S7-400 CPUs?
The published FB26 is built for S7-300 with 14-byte and 18-byte SSL records. S7-400 returns extended records (28 bytes and 36 bytes for the same SSLs), so FB26 must be recompiled with larger buffer allocations and the field offsets re-mapped. The FAQ document lists the S7-400 reference values.
Will the indicator latch if I only edit the hardware configuration (HW Config) and not the program blocks?
Yes, if you also read SSL_W#16#0131 (component identification) and compare the slot and order-number fields. Without that, a pure HW Config change will pass through FB26's default fingerprinting undetected.
Can I make the indicator survive a CPU-to-CPU program transfer (card clone)?
No - the snapshot DB is part of the program, so a clone reproduces the cleared snapshot as well. If you need anti-clone behavior, derive the fingerprint from CPU serial number (SSL_W#16#0111) and compare against a non-volatile "last known serial" stored outside the program.
Why does PROG_MODIFIED assert once on cold start even on a known-good program?
FB26's snapshot is initialized at first call to zeros, so any non-zero SSL byte looks like a change. Initialize the snapshot DB from the actual SSL contents on the first cycle, or use a one-shot flag (first-scan bit) to skip the comparison on the first OB1 pass.