Finite State Machines, IEC 61131

Using Finite State Machines in PLC programs

Finite state machines (FSB) are a great tool for modeling equipment and machine functionality. It provides user with tools to model behavior of equipment, well defined programming interface and ability to easily monitor and trace machine operation. Produced code is simple, clear and easy to maintain. Engineering effort is moved from coding to modeling behavior of equipment using state charts. A simple valve control will be used to demonstrate this approach.

To start we will use FSB to model a valve (hydraulic/pneumatic). Valve have one spool which, when energized, will extend cylinder to its final position. When deenergized cylinder will retract to initial position. One DO is used for energizing spool and two DI for monitoring initial and final position. Additionally three more signals are used:

  • Emergency stop
  • Equipment ready – Signal indicating it is safe to move without possible collision with another equipment
  • MCCOK – Usually feedback from valve power supply

Functionality is simple: When deenergized valve is in its initial state and cylinder is retracted to initial position. Initial position is indicated by limit switch (EndPosOFF). On rising edge of CmdON (^CmdON) valve will be energized and cylinder will travel for some finite time to its end position. End position is indicated by end limit switch (EndPosON). Command CmdOFF will deenergize the valve and cylinder will travel to its initial position. While CmdOFF is active it is not possible to energize the valve. New command will be active again on ^CmdON. This functionality is represented on following diagram.

valvesingle

