S7-1200 SCL Programming: Outputs and OB/FB/FC/DB Guide

David Krause20 min read
S7-1200SiemensTutorial / How-to
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

Overview

Structured Control Language (SCL) is the high-level, PASCAL-based programming language for SIMATIC S7-1200 CPUs in TIA Portal. SCL coexists with ladder (LAD) and function block diagram (FBD) in the same project, and it is the language of choice for data manipulation, mathematical operations, loop constructs, and conditional logic where LAD becomes unwieldy. The official language description for the S7-1200 family is in the SIMATIC S7-1200 manual collection on docs.tia.siemens.cloud.

The most common first-time issue for an SCL beginner is that a freshly written block compiles cleanly but never drives the digital output. The root cause is almost always block call hierarchy: the SCL block must be invoked from an organization block (OB) that the CPU firmware actually executes, and the output must not be overwritten elsewhere in the program. This reference walks through the S7-1200 execution model, the role of OB/FB/FC/DB, the correct syntax for forcing a digital output in SCL, and the field-proven checks to verify the program is running on the target hardware.

The reference platform is the S7-1200 CPU family (for example, 6ES7214-1AG40-0XB0) with firmware V4.4 to V4.7 supported by TIA Portal V15.1 to V18. The newer S7-1200 G2 line (firmware V5.0) requires TIA Portal V20. Where a behavior differs between firmware lines, the article calls it out explicitly.

Prerequisites

Before writing a single line of SCL, confirm the following are in place. Most "SCL does nothing" symptoms can be traced to one of these items being missed at the first commissioning.

  • CPU: a SIMATIC S7-1200 CPU with a digital output (onboard relay, onboard transistor, or an SM 1222 signal module such as 6ES7222-1HF32-0XB0 for relay outputs, or 6ES7222-1BH32-0XB0 for transistor outputs).
  • Software: TIA Portal V15.1 or later for S7-1200 firmware V4.x. TIA Portal V18 is recommended for current field deployments. TIA Portal V20 is required only for the S7-1200 G2 (firmware V5.0) family.
  • Firmware: matching CPU firmware. Use Online & Diagnostics → Accessible nodes to read the CPU's firmware version before going online.
  • Wiring: 24 VDC supply to the output module, COM terminal connected, and the load wired to the relevant output terminal (for example, Q0.0 on a CPU 1214C DC/DC/DC or the first channel of an SM 1222 RLY).
  • Project: a TIA Portal project with the S7-1200 device added, an Ethernet or PROFIBUS connection configured, and the CPU in STOP when downloading the first program.
  • Library references: the S7-1200 manual collection (system, programming, and SCL reference) downloaded or browsed from docs.tia.siemens.cloud.

S7-1200 Block Architecture: OB, FB, FC, and DB

Every S7-1200 program is a hierarchy of blocks. Each block type has a distinct lifecycle, memory model, and call convention. Choosing the right block is the foundation of any working SCL application.

Organization Blocks (OB)

OBs are the interface between the CPU operating system and the user program. The CPU firmware calls OBs automatically based on events; user code cannot call an OB explicitly from SCL. The relevant OBs for the S7-1200 are:

OB Trigger Default priority Use
OB 1 (Main) Cyclic, free-running 1 Main program loop; runs continuously while CPU is in RUN
OB 10 (Time-of-day) Configured clock time 2 Daily/scheduled tasks
OB 20 (Cyclic interrupt) Configured period (ms) 3 to 24 Fixed-cycle control loops
OB 35 (Cyclic interrupt, 100 ms default) 100 ms 12 Often used for PID sample times
OB 40 (Hardware interrupt) DI/DO module event 16 to 23 Fast response to process events
OB 80 (Time error) Cyclic or interrupt time exceeded 26 Error handling
OB 82 (Diagnostic interrupt) Module diagnostic event 24 Module diagnostics
OB 100 (Warm restart) STOP→RUN transition 27 Initialize once at startup
OB 121 (Programming error) SCL/STL access error Same as triggering OB Error handling
OB 122 (I/O access error) Faulted I/O read/write Same as triggering OB Error handling

