Object oriented state machine

Object oriented state machine © GPL3+

Educational toy project to show how to write a simple object oriented state machine with your own class hierarchy.

  • 729 views
  • 1 comment
  • 3 respects

Components and supplies

Apps and online services

About this project

State machine in a simple project

Many of us who want to program robots or similar applications with the Ardiuno need to run a state machine. For example, my object avoiding robot wants to:

1) Scan the enironment and find the longest possible distance to travel.

2) Turn into the corresponding direction.

3) Drive into that direction while constantly measuring the distance to the next object in front.

4) Stop if that distance is smaller than some minimum value.

5) Go back to step 1.

So how can we write the code in the loop() function to achieve the above? There are 3 possibilities:

1) We write a very lengthy code block with a switch or if / else statement. We might introduce an enum to label the state but if we have more than a couple of states and more than a little bit of logic to run in each state, then that code becomes difficult to read, more difficult to understand and even more difficult to maintain.

2) We use one of the FSM (finite state machine) libraries and write the corresponding adapters to these.

3) We write our own simple object oriented state machine that is taylored towards our project.

What I found on the web was that people mainly went for 1) or 2) in their projects. I will further down explain how to progress using 3).

There is a lot of useful information about (finite) state machines on the web. For example:

https://github.com/jonblack/arduino-fsm

For many simple Arduino like projects some of the available packages could be a little bit of an overkill.

Explanation of the code

In order to show the elegance and power of an OO approach I have created a little toy project that demonstrates a state machine with two states only. The state machine initialises two states called white and green

The idea is to have a base class State with two virtual functions enter() and run() .

The enter() code is only called once when a state transition occurs. The run() function is called repeatedly and returns a State*. This is used by the caller in order to detect if the state is changing. If this is the case, the enter() function is called again on the new state.

I have implemented enter() as empty in the base class, but it could be a pure virtual function, too. I have not added an exit() function although it could be useful to have and FSM libraries will likely have it. I discovered in my project that with the two functions enter() , run() I have enough flexibility to do everything.

Now the loop() function only checks if the state has changed and calls enter if that is the case. Then it calls run() as long as this returns the same state. Hence the logic to determine if a state prevails or is about to change is solely located in the run() function of each state. In a multi state process the function could return different states depending on different input parameters, for example, the return value of sensor readings or the pushing of a button.

As we want to utilise the computational power of the Arduino as good as possible, we don't want to use any blocking function, neither in enter() nor in run(). Please note, that I have violated that principle in the enter() method by using a delay() call inside the auxiliary function flash() .

In a real-world example you would avoid that, and only utilise calls that return immediately. This has been done accordingly in the run() functions of the states, where a timer is used in order to determine which part of that function should be called.

We create a unique instance of each of the two states.

The state machine toy project

The project simply consists of two LEDs in white and green attached to pin 13 and 12. The two classes White and Green that implement State produce a flashing light signal upon entering. During the run() function they will keep the LED switched on for 1s or 2s respectively. At the end of the run() function we return the other state.

This video is showing the two states White and Green. Sorry for using a cheap Chinese Arduino clone here, as my proper Arduino is buried deeply in my other project.

The advantages of using your own state machine

It is probably obvious that the approach 3) presented here will outperform solution 1) above. Assume you had 4 states instead of two and would perform very different tasks in the initial run of each state and return different states from the run() function, depending on other input parameters. Coding this in a big loop() function that keeps track of each state and needs to keep track if the state is called the first time or the 2nd and more times will produce lengthy inelegant code.

Also you can add more states to the code without ever requiring to update you loop() function. You can keep variables like the timer readings locally in your classes without polluting the global namespace (I haven't made use of this in my example code, though).

Solution 2) will likely provide you with a good implementation, too, but you will be tied to the provided framework of the corresponding library. In our example it will be very easy, for example, to detect which was the preceding state. We can simply change the interface of enter() to enter(State*) and then run different codes depending on which state we came from.

Code

OO State MachineC/C++
Simple example of an object oriented state machine with two states.
class State {
  public:
  virtual ~State(){}
  virtual void enter(){};
  virtual State* run() = 0;
};

class White: public State {
  public:
  virtual void enter() override;
  virtual State* run() override;
};

class Green: public State {
  public:
  virtual void enter() override;
  virtual State* run() override;
};

static White white;
static Green green;
unsigned long lastMillis = 0;

void flash(int pin) {
   for(int i = 0; i<20; ++i) {
    digitalWrite(pin,!digitalRead(pin));
    delay(30);
  } 
}

State* White::run() {
  if (millis() < lastMillis + 1000) {
    digitalWrite(13,HIGH);
    return this;      
  }
  else {
    lastMillis = millis();
    digitalWrite(13,LOW);
    return &green;  
  }
}

void White::enter() {
  flash(13);
  // update the timer
  lastMillis = millis();
}

State* Green::run() {
  if (millis() < lastMillis + 2000) {
    digitalWrite(12,HIGH);
    return this;      
  }
  else {
    lastMillis = millis();
    digitalWrite(12,LOW);
    return &white;  
  }
}

void Green::enter() {
  flash(12);
  // update the timer
  lastMillis = millis();
}

void setup() {
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
}

bool isChanged = false;
State* state = &white;
State* lastState = state;
void loop() {
  // if state changed last time, call enter
  if (isChanged)
    lastState->enter();
  // call run repeatedly
  state = lastState->run();
  // if state changed, record it;
  isChanged = (state != lastState);
  // reset lastState to current
  lastState = state;
}

Schematics

Fritzing diagram of state machine
Statemachine1 bb c47jlkjrkz

Comments

Similar projects you might like

Controlling Arduino State Machine via Web

Project tutorial by phpoc_man

  • 1,778 views
  • 4 comments
  • 22 respects

Morse Code Machine

Project showcase by brzi

  • 2,732 views
  • 1 comment
  • 7 respects

Arduino Morse Code Machine

Project showcase by Vlada Krsmanovic

  • 10,443 views
  • 1 comment
  • 31 respects

The Compliment Machine

Project in progress by Mia Belli -20 and Sophia Greenland

  • 2,618 views
  • 2 comments
  • 10 respects

Dicey

by Englishscone

  • 2,605 views
  • 0 comments
  • 12 respects

Snacks Vending Machine Powered By Arduino

Project tutorial by Sevenmojoe

  • 17,704 views
  • 13 comments
  • 84 respects
Add projectSign up / Login