Since we will use this type of diagram in other posts little explanation is required:

  • Ovals represent states. Each state is identified by name and number (OFF 16#0001). Each FSB start in initial state (Init 16#0000). Rising edge of Enable signal is required to start FSB functionality. Enable must be TRUE at all times, otherwise FSB will move to Init state and finctionality fill be disabled.
  • Transitions from one to next state are represented by arrows. Direction of an arrow indicates next state. Small bullet on beginning of transition indicates priority of transition. Smaller the number, transition have higher priority. Blue bullet indicate controlled state change, red ones error state changes
  • Dashed rectangle with bullets represent group of states with state change activated by common signal. This signal will move FSB to common state regardless of current state of FSB.

Presented diagram shows only transitions that represent control functionality of FSB. Typically control diagram must be accompanied by error transition diagram .

valvesingleerror

Since error handling represent large portion of the code, dividing functionality like this gives programmer possibility to focus on control or error handling logic separately. Of course these two diagrams can be combined to one, but it is my opinion that this approach may have its benefits.

Generally, there are three types of states. FSB will be in initial state as long as Enable signal is FALSE. In initial state FSB functionality is disabled. Resident state is a state in which FSB can stay for indefinite period of time (in our case ON, OFF, Aborted). Transition out of this state is only exterior event based (command or feedback). Transition states are ones in which FSB remains for finite time and indicate transition (physical or logical) form one to another resident state (in our case WaitON, WaitOFF and Aborting). Transition states, in general should be guarded with timers (in our case DelayON, DelayOFF). If the timer expires, automatic transition from this state must be triggered. As a rule of thumb, transition from one to another resident state is possible only through transition state. Direct transition from one to another resident state should be avoided. For this reason Aborting state is inserted in front of Aborted state. Automatic transition to Aborted takes place when system reaches EndPosOFF.

Valve functionality is realized in one Function Block (FB) in Siemens TIA Portal. Typical use is shown on next picture.

ValveSingleUse

This is setup for simulating commands, parameters, feedback signals from limit machine and control cabinet. Function block outputs represent full set of signals which can be used for control logic and troubleshooting problems.

Structured text code for valve control is rather simple. FSM code is in form of one CASE instruction. Control transitions only move FSM from one to another state. Error transitions, in addition, set error code consisting of cause of error and state in which error is set. Outputs are generated by validating current operating state of the valve. No complicated logic, no SET/RESET. Fundamental difference form using “standard” logic is that when using FSM we search and code transitions in valve operation. in “standard” logic we look for states and code logic which will keep valve in one of these states. To code functionality like this in “standard” logic would require much more effort, code and work.

Another difference is: coding FSM is simple. Making functional FSM diagram is not. Most of the engineering work is done to produce a FSM diagram which will cover all use cases and all functionality. To give an example: On first diagram, transition from WaitOFF to WaitON state on repeated CmdON is missing. This is deliberate design decision. In this way we prevent repeated on command until cylinder reaches EndPosOFF. This may or may not be desired behavior of the device and must be taken into account during design. Adding or removing this functionality is trivial using FSM. Adding this functionality uslig “standard” logic may be very difficult. One more example: Watchdog timers only move FSM from transient states (WaitON, WaitOFF) to resident states (ON, OFF). In transient states we wait for end limits and when they are reached we move on. If not an error should be raised. We could do it in transient states but then we would duplicate the code since same check must be made in resident states. This approach of only moving to another state when watchdog expires makes simulation of system very easy, as shown on previous picture.

If FSM are used throughout complete PLC program to model functionality of the system from basic elements like sensors and actuators, to complete machine logic, we get system that is well structured, easily modified and easy for an end user. I will try to demonstrate this in posts that will follow.

FUNCTION_BLOCK "ValveSingle"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
////=============================================================================
//// Dragan Grujicic
//// code4plc.org
//// (c)Copyright (2018) All Rights Reserved
////-----------------------------------------------------------------------------
////
//// Single valve functionality using finite state machines (FSM)
////
////
////
////
////-----------------------------------------------------------------------------
//// Change log table:
//// Version Date Expert in charge Changes applied
//// 01.00.00 dd.mm.yyyy (Name of expert) First released version
////=============================================================================
   VAR_INPUT
      Enable : Bool;   // Enable valve control
      CmdON : Bool;   // Switch valve ON. Active on TRUE
      CmdOFF : Bool;   // Switch valve OFF. Active on TRUE (FALSE for normal operation)
      EmergencyStop : Bool;   // Emergency stop of the valve
      MCCOK : Bool;   // Power supply OK
      EquipmentReady : Bool := true;   // Mechanical equipment ready. Must be TRUE all time valve is in operation
      EndPositionON : Bool;   // End limit switch in ON position
      EndPositionOFF : Bool;   // End limit switch in OFF position
      TimeDlyON : Time := T#1s;   // [s] Time required to reach end position in ON direction
      TimeDlyOFF : Time := T#1s;   // [s] Time required to reach end position in OFF direction
      Reset : Bool;   // Reset control errors
   END_VAR

   VAR_OUTPUT
      Valid : Bool;   // Control signals valid: enable and no error
      ON : Bool;   // Control signal for the valve: ON untill #CmdOFF
      ONlimited : Bool;   // Control signal for the valve: ON until #EndPositionON or #CmdOFF
      ReadyRun : Bool;   // Valve ready run: Ready to follow command
      Busy : Bool;   // Valve control busy: Valve in state other than OFF
      ActiveState : UInt;   // Current valve state
      AtPositionON : Bool;   // End limit switch in ON position
      AtPositionOFF : Bool;   // End limit switch in OFF position
      Error : Bool;   // Valve error active
      Status : UInt;   // Status signal indicating current valve state
   END_VAR

   VAR
      iActiveState : UInt;   // Current valve state
      iNextState : UInt;   // Next valve state
      iValid : Bool;   // Valve control processing valid
      iError : Bool;   // Valve error active
      iErrorID : UInt;   // Error ID
      TON_DlyON {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
      TON_DlyOFF {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
      R_TRIG_CmdON {OriginalPartName := 'R_TRIG_1200'; LibVersion := '1.0'} : R_TRIG;
      R_TRIG_Reset {OriginalPartName := 'R_TRIG_1200'; LibVersion := '1.0'} : R_TRIG;
      R_TRIG_Enable {OriginalPartName := 'R_TRIG_1200'; LibVersion := '1.0'} : R_TRIG;
   END_VAR

   VAR CONSTANT
      STATE_INITIAL : UInt := 16#0000;   // Initial state
      STATE_OFF : UInt := 16#0001;   // Valve OFF
      STATE_WAIT_ON : UInt := 16#0002;   // Valve is ON and moving to end limit ON
      STATE_ON : UInt := 16#0003;   // Valve is ON
      STATE_WAIT_OFF : UInt := 16#0004;   // Valve is OFF and moving to end limit OFF
      STATE_ABORTING : UInt := 16#0005;   // ERROR detected and motor is switched OFF: Waiting to stop
      STATE_ABORTED : UInt := 16#0006;   // ERROR detected and motor is switched OFF: Waiting for reset
      ERROR_UNKNOWN_STATE : UInt := 16#8010;   // Unknown current state error
      ERROR_MCC_NOT_VALID : UInt := 16#8020;   // Power supply for the valve not OK
      ERROR_EQUIPMENT_NOT_READY : UInt := 16#8030;   // Mechanical equipment of the valve not ready
      ERROR_EMERGENCY_STOP : UInt := 16#8040;   // Emergency stop active
      ERROR_END_POS_ON : UInt := 16#8050;   // Equipment in ON position with valve OFF
      ERROR_END_POS_OFF : UInt := 16#8060;   // Equipment in OFF position with valve ON
   END_VAR

BEGIN
	//*********************************************************************************************//
	//*********************** Valve State Transition Detection ************************************//
	#R_TRIG_Enable(CLK:=#Enable);
	#R_TRIG_CmdON(CLK:=#CmdON);
	#R_TRIG_Reset(CLK:=#Reset);

	//*********************************************************************************************//
	//******************************* Timer Evaluation ********************************************//
	#TON_DlyON(IN:=#iActiveState = #STATE_WAIT_ON,
	           PT:=#TimeDlyON);
	#TON_DlyOFF(IN:=#iActiveState = #STATE_WAIT_OFF,
	           PT:=#TimeDlyOFF);

	//*********************************************************************************************//
	//*************************** Valve State Transitions *****************************************//
	IF #Enable THEN
	    //Check to see if #EmergencyStop, #MCCOK and #EquipmentReady are OK
	    //If not automaticaly go to #STATE_ABORTING, but only if there is not active error on valve
	    //These signals need to be TRUE all the time.
	    //Check this frist so we do not need to check it in state transitions
	    IF NOT  #iError AND NOT #EmergencyStop THEN
	        #iNextState := #STATE_ABORTING;
	        #iError := TRUE;
	        #iErrorID := #ERROR_EMERGENCY_STOP OR #iActiveState;
	    ELSIF NOT  #iError AND NOT #MCCOK THEN
	        #iNextState := #STATE_ABORTING;
	        #iError := TRUE;
	        #iErrorID := #ERROR_MCC_NOT_VALID OR #iActiveState;
	    ELSIF NOT  #iError AND NOT #EquipmentReady THEN
	        #iNextState := #STATE_ABORTING;
	        #iError := TRUE;
	        #iErrorID := #ERROR_EQUIPMENT_NOT_READY OR #iActiveState;
	    ELSE
	        //Valve state transitions
	        CASE #iActiveState OF
	            0: //Initial state: Move to #STATE_OFF only on #Enable
	                IF  #R_TRIG_Enable.Q THEN
	                    #iNextState := #STATE_OFF;
	                END_IF;

	            #STATE_OFF:
	                //OFF state: Move to #STATE_WAIT_ON on rising edge of #CmdON.
	                //#CmdOFF must be FALSE at all times for transition to take place
	                //If #EndPositionOFF is lost or system is in #EndPositionON raise #Error
	                IF NOT #CmdOFF AND #R_TRIG_CmdON.Q THEN
	                    #iNextState := #STATE_WAIT_ON;
	                ELSIF #EndPositionON THEN
	                    #iNextState := #STATE_ABORTING;
	                    #iError := TRUE;
	                    #iErrorID := #ERROR_END_POS_ON OR #iActiveState;
	                ELSIF NOT #EndPositionOFF THEN
	                    #iNextState := #STATE_ABORTING;
	                    #iError := TRUE;
	                    #iErrorID := #ERROR_END_POS_OFF OR #iActiveState;
	                END_IF;

	            #STATE_WAIT_ON:
	                //Valve active and system is traveling to #EndPositionON.
	                //IF #CmdOFF is active at any moment we shut off valve without
	                //possibility to recover until #STATE_OFF is reached
	                //If watchdog time elepses before #EndPositionON is reached move to next state
	                //We do not raise error here but wait for ON state
	                IF #CmdOFF THEN
	                    #iNextState := #STATE_WAIT_OFF;
	                ELSIF #EndPositionON OR #TON_DlyON.Q THEN
	                    #iNextState := #STATE_ON;
	                END_IF;

	            #STATE_ON:
	                //Valve is active AND system is at #EndPositionON.
	                //If #CmdOFF is active at any moment we shut off valve
	                //without possibility to recover until #STATE_OFF is reached
	                //If #EndPositionON is lost or system is in #EndPositionOFF raise #Error
	                IF #CmdOFF THEN
	                    #iNextState := #STATE_WAIT_OFF;
	                ELSIF #EndPositionOFF THEN
	                    #iNextState := #STATE_ABORTING;
	                    #iError := TRUE;
	                    #iErrorID := #ERROR_END_POS_OFF OR #iActiveState;
	                ELSIF NOT #EndPositionON THEN
	                    #iNextState := #STATE_ABORTING;
	                    #iError := TRUE;
	                    #iErrorID := #ERROR_END_POS_ON OR #iActiveState;
	                END_IF;

	            #STATE_WAIT_OFF:
	                //Valve is not active and system is traveling to #EndPositionOFF.
	                //Wait for #EndPositionOFF or #TimeDelayOFF before moving TO #STATE_OFF *)
	                IF #EndPositionOFF OR #TON_DlyOFF.Q THEN
	                    #iNextState := #STATE_OFF;
	                END_IF;

	            #STATE_ABORTING:
	                IF #EndPositionOFF THEN
	                    #iNextState := #STATE_ABORTED;
	                END_IF;

	            #STATE_ABORTED:
	                IF #EndPositionOFF AND #R_TRIG_Reset.Q THEN
	                    #iNextState := #STATE_OFF;
	                    #iError := FALSE;
	                    #iErrorID := 0;
	                END_IF;

	            ELSE  //If current state is out of 0 TO #MAX_STATES go to state 0 and raise error
	                // !!!!! DO NOT MODIFY !!!!! //
	                #iNextState := 0;
	                #iError := TRUE;
	                #iErrorID := #ERROR_UNKNOWN_STATE;
	        END_CASE;
	    END_IF;

	ELSE
	    #iNextState := 0;
	    #iError := FALSE;
	END_IF;

	//*********************************************************************************************//
	//************************************ Change State *******************************************//
	#iActiveState := #iNextState;
	#iValid := #Enable;

	//**********************************************************************************************//
	//********************************** Set Outputs ***********************************************//
	#Valid := #iValid;
	#ON := #iValid AND ((#iActiveState = #STATE_WAIT_ON) OR (#iActiveState = #STATE_ON));
	#ONlimited := #iValid AND (#iActiveState = #STATE_WAIT_ON);
	#Busy := #iValid AND (#iActiveState  #STATE_OFF AND #iActiveState  0);
	#ReadyRun := #iValid AND NOT #iError;
	#ActiveState := #iActiveState;
	IF #iError THEN
	    #Status := #iErrorID;
	ELSE
	    #Status := #iActiveState;
	END_IF;
	#AtPositionON := #iValid AND #EndPositionON;
	#AtPositionOFF := #iValid AND #EndPositionOFF;
	#Error := #iValid AND #iError;

END_FUNCTION_BLOCK

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s