A Demo-Code explaining the switch-case state-machine and how to do things (almost) in parallel

Hi everybody,

from time to time I enjoy writing demo-codes that want to explain programming-functionalities.
There are a lot of different approachs to explain something. This is meant as add another variant
to offer a wider range of how it can be explained. If you don't like it or don't understand it
just skip and try the next one.

Sooner or later almost every coder comes across that she/he needs a functionality that does
two, three or more things "at the same time".

And/or several steps must be executed in a well defined sequence

or even more complex if A then go on way 1 if B continue with way 2
including but if C happens then do "9" !

In the real world billions of variations of this functionality can be found
so I want to add some typical cases here to make it easier to find this thread

blink LED while motor is running

create 5 pulses ON/OFF then go on (IO-pin LOW / HIGH)

IO-Pin HIGH for 5 seconds then .....

one IO-pin LOW for 2 minutes in parallel blink LED / check for .....

(I will add real cases over time as they appear as questions in the forum)

So here is the demo-code. It uses the serial monitor to show what is going on in the code.
I think this will support to understand the principles. The explanations are medium long
So it will not cover everything but goes beyond "here is the plain code analyse it yourself"

so here is the demo-code

// this demo-program shows how to use the switch-case statement
// to create a functionality as described at the bottom of this file

unsigned long myCounter;

enum myStates {
  sayHello, 
  startCounting,
  countTo10, 
  sayGoodbye,
  wait5seconds
  };
enum myStates myStateVar;

unsigned long waitingTime = 5000;
unsigned long StartWaiting;
unsigned long currentMillis;  
unsigned long oneSecondStart;

void setup() {
  Serial.begin(115200);
  Serial.print( F("\n Setup-Start  \n") );
  myCounter = 0;
  myStateVar = sayHello;
}

boolean oneSecondIsOver(unsigned long StartTime, unsigned long rightNow) {
  if (rightNow - StartTime >= 1000) {
    return true;
  }
  else {
    return false;
  }
}

void myStepChain() {
  currentMillis = millis();
  
  switch (myStateVar) {
    case sayHello:
      Serial.println( F("Hello user!") );
      myStateVar = startCounting;
      break;

    case startCounting:
      myCounter = 0;
      Serial.println( F("I start counting new") );
      myStateVar = countTo10;
      break;     

    case countTo10:
      myCounter++;
      Serial.print( F("I'm counting up counter=") );
      Serial.println(myCounter);

      if (myCounter == 10) {
        myStateVar = sayGoodbye;
      }
      break;

    case sayGoodbye:
      Serial.println( F("goodbye see you next round in 5 seconds") );
      StartWaiting = currentMillis;
      myStateVar = wait5seconds;
      break;

    case wait5seconds:
      if (currentMillis - StartWaiting >= waitingTime) {
        Serial.print( F("seconds waited ") );
        Serial.print( (currentMillis - StartWaiting) / 1000 );
        Serial.println();
        Serial.println();
        myStateVar = sayHello; // reset to starting step to repeat
      }
      
      myCounter++;
      
      if (oneSecondIsOver(oneSecondStart,currentMillis) ) {
        Serial.print( F("one second over") );
        Serial.print( F(" counting very fast myCounter=") );
        Serial.println(myCounter);
        oneSecondStart = currentMillis;
      }
      break;            
  }    
  //delay(100);
}


void loop() {
  myStepChain();
}



/* pre-ambel:  programming something more complex than  
   switch LED on wait 1 second switch LED off wait 1 second
   requires knowledge. More or less complex knowledge
   And this needs a rather big minimum of words to explain 
   That is the reason why this text has more than two lines
   
in writing programs it is always a good idea to write down
the functionality of the program in NORMAL WORDS.
This code uses the serial monitor to make visible what the code
is doing. So no additional hardware is required

The functionality of this program is 
1. say "Hello"  (printing this message only ONCE)
   
2. inform "I initiate counting" (printing this message only ONCE)

3. counting up to 10 fast (printing each number)

4. say "good bye" (printing this message only ONCE)

5. wait 5 seconds 
  but not only waiting! 
  in PARALLEL to the waiting a counter is counting up very fast
  in PARALLEL to the waiting print a message once per second 

repeat this pattern.

This means the program is doing different things in a defined order
and is able to do a thing A in parallel to a thing B and in parallel 
to a thing C

This basic principle can be transferred to things like
- an LED is blinking while a display shows a message 
  "press button to start"


- a motor is switched on to run while in parallel a LED is blinking
  and the motor stops after X seconds

    
- a motor is switched on to run while in parallel a LED is blinking
  and the motor stops if a button is pressed / or a switch is closed


- a heating is switched on starting to heat and in parallel aquire
  new temperature-measurings until a certain temperature is reached 
  then switch of the heating
  while in parallel a humifier is switched on to increase humidity
  and in parallel new humidity-measurings are aquired until 
  a certain level is reached and then switch off

  where the times it takes do reach the right temperature-level
  and humidity-level can be very different


###### everyday analogon 
  The basic principle behind this is to make a servant watching 
  the scene and when certain things happen take a short action

  servant stay a little aside from the dining table and watch
  all guests dining
  if somebodies glas is empty go over and refill the glass
  keep an eye on the soup-pot if the pot is empty shout to the cook
  "bring a new pot"

  The servant is changing his focus very quickly from task to task
  this could be described as step in (a certain task) step out - repeat
  check if a glas is empty
  change to
  check soup-level
  change to 
  ....
  
  in opposite to: 
  walk over to the soup-pot fix your eyes on the soup-level and if 
  soup-level is low enough shout for the cook to bring a new pot

  This means your program is entering a first "task" 
  does a single step and then quickly leaves the first "task" 
  to enter the second "task"
  does a single step and then quickly leaves the second "task" 
  ....

  the repeating is taken to a higher level

loop() {
  task1(); // enter and quickly leave again 
  task2(); // enter and quickly leave again 
  task3(); // enter and quickly leave again 
  ...  
}

if each task requires multiple steps to be done
this is done by a switch-case-statement

like refilling a glas
- open bottle
- bring bottle into position to pour
- turn neck of the bottle down
- pour wine into the glas checking the level
- if glas is filled 
- turn neck of the bottle up
- close bottle
- move back to watching position

  after opening the bottle and bring bottle into position
  the servant is able to take a quick look to the soup-pot 
  seeing still enough soup
  OK let's just pour the wine
  or if soup-pot is empty shouting "cook bring a new pot with soup"
  
  Which means he is entering and leaving a task quickly to do 
  a small step of another task

  That is how multi-tasking programming works
  and it is done by using one BIG L----O----O----P

loop() {
  task1(); // enter and quickly leave again 
  task2(); // enter and quickly leave again 
  task3(); // enter and quickly leave again 
  ...  
}
  and quickly jumping in/out functions where each function has its
  own switch-case-statement to work through sequential steps
  that must be done in a defined order
*/    

best regards Stefan

1 Like

cool sharing. thanks, I'm sure it will be helpful.

Couple suggestion to "simplify" the code

Replace enum myStates myStateVar; with myStates myStateVar; (enum not needed).

Replace

with

inline boolean oneSecondIsOver(unsigned long StartTime, unsigned long rightNow) {
  return (rightNow - StartTime) >= 1000ul;
}

Side note: given waitingTime is a variable, I would not hardwire 5s in the state name wait5seconds as you could change that to a different duration.

Nice example of using states.
For your task's you could use this diagram
multitaskingDiagramSmall