 # Beginners: using the switch - case statement

The switch - case statement is a powerful construct that is often under-used by beginners.

Basically it allows you to perform tests on a value (or range of values) and make decisions - a bit like the IF statement.

This tutorial has three examples that progressively introduce some simple ideas to help you use the switch construct in your programming.

You will need an Arduino Uno (the programs will run on most arduinos) three leds - preferably red, yellow, green - some resistors around 220 ohms - 2k2 - and a potentiometer.

• Example 1 which follows tests a value from the ADC and shows "good" and "alarm" conditions on LEDs
• Example 2 reads values from the keyboard via the serial monitor and identifies them as capitals , numbers etc.
• Example 3 uses the switch construct with an EASY example of millis() timing to build a traffic light "state machine". STILL EASY.

Example 1:
Suppose you've read in a value from a potentiometer using the ADC. We will start by dividing it by 64 to get a value "reading" holding a number in the range 0 -- 15.

Now we will use some LEDs to show the state of the value.

0 - red; 1 - red & amber; 2 to 4 - amber; 5 to 14 - green; and 15 - green & amber.

You can easily do this with nested if statements - but here's how to do it with "switch"

``````switch (reading) {
//here we are testing for a single value
case 0: { // put the red LED on
}
break;

case 1 : { //red and amber on
}
break;

//you can also test for a range of values
case 2 ... 4 : { // just show amber
}
break;

case 5 ... 14: { // all good - show green
}
break;

case 15: { //bit high - show green and amber
}
break;

}
``````

Note - in specifying a range you must use this exact format

case (low value) space three dots space (high value): as shown above.

OK I hate Fritzing but here is how to connect the potentiometer and leds to your Uno. And now the full code for the sketch:

``````/*
This simple sketch is an example showing how the switch - case structure can be used to avoid nested if statements
LEDs are connected to pins 2,3,4 on the uno, with 470 ohm resistors to ground
a potentiometer between 0V and +V provides a variable input to A0
J. Errington 23 October 2020
*/

// assign pin numbers for the leds
const byte led_R = 4;
const byte led_Y = 3;
const byte led_G = 2;

//input on analog pin 0
const byte vIn = A0;

void setup() {
Serial.begin(9600);
pinMode(led_R, OUTPUT);
pinMode(led_Y, OUTPUT);
pinMode(led_G, OUTPUT);

}

void loop() {
delay(100);
// voltage warning lights
case 0: { //R
digitalWrite(led_R, HIGH);
digitalWrite(led_Y, LOW);
digitalWrite(led_G, LOW);
}
break; // try the effect of commenting this out

case 1 : { // RA
digitalWrite(led_R, HIGH);
digitalWrite(led_Y, HIGH);
digitalWrite(led_G, LOW);
}
break;

case 2 ... 4 : { //A
digitalWrite(led_R, LOW);
digitalWrite(led_Y, HIGH);
digitalWrite(led_G, LOW);
}
break;

case 5 ... 14: { //G
digitalWrite(led_R, LOW);
digitalWrite(led_Y, LOW);
digitalWrite(led_G, HIGH);
}
break;

// if its not any of the above then it must be 15.
default: { //GA
digitalWrite(led_R, LOW);
digitalWrite(led_Y, HIGH);
digitalWrite(led_G, HIGH);
}
}
}//loop
``````

"default" is just a catch-all case for any values that dont match the specified tests.

Example 2: testing characters follows. switch_warnvolts.ino (1.69 KB)

Example 2: Testing non-numeric values

The switch statement can use any variable of integer type - char, signed or unsigned integer, or enumeration. Here is a simple example that reads in a character and tests what it is.
The example also shows how you can test for a set of specific cases.

if you are using the serial monitor you will need to select “no line ending” else you will see lots of non-print characters.

``````/*
This simple sketch is an example showing how the switch - case structure can be used with character values
J. Errington 28 October 2020
*/

char letter;

void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB
}
}

void loop() {
Serial.println("\n Please enter a character from the keyboard");
}
// send data only when you receive data:
if (Serial.available() > 0) {
delay(10);
Serial.print("You entered: ");
Serial.print(letter);

switch (letter) {
//because there is no "break" any of the following four cases will result in the serial.println being executed
case ',':
case '.':
case ';':
case ':': {
Serial.println(" - its punctuation");
}
break;
case 'A' ... 'Z' : {
Serial.println(" - its a capital");
}
break;
case 'a' ... 'z' : {
Serial.println(" - its a miniscule");
}
break;
case '0' ... '9' : {
Serial.println(" - its a number");
}
break;
default: {
Serial.println(" - its a symbol or npc");
}
}
}

}//loop
``````

and now - Example 3 a VERY SIMPLE state machine using millis() for timing

switch_chars.ino (1.38 KB)

Example 3: A SIMPLE state machine to show the traffic light sequence

A single traffic light is a good example of a system that can have clearly defined states; and the switch construct makes it easy to see how this can be implemented as a state machine.

Use the same arrangement of LEDs (see the fritzing) that you used in the first example.

