Switch Case or Function

Goodday all,

I've been working with the arduino for quite some time now and I managed to build me an RGB LED stairway. This has worked for months, untill my mother-in-law thought the sensor was a button ::) ::) :( . Now as i'm typing this thinking the sensor is probably dirty thats why it starten tripping, but ever since it did not work quite as well anymore. So I thought a great moment to rethink my code.

The code is pretty simple, it uses an distance sensor pointing at the wall to measure the distance and when it reaches a less then a certain distance (eg. somebody's leg passes by) it turns on the TLC5940 driven led sequence.

To do this I created a couple of functions for the loop to call when the sensor is triggerd.
example of the code:

void loop(){
int LDRR = analogRead(LDR_Pin);
Serial.print("LDR: ");
Serial.println(LDRR);
CheckBsens();

if(bClimbStarted && LDRR <= 100) {
Serial.println("Firing climbing sequence.");
Climbing();
}
else {
CheckTsens();

Not so long ago I came across the term "state machine" which uses switch case to control the flow. I was thinking of typing in something in the lines of:

Case:

  1. Check LDR
    LDR high > 2
  2. Check sensor
    top > 3
    bottom > 4
    LDR low > 1
  3. run top/down (on)
    run down/top (off)
    state 1
  4. run down/top (on)
    run top/down (off)
    state 1

Now i'm wondering which is the best/preferd way to minimize use of resources and is the least error prone, working with function or with a (few) switch case(s). And of course also thinking about the future when I might want to add optional modes, (test)buttons, display, you name it (No, really name it, I love ideas).

Thanks in advance

What are the states? 1 is a crappy name for a state. If you can assign meaningful names to states, then you can create an enum, called state, with the names as values. Then, you can use switch(state) and case someMeaningfulName:. Until then, don't even think about state machines.

I tend to think of states as applying to an object (e.g., a sensor), but it could be the entire program, I guess. Virtually any program can be thought of as Five Program Steps, which is really different than a state machine. See post #12 at:

https://forum.arduino.cc/index.php?topic=261445.0

which may help you organize your program.

When using a statemachine, the reading of the sensors and buttons is not necessarily a case; it's usually done outside the switch/case. Your states (cases in the switch) reflect what must happen; your runTopDown and runDownTop will probably be statemachines as well (e.g. if you have multiple lights that must switch on and off in a given sequence).

A statemachine is probably less error prone and easier to maintain / add functionality. You will still use functions else your switch/case will grow out-of-proportions. But e.g. displaying a time is not part of the statemachine; it will always be displayed.

I use this model all the time.

  • I use a C++ enum for the state names.
  • the loop is maily a single big switch impementing a state machine
  • I only modify the state variable inside that switch statement.

My loop always looks like this:

enum State {
  IDLE,
  STEAM_CLOSING,
  ARM_ROTATING,
  HORD_ACTUATION_TIMEOUT  
} state;

void setup() {
  // do stuff to get us into IDLE state

  state = IDLE;
}

loop() {
  read inputs (including millis());

  switch(STATE) {
  case IDLE:
    if(some condition) {
      start the arm rotating;
      state = ARM_ROTATING;
    }
    else if(some other condition) {
      start the process of closing the steam
      state = STEAM_CLOSING;
    }
    break;

  case ARM_ROTATING:
    if(theArmStillNeedsToRotateSomeMore()) {
      rotate the arm some more;
      // stay in ARM_ROTATING state
    }
    else {
      begin hord actuation();
      state = HORD_ACTUATION_TIMEOUT;
    }
   break;

   et cetera.

  } 
}

So you can map out the flow by looking just at the loop.

If the process of moving into a state is complex and/or is used in more than one other place, that gets broken out into a function.

If I have complex things with substates (eg, debouncing buttons), these get broken out into classes with a setup() and loop(). These setup and loop methods are run by the main setup and loop. Of course, I never use delay() - well, almost never.

First of all to all thank you for taking the time to reply.

PaulS:
What are the states? 1 is a crappy name for a state. If you can assign meaningful names to states, then you can create an enum, called state, with the names as values. Then, you can use switch(state) and case someMeaningfulName:. Until then, don't even think about state machines.

The case i placed was rough draft to see how I will be filling it in and which names to assign, a mind mappish idea. thank you for the feedback

econjack:
I tend to think of states as applying to an object (e.g., a sensor), but it could be the entire program, I guess. Virtually any program can be thought of as Five Program Steps, which is really different than a state machine. See post #12 at:
Planning and Implementing an Arduino Program - Programming Questions - Arduino Forum
which may help you organize your program.

Great, basic and easy steps, thank you. I will be looking for that book.

sterretje:
When using a statemachine, the reading of the sensors and buttons is not necessarily a case; it's usually done outside the switch/case. Your states (cases in the switch) reflect what must happen; your runTopDown and runDownTop will probably be statemachines as well (e.g. if you have multiple lights that must switch on and off in a given sequence).

A statemachine is probably less error prone and easier to maintain / add functionality. You will still use functions else your switch/case will grow out-of-proportions. But e.g. displaying a time is not part of the statemachine; it will always be displayed.

Is there a reason why it is usually outside of the case?
At the momoent the runTopDown and runDownTop are functions, but I do want to make statemachines of them. This so I can more easily change wat happens when the sensor is tripped.

PaulMurrayCbr:
I use this model all the time.

  • I use a C++ enum for the state names.
  • the loop is maily a single big switch impementing a state machine
  • I only modify the state variable inside that switch statement.

My loop always looks like this:

So you can map out the flow by looking just at the loop.

If the process of moving into a state is complex and/or is used in more than one other place, that gets broken out into a function.

If I have complex things with substates (eg, debouncing buttons), these get broken out into classes with a setup() and loop(). These setup and loop methods are run by the main setup and loop. Of course, I never use delay() - well, almost never.

Thank you for a peek in your base code and your explanation.

I see that its not a question of which is best, but of how to incorperate both for different purposes. State machine is least error prone, but certain process can create error and are best called upon as a function and/or classes.

Is there a reason why it is usually outside of the case?

Part of a state machine is the states. Part of a state machine is how and when to transition from one state to another. The switches may provide input that says you need to effect a transition. That input should not limited to any given state (case). Of course, that input might not matter if you are in a particular state. Still, it is best to separate collecting input (reading switches, etc.) from where you use the data.

Imagine a controller for a train crossing gate. It might have several states - no train in sight, train approaching the crossing, train crossing, and train departing crossing. There might be other states, like no cars in sight, car approaching from one direction, car stopped on one side, car approaching from other direction, and car stopped on other side.

On every pass through loop(), you'd certain care about whether it was necessary to transition from no train in sight to train approaching the crossing. There are then two ways to do this. One would be to see if you were in the no train in sight state. If so, determine if there is a train approaching. If so, transition to the train approaching state, where you start the lights flashing and the gates lowering.

The other way would be to determine if there is a train approaching, regardless of what the current state is. Then, if the state is no train in sight, and there is a train approaching, a transition is necessary.

I prefer the first approach if collecting the necessary data takes a long time. I prefer the second approach if collecting the data takes next to no time.