All posts
codesys6 min read

5 CODESYS Function Block Examples Every PLC Programmer Needs

Ready-to-use CODESYS function block examples in Structured Text: motor control, state machine, alarm handler, moving average filter, and recipe manager. Copy, paste, and adapt for your projects.

PLC Assist Team

5 CODESYS Function Block Examples Every PLC Programmer Needs

Function blocks are the building blocks of professional PLC code. A well-designed FB can be reused across dozens of projects, saving hours of development time.

Here are five function blocks that every CODESYS programmer ends up needing. All examples are written in IEC 61131-3 Structured Text and ready to use in any CODESYS V3 project.

1. Motor Control with Interlocks

The most common function block in industrial automation. This one handles start/stop, overcurrent protection, and runtime tracking.

FUNCTION_BLOCK FB_MotorControl
VAR_INPUT
    bStart       : BOOL;   // Start command
    bStop        : BOOL;   // Stop command
    bEmergency   : BOOL;   // Emergency stop (NC)
    bInterlock   : BOOL;   // External interlock (TRUE = OK)
    rCurrent     : REAL;   // Measured motor current [A]
    rCurrentMax  : REAL := 20.0;  // Overcurrent trip [A]
    tStartDelay  : TIME := T#500MS;
END_VAR

VAR_OUTPUT
    bRunning     : BOOL;   // Motor is running
    bFault       : BOOL;   // Fault active
    nFaultCode   : INT;    // 0=OK, 1=Overcurrent, 2=Interlock, 3=E-Stop
    tTotalRuntime : TIME;
END_VAR

VAR
    bMotorCmd    : BOOL;
    rtrigStart   : R_TRIG;
    tonStart     : TON;
    tonRuntime   : TON;
    bLatched     : BOOL;
END_VAR

// Edge detection on start
rtrigStart(CLK := bStart);

// Fault detection
IF NOT bEmergency THEN
    bFault := TRUE;
    nFaultCode := 3;
    bLatched := FALSE;
ELSIF NOT bInterlock THEN
    bFault := TRUE;
    nFaultCode := 2;
    bLatched := FALSE;
ELSIF bRunning AND rCurrent > rCurrentMax THEN
    bFault := TRUE;
    nFaultCode := 1;
    bLatched := FALSE;
ELSE
    nFaultCode := 0;
END_IF

// Start/Stop logic
IF bFault THEN
    bLatched := FALSE;
ELSIF rtrigStart.Q AND NOT bFault THEN
    bLatched := TRUE;
ELSIF bStop THEN
    bLatched := FALSE;
END_IF

// Start delay
tonStart(IN := bLatched AND NOT bFault, PT := tStartDelay);
bMotorCmd := tonStart.Q;
bRunning := bMotorCmd;