This code also introduces the ENUM type declaration, and a very easy way to understand millis() timing.

At the start of each state we record the time "timeStart". Then the elapsed time "timeElapsed" in that state is the difference between the time now - milllis() - and the start time.

millis() - timeStart = timeElapsed

When the elapsed time exceeds the time required in that state timeStart is reset and the next state selected. ``````//*
This simple sketch is an example showing how the switch - case structure can be used to implementa state machine
LEDs are connected to pins 2,3,4 on the uno, with 470 ohm resistors to ground
J. Errington 29 October 2020
*/
// assign pin numbers for the leds
const byte led_G = 2;
const byte led_Y = 3;
const byte led_R = 4;

enum vtlStates {AMBER, RED, RA, GREEN}; // the states a single traffic light can be in
// the above line makes a variable data type "vtlStates" and assigns values 0, 1, 2, 3 to AMBER, RED, RA, GREEN
vtlStates tlState;  //create a variable tlState

/*
*** if youre not happy with enum, this also works ***
const byte AMBER=0, RED=1, RA=2, GREEN=3;
byte tlState;
*/

const int timings[] = {3000, 10000, 2000, 10000}; //array to hold the time for each state AMBER, RED, RA, GREEN
unsigned long timeStart, timeElapsed ; //will hold the time the state started and the time elapsed in the current state

void setup() {
pinMode(led_G, OUTPUT);
pinMode(led_Y, OUTPUT);
pinMode(led_R, OUTPUT);
timeStart = millis();
}

void loop() {
switch (tlState) {
case AMBER : {
//set the lights for this state
digitalWrite(led_R, LOW);
digitalWrite(led_Y, HIGH);
digitalWrite(led_G, LOW);
//is it time for the next state?
timeElapsed = millis() - timeStart;
if (timeElapsed > timings[AMBER]) {
//restart the timer and change the state
timeStart = millis();
tlState = RED;
}
}
break;

case RED: {
digitalWrite(led_R, HIGH);
digitalWrite(led_Y, LOW);
digitalWrite(led_G, LOW);
//is it time for the next state?
timeElapsed = millis() - timeStart;
if (timeElapsed > timings[RED]) {
//restart the timer and change the state
timeStart = millis();
tlState = RA;
}
}
break;

case RA: {
digitalWrite(led_R, HIGH);
digitalWrite(led_Y, HIGH);
digitalWrite(led_G, LOW);
//is it time for the next state?
timeElapsed = millis() - timeStart;
if (timeElapsed > timings[RA]) {
//restart the timer and change the state
timeStart = millis();
tlState = GREEN;
}
}
break;

case GREEN: {
digitalWrite(led_R, LOW);
digitalWrite(led_Y, LOW);
digitalWrite(led_G, HIGH);
//is it time for the next state?
timeElapsed = millis() - timeStart;
if (timeElapsed > timings[GREEN]) {
//restart the timer and change the state
timeStart = millis();
tlState = AMBER;
}
}
break;
}
}//loop
``````

ENUM and timings[] array explained

This is all very simple. (ROFL)

enum vtlStates {AMBER, RED, RA, GREEN};

this makes a new type of variable. It can only take one of the listed values, and it assigns numbers to them in order, starting at zero.

tlState is a number that we will use to hold the current state; so we COULD declare it as
byte tlState; where byte is the type and tlState the name of the variable.

vtlStates tlState; where vtlStates is the type, and tlState the name of the variable.

This allows us to use meaningful names (AMBER etc) for the system state.

const int timings[] = {3000, 10000, 2000, 10000};

timings is an array - a set of integer values (declared as constants) - so in memory it looks like this:

   

The position of a value in array is called its index; starting at zero.
So timings is 3000; timings = 10000; timings=2000 and timings = 10000;

When using arrays you need to be careful not to exceed the region used (the “bounds” of the array); we dont know what we would find in timings!

By using an enumerated type we can get values from the array as
timings[AMBER] - which is the same as timings;

it also protects from exceeding the array bounds because we can not have a value of 4 in “tlState”, the only values allowed are AMBER, RED, RA, GREEN corresponding to 0,1,2,3 switch_tl_state.ino (2.77 KB)

1 Like

I used it this week to display a check screen before starting with the proper program on the second pass through.
It is pretty handy...

I would humbly offer two suggestions to enhance that:

• Put all the switch..case stuff in a function and call it from loop(). That keeps loop() nice and clean, and it becomes a sort of textual-flow-chart to aid the reader in the logic of the sketch. (In real life there may be many more functions, and it's a good practice (imo) to have loop() doing little else than call them.)
• Add blink without delay in a function too, also called from loop(). Then (especially for the beginner) it's easy to see that the traffic light timing is indeed delay()-less, since bwod is going at the same time.

I have a state machine template along those lines, whose loop() is:

``````void loop()
{
doBWOD();
manageStates();
} //loop
``````

manageStates() is a minimal but compilable switch..case block to act as a place-holder and I just copy/paste as many cases as I need and fill in the specifics.