A production-representative implementation of a reusable Function Block motor control library in Siemens TIA Portal, demonstrating multi-instance Data Block patterns, fault-detection timers, and structured control hierarchies aligned with IEC 61131-3 and Siemens S7 programming standards.
This project extends the foundational start/stop motor control work into a fully modular, reusable architecture using Siemens Function Blocks (FBs) and Instance Data Blocks (DBs) — the standard structural pattern for scalable industrial automation programs on the S7 platform. The central design objective was not simply to control a motor, but to build a control library: a reusable software component that can be instantiated multiple times across a line, a plant area, or an entire facility, without duplicating logic.
Two Function Blocks form the core of the system. FB_Motor [FB1] encapsulates all logic and state
for a single motor axis — start/stop latching, fault detection, run-time feedback monitoring, and a watchdog
timer that flags a failure if the motor does not confirm running within a defined window.
FB_LineControl [FB2] acts as the supervisory coordinator, instantiating two FB_Motor
instances as multi-instance static variables, routing line-level signals to each motor, and managing a
shared reset condition. The result is a two-motor production line managed by a single block call from OB1.
The Siemens S7 execution model enforces a strict separation between logic (Program Blocks) and state (Data Blocks). Unlike CODESYS-based platforms where a function block's static data can be declared inline within the same compilation unit, TIA Portal allocates a dedicated Instance DB for every FB call — either as a standalone DB or as a nested static variable within a parent FB's own DB.
The critical architectural decision here is using multi-instance design rather than creating
separate DBs for each motor. When Motor1 and Motor2 are declared as static variables
of type "FB_Motor" inside FB_LineControl, their instance data is embedded directly
within DB1. This eliminates DB proliferation as motor count grows and keeps the entire line's
state visible within a single data structure — which is advantageous for diagnostics, SCADA integration,
and HMI variable mapping.
FB_LineControl as Static → "FB_Motor"LatchedRun, StartTimer, fault bitsFB_LineControl is called once; internally calls both motor instances sequentially
FB_Motor is the atomic control unit — the reusable component that models a single motor axis
with all associated control logic. Its interface was designed to be fully generic: it accepts hardware signals
as Bool inputs and drives outputs independently, making it suitable for any actuator type that fits the
start/stop/feedback paradigm (motors, pumps, fans, conveyors).
Start — Bool: momentary start command from operator or supervisory logicStop — Bool: stop command; evaluated before start on every scanFaultReset — Bool: clears the latched fault condition and resets the fault outputRunFeedback — Bool: auxiliary contact or drive ready signal confirming motor is runningFaultInput — Bool: external fault signal — motor overload, drive trip, or E-stopMotorRunning — Bool: latched run state; drives the output coil or drive enableFaulted — Bool: latched fault flag; indicates motor is in a faulted, non-runnable stateLatchedRun — Bool: internal latch bit holding the energised state between scansStartTimer — TON_TIME: run-confirmation watchdog; triggers fault if feedback absent after startThe block is implemented in SCL (Structured Control Language), Siemens' IEC 61131-3 compliant structured text dialect. SCL is selected over Ladder here because the conditional logic — prioritised fault evaluation, timer management, and output assignment — is more legible and less error-prone in a textual language. The logic evaluates in strict priority order per scan:
(* Priority 1: Fault Reset clears the latched fault state *) IF #FaultReset THEN #Faulted := FALSE; END_IF; (* Priority 2: External fault input or run-confirmation timeout latches fault *) IF #FaultInput THEN #Faulted := TRUE; #LatchedRun := FALSE; (* Immediate de-energise on fault *) END_IF; (* Priority 3: Start/Stop latching logic — fault state blocks start *) IF #Faulted THEN #LatchedRun := FALSE; ELSIF #Start THEN #LatchedRun := TRUE; ELSIF #Stop THEN #LatchedRun := FALSE; END_IF; (* Run-confirmation watchdog: timer starts with LatchedRun; fault if no feedback *) #StartTimer(IN := #LatchedRun, PT := T#3s); IF #StartTimer.Q AND NOT #RunFeedback THEN #Faulted := TRUE; #LatchedRun := FALSE; END_IF; (* Output assignment *) #MotorRunning := #LatchedRun; #Faulted := #Faulted;
FaultReset has not yet executed. The resolution is to provide an explicit default initialisation
of Faulted := FALSE in the variable interface table's Default Value column, ensuring the DB
initialises the variable to a known state at first download. The block compiled successfully with 0 errors
and 0 warnings after this correction (visible in Screenshot 3 → Screenshot 6).
FB_LineControl is the supervisory layer responsible for managing two independent motor instances
as a coordinated production line. Its role is signal routing: it receives line-level inputs (Start1/Stop1,
Start2/Stop2, feedback and fault signals for each axis, and a shared ResetAll) and maps them to the
corresponding FB_Motor inputs at each scan. It does not implement its own latching or fault logic —
all such behaviour is encapsulated within the FB_Motor instances it contains.
Start1, Stop1 — operator push-buttons or SCADA commands for axis 1RunFb1 — run feedback from motor 1 auxiliary contactFault1 — fault input from motor 1 overload or drive tripStart2, Stop2 — independent controls for axis 2RunFb2 — run feedback from motor 2Fault2 — fault input from motor 2ResetAll — Bool: a single reset command routed to both motor instances simultaneously, enabling a single operator action to clear faults across the entire line
The key implementation detail visible in Screenshot 4 is the Static variable section of
FB_LineControl: Motor1 and Motor2 are declared with data type
"FB_Motor". This instructs TIA Portal to allocate the full FB_Motor interface —
including all its static variables — as a nested structure within FB_LineControl's own DB.
No separate DBs for Motor1 or Motor2 are created.
(* FB_LineControl SCL body — motor instance calls *) #Motor1( Start := #Start1, Stop := #Stop1, FaultReset := #ResetAll, RunFeedback := #RunFb1, FaultInput := #Fault1 ); #Motor2( Start := #Start2, Stop := #Stop2, FaultReset := #ResetAll, RunFeedback := #RunFb2, FaultInput := #Fault2 );
ResetAll to FaultReset on both instances is an intentional design choice.
In a real panel, a single reset push-button clears all motor faults simultaneously — the operator does not need
to reset each axis independently. This mirrors standard MCC (Motor Control Centre) reset behaviour and reduces
operator action count after a line trip event.
Organisation Block 1 (OB1) is the cyclic execution entry point for the S7-1200. In this project, OB1 contains
a single ladder rung that calls FB_LineControl [FB2] with its assigned instance DB
(FB_LineControl_DB [DB1]). All inputs are connected to their corresponding absolute or symbolic
addresses — in the simulation environment, test values are wired directly as static literals
(e.g., true, false) to validate the calling interface before hardware I/O is mapped.
The rung visible in Screenshot 7 shows the standard Siemens FB call block representation in Ladder: the function block name and its instance DB are shown above the block boundary, input parameters are listed on the left with their connected signal sources, and EN/ENO flow controls execution. The FB runs on every OB1 scan as long as the enable rung is satisfied.
Motor3 : "FB_Motor"
to FB_LineControl's static section, add the corresponding input signals to its interface,
and add the FB_Motor call with signal routing in the SCL body. OB1 and FB_Motor itself require
zero changes. This demonstrates the library-oriented design philosophy in practice.
Start, Stop,
FaultReset, RunFeedback, and FaultInput, all typed as Bool with
Non-retain storage. The Output section exposes MotorRunning and Faulted.
The Static section — critical to the DB-backed architecture — shows LatchedRun (Bool)
as the internal state latch, and StartTimer typed as TON_TIME, which is the
run-confirmation watchdog timer whose elapsed time is stored persistently in the instance DB across every
scan cycle. The project tree confirms both FB_LineControl [FB2] and FB_Motor [FB1]
exist under Program Blocks for the CPU 1212C.
#Faulted when FaultReset is
asserted. Lines 10–17 show the priority-ordered start/stop logic: fault condition evaluates first and
forces #LatchedRun to FALSE, overriding any start command. The ELSIF chain ensures the
start latch only sets when the block is not in a faulted state. This priority model reflects industrial
safety conventions — fault state always wins over run commands on the same scan. The SCL comment
"Start/Stop logic" at line 10 was deliberately placed to assist code review and maintenance.
FB_Motor: "The parameter '#Faulted'
might not be initialized." This is a static analysis warning generated because the compiler cannot
guarantee that #Faulted holds a defined value before it is read in the IF condition on line
11 — specifically in the case where FaultReset on line 7 is FALSE and no prior assignment
has occurred. In a real download scenario, the variable defaults to the DB initial value (FALSE), so
runtime behaviour is correct; however, TIA Portal correctly flags this as a code quality issue. The
resolution — adding an explicit initialisation default to the interface table — was applied before final
compilation, as confirmed in Screenshot 6.
MotorRunning,
Faulted) are accessible from the parent DB directly for SCADA or HMI integration without
requiring additional pass-through outputs on the line controller. The Static section at rows 17–18 is the
architectural core: Motor1 and Motor2 are declared with type
"FB_Motor", instructing TIA Portal to embed the full FB_Motor instance data structure
within DB1 — eliminating the need for two separate instance DBs.
#Motor1 call with named parameter assignment: Start1 → Start,
Stop1 → Stop, ResetAll → FaultReset, RunFb1 → RunFeedback, Fault1 → FaultInput. Lines 9–14 show the
identical structure for #Motor2 using its corresponding input signals. Named parameter
assignment (using :=) is the recommended Siemens style for FB calls — it is order-independent,
self-documenting, and robust to future interface extensions where new optional parameters might be added
without breaking the call site. The symmetry of the two call blocks demonstrates the modularity of the
architecture: identical logic, independent instances, separate state.
%DB1 / "FB_LineControl_DB" as the assigned instance DB and
%FB2 / "FB_LineControl" as the function block reference. The input connections show
Start1 wired to true, RunFb1 wired to true (simulating a running motor with
feedback present), and all remaining inputs wired to false — a deliberate test configuration
that validates the start and feedback path for Motor1 while Motor2 is held in a stopped state. This
screenshot confirms the complete integration of the FB library into the executable program: the block is
callable, the DB is assigned, and the input interface is fully connected.
The initial FB_Motor compilation produced a static analysis warning: "The parameter '#Faulted' might not be
initialized." This surfaced because TIA Portal's SCL compiler performs conservative flow analysis —
it traces all code paths and flags any variable that could be read before a guaranteed write. In the fault
evaluation block, #Faulted is read in the condition of the outer IF statement before it is
written in the FaultInput branch. Although the DB initial value of FALSE ensures correct runtime behaviour,
the compiler correctly identified that this is not provable from the code structure alone. Resolution:
explicit default value assignment in the interface table, ensuring the variable is always initialised at
DB creation time. This also improves maintainability — future developers can see the intended initial state
without tracing the code logic.
Understanding that TIA Portal requires the DB to be re-generated and re-downloaded whenever the FB interface changes was a key operational learning. Adding or removing a parameter from an FB's interface renders the associated DB stale — the portal marks it with a warning indicator, and the program will not download cleanly until the DB is re-compiled. This is a tighter coupling than CODESYS, where interface changes are handled more transparently. Managing this sequence — change FB interface → compile FB → compile DB → download — is an essential operational habit in Siemens projects.
PLCSIM Advanced, available in the local TIA Portal installation, provides full I/O simulation including force tables and watch tables for live variable monitoring. The TIA Portal cloud environment used here does not support PLCSIM, which meant the simulation phase — where inputs would be forced and output states observed in real time — could not be executed. This required a shift in validation strategy: logic correctness was verified through structured code review, compilation output analysis, and conceptual walkthrough of each code path rather than live forcing. This approach — design-by-review rather than design-by-debugging — is actually representative of how safety-critical code is validated in regulated environments where live simulation may not be available.
Declaring a Static variable as type "FB_Motor" inside FB_LineControl requires
that FB_Motor is already compiled and its interface is consistent. During development, an
out-of-order compile sequence caused TIA Portal to report an unresolved type reference on the Static
variable declaration. The resolution was straightforward — compile FB_Motor first, then
FB_LineControl — but this dependency order is worth documenting: in larger projects with deeper
FB nesting, a dependency graph of block compilation order is a useful project management artefact.