A Simple 2 state machine: How would you have done it?

theColonel26

Lifetime Supporting Member
Join Date
Feb 2014
Location
West Michigan
Posts
784
So I just wrote this 2 state, state machine. For sending a request to be added to a queue on a remote PLC.

Pretty simple,

  1. Sit idle until Recipe number is valid and user presses a button
  2. Then move to next state
  3. Send RecipeNumber and Send Request to Enqueue.
  4. Wait for Ack back from Machine that the recipe has been successfully enqueued or that the request has timed out.
  5. Then reset everything back to idle.
How would you guys have done this?


 
Random thoughts:

I personally like to use a combination of a DINT (state) and BOOL's for state machine. Simple psudocode:

EQU State 1 BST OTE State1 NXB XIC Condition MOV 2 State BND

I don't like to have conditions that prevent the state BOOL from being True when the State number is equal to the state bool.

I don't like to have too many things in the state machine. The "work" is done out side of the state machine, which is driven by the state BOOL.

If there are a series of conditions that drive changing the state, I like to have them outside of the state machine, driving a BOOL that with a description that explains the conditions were met.

In actuality, we have a state machine UDT. We put gaps in between states (sate 10, 20, 30, etc) just in case we need to add additional states.
 
Random thoughts:

I personally like to use a combination of a DINT (state) and BOOL's for state machine. Simple psudocode:

EQU State 1 BST OTE State1 NXB XIC Condition MOV 2 State BND

I don't like to have conditions that prevent the state BOOL from being True when the State number is equal to the state bool.

I don't like to have too many things in the state machine. The "work" is done out side of the state machine, which is driven by the state BOOL.

If there are a series of conditions that drive changing the state, I like to have them outside of the state machine, driving a BOOL that with a description that explains the conditions were met.

In actuality, we have a state machine UDT. We put gaps in between states (sate 10, 20, 30, etc) just in case we need to add additional states.

What is a NXB instruction?

What does your State machine UDT consist of?

Just the values of the states Like State.A = 1, State.EatingACheeseBurger = 985, etc?

I am actually switching to using an INT as the backing for the state right now. My bit chaining works fine for a linear sequence but I am actually expanding this now to be able to branch in to 2 different states. Idle then Enqueue or Clear Queue, then back to Idle

Thanks for the tips. I'll post an update when I am done.
 
Last edited:
What is a NXB instruction?

What does your State machine UDT consist of?

Just the values of the states Like State.A = 1, State.EatingACheeseBurger = 985, etc?

I am actually switching to using a INT has the backing for the state right now. My bit chaining works fine for a linear sequence but I am actually expanding this now to be able to branch in to 2 different states. Idle then Enqueue or Clear Queue, then back to Idle

Thanks for the tips. I'll post an update when I am done.

BST = Branch Start
NXB = Next Branch
BND = End branch

I find myself typing in shells of rungs, then filling in the tag names. Like

XIC BST XIC OTE NXB XIC MOV BND
 
oooh ok

This is what I have now. Cleaner I think.

And yes I agree complex states should probably be implemented in another place so as not to clutter up the main state machine transition logic.


 
I usually have to integers STATE_NOW and STATE_NEXT


On EQU is comparison with STATE_NOW and move is to STATE_NEXT


On last rung there is move from STATE_NEXT to STATE_NOW.


That way 2 or more consecutive STATEs aren't skipped on same program scan if all next STATE conditions are met.


Whit timer before copying next to state now you can add minimun time / STATE. Helps debugging.
 
You may consider a buffer step, so you don't blast through each step. Having a buffer gives you a one scan delay between steps, so you can service the rest of the program if you need to. Put a MOV at the top, buffer moves into step. The left side is step, the right side is buffer. Sometimes it's not necessary to do this, but I always get into the habit of doing it.

Edit, which is basically what Lare stated. I didn't read his post.
 
You may consider a buffer step, so you don't blast through each step.
This is generally a good idea but I looked at the ladder and didn't see any obvious places where there would be race condition.


Story about race conditions.

Way back in 1992 I was in Eregli, Turkey helping install hydraulic servo controls. There was a PLC programmer trying to make a down coiler work. The down coiler basically lowers a roll of steel to where a fork lift or similar can move it to where it cools. The programmer had two complicated rungs. One for up and one for down. It suffered from race conditions and he couldn't fix it. I looked at his program and asked about the coil names/numbers and what they did and wrote the ladder out on a piece of paper. My version took 9-11 rungs. The PLC programmer complained about that but I told him to save his program and try mine. It doesn't take long to enter 11 simple rungs. In a half hour he got the down coiler to work. It was the race conditions that were killing him. At this time I still hadn't programmed a PLC but I knew relay logic from the navy. BTW, it was a Reliance PLC.
 
I don't know if it helps your current iteration, but the Rung 1 in Post #1 has some redundancy; I am pretty sure the two configurations in this image will behave identically.

