Learn

Arduino enum, on a Pomodoro timer that knows which session it is in

Beginner

Or see every lesson.

  • 455parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
Arduino Uno: a Pomodoro timer with its session in an enumlive0.000 s 0.00x
Press Run, then press the skip button
Press skip to move the enum on: Focus, a short break, Focus again.

enum Phase { FOCUS, SHORT_BREAK, LONG_BREAK }; gives names to the whole numbers 0, 1 and 2, and makes Phase a type that holds one of them. A variable of that type says what state a program is in, in words instead of numbers, and a switch on it gives each state its own block.

The circuit above is a Pomodoro timer on an Uno: a TM1637 display, a red focus lamp, a green break lamp and two buttons. Its sketch keeps the session in Phase phase. Press Run, then press the second button, skip. The serial monitor prints "Skipped" and "Short break (1 session done)", and the green lamp takes over from the red. Skip again and it prints "Focus (1 session done)".

Below is how the names map to numbers, printing an enum by name, enum with switch, enum class, and the errors an enum gives, each run on an Uno with what it printed.

The timer’s enum

Here are the two lines from the sketch running above. : uint8_t stores a Phase in one byte; without it an enum on the Uno takes 2, the size of an int. The sketch then reads like the method it follows: if (phase == FOCUS), phase = LONG_BREAK. Nothing at run time knows the names. They are for you and for the compiler, which now refuses a phase that is not one of the three.

firmware/uno/examples/pomodoro (the enum running above)
enum Phase : uint8_t { FOCUS, SHORT_BREAK, LONG_BREAK };
Phase phase = FOCUS;

The numbers behind the names

The first name is 0 and each name after it is one more, unless you give it a value. Below, Serial.println(c) with c set to BLUE printed 2, and FAST printed 50 because it was given 50. sizeof(Color) printed 2. Values are handy when the numbers mean something outside the sketch, such as a speed or a command byte.

Two names can share a value, and a name can be given the value of another plus one, MEDIUM = SLOW + 15. Names left without a value keep counting on from the last one that has one, so after SLOW = 10, a bare FASTER would be 11. Keep the list in one place at the top of the sketch, where every state the program can be in is easy to read at a glance.

Names are numbers
enum Color { RED, GREEN, BLUE };
enum Speed { SLOW = 10, MEDIUM = 25, FAST = 50 };

void setup() {
  Serial.begin(9600);
  Color c = BLUE;
  Serial.println(c);
  Serial.println(FAST);
  Serial.println(sizeof(Color));
}

void loop() {
}

Printing an enum by name

Serial.println() prints the number, never the name. To print the name, keep the names in an array in the same order and use the enum as the index: the sketch below printed "green". The Pomodoro sketch does it with a function that returns "Focus", "Short break" or "Long break", which is safer when values are given by hand, because then the names no longer match the positions in an array.

An enum as an index
enum Color { RED, GREEN, BLUE };
const char *colorNames[] = {"red", "green", "blue"};

void setup() {
  Serial.begin(9600);
  Color c = GREEN;
  Serial.println(colorNames[c]);
}

void loop() {
}

enum and switch

A switch on an enum is the usual shape of a state machine: one case per state, each doing its job and choosing the next state. Leave a state out and the compiler notices. The sketch below has three lights and only two cases; clang warned enumeration value 'YELLOW' not handled in switch, and the sketch printed "red" and "green" and then nothing, because it reached YELLOW and had no case for it. Add the case, or a default: on purpose.

A switch with a state missing
enum Light { RED, GREEN, YELLOW };
Light light = RED;

void setup() {
  Serial.begin(9600);
}

void loop() {
  switch (light) {
    case RED:
      Serial.println("red");
      light = GREEN;
      break;
    case GREEN:
      Serial.println("green");
      light = YELLOW;
      break;
  }
  delay(100);
}

enum class: names that stay in their own box

A plain enum’s names belong to the whole sketch, and they quietly turn into ints. enum class keeps both from happening: write the type before every name, Mode::Blink, and compare only with other Modes. The sketch below printed "blinking", and (int)m printed 1.

The price is that it will not pass for a number. Serial.println(m) stopped with no matching member function for call to 'println', and if (m == 1) with invalid operands to binary expression ('Mode' and 'int'), clang adding no implicit conversion for scoped enum. GCC says no match for 'operator=='. Cast it, as (int)m, when you really want the number.

enum class
enum class Mode { Off, Blink, On };

void setup() {
  Serial.begin(9600);
  Mode m = Mode::Blink;
  if (m == Mode::Blink) {
    Serial.println("blinking");
  }
  Serial.println((int)m);
}

void loop() {
}

The errors an enum gives

A name Arduino already uses: enum Level { LOW, MEDIUM, HIGH }; stopped with expected identifier, twice, because LOW and HIGH are already defined as 0 and 1 before your sketch starts. Pick other names, such as LEVEL_LOW, or use enum class.

The same name in two enums: OFF in both a Light and a Fan enum gave redefinition of enumerator 'OFF'; GCC says 'OFF' conflicts with a previous declaration. Plain enum names share one space, so prefix them or use enum class.

Counting through it: c++ on a Color stopped with cannot increment expression of enum type 'Color', and Color c = 2; with cannot initialize a variable of type 'Color' with an rvalue of type 'int'; GCC says invalid conversion from 'int' to 'Color'. Step it with a cast, c = (Color)((c + 1) % 3);, which went 0, 1, 2 and back to 0.

Questions

What is an enum in Arduino?

A list of names for whole numbers that also makes a new type. It is how a sketch names its states, modes or menu items so that the code says FOCUS instead of 0.

How do I print the name of an enum?

Keep the names in a const char * array in the same order and print colorNames[c], or write a function with a switch that returns the name. Serial.println() on its own prints the number.

Is enum better than #define for states?

Yes. An enum gives a real type, so the compiler can refuse a value that is not a state and warn when a switch misses one. Names made with #define are only text swapped in before compiling.

What is the difference between enum and enum class?

A plain enum’s names are visible everywhere and turn into ints by themselves. An enum class keeps its names behind its type, Mode::On, and needs a cast to become a number, so two enums can both have an OFF.

Sources

Where the facts on this page come from

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.