// Runtime counter
tonRuntime(IN := bRunning, PT := T#24H);
tTotalRuntime := tonRuntime.ET;

// Reset fault when conditions clear and stop is pressed
IF bFault AND bStop AND bEmergency AND bInterlock AND (rCurrent <= rCurrentMax) THEN
    bFault := FALSE;
END_IF

Usage:

fbMotor1(
    bStart := bHMI_StartMotor1,
    bStop := bHMI_StopMotor1,
    bEmergency := bEmergencyCircuit,
    bInterlock := bSafetyGatesClosed,
    rCurrent := rMotor1Current
);

bDO_Motor1 := fbMotor1.bRunning;

2. Generic State Machine

A reusable state machine pattern with automatic timeout and error handling.

TYPE E_SeqState :
(
    SEQ_IDLE        := 0,
    SEQ_INIT        := 10,
    SEQ_STEP_1      := 20,
    SEQ_STEP_2      := 30,
    SEQ_STEP_3      := 40,
    SEQ_COMPLETE    := 100,
    SEQ_ERROR       := 999
);
END_TYPE

FUNCTION_BLOCK FB_Sequence
VAR_INPUT
    bExecute     : BOOL;   // Start sequence
    bReset       : BOOL;   // Reset from error
    bAbort       : BOOL;   // Abort sequence
    tStepTimeout : TIME := T#30S;  // Max time per step
END_VAR

VAR_OUTPUT
    eState       : E_SeqState;
    bBusy        : BOOL;
    bDone        : BOOL;
    bError       : BOOL;
    nCurrentStep : INT;
END_VAR

VAR
    rtrigExecute : R_TRIG;
    tonTimeout   : TON;
    bStepActive  : BOOL;
END_VAR

rtrigExecute(CLK := bExecute);

// Timeout monitoring
bStepActive := eState > E_SeqState.SEQ_IDLE
               AND eState < E_SeqState.SEQ_COMPLETE;
tonTimeout(IN := bStepActive, PT := tStepTimeout);

IF tonTimeout.Q THEN
    eState := E_SeqState.SEQ_ERROR;
END_IF

// Abort
IF bAbort AND bBusy THEN
    eState := E_SeqState.SEQ_IDLE;
END_IF

CASE eState OF
    E_SeqState.SEQ_IDLE:
        bBusy := FALSE;
        bDone := FALSE;
        bError := FALSE;
        IF rtrigExecute.Q THEN
            eState := E_SeqState.SEQ_INIT;
            bBusy := TRUE;
        END_IF

    E_SeqState.SEQ_INIT:
        nCurrentStep := 1;
        tonTimeout(IN := FALSE);  // Reset timeout
        // Add initialization logic here
        eState := E_SeqState.SEQ_STEP_1;

    E_SeqState.SEQ_STEP_1:
        nCurrentStep := 1;
        // Add step 1 logic here
        // When done:
        tonTimeout(IN := FALSE);
        eState := E_SeqState.SEQ_STEP_2;

    E_SeqState.SEQ_STEP_2:
        nCurrentStep := 2;
        // Add step 2 logic here
        tonTimeout(IN := FALSE);
        eState := E_SeqState.SEQ_STEP_3;

    E_SeqState.SEQ_STEP_3:
        nCurrentStep := 3;
        // Add step 3 logic here
        tonTimeout(IN := FALSE);
        eState := E_SeqState.SEQ_COMPLETE;

    E_SeqState.SEQ_COMPLETE:
        bBusy := FALSE;
        bDone := TRUE;

    E_SeqState.SEQ_ERROR:
        bBusy := FALSE;
        bError := TRUE;
        IF bReset THEN
            eState := E_SeqState.SEQ_IDLE;
        END_IF
END_CASE

3. Alarm Handler

Track alarms with timestamps, acknowledgment, and history.

FUNCTION_BLOCK FB_Alarm
VAR_INPUT
    bCondition    : BOOL;     // Alarm condition (TRUE = alarm)
    bAcknowledge  : BOOL;     // Operator acknowledges
    tDebounce     : TIME := T#1S;  // Prevent flicker
    sDescription  : STRING(80);
END_VAR

VAR_OUTPUT
    bActive       : BOOL;     // Alarm condition is TRUE now
    bUnacknowledged : BOOL;   // Needs operator attention
    bLatched      : BOOL;     // Was active, may have cleared
    nActivations  : DINT;     // Total times alarm triggered
END_VAR

VAR
    tonDebounce   : TON;
    rtrigAlarm    : R_TRIG;
    rtrigAck      : R_TRIG;
    bPrevActive   : BOOL;
END_VAR

// Debounced alarm condition
tonDebounce(IN := bCondition, PT := tDebounce);
bActive := tonDebounce.Q;

// Rising edge -- new alarm event
rtrigAlarm(CLK := bActive);
IF rtrigAlarm.Q THEN
    bLatched := TRUE;
    bUnacknowledged := TRUE;
    nActivations := nActivations + 1;
END_IF

// Acknowledgment
rtrigAck(CLK := bAcknowledge);
IF rtrigAck.Q THEN
    bUnacknowledged := FALSE;
    // Clear latch if condition is gone
    IF NOT bActive THEN
        bLatched := FALSE;
    END_IF
END_IF

// Auto-clear latch when acknowledged and condition clears
IF NOT bActive AND NOT bUnacknowledged THEN
    bLatched := FALSE;
END_IF

Usage -- managing multiple alarms:

VAR
    fbAlarmHighTemp   : FB_Alarm;
    fbAlarmLowPress   : FB_Alarm;
    fbAlarmOverspeed  : FB_Alarm;
    bAnyAlarmActive   : BOOL;
    bAnyUnacked       : BOOL;
END_VAR

fbAlarmHighTemp(
    bCondition := rTemperature > 85.0,
    bAcknowledge := bHMI_AckAll,
    sDescription := 'High temperature'
);

fbAlarmLowPress(
    bCondition := rPressure < 2.0,
    bAcknowledge := bHMI_AckAll,
    sDescription := 'Low pressure'
);

bAnyAlarmActive := fbAlarmHighTemp.bActive OR fbAlarmLowPress.bActive;
bAnyUnacked := fbAlarmHighTemp.bUnacknowledged OR fbAlarmLowPress.bUnacknowledged;

4. Moving Average Filter

Essential for noisy analog signals from sensors.

FUNCTION_BLOCK FB_MovingAverage
VAR_INPUT
    rInput    : REAL;     // Raw input value
    bEnable   : BOOL := TRUE;
    bReset    : BOOL;
END_VAR

VAR_OUTPUT
    rFiltered : REAL;     // Filtered output
    bValid    : BOOL;     // TRUE when buffer is full
END_VAR

VAR CONSTANT
    BUFFER_SIZE : INT := 20;
END_VAR

VAR
    arBuffer  : ARRAY[0..19] OF REAL;
    nIndex    : INT := 0;
    nCount    : INT := 0;
    rSum      : REAL := 0.0;
    i         : INT;
END_VAR

IF bReset THEN
    FOR i := 0 TO BUFFER_SIZE - 1 DO
        arBuffer[i] := 0.0;
    END_FOR
    nIndex := 0;
    nCount := 0;
    rSum := 0.0;
    rFiltered := 0.0;
    bValid := FALSE;
    RETURN;
END_IF

IF NOT bEnable THEN
    RETURN;
END_IF

// Subtract oldest value, add new value
rSum := rSum - arBuffer[nIndex] + rInput;
arBuffer[nIndex] := rInput;

// Advance index (circular buffer)
nIndex := nIndex + 1;
IF nIndex >= BUFFER_SIZE THEN
    nIndex := 0;
END_IF

// Track fill level
IF nCount < BUFFER_SIZE THEN
    nCount := nCount + 1;
END_IF

bValid := (nCount >= BUFFER_SIZE);

// Calculate average
IF nCount > 0 THEN
    rFiltered := rSum / DINT_TO_REAL(nCount);
END_IF

Usage:

VAR
    fbFilterTemp : FB_MovingAverage;
    rRawTemp     : REAL;  // From analog input
    rSmoothTemp  : REAL;
END_VAR

fbFilterTemp(rInput := rRawTemp);
rSmoothTemp := fbFilterTemp.rFiltered;

5. Recipe Manager

Store and recall parameter sets for different products.

TYPE ST_Recipe :
STRUCT
    sName         : STRING(40);
    rTemperature  : REAL;   // Setpoint [C]
    rPressure     : REAL;   // Setpoint [bar]
    rSpeed        : REAL;   // Setpoint [RPM]
    tMixTime      : TIME;   // Process duration
    nCycles       : INT;    // Repeat count
END_STRUCT
END_TYPE

FUNCTION_BLOCK FB_RecipeManager
VAR_INPUT
    nLoadIndex    : INT;    // Recipe to load (0-based)
    bLoad         : BOOL;   // Load command
    bSave         : BOOL;   // Save current to index
END_VAR

VAR_OUTPUT
    stActiveRecipe : ST_Recipe;  // Currently active recipe
    bLoaded        : BOOL;
    bError         : BOOL;
END_VAR

VAR CONSTANT
    MAX_RECIPES : INT := 10;
END_VAR

VAR PERSISTENT
    arRecipes : ARRAY[0..9] OF ST_Recipe;
END_VAR

VAR
    rtrigLoad : R_TRIG;
    rtrigSave : R_TRIG;
END_VAR

rtrigLoad(CLK := bLoad);
rtrigSave(CLK := bSave);

bError := FALSE;

// Load recipe
IF rtrigLoad.Q THEN
    IF nLoadIndex >= 0 AND nLoadIndex < MAX_RECIPES THEN
        stActiveRecipe := arRecipes[nLoadIndex];
        bLoaded := TRUE;
    ELSE
        bError := TRUE;
    END_IF
END_IF

// Save current recipe to slot
IF rtrigSave.Q THEN
    IF nLoadIndex >= 0 AND nLoadIndex < MAX_RECIPES THEN
        arRecipes[nLoadIndex] := stActiveRecipe;
    ELSE
        bError := TRUE;
    END_IF
END_IF

Tips for Writing Better Function Blocks

  1. Keep them focused -- one FB, one responsibility
  2. Use meaningful prefixes -- b for BOOL, r for REAL, n for INT, t for TIME, s for STRING, fb for function block instances
  3. Document inputs and outputs with inline comments showing units
  4. Use edge detection (R_TRIG, F_TRIG) for command inputs to prevent repeated execution
  5. Default to safe states -- outputs should be safe (off, closed, stopped) when the FB initializes

Generate Function Blocks with AI

Writing these function blocks takes time. With PLC Assist, you can describe what you need in plain English and get complete, ready-to-use Structured Text code generated for your CODESYS project. The AI understands IEC 61131-3 patterns and generates code that follows best practices.


PLC Assist is an AI-powered engineering assistant for industrial automation. Start free -- 25 AI requests included, no credit card required.