Learning about FSM: what are the ‘states’ here?

Then write it as a classic state machine driven by a switch statement. It's not really necessary for this program, but it'll be a way to learn them. It likely will not work to start with and you'll make some classic FSM mistakes, but perhaps it will bring you closer to enlightenment :grinning:

1 Like

You have now a state machine of sorts. Not much of one, but it is state-driven.

The thing that might make anyone not want to call it a state machine is the way the timing is handled. And maybe some other fine point of the true definition of an FSM.

The important progress in doing it in this new way whatever you call it does not have any immediate benefit to you. As we have seen it can actually raise some new questions that must be addressed.

If you had anything else to do with all the time that is now essentially just asking "is it time to change what up" rather than just going dumb in a delay() of serious length, this new way would pay off.

Except for the relatively short delays that are still in #17, you have the full power of the processor available - loop will be looping quite rapidly, and a number of other FSMs (or whatever you want to call mechanisms that do not block (delay())) could enjoy frequent attention and run as if they had the processor to themselves.

wokwi doesn't have a frequency counter (yet!) but it is instructive to twiddle a spare I/O line at the top level of the loop() function and see it running many thousands of times per second. A blinking light would be a blur.

I put the code on a Nano Every I happen to have for real right here and added this

     digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));

to the loop() at the top level, it measures 29 KHz, which means the loop is getting called 58 thousand times a second. Unless a KA tap was being performed, where my cheap meter just got a bit confused for a few hundred ms.

TBC in setup() I set the pinMode(LED_BUILTIN, OUTPUT);

An obsessive type could push the same kind "FSM" approach down into the servo movement code and truly eliminate all delay() function calls.

This might make a difference - now any other tasks you placed in the loop() function would stutter and hiccup when the delay() was called.

Like a Knightrider Larsen scanner would be happily going back and forth, and stop briefly and perhaps noticeably when the servo taps were invoked. Hey, that might actually be a good thing!

It really makes no difference here - your first cut works fine and it is for a real purpose so can be deployed. But it is good that you want to think about this small thing being done differently. You may have in your future occasion to exploit what you learn here.

a7

1 Like

@Terrypin
You are getting some excellent help but I thought it might help (or confuse you more!) if I gave you a real life example of a finite state machine, one I think you are likely to be familiar with and which does not involve a micro-controller.

A restaurant operates as a finite state machine, with the waiter(s) and the chef acting as the processor. The state are, roughly:

  • New customer(s) waiting to be seated
  • Seat new customers and give them menus
  • Take food order and give to kitchen
  • Bring food from kitchen and give to customers
  • Wait for customers to finish eating, while occasionally checking if they want anything else
  • Take away dirty plates
  • Offer desert menu
  • Take desert order if required
  • Give bill
  • Take payment
  • Show customer to the door

I presume you are familiar with the above. I presume you realise that the waiter does all that for you and for several other customers, all of which will be in different states to you and each other. The waiter keeps track of what state each customer is at and attends to them appropriately.

That's it, that's a finite state machine.

2 Likes

Thanks a7, very helpful.

I got your latest rebuild of @gcjr's code running (minus servo) in the sim on my ipad. But I continue to have trouble using wokwi on my iPad in Safari. Switched to Chrome (on the iPad) and happily that does offer paste. I thought I'd finally fathomed how to rename to get a new link. But on opening that it still shows the old name - a problem I think you also had?

On the Win 10 PC, my preferred browser (an old version of Waterfox) shows the code but runs the sim with no schematic displayed! Chrome seems OK. Edge too. And Opera. Animation identical in all of them.

Hope we can also continue to discuss wokwi, perhaps in a new thread as this is already complicated enough.

Meanwhile do you know of an email address for its developer Urish, or an active forum?

Thanks Perry, but aren't those tasks not states?

See also my post to @marco_c in #23

They could be tasks, it depends how you model the system. If you take a waiter centric view, tasks make sense,although they may simply be the tasks that the waiter does when in a particular state.

On the other hand, if you look at the customer, Perry's list represents the states that the customer can be in.

I think there are a few ppl around these fora using wokwi that are ahead of me, no matter how you measure it. I do think wokwi needs more publicity! I am tots over dragging out a board and lashing up enough circuitry to play with code I see and am interested in.

...do you know of an email address for its developer Urish, or an active forum?

In the wokwi project there's the Docs, there's good stuff in there if it is a bit spread out and/or lagging reality or incomplete.

Under you user icon there is a link to the wokwi discord, which is a live multichannel chat type thing. I have never gone there and not found Urish or someone who will answer good questions or gently point me to where I should have found my answers.

I don't recall how to get there just now, but there are also a few areas on GitHub where issues and questions and features request can be posted. Response there has been very good as well.

Tip: components in the schematic area have a pop-up question mark link directly to the page in the documentation you need.

I have resolved at least half my renaming/saving/copying problem. I will try to get a known good set of steps learnt and pass that along.

As for the iPad, I am too lazy to struggle with it and will just live with it works better on the big rig. I'll call it a good thing that it is unavailable to me 24/7, mebbe I'll get more sleep. :wink:

a7

1 Like

I've never found it useful to distinguish between a task and a state, to my way of thinking they are so intimately linked they are pretty much the same thing. I don't know if that says something about tasks and states or about how I think.

My only point was to address that you seem at some level not to be able to get your head around what a state machine is, I am trying to show you that you already know but have not realised it.

1 Like

Example of a FSM 2D diagram.

I see from your blog article (part 1) that you have not defined states but 2 'sub-tasks' ? Isn't that a radically different way of tackling a project?

