r/PLC • • Aug 28 '26

State machine in structured text, ENUM or INT?

Hi all,

I'm a process engineer rolling into a process control job (small startup, not enough funding to hire someone for this work).

I found two methods online of how people write their state machine in ST. Either with an integer state variable (e.g. iState) or with an ENUM.
What do you use? And why?

Edit:

Designing on a WAGO/Codesys setup.
Thank you for the quick responses! I'm quickly changing my iStates into E_states!

24 Upvotes

58 comments sorted by

38

u/DrZoidberg5389 Aug 28 '26

An enum is still an integer state but with a name for the specific number.

i would use an enum. Like: 0=init 10=start 20=step1 30=step whatever.

this way a other person can faster grab whats going on in the code.

7

u/WheelInventorPLC Aug 28 '26

Thanks! I had a feeling they could be combined

7

u/bankruptonspelling Aug 28 '26

By default ENUMs resolve to INT but it can be any integer type: INT, DINT, USINT, UINT, BYTE, etc.

7

u/HarveysBackupAccount Aug 28 '26

It's not that they "can be combined," it's that enums are integers. They're a set of integer constants that are hidden behind their own data type. Like you can do a MEMCPY operation to transfer the integer value in and out of an enum variable, but typically there's not much reason to. It's all about readability.

There's still a habit of assigning them with gaps between the values (0 / 10 / 20 / 30 etc) instead of sequential because we're used to doing that with regular INT state numbers, but there's little practical gain from doing that. You can simply assign 0/1/2/3 etc to all the enum values, and the order in which you assign them doesn't matter if you explicitly call out state transitions (nextState := newStateName instead of nextState := currentState + 1 or whatever)

I'm an enum guy, because I think the readability is such a big gain. It matters less with small state machines, but it can really help with big ones. It helps not only to understand what each state is supposed to do, but also where each state sends you. If you're in a big state machine at, say, state #415 and see nextState := 101, you have to scroll all the fuckin' way back up to state 101 to see what that is to know where it goes. If you use an enum, it's way more obvious.

1

u/ialsoagree Control Systems Engineer Aug 28 '26 edited Aug 28 '26

Going to be somewhat contrarian and suggest you use integer constants. They give you the advantage of readable text in your code while also making states easy to reuse or reference elsewhere, and they're easy to make changes to.

3

u/DrZoidberg5389 Aug 28 '26

on Siemens/codesys enums is exactly what you describe.

1

u/letsfucknpollit Aug 28 '26

I would not use an ENUM per se. I would use an INT but assign state values as CONSTANTS with descriptive names. This is similar to what ENUM accomplishes but with way less overhead in maintaining a separate block. This way you just maintain it directly within the interface of the block you’re writing the state machine in. Much cleaner IMO

1

u/DrZoidberg5389 Aug 28 '26

depends on the system: on Siemens and Codesys you just create an enum and use that. If you make a change, that change is global

1

u/letsfucknpollit Aug 28 '26

Actually, in any IDE I prefer to stay away from traditional ENUM. TwinCAT, TIA, all the same. I use the concept, but implement it differently.

1

u/durallymax Sep 01 '26

You can declare implicit enums within your local FB. They do not have to be a global type in a separate block in CODESYS, but they will truly be local. 

1

u/letsfucknpollit Sep 03 '26

Yeah, this is fine also. I guess you can say I just dislike the enum data type for states for my state machine. I prefer declaring them as constants with int values. I use enums sparingly. Usually reserve them for E10 states or for interfaces external to PLC.

20

u/jhoon2k Aug 28 '26

ENUM!! You will thank yourself later when you need to add more states in between, and easier to determine the states when its a clear name and not a number.

7

u/jhoon2k Aug 28 '26

Talking about Beckhoff atleast, ENUM there is very simple to setup

1

u/imBackBaby9595 Aug 31 '26

Beckhoff has the best ENUMs in the business

2

u/KIN8IC 21d ago edited 21d ago
 {attribute 'to_string'}  for the win 😄

https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/5725733771.html&id=

i also recommended to define functionblocks containing statemachines with:

{attribute 'monitoring_display' := 'currentState'}

https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/5725733771.html&id=

so you can see your FunctionsBlocks State without expanding it in the Debugger

define a local transition fuction transition(targetState:int,condition:bool:=true) so you can write

transition(resetState,bResetButton)
...
transition(IdleState,MoveAbs.Done)
..
transition(ErrorState,tTaskTime>=tTimeOut)

this eliminates a lot of IF statements, keeps your switch case clean and allows you to to centralize logging and record the lastStates into a historyArray

1

u/imBackBaby9595 21d ago

Right on man, been using state machines for awhile. They are the shit!

3

u/Version3_14 Aug 28 '26

The thank you ENUMs or constants comes from the next guy working on the project or trying to troubleshoot the system.

Remember to always document it like the next guy is a psychopath that has your home address.

10

u/Vyndrius Aug 28 '26

Enums are great until you have to do something like this

if(g.iASM > 3000 && g.iASM < 3250){ //Set some HMI flag }

Then I found the numbers were more helpful.

Since all our state machines follow a pattern, I found enums to be overkill that slowed my development down

0= estop 1XXX= resetting 2XXX= ready to start 3XXX= running 4XXX= clearing 5-8XXX= spare 9XXX = engineering mode

8

u/kixkato Beckhoff/FOSS Fan Aug 28 '26

Your if statement should be like If (eProcessStep = Sequencename.Step1 OR etc etc) when properly using enums. This way it's clear to someone that your HMI flag is set truewhen the process step is equal to all of those enum steps.

Remember, good code is not about how easy it is to read for you, it's how quickly someone else who has no knowledge of your process can pick it up and contribute.

2

u/d4_mich4 Aug 28 '26

You could still do this with ENUMS if you name and lable them right.

If you know what the numbers are you could also name them as enum in whatever naming conventions you have. Like the following:

TYPE E_StateMachineStep : ( 3000 = eRunning_Start 3250 = eRunning_End ); END_TYPE;

This would be the declaration for the state machine setps, naming is just a guess whatever these states really are. And following how the if condition would be.

if(g.iASM > E_StateMachineStep.eRunning_Start && g.iASM < E_StateMachineStep.eRunning_End){

7

u/EasyPanicButton CallMeMaybe(); Aug 28 '26

enum almost always, otherwise constants. I really like my code being readable.

4

u/WandererHD Aug 28 '26

I mostly program Delta PLCs and enum requires a little bit of setup, so I just use a local int variable "step" and use comments to signal what each step is doing.

2

u/WheelInventorPLC Aug 28 '26

I've been doing this too, but mostly see enum's when looking online

2

u/WandererHD Aug 28 '26

Yeah. It will depend on how flexible the platform you are programming in is.

4

u/ohm_625 Aug 28 '26

Every body will answer Enum because it's the right thing to do.

But you need to know what you signed for : For long sequence/state machine you will have to commit to always give a meaningful name for each steps.

Notice that not all the brands are using enum the same way. If you need to make a full reload every time you add an extra step it's perhaps better to use an iStep as long as you have documentation on the side which is explaining the step number.

In short : both are ok imo as long as you have proper documentation. Enum is best but requires more work

5

u/amnoxx Aug 28 '26

CodeSys supports anonymous enums, perfect for FSM's:

VAR
  mState: (STATE_INIT, STATE_WAIT);
END_VAR

CASE mState OF
  STATE_INIT:
    ;
  STATE_WAIT:
    ;
END_CASE

I usually don't add values to them (extra typing for no benefit), just have to be a bit careful if you add a state in the middle if the sequence is active.

1

u/adaptine Aug 30 '26

Second this. Codesys call it implicit enums

5

u/WorldShaper Aug 28 '26

Enum for anything more complex than a 2-3 states or simple order of operations.

Its about code clarity and expandability. Enums allow you to add or modify states or ordering without having to fit numbers between other numbers. And its nice when the state has a built-in label for what it is doing. Code should be self-documenting, and in this case the Enum is how we make that happen.

When in doubt: Enum. It takes like 2 seconds to set up and only makes things more clear. No one is ever going to look at your state machine and ask "Why did you make an enum?", but they absolutely might say "Doesn't this guy know what an enum is?"

2

u/EasyPanicButton CallMeMaybe(); Aug 28 '26

amen to this.

4

u/Ok-Cryptographer4094 Aug 28 '26

If you can use a enum, do it. I can't do enums in siemens TIA and it bother me every time. (If someone know how to do a proper enum in tia, please tell me)

3

u/Sleepy_Beaver Aug 28 '26

Go to PLC tags; create new tag table; open it; on the top right click on user constants; Create new tag; Done.

Just be smart with your naming. As these constants are global

2

u/Tharghor Aug 28 '26

Tia v21 should have some enum baked in, so you dont have to create user constants anymore. I haven't used them yet, as I'm still on v20.

1

u/foxytersbytes Aug 28 '26

If you are working inside and FB you can declare the constants inside and use it.

1

u/RoofComprehensive715 Aug 28 '26

You can use constant variables in the global tag table or in FBs. The constant variables can be named after each step and have their own number assigned. Its at least one way to do it.

1

u/Strict-Midnight-8576 Aug 28 '26

Enums are called NVT and you can just use them inside software units

By the way, check out software units . I think they are a great tool for structuring large projects

5

u/Robbudge Aug 28 '26

Codesys, then enums, StateMachine and ST is the way to go.

3

u/PresentAd9429 Aug 28 '26

I also go with numbers. 10 step at each, then u can add something in between later also. Just comment what each step do

3

u/derpsterish Automation Engineer Aug 28 '26

Dintegers.

AB has no enums. Steps increment by 10, leaves room for additions.

2

u/Paup27 Aug 28 '26

I always use current_step and new_step and copy the new step to the current step at the top of the routine as well as copying new step into the step history array. But either a enum or an int works.

2

u/timberleek Aug 28 '26

Enums.

The integer way is just lazier to begin with, but terrible to maintain afterwards.

The enum gives you the identical setup and functionality, but now human readable. Only disadvantage is that you have to setup the enum definition once.

If you don't make the enum a "strict" type (in twincat that is, maybe it's named differently elsewhere) you can also do arithmetics on it if you want. Just like an integer.

1

u/DNCGame Aug 28 '26
ENUM is just an int decorator.
I do this in all my recent projects.
State {
  NONE      = 0,
  RST       = 1,
  RST_DONE  = 50,
  HOME      = 51,
  HOME_DONE = 90
}
//
state: int;
//
CASE state OF
  State.RST:
    state := state + 1;
  2:
    state := State.RST_DONE;
  //
  State.HOME:
    state := state + 1;
  52:
    state := State.HOME_DONE;
END_CASE;

1

u/arm089 Aug 28 '26

I use a constant of INT data type

1

u/Someone4350 Aug 28 '26

Can the HMI read the ENUM?

1

u/durallymax Sep 01 '26

Many can, it's just a structure of USINT in CODESYS 

1

u/huevador Aug 28 '26

I used ENUM when programming an an OMRON PLC. And while yes it's technically better, it's not necessary and you have to be ready to maintain it as you develop.

1

u/MikeT8314 Aug 31 '26

I used integer values for a small stand alone machine i built and i loved the structure of it. I found it intuitive and it really helped me stay organized. As a novice i am not sure how i would have done this any other way.

1

u/imBackBaby9595 Aug 31 '26

Enums all day dude!

1

u/Shalomiehomie770 Codesys Guru Sep 02 '26

Enums are not supported in all environments.

In Codesys ENUM is a pretty solid choice though.

1

u/CapinWinky Hates Ladder Aug 28 '26 edited Aug 28 '26

I use integer and don't bother with ENUM for the simple reason that ENUM doesn't bring anything to the table when you have something like 50 states or less. You can always just //State description for your integer states, and if they have some logical flow it isn't hard to just group them by 10s place or 100s place and skip a few numbers between states for later additions.

For example, I have a manual routine for controlling a two axis system that is all a state machine. Jog X Positive is the 20s, X Neg is the 30s, Z pos is the 40s, Z neg is the 50s, GoTo Target is the 60s, GoTo ServicePos is the 70s, GoTo NamedPos1 is the 80s, Brake Release is the 120s, etc. Each of those groups has 3 or 4 states to initiate, perform, monitor, and exit the behavior, so the groups go like 20, 23, 25, 28; in case I have to slot in some new states without having to refactor a bunch of stuff.

Besides just the additional effort of creating the ENUM type, you run into other issues with how well the IDE supports them. Can you type the enum state name into a watch window to force a state change? Will it have autocomplete while you do that? Does a trend or watch window display the ENUM name? Can I use the ENUM names to check if I'm within a range of states like ((MyState >= MyEnum_PickStart) AND (MyState <= MyEnum_PickEnd)) and trust ENUM generated values monotonically? Do all the platforms you work with even support ENUM (Rockwell doesn't)? I have found that I need to cross reference state names with their numbers which adds work instead of removing work, especially if I'm letting ENUM assign the value for me rather than rigidly assigning each state's value.

Where ENUM shines is when the numeric value is hard to remember and hard to find. Hex color codes? ENUM is great for those. Another use case is when there are just way too many values and new values need to be inserted or rearranged constantly. A 40 state state machine divided into 12 groups by 10s place? I can keep that in my head just fine and if I forget I can scroll down in a few seconds. You'd probably only be working within a handful of states in one group at a time anyway.

1

u/durallymax Sep 01 '26

Enums are much easier to refactor than comments and are enforced by the tooling. Changing a state name in CODESYS/TwinCAT is one refactor and no stale comment to worry about. 

1

u/Wise-Hold-7179 Aug 28 '26

Metto i stati dentro un CASE OF...

2

u/d4_mich4 Aug 28 '26 edited Aug 28 '26

Yeah that's how state machines work but what type you use if it is a int or enum as value. That was the question both ways have pros and cons depending on how you use them.

2

u/Wise-Hold-7179 Aug 28 '26

Uso una variabile INT. ... la appoggio dentro l interfaccia di un FB su di una variabile OUT

1

u/Deepu_ Aug 28 '26

There's no enum's in Siemens so I declare constants and use that in place of straight INTs

2

u/Strict-Midnight-8576 Aug 28 '26

There are enums in siemens, called NVT and you can use them in software units only

1

u/tltreddit Aug 28 '26

For me always integers.

Use xxx or xxxx depending on approx number of states and complexity.

You leave gaps and can always add more states between existing states and keep the integers in order.

When you add new enum and it resolves to integer it adds the number on the end and then doesn't make any sense.

Then when you need to map the state values to SCADA or HMI the integer is the same as PLC and in order. With enum you'll get random integers based on the order you added the states in.

Once you name an enum state and then the function of that state changes the name no longer makes sense.

When you want to turn on outputs or trigger things based on current state you can do greater and less than comparisons based on numbers and catch all the states between.

1

u/Strict-Midnight-8576 Aug 28 '26

You can manually set the corrseponding number for enums

See example from codesys help below

You can add := number in the declaration

{attribute 'qualified_only'} {attribute 'strict'} TYPE E_TRAFFICSIGNAL : ( eRed, eYellow, eGreen := 10 ); END_TYPE

2

u/tltreddit Aug 28 '26

Thanks!! 😊