greywuuf wrote:UGHHHHH!!!!!My brain hurts.
That's a normal first reaction..

In their raw form, state machines are too abstract to make much sense. They're easier to grok if you assign meanings to the individual states and transitions. That reduces the central operating principle to "if I'm here, and I see that, go there."
To make things more specific, let's work an example: say we have a kit that uses LEDs and current-limiting resistors. We want those to be paired, and we have a vibrator bowl that will bring parts to us one at a time. We've just walked into the workshop to find Igor dumping a bag of LEDs *and* a bag of resistors into the bowl.
Rather than freaking out, we pat him on the hump and pull out some hardware. With a bit of tinkering we make a device that can tell resistors from LEDs (two leads on one end -vs- one lead on either end), put that at the vibrator bowl's output, and hook it up to state machine.
To test it, we start running parts through and keep track of what we've seen. In this case, we have four states we care about: "haven't seen anything yet", "saw a resistor, still need an LED", "saw an LED, still need a resistor", and "saw a complete pair".
- The machine starts in "haven't seen anything" state. If the next part is a resistor it moves to "saw a resistor", if the next part is an LED, it moves to "saw an LED".
- If the machine is in "saw a resistor" state and sees another resistor, it stays in the same state. If it sees an LED, it moves to "saw a complete pair".
- If the machine is in "saw an LED" state and sees a resistor, it moves to "saw a complete pair". It it sees an LED, it stays in "saw an LED".
- If the machine is in "saw a complete pair" state, it can go back to "haven't seen anything" no matter what it sees.
That's nice, but it doesn't pack kits. To do that, we need some 'auxiliary outputs'.. values that are associated with a transition from one state to the next, but don't change the way the basic machine works. In this case, we want to add a couple of servos with cups that will catch and move parts.
The first servo catches parts directly out of the resistor-or-LED recognizer. If you send it one signal, it tips the part back into the bowl. If you send it another signal, it tips the part into the second cup. The second cup catches parts from the first, and dumps them into the kit bag.
Running through the states again to add the auxiliary outputs, we get the following:
- If the machine is in "haven't seen anything" state and sees any part, we want servo 1 to tip the part into servo 2's cup, and we want servo 2 to stand still.
- If the machine is in "saw a resistor" state and sees another resistor, we want the machine to stay in the same state, we want servo 1 to tip the part back into the bowl, and we want servo 2 to stand still. If the next part is an LED, we want to move to "saw a complete pair" state, we want servo 1 to tip the part into servo 2's cup, and we want servo 2 to stand still.
- We want the same actions the other way for "saw an LED" state.
- If the machine is in "saw a complete pair" state, we want servo 1 to tip the next part back into the bowl no matter what it is, we want servo 2 to tip the pair we've collected into the kit bag, and we want the machine to go back to "haven't seen anything" state.
Reducing all that to numbers, we can have the parts recognizer give us a 0 when it sees a resistor and a 1 when it sees an LED. We can define the states as: 00 (haven't seen anything), 01 (saw a resistor), 10 (saw an LED), and 11 (saw a pair). When we combine the states with inputs, we get keys into a lookup table:
- 000 (haven't seen anything, next part is a resistor)
- 001 (haven't seen anything, next part is an LED)
- 010 (saw a resistor, next part is a resistor)
- 011 (saw a resistor, next part is an LED)
- 100 (saw an LED, next part is a resistor)
- 101 (saw an LED, next part is an LED)
- 110 (saw a complete pair, next part is a resistor)
- 111 (saw a complete pair, next part is an LED)
Our basic state machine would tell is what state to choose for each condition:
{ 000:01, 001:10, 010:01, 011:11, 100:11, 101:10, 110:00, 111:00 }
and then we can tack on the following auxiliary bits:
- servo 1: tip the part into the bowl (0)
- servo 1: tip the part into servo 2's cup (1)
- servo 2: stand still (0)
- servo 2: tip the parts into the kit bag (1)
which gives us the following bit pattern for the whole machine:
{ 000:0110, 001:1010, 010:0100, 011:1110, 100:1110, 101:1000, 110:0001, 111:0001 }
That table is an extremely consise way of expressing everything I said above.
That's a two-bit state machine with one bit of input and two bits of auxiliary data. We can extend the auxiliary data as far as we want without having to think too hard, since it doesn't change the actual machine. Say we want to add an RGB LED that shows us what state the machine is in ( 00:red, 01:yellow, 10:cyan, 11:green ). We just tack the appropriate RGB values onto the end of the values in our transition table like so:
{ 000:0110100, 001:1010100, 010:0100110, 011:1110110, 100:1110011, 101:1000011, 110:0001010, 111:0001010 }
The item '011:1110110' parses out as '[state:01][input:1]:[move to state:11][servo 1:1][servo 2:0][rgb:110]'
When you void a product warrany, you give up your right to sue the manufacturer if something goes wrong and accept full responsibility for whatever happens next. And then you truly own the product.