For first commissioning, OB 1 is the only block you need. The CPU starts execution at OB 1 and runs the contained statements on every cycle (the cycle time is governed by the configured maximum cycle time, default 150 ms for the S7-1200). If a function (FC) or function block (FB) is not called from OB 1 or from another active OB, it will not execute, even if it compiles cleanly.

Function Blocks (FB)

An FB is a code block with its own static memory. Each instance of an FB is backed by an Instance Data Block (IDB) that retains the FB's static variables between scans. FBs are appropriate for stateful logic such as motor starters, valve sequencers, PID controllers, or any routine that must remember values across cycles. An FB without a call from OB 1, an interrupt OB, or another active block will not run. The instance DB exists in the project, but the code inside the FB never executes unless the FB is in the call stack.

Functions (FC)

An FC is a stateless subroutine. It has no assigned instance DB; it operates on its inputs and reads/writes global memory (I, Q, M, DB) or returns a return value to the caller. Use FCs for pure calculations, type conversions, and short I/O mappings. Like FBs, FCs must be called from an active OB to execute. FCs may also be called from within other FBs and FCs, allowing a multi-level call hierarchy.

Data Blocks (DB)

DBs are pure memory containers. There are two kinds:

  • Global DBs: declared in the project tree, accessible by all blocks via the DB name and dot-qualified variable. Use for recipes, machine constants, HMI-visible data, and communication buffers.
  • Instance DBs (IDB): automatically generated by the compiler when an FB is instantiated. They hold the FB's static and temp variables.

A DB does not "run" on its own. It only stores data; logic must reside in OB/FB/FC. A common error is to put := 1; assignments inside a DB declaration, which is syntactically impossible; only initial values can be entered in the DB's "Start value" column.

Why an FB-only project fails

A common mistake for SCL beginners is to write "MyTag" := 1; directly inside an FB and expect the output to react immediately. If the FB is never called from OB 1, the CPU firmware never enters the FB, so the assignment is never executed. The SCL code compiles because the syntax is valid; the runtime simply never visits the block. This is the single most common reason a brand-new S7-1200 SCL program does not work on first download.

SCL Language Fundamentals for the S7-1200

According to the S7-1200 manual collection, SCL supports the STEP 7 block structure plus high-level constructs:


// Conditional
IF condition THEN
  ...
ELSIF other_condition THEN
  ...
ELSE
  ...
END_IF;

// Multi-way branch
CASE selector OF
  1:   action_1;
  2:   action_2;
  ELSE default_action;
END_CASE;

// Counted loop
FOR i := 1 TO 10 DO
  sum := sum + i;
END_FOR;

// Conditional loop
WHILE not_done DO
  ...
END_WHILE;

// At-least-once loop
REPEAT
  ...
UNTIL done
END_REPEAT;

Each statement ends with a semicolon (;). Blocks of statements are delimited by the language-specific END_xxx; terminator. This trailing-semicolon discipline differs from LAD (where the rung terminator is implicit) and is the most common syntax error for newcomers.

Variables and Addressing

SCL on S7-1200 uses symbolic addressing. Each tag in the PLC tag table (or DB) has a symbolic name; the compiler resolves it to an absolute address. Direct IEC-style addresses such as %Q0.0 are not used inside S7-1200 SCL user code; the address is referenced through the symbol declared in the tag table or DB.


"MyInput"        // reads the tag named "MyInput" from the default tag table
"MyDB".Field     // reads "Field" from the global DB "MyDB"
#iLocal           // reads a local variable "iLocal" declared in this block

Quoted strings are global symbols; names prefixed with # are block-local symbols. The compiler is case-sensitive: "Output_Relay" and "output_relay" are different symbols.

Assignment Operator

The assignment operator in SCL is :=. The left-hand side must be a writable tag (output, memory, DB variable, or FB output). Examples:


// Force output high unconditionally
"Output_Relay" := TRUE;

