Microcontroller state machine with enum tutorial

Flag variables are great, and totally not evil, when you just have two states: ON or OFF. What about when you have multiple states? Is there an option better than creating multiple flag variables?

The C-language has a declaration type just for this purpose. It is called an enumeration, or enum.

Setting up a state machine with enum is a surprisingly simple. Arduino and embedded programmers should use them!

All you need to do is create descriptive tag names, and let the compiler assign them an integer value. Unlike a #define which is just a macro replacement, the compiler treats an enum as your personal variable type.

This behavior comes in handy when you’re creating states for a state machine. I show how to create a simple state machine with enum, to blink an LED with millis(), in this post.

Using an enum is like making your own personal variable. See how in this tutorial.

Creating States

Start by thinking about the “states” that this LED will go through. We want it to blink. So that means either it’ll be blinking or not blinking. How many states can you count? You might think, “that’s easy, it’s 2! BLINK and NOT_BLINK.” That is true, but only half of them.

What about the actual blinking? When the LED should be “blinking”, in code that means the LED itself will be ON and OFF. So that’s two more states for our LED.

Creating an enum

Here are the four states the LED can exist in: Blinking_enabled, Blinking_disabled, LED_on, and LED_off. Here is how to create the enum, named “blinkStates”, with those 4 states.


enum blinkStates {
  BLINK_DIS, // blink disable
  BLINK_EN, // blink enable
  LED_ON, // we want the led to be on for interval
  LED_OFF // we want the led to be off for interval
};

Just like the compiler knows what an “int” or “float” is, now it knows what a “blinkStates” variable type is. This new variable type isn’t very useful yet, because we need to create a variable of this type.


enum blinkStates ledState;

Example State Machine with enum Code

Now that we’ve defined our state machine and enum, we need some code to go with it. Here’s a relatively simple example. One character Serial commands will control whether or not to blink the LED. Using millis() to control the rate of blinking, mean we will never miss a command. You could easily swap out the serial code for push buttons. (This is why millis() makes it appear like you can multitask on an Arduino.)


enum blinkStates {
  BLINK_DIS, // blink disable
  BLINK_EN, // blink enable
  LED_ON, // we want the led to be on for interval
  LED_OFF // we want the led to be off for interval
};

enum blinkStates ledState;
const int ledPin = 13;
long interval = 1000;
unsigned long previousMillis = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentMillis = millis();

  // use ! and + characters to control the blinking.
  if (Serial.available() > 0) {
    char incomingByte = Serial.read();
    // turn off LED
    if (incomingByte == '!')
      ledState = BLINK_DIS;

    // turn on LED
    if (incomingByte == '+')
      ledState = BLINK_EN;
  }

  // enums are just integers, so let's see if
  // it has been enabled.
  if (ledState > BLINK_DIS) {
    if ((unsigned long)(currentMillis - previousMillis >= interval)) {
      // enough time has passed, let's change the state of the LED.
      if (ledState == LED_ON) {
        // time to turn off the LED
        ledState = LED_OFF;
        digitalWrite(ledPin, LOW);
      } else {
        // time to turn on the LED
        ledState = LED_ON;
        digitalWrite(ledPin, HIGH);
      }
      // reset our timer
      previousMillis = currentMillis;
    }
  } else if (ledState == BLINK_DIS) {
    // blinking is disabled, so turn off LED
    digitalWrite(ledPin, LOW);
  }
}
// Code by james@baldengineer.com
// More at: /state-machine-with-enum-tutorial.html

Looking deeper at our enum

One line of code in this program is critical to understand, and it is this if-statement:


if (ledState > BLINK_DIS) {

Remember that enums are just integers. So we can use them as if we assigned each of our “states” an integer number. Since BLINK_DIS appears first in the enum’s list, it is assigned the lowest integer.

The simple if-statement just checks if the LED is active. The code’s logic assumes that if the state is not BLINK_DIS, something should be happening.

What is inside of an enum

This code example illustrates the simplicity of an enum. We will print out the values for each of our states.


enum blinkStates {
  BLINK_DIS, // blink disable
  BLINK_EN, // blink enable
  LED_ON, // we want the led to be on for interval
  LED_OFF // we want the led to be off for interval
};

enum blinkStates ledState;

void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.println(F("---------"));
  Serial.print(F("BLINK_DIS = "));
  Serial.println(BLINK_DIS);

  Serial.print(F("BLINK_EN = "));
  Serial.println(BLINK_EN);

  Serial.print(F("LED_ON = "));
  Serial.println(LED_ON);

  Serial.print(F("LED_OFF = "));
  Serial.println(LED_OFF);
}

void loop() { }
// Code by james@baldengineer.com
// More at: /state-machine-with-enum-tutorial.html

When I run it on my Arduino Uno and my MSP430 based Launchpad, I get the following results.


---------
BLINK_DIS = 0
BLINK_EN = 1
LED_ON = 2
LED_OFF = 3

So what you can see with this example is that C is using integers to represent our simple to use names.

Using an enum is like making your own personal variable. See how in this tutorial.