I think the distinction is a moot one in Computer Science. In Mathematics a FSM simply moves from one state to another (ie, it is generally an abstract concept). In computing, a state frequently (but not always) involves 'doing' something and so the FSM becomes a tool or technique used to organise tasks in a logical manner. The advantage of FSM constructs are that the tasks can be smaller, more obvious, and easier to debug. When there is no underling multi-tasking support they can also be used to remove 'busy' waits (aka delay() in Arduino-land) and provide for some level of concurrency.

Perry's example of the restaurant is a good one. Both the customer and the waiter, in fact, are running a FSM. The customer is just running their own, while the waiter is running a 'supervisory' FSM coordinating their service across a number of different customers.

For example, they use the waiting when the customer reads the menu to do something for another customer. The waiter may come back to the customer when he thinks that enough time has passed ("Are you ready to order now?") or the customer may call him over to take the order - usually noticed when the waiter is 'waiting' between one of their own tasks.

Similarly FSM can be running independently and then signal between each other that something needs to change. One example of this could be a FSM that runs an indicator LED. It stays in an idle state monitoring the main FSM until it sees it change to X state, at which point it turns the LED on and switches to a waiting state before turning off the LED and going back to the idle state. This is nothing more than a blinkWithoutDelay with a small twist. The result is the LED FSM runs without the main thread needing to do anything explicit about the LED.

Natural splitting point for tasks is when they are waiting for something else to happen or if they are doing something that will take a long time. Reading your description, you basically have a few long 'waiting tasks' followed by some short 'activity tasks' for the camera and the LEDs.

So for what its worth, I would construct 2 FSMs:

CAMERA FSM
INITIALISE
capture current time (millis())
state = IDLE

IDLE
if (motion sensor detected) state = START_VIDEO
else if (keepaliveTime has passed) state = TAKE_PICTURE

TAKE_PICTURE
// note: this state is monitored by the status LED FSM
take photo (actuate servo, etc)
state = INITIALISE

START_VIDEO
turn red LED on
start video recording (actuate servo)
capture current time
state = TAKE_VIDEO

TAKE_VIDEO
if (videoRecordTime elapsed)
{
turn recording off (actuate servo, etc)
turn red led off
state = INITIALISE
}

YELLOW LED FSM
IDLE
if (CAMERA_FSM state = TAKE_PICTURE)
{
capture current time
turn LED on
state = WAITING
}

WAITING
if (LEDOnTime has passed)
{
turn LED off
state = IDLE
}

and in the loop() function I would simply have 2 calls to the FSM functions
loop()
{
cameraFSM();
ledFSM();
}

Obviously this is just a sketch without the messiness of 'real' code, but it is taken directly from your description and I hope it illustrates how you take one form into the other.

1 Like

Thanks, got it! I've clearly been fretting over that task/state distinction unnecessarily. Studying your restaurant example again together with @marco_c's latest post finally dropped the penny for me on that one. :slightly_smiling_face:

1 Like

Excellent, thank you so much!

Really appreciate your taking the time to explain the concepts so thoroughly and demonstrate them with my small project.

Lot of work ahead before I expect to be using FSM in my sketches with confidence, but thanks to you and others for helping me over these first hurdles.

Terry

Nice - albeit a tad more complex than I anticipate as a home practical electronics hobbyist :slightly_smiling_face:

Text resolution bit low in places but I'm guessing maybe bowling alley?

Discord doesn't look my scene. But despite its gaudy and cluttered opening screen I "claimed an account" and was then at a loss. Did get a 'DM' from you, apparently saying Hello; did you just join too? Anyway, unable to reply to it because "Your message could not be delivered. This is usually because you don't share a server with the recipient or the recipient is only accepting direct messages from friends."

Given what a potentially terrific tool Wokwi is for the huge number of world wide Arduino users, I'm surprised that there's apparently no 'normal' dedicated forum or group already.

Re using it on iPad, did you try leaving Safari and opening a project in Chrome instead?

T

Many thanks Doug, that was another very helpful learning exercise. Here's my Wokwi simulation:

It was an especially interesting example for me because I built a curtain controller in 2004 that obviously used a similar principle. It's still working fine in the bedroom, but took me weeks to design and build, maybe months. If only I'd had Arduino then! Should anyone ever want to dramatically illustrate the distinction between legacy and MCU electronics I'll zip up some files!

Terry

1 Like

Please do share. It will be interesting to see.

case doorIsDown: // Nothing happening, waiting for switchInput
      Serial.println("door down");
      if (digitalRead(switchInput) == LOW) { // switchInput  pressed
        timerMillis = millis(); // reset the timer
        doorState = doorOpening; // Advance to the next state
        break;
      }
      else {   // Switch not pressed
        break; // State remains the same, continue with rest of the program
      }

This looks a little clumsy. There is no need for the else clause and the code could be

case doorIsDown: // Nothing happening, waiting for switchInput
      Serial.println("door down");
      if (digitalRead(switchInput) == LOW) { // switchInput  pressed
        timerMillis = millis(); // reset the timer
        doorState = doorOpening; // Advance to the next state
      }
      break; // continue with rest of the program

Putting state machines inside of state machines let me process text files based on content.

If the state variable is made of T/F bits of process, tasks can set bits w/o regard to the others or the state value itself. Every set of states has a unique number anyway.

Please do share. It will be interesting to see.

OK, sure, here's a representative sample of the many files I accumulated. Some added well after the thing was up and running, as I tidied up the documentation.

https://www.dropbox.com/s/b96o5watbfj1p29/CurtainController-SampleFiles.zip?raw=1

The mechanics, motor power supply, and most wiring would be similar if I was making it today with Arduino. But I think the contrast in effort, time and circuit space would be spectacular.

If anyone wants further info then I had several discussions in the Usenet group sci.electronics.design, probably starting with 'Cordless screwdriver innards?' on 5th July 2004.

1 Like