// Conditional assignment
IF "Start_Button" THEN
  "Output_Relay" := TRUE;
ELSE
  "Output_Relay" := FALSE;
END_IF;

// Multi-way
CASE "Mode_Select" OF
  0: "Output_Relay" := FALSE;
  1: "Output_Relay" := TRUE;
  ELSE "Alarm_Lamp" := TRUE;
END_CASE;

Step-by-Step: Triggering a Digital Output from SCL

The minimum viable SCL program that drives a relay output on an S7-1200 is two clicks and one line of code. Perform the steps in order.

  1. Create a new project in TIA Portal and add the S7-1200 CPU (for example, CPU 1214C DC/DC/DC, order number 6ES7214-1AG40-0XB0).
  2. Open PLC tags → Default tag table and declare a tag: name Output_Relay, data type Bool, address %Q0.0. Confirm the address matches the physical output you wired. On a CPU 1214C with relay outputs, the first onboard relay is typically on the lower terminal block labeled Q0.0.
  3. In the project tree, double-click Program blocks → Main [OB1] to open OB 1.
  4. Switch OB 1 to SCL: from the language selector in the top toolbar, choose SCL. Confirm the change when prompted. The body of OB 1 is now an SCL editor.
  5. Inside OB 1, type exactly the following: "Output_Relay" := TRUE;
  6. Compile the project: in the project tree, right-click the CPU → Compile → Software. The SCL block should compile without errors. If the symbol appears red squiggly, the PLC tag is not yet declared; go back to step 2.
  7. Download to the CPU: from the menu bar, Online → Download to device. The first download will require STOP; subsequent online edits can use the "Download and RUN" option.
  8. Switch the CPU to RUN. The relay output should energize within one scan cycle. Verify with a multimeter across the output contacts, or watch the LED on the front of the SM 1222 / CPU that mirrors the output state.
Why put the statement in OB 1 and not in an FB?
OB 1 is automatically invoked by the CPU operating system once per scan. A new FB is not. If you place "Output_Relay" := TRUE; in an FB and never call that FB from OB 1, the line of SCL will compile, but the CPU firmware will never enter the block, so the output stays low. This is the most common cause of the "SCL compiles, but the output does not turn on" symptom on the S7-1200.

Calling an FC from OB 1

If you prefer to keep the logic in a reusable block, create an FC (Project tree → Program blocks → Add new block → Function), name it Set_Output, and put the assignment inside the FC body. Then from OB 1, call the FC:


// OB 1 (SCL)
"Set_Output"();

The empty parentheses are required for a parameterless FC. You can also drag and drop the FC from the project tree into OB 1 to insert a CALL-style invocation. The FC will then run once per OB 1 scan, and the output will track the FC's logic.

Calling an FB from OB 1

For stateful logic, add a new FB (for example, Motor_Control). The compiler creates a multi-instance or single-instance DB; the latter is the default for S7-1200. In OB 1, call the instance DB:


// OB 1 (SCL)
"Motor_Control_DB"();   // single-instance call

Inside the FB, write to the FB's static, temp, or output variable, or directly to a global output tag. Save the FB, recompile, and re-download. To confirm the call hierarchy is correct, open Online & Diagnostics → Call environment; it should show OB 1 → FB "Motor_Control" with the instance DB name in the Instance column.

Calling a Multi-Instance FB

S7-1200 supports multi-instance FBs (an FB instantiated inside another FB's static area). Declare the inner FB as a static variable of type FB_Inner in the outer FB's interface, then call it by its local name:


// Inside the outer FB (SCL)
#Heater_PID();
#Valve_PID();

Multi-instance avoids polluting the global DB namespace and is the recommended pattern for reusable subsystems. Details are documented in the SCL reference at docs.tia.siemens.cloud.

Memory Areas, Symbol Resolution, and Data Types

The S7-1200 memory model is partitioned into process image, bit memory, and DB areas. When you write "Output_Relay" := TRUE;, the compiler writes to the process image output (PAA, or %Q in IEC notation). At the end of the OB 1 cycle, the CPU copies the PAA to the physical output terminals.

Area Default tag syntax IEC 61131 form Notes
Process input image (PAA/PVE) Bool tag at %I0.0 %I0.0 Updated at start of OB 1 cycle
Process output image (PAB/PAA) Bool tag at %Q0.0 %Q0.0 Written to terminals at end of OB 1 cycle
Bit memory (Merker) Bool tag at %M0.0 %M0.0 Retentive if configured in PLC tag table
Global DB "MyDB".Variable n/a Optimized access by default on S7-1200
Temporary local Declaration as Temp in FB/FC interface n/a Uninitialized each call; do not rely on value
Static local Declaration as Static in FB interface n/a Stored in instance DB; persists between calls
Constants Declared in a global DB or tag table with attribute "Non-modifiable" n/a Useful for tuning values, setpoints

On the S7-1200 the default block access is "optimized" (symbolic only). Disable the optimized access in the block properties only if you require fixed absolute addresses for an external tool; otherwise, use symbolic names exclusively. Mixing symbolic and absolute access in the same block confuses the cross-reference tool and is a frequent source of "addressed but never read" warnings.

Common Pitfalls and Error Codes

When SCL does not behave as expected, work through the following matrix before re-writing the program. Each row is a real-world symptom reported on the S7-1200 platform.

Symptom Likely cause Diagnostic Fix
Output stays OFF, no error in CPU diagnostic buffer SCL block not called from OB 1 Online & Diagnostics → Call environment shows "—" for the block Call the block from OB 1 (or an interrupt OB) and re-download
Output stays OFF, CPU in STOP with SF LED OB 121 or OB 122 missing Open Online → Diagnostics; read event ID 0x2571 / 0x35xx Add OB 121/OB 122, or correct the address and recompile
Output flickers between scan boundaries Two blocks writing the same %Q Cross-reference (right-click CPU → Cross-reference) shows multiple writers Centralize the output write in one block
Tag in the SCL editor appears red Symbol not declared or wrong scope Go To → Tag Definition Re-declare the tag in the default tag table or DB and recompile
Compiler: "Identifier '...' not declared" Compiler cannot resolve the name Build message panel in TIA Portal Check spelling, case sensitivity, and the symbolic addressing project setting
CPU stays in STOP after download with "Programming error in OB 1" Type mismatch (e.g., writing INT to a BOOL) Diagnostic buffer event 0x2571 / 0x35xx Correct the data type; recompile
Output toggles once then stays OFF OB 100 or startup logic resets it Inspect OB 100 (warm restart) code Clear or skip the reset; rely on OB 1 only
Compile error: "Statement is not allowed in this block" Direct %I/%Q absolute address literal in OB 1 SCL Compile log Use symbolic tags declared in the default tag table
Output stays TRUE in STOP mode (relay contact) Relay output is fail-safe latched by external load (free-wheeling diode missing on DC load) Measure voltage across the load; observe contact polarity Add a free-wheeling diode across inductive DC loads
Download fails with "Differing block types" PC project block type does not match online block type Online → Accessible nodes; compare block headers Compile and download again; do not mix V13/V14/V15 project files

SCL Programming Patterns and Idioms

Once the basics work, use these canonical patterns to keep the code maintainable and to avoid common runtime issues.

Edge Detection (One-Shot)

The S7-1200 does not have a native edge-detection instruction callable directly in SCL with the := syntax, but you can implement one in an FB using a static variable:


// FB interface
VAR STATIC
  "LastScan_Start" : BOOL;
  "RisingEdge_Start" : BOOL;
END_VAR

// FB body
IF "Start_Button" AND NOT #"LastScan_Start" THEN
  #"RisingEdge_Start" := TRUE;
ELSE
  #"RisingEdge_Start" := FALSE;
END_IF;
#"LastScan_Start" := "Start_Button";

This is functionally identical to a P-contact in LAD and is the idiomatic way to detect one-shot edges in SCL.

State Machine with CASE

A multi-state controller fits naturally in SCL:


CASE #State OF
  0:  // Idle
      IF "Start_Button" THEN #State := 1; END_IF;
  1:  // Running
      "Output_Relay" := TRUE;
      IF "Stop_Button" THEN #State := 2; END_IF;
  2:  // Coast
      "Output_Relay" := FALSE;
      #State := 0;
  ELSE
      "Output_Relay" := FALSE;
      #State := 0;
END_CASE;

Read and Write to a Global DB


"RecipeDB".TargetTemperature := 75.0;
"RecipeDB".RampTime_Sec        := 120;
IF "RecipeDB".RampTime_Sec > 0 THEN
  "Output_Relay" := TRUE;
END_IF;

Watchdog and Cyclic Time

For deterministic loops, use a cyclic interrupt OB (OB 30 to OB 38) with a fixed period. The S7-1200 supports periods down to 1 ms on supported firmware; the default OB 35 is 100 ms. Drive a PID loop or a fast sampling task from a cyclic interrupt OB rather than from OB 1 to avoid cycle jitter.

Migrating SCL from S7-300/400 to S7-1200

SCL syntax is broadly compatible across S7-300/400 and S7-1200, but several constructs differ and must be re-written when porting code from older CPU families. The S7-1200 SCL reference at docs.tia.siemens.cloud lists the differences; the most common ones are summarized here.

  • Timer and counter instances: S7-300/400 used "T_DB".S5Time with separate inputs S, R, TV, BI, BCD. On S7-1200, the IEC timers (IEC_Timer_0_DB from the "Timers" library) use input/output parameters: IN, PT, Q, ET. Existing S5-time code must be re-encapsulated in calls to TP, TON, TOF, or TONR.
  • Bit memory direct access: S7-300/400 allowed M bits via direct M0.0 access in SCL; on S7-1200 the same access works in absolute form, but optimized blocks may require you to declare a tag pointing to %M0.0 in the default tag table and reference it symbolically.
  • System clock: S7-300/400 used SFC 0 / SFC 1 (READ_CLK / SET_CLK). S7-1200 replaces these with the RD_SYS_T and WR_SYS_T instructions in the extended instructions, or with operations on the DTL (Date_And_Time_Long) data type.
  • Multi-instance FBs: Multi-instance FB nesting is supported on S7-1200, but the syntax must match the FB's declaration in the interface editor. Multi-instances used in the multi-instance DB on S7-300/400 must be moved to the FB's static area in the S7-1200 project.
  • Symbolic vs absolute DB access: S7-300/400 used DB10.DBX0.0 notation. S7-1200 prefers "MyDB".Variable with the optimized access setting enabled.

Always recompile in the S7-1200 TIA Portal project; do not copy the STL/SCL source file directly. The S7-300/400 SCL may use instructions not present in the S7-1200 instruction set, and the compiler will surface them as build errors.

Verification and Commissioning

After downloading, run the following checks before declaring the program "working". Each item is fast and is documented in the S7-1200 system manual.

  1. Online & Diagnostics → Call environment: confirm OB 1, FC, and FB appear in the call stack and that the path is OB 1 → FC/FB → (none). If a block is not in the call stack, it is not executing.
  2. Monitor & force tables: open a watch table, add the output tag, and observe it transition to TRUE within one scan cycle of going to RUN. Right-click → Modify operand to 1 is a fast manual force for short tests, but release the force before leaving the cell.
  3. Cross-reference: in the project tree, right-click the CPU → Cross-reference. Confirm the output tag has exactly one writer. Multiple writers cause race conditions and produce apparently random output behavior.
  4. Diagnostic buffer: there should be no OB 121 (programming error), no OB 122 (I/O access error), no OB 80 (time error), and no communication errors. The buffer's most recent events are read with Online → Online & Diagnostics → Diagnostic buffer.
  5. Cycle time: confirm the cycle time stays below the configured maximum cycle time (default 150 ms for the S7-1200). Heavy SCL loops, large FOR loops, or many indirect-address operations can exceed the limit and trip OB 80. Use a EXIT; guard or move time-critical work into a cyclic interrupt OB.
  6. Force table release: before final commissioning, go to Online → Force table and remove all force entries. Forced tags remain forced across power cycles and downloads, which is a common cause of "the output is stuck ON" on the shop floor.
Safety: Output := TRUE; is unconditional and is intended only for first commissioning. Once the basic program is verified, replace the unconditional assignment with conditional logic that reads input states, interlocks, and operator commands. Never commission a relay that drives a hazardous load (motor contactor, hydraulic valve, heater element) with a hard-coded TRUE in OB 1. Use the standard Siemens safety pattern: dual-channel wiring, a 6ES7 safety CPU if required, and a tested E-Stop chain in hardware.

Field-Proven Diagnostic Checklist

Use this checklist when an S7-1200 SCL block does not behave as expected. The items are ordered from fastest to slowest to check.

  1. Is the CPU in RUN? (LED display or Online → Online & Diagnostics → Operating mode.)
  2. Is the OB 1 code present and downloaded? (Right-click OB 1 → Compare online/offline.)
  3. Is the FC/FB in the call stack? (Online & Diagnostics → Call environment.)
  4. Is the symbol declared at the right address? (PLC tags → Default tag table.)
  5. Is the address actually wired to the load? (Multimeter across the output contact.)
  6. Is the load supplied with the correct voltage? (24 VDC across the load, with the relay contacts closed.)
  7. Is another block overwriting the output? (Cross-reference.)
  8. Is a force table entry pinning the output? (Online → Force table.)
  9. Is OB 100 resetting the output? (Inspect the warm restart OB.)
  10. Is the diagnostic buffer clean? (No OB 80, OB 121, OB 122 events.)

More than 90% of "SCL does not work" cases on the S7-1200 fail on items 1 through 4. The remaining items catch the rare cases of dual-write, force-table lock, or cyclic overload.

FAQ

Why does my SCL assignment compile but never run?

The SCL block is almost certainly not being called from OB 1 (or from an active interrupt OB such as OB 35). The CPU firmware only executes OB 1, OB 100, OB 80/82, OB 121/122, and the time-of-day / cyclic OBs that are present in the project. Place the assignment in OB 1, or call your FB/FC from OB 1, then re-download and switch the CPU to RUN.

What is the difference between an OB, FB, FC, and DB on the S7-1200?

OBs are called automatically by the operating system (OB 1 is the main cyclic block). An FB is a code block with its own instance DB for static data and is used for stateful logic. An FC is a stateless subroutine with no instance DB. A DB is pure data storage. The S7-1200 system manual and the SCL reference at docs.tia.siemens.cloud are the authoritative sources for the block types and their lifecycles.

Can I assign a digital output directly with SCL the same way as in ladder?

Yes. Declare a tag of type Bool at address %Q0.0 in the default tag table, then in OB 1 SCL write "TagName" := TRUE;. The CPU copies the process image to the physical terminals at the end of the OB 1 cycle, so the relay or transistor output transitions within one scan.

What TIA Portal version is required to program the S7-1200 in SCL?

Any TIA Portal from V13 SP1 onward supports SCL on the S7-1200. TIA Portal V15.1 or later is recommended for current firmware (V4.4 and up). The S7-1200 G2 family with firmware V5.0 requires TIA Portal V20. Always match the TIA Portal version to the CPU firmware per the Siemens compatibility list, and avoid mixing project versions across multiple engineers on the same program.

My CPU goes to STOP after I download SCL. What should I check first?

Open Online → Online & Diagnostics → Diagnostic buffer and read the most recent event. OB 121 (programming error) typically points to a type mismatch or undeclared symbol; OB 122 points to a missing I/O module or wrong address. Correct the source, recompile, and re-download. If OB 80 appears, the cyclic or interrupt time was exceeded; reduce the workload or move it to a dedicated OB.

Back to blog