When building embedded systems, nested if/else blocks quickly become unmaintainable, especially when managing distinct operational modes like fan speeds, menu navigation, or lighting sequences. The switch statement is the backbone of embedded state machines, offering cleaner logic paths, faster execution via compiler-generated jump tables, and strict scope control. This guide provides a production-ready switch statement Arduino example targeting the modern ATmega4809 architecture, demonstrating how to cycle through five distinct PWM-driven RGB states using a debounced mechanical pushbutton.

Unlike the classic ATmega328P-based Nano, this code targets the Arduino Nano Every (ABX00028). The Nano Every offers 48KB of Flash and 6KB of SRAM, running at 20MHz, which provides ample headroom for state-machine logic without the memory constraints of older 8-bit boards. If you are using a classic Nano or Uno, the code remains 100% compatible, but you will be limited to 2KB of SRAM.

State Machine Architecture & Timing Specifications

Before writing code, map your states. A common mistake in embedded programming is defining states on the fly. Below is the exact state transition table for our RGB controller. This table defines the memory footprint and execution timing for each branch of our switch block.

Table 1: RGB State Machine Transition & Timing Matrix
State Enum Trigger Condition R / G / B PWM (0-255) Avg. Execution Time SRAM Overhead
MODE_OFF Button Press (Edge) 0 / 0 / 0 1.2 µs 0 Bytes
MODE_RED Button Press (Edge) 255 / 0 / 0 (Scaled) 4.8 µs 0 Bytes
MODE_GREEN Button Press (Edge) 0 / 255 / 0 (Scaled) 4.8 µs 0 Bytes
MODE_BLUE Button Press (Edge) 0 / 0 / 255 (Scaled) 4.8 µs 0 Bytes
MODE_RAINBOW Button Press (Edge) Calculated via Sine 145.0 µs 12 Bytes (Vars)
Pro-Tip on Execution Time: The MODE_RAINBOW state requires floating-point or lookup-table math to generate smooth color transitions. On the 20MHz ATmega4809, this takes roughly 145µs per loop iteration. If you were running this on a 16MHz ATmega328P without hardware multiplication, that same math could block the main loop for over 300µs, causing button-read latency.

Hardware BOM & Pin Mapping

Precision in hardware selection prevents ghosting and PWM flicker. Do not use a common-anode RGB LED for this specific code block without inverting the PWM logic in the switch cases.

Bill of Materials (BOM)

  • Microcontroller: Arduino Nano Every (ABX00028) - ~$11.50
  • RGB LED: Lite-On LTST-SP1WTJ (Common Cathode, 5mm Diffused) - ~$0.15
  • Potentiometer: Bourns PTV09A-4020F-B103 (10kΩ Linear, Knurled Shaft) - ~$0.80
  • Pushbutton: Omron B3F-1000 (Momentary, 12x12mm, 160gf) - ~$0.20
  • Resistors: 3x 220Ω 1/4W Carbon Film (Yageo CFR-25JR-52-220R) for LED current limiting.
  • Pull-up Resistor: 1x 10kΩ for the button input (or use internal INPUT_PULLUP).

Pin Mapping Table

Component Arduino Nano Every Pin ATmega4809 Port/Alt Notes
Red LED Anode D9 PORTB / TCA0 WO0 Hardware PWM capable
Green LED Anode D10 PORTB / TCA0 WO1 Hardware PWM capable
Blue LED Anode D11 PORTB / TCA0 WO2 Hardware PWM capable
Potentiometer Wiper A0 PORTD / ADC0 AIN0 10-bit ADC resolution
Pushbutton Output D2 PORTA / EXTINT2 Configured with internal pull-up

The Switch Statement Arduino Example: Complete Code

The following code implements a non-blocking state machine. It uses millis() for button debouncing rather than delay(), ensuring the rainbow calculation loop is never stalled by mechanical switch bounce. According to the official Arduino switch...case reference, using an enum paired with a switch block allows the compiler to optimize the branching logic far better than chained if statements.

#include <math.h>

// --- PIN DEFINITIONS ---
#define PIN_LED_R     9
#define PIN_LED_G     10
#define PIN_LED_B     11
#define PIN_POT       A0
#define PIN_BUTTON    2

// --- STATE MACHINE ENUM ---
enum RGBState {
  MODE_OFF,
  MODE_RED,
  MODE_GREEN,
  MODE_BLUE,
  MODE_RAINBOW
};

RGBState currentState = MODE_OFF;

// --- DEBOUNCE VARIABLES ---
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 50ms debounce

// --- RAINBOW MATH VARIABLES ---
float hue = 0.0;

void setup() {
  Serial.begin(115200);
  
  pinMode(PIN_LED_R, OUTPUT);
  pinMode(PIN_LED_G, OUTPUT);
  pinMode(PIN_LED_B, OUTPUT);
  pinMode(PIN_POT, INPUT);
  pinMode(PIN_BUTTON, INPUT_PULLUP); // Uses internal 20k-50k pull-up
  
  // Ensure all LEDs are off on boot
  analogWrite(PIN_LED_R, 0);
  analogWrite(PIN_LED_G, 0);
  analogWrite(PIN_LED_B, 0);
  
  Serial.println(F("RGB State Machine Initialized."));
}

void loop() {
  // 1. Read and Debounce Button
  bool currentButtonState = digitalRead(PIN_BUTTON);
  
  if (currentButtonState != lastButtonState) {
    lastDebounceTime = millis();
  }
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // If the button state has changed and it is currently LOW (pressed)
    if (currentButtonState == LOW && lastButtonState == HIGH) {
      advanceState();
    }
  }
  lastButtonState = currentButtonState;

  // 2. Read Brightness Potentiometer (0-1023 mapped to 0-255)
  int rawPot = analogRead(PIN_POT);
  float brightnessMultiplier = map(rawPot, 0, 1023, 0, 255) / 255.0;

  // 3. Execute State Machine
  switch (currentState) {
    case MODE_OFF:
      analogWrite(PIN_LED_R, 0);
      analogWrite(PIN_LED_G, 0);
      analogWrite(PIN_LED_B, 0);
      break;

    case MODE_RED:
      analogWrite(PIN_LED_R, 255 * brightnessMultiplier);
      analogWrite(PIN_LED_G, 0);
      analogWrite(PIN_LED_B, 0);
      break;

    case MODE_GREEN:
      analogWrite(PIN_LED_R, 0);
      analogWrite(PIN_LED_G, 255 * brightnessMultiplier);
      analogWrite(PIN_LED_B, 0);
      break;

    case MODE_BLUE:
      analogWrite(PIN_LED_R, 0);
      analogWrite(PIN_LED_G, 0);
      analogWrite(PIN_LED_B, 255 * brightnessMultiplier);
      break;

    case MODE_RAINBOW:
      // Generate smooth RGB values using sine waves offset by 120 degrees
      hue += 0.02;
      if (hue > 6.28318) hue = 0.0; // 2 * PI
      
      int r = (sin(hue) * 127.5) + 127.5;
      int g = (sin(hue + 2.094) * 127.5) + 127.5;
      int b = (sin(hue + 4.188) * 127.5) + 127.5;
      
      analogWrite(PIN_LED_R, r * brightnessMultiplier);
      analogWrite(PIN_LED_G, g * brightnessMultiplier);
      analogWrite(PIN_LED_B, b * brightnessMultiplier);
      break;

    default:
      // Failsafe: Reset to OFF if memory corruption alters the enum state
      currentState = MODE_OFF;
      Serial.println(F("Error: Unknown state. Resetting to OFF."));
      break;
  }
}

void advanceState() {
  // Cycle through the enum values
  int nextState = currentState + 1;
  if (nextState > MODE_RAINBOW) {
    nextState = MODE_OFF;
  }
  currentState = (RGBState)nextState;
  
  Serial.print(F("State changed to: "));
  Serial.println(currentState);
}

Debugging: Handling Switch Fall-Through and Scope Errors

When scaling state machines, the C++ compiler is your best debugging tool. If you compile with strict warnings enabled (standard in modern Arduino IDE 2.x and PlatformIO), you will likely encounter this exact error string when modifying the code:

error: enumeration value 'MODE_RAINBOW' not handled in switch [-Werror=switch]

According to the GCC Warning Options documentation, the -Wswitch flag warns whenever a switch statement has an index of an enumerated type and lacks a case for one or more of the named codes of that enumeration. This is a critical safety feature in embedded systems.

The First 3 Things to Check When It Fails

  1. Missing break; Statements (Fall-Through): If your RGB LED cycles to Red, but immediately flashes Green and Blue before settling, you forgot the break; at the end of the MODE_RED case. Without break;, C++ executes the next sequential case block automatically. Always terminate cases with break; or return; unless intentional fall-through is explicitly documented.
  2. Enum-to-Switch Mismatch: If you add MODE_WHITE to your enum block but forget to add a corresponding case MODE_WHITE: inside the switch block, the compiler will throw the -Wswitch error. Always define your switch cases immediately after updating the enum.
  3. Uninitialized State Variables: If currentState is not initialized at the top of the sketch (e.g., RGBState currentState = MODE_OFF;), it may default to 0. If your enum starts at 1, the switch will bypass all cases and hit the default: block, causing erratic boot behavior. Always explicitly initialize state variables.
Safety Warning: Never use a switch statement to control high-voltage AC relays without hardware interlocks and software watchdog timers. A software crash in the default case could leave a relay permanently energized. For mains voltage control, use zero-crossing SSRs and hardware-enforced timeouts.

Extending and Simplifying the Build

As your project grows, hardcoding PWM values inside the switch block becomes tedious. Here are two professional strategies to modify this architecture.

How to Extend: Adding Non-Volatile Memory

If you want the device to remember its last state after a power cycle, extend the build by integrating the Nano Every's EEPROM. Add #include <EEPROM.h> to the top of the sketch. Inside the advanceState() function, write the new state to address 0: EEPROM.update(0, currentState);. In the setup() function, read it back: currentState = (RGBState)EEPROM.read(0);. This adds roughly 400 bytes to your flash footprint but drastically improves user experience.

How to Simplify: Array-Based State Mapping

If your states only change static PWM values (no complex math like the rainbow mode), you can eliminate the switch statement entirely for those branches. Define a 2D array mapping states to PWM values:

const uint8_t pwmMap[4][3] = {
  {0, 0, 0},       // MODE_OFF
  {255, 0, 0},     // MODE_RED
  {0, 255, 0},     // MODE_GREEN
  {0, 0, 255}      // MODE_BLUE
};

Then, replace the first four cases in your switch block with a single lookup: analogWrite(PIN_LED_R, pwmMap[currentState][0] * brightnessMultiplier);. This reduces code size and execution time, reserving the switch statement strictly for states that require unique procedural logic, like the sine-wave calculations in MODE_RAINBOW.