xxx.png
 
I don't know if it helps your current iteration, but the Rung 1 in Post #1 has some redundancy; I am pretty sure the two configurations in this image will behave identically.

View attachment 59667
Yes, you are correct :oops: The S_Q_Idle_Trigger_Done was on purpose (Don't remember why right now). But the 2 -(u)- HMI_W_EnqueueElectedRecipeButtons were not.

I have since rewritten this so it not really relevant any more. I posted the new version farther down.
 
I usually have to integers STATE_NOW and STATE_NEXT


On EQU is comparison with STATE_NOW and move is to STATE_NEXT


On last rung there is move from STATE_NEXT to STATE_NOW.


That way 2 or more consecutive STATEs aren't skipped on same program scan if all next STATE conditions are met.


Whit timer before copying next to state now you can add minimun time / STATE. Helps debugging.

You may consider a buffer step, so you don't blast through each step. Having a buffer gives you a one scan delay between steps, so you can service the rest of the program if you need to. Put a MOV at the top, buffer moves into step. The left side is step, the right side is buffer. Sometimes it's not necessary to do this, but I always get into the habit of doing it.

Edit, which is basically what Lare stated. I didn't read his post.
Thanks Guys I think I am going to do that.

Does it matter if the MOV from Next to Active (Now)is at the top or bottom of the Routine?

Trying to think how that affects other routines, in that task. it's making my brain hurt.
 
I always have at the top, or before that routine scans. If put directly after it will update before servicing the rest of the program, which is useless. It all depends on if your application requires it. As others stated, your example doesn't seem to need it. But, I always do it as habit.
 
And I almost allways have it last rung of subprogram.

It only makes one scan delay there to whole program.

p.s Actually not even that, rungs before move state to next, so copying next (buffer) to now variable (active) at last or first rung don't make any difference.
 
Last edited:
I always have at the top, or before that routine scans. If put directly after it will update before servicing the rest of the program, which is useless. It all depends on if your application requires it. As others stated, your example doesn't seem to need it. But, I always do it as habit.
That is true but now that I think about it, that would only matter if you were using the State INT to trigger things elsewhere instead of a Bool for each state.

I put it at the top though rather safe that sorry.
 
I usually have to integers STATE_NOW and STATE_NEXT

On EQU is comparison with STATE_NOW and move is to STATE_NEXT

On last rung there is move from STATE_NEXT to STATE_NOW.

That way 2 or more consecutive STATEs aren't skipped on same program scan if all next STATE conditions are met.

Whit timer before copying next to state now you can add minimun time / STATE. Helps debugging.

I remember a thread a few months back with a functionally similar approach, although with bits for states. All state transitions used the Start/Stop pattern i.e.
Transition_to_state_N In_state_N+1 In_state_N
--+----------] [------------+------]/[-------------( )------
| |
| In_state_N |
+----------] [------------+

Obviously the Stop test [XIO In_state_N+1] could be multiple XIOs ANDed together for all of the possible states that could follow state N.

The logic for that Start branch [XIC Transition_to_state_N] could be in that branch, or it could be a separate rung e.g.
In_state_N-2 Jump_from_N-2_to_N Transition_to_state_N
--+----] [--------------] [-------------------+--------( )------
| |
| In_state_N-1 Jump_from_N-1_to_N |
+----] [--------------] [-------------------+

I prefer the separate rung approach: it keeps the business logic separate from the state logic, and so it is easier to debug, although with canonical Start/Stop patterns there will rarely be a need to debug the state logic. But that is only my personal preference, and it is perfectly valid to put the business logic in the Start branch of the state logic e.g. it could be a pair of XIOs [XIO In_state_N-1 XIC Input_prox_state_N-1_complete].

It's a bit long-winded (like me;)) but it is easy to read and very reliable. I look at the logic in Post #1 and think a possible reason, that the unnecessary branch/logic was not noticed until a fresh set of eyes looked at it, was because of all the mixed-up cruft of business + state logic, which made it hard to grok everything at once.
 

Similar Topics

Hi all, Writng a FB in ST on Beckhoff TC for a pulser which turns on and off on a cycle, is paused by turning bControlInput to FALSE, but resumes...
Replies
6
Views
243
I'm trying to build my Classic Step 7 programming skills this weekend. I get stuck on little things that are not covered in YouTube tutorials. I'm...
Replies
7
Views
309
I have a program that does a 7 second "scan" sensor calibration routine whenever a setting (setting is called assistance level or "AL" and ranges...
Replies
3
Views
205
Hi all, I have a simple question that I have overcomplicated and gotten stuck on. I have a variable, we can call it "light" that I need to stay...
Replies
4
Views
295
Hi all! I am using unitronics Unistream plc, controlling two axis X and Y of polishing machine. Control is very simple as I have movement in each...
Replies
34
Views
3,925
Back
Top Bottom