Bird
Raised Fist0
Arduinoprogramming~10 mins

Timing-based state machines in Arduino - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - Timing-based state machines
Start
Initialize variables
Check current time
Compare with last state change time
If enough time passed?
NoStay in current state
Yes
Change to next state
Update last state change time
Perform actions for current state
Repeat loop
The program checks the time repeatedly and changes states only after a set delay, updating the state and time accordingly.
Execution Sample
Arduino
unsigned long previousMillis = 0;
const long interval = 1000;
int state = 0;

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    state = (state + 1) % 3;
  }
}
This code cycles through 3 states every 1000 milliseconds using timing checks.
Execution Table
StepcurrentMillis (ms)previousMillis (ms)Condition (currentMillis - previousMillis >= interval)Actionstate
1000 - 0 >= 1000? FalseNo state change0
25000500 - 0 >= 1000? FalseNo state change0
3100001000 - 0 >= 1000? TrueUpdate previousMillis=1000, state=11
4150010001500 - 1000 >= 1000? FalseNo state change1
5200010002000 - 1000 >= 1000? TrueUpdate previousMillis=2000, state=22
6250020002500 - 2000 >= 1000? FalseNo state change2
7300020003000 - 2000 >= 1000? TrueUpdate previousMillis=3000, state=00
💡 Loop runs continuously; this trace shows first 7 steps with state changes every 1000 ms.
Variable Tracker
VariableStartAfter Step 3After Step 5After Step 7
previousMillis0100020003000
state0120
currentMillis0100020003000
Key Moments - 3 Insights
Why doesn't the state change at step 2 when currentMillis is 500?
Because the condition currentMillis - previousMillis >= interval is false (500 - 0 < 1000), so the state stays the same as shown in execution_table row 2.
Why do we update previousMillis when the state changes?
Updating previousMillis to currentMillis resets the timer, so the next state change happens after another full interval, as seen in steps 3, 5, and 7.
Why does the state cycle back to 0 at step 7?
Because state is updated with (state + 1) % 3, which cycles through 0,1,2 repeatedly, shown in execution_table step 7.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of state at step 5?
A1
B2
C0
D3
💡 Hint
Check the 'state' column in execution_table row 5.
At which step does the condition first become true?
AStep 3
BStep 2
CStep 4
DStep 5
💡 Hint
Look at the 'Condition' column in execution_table to find when it first says True.
If interval was changed to 500 ms, what would happen to the state changes?
AState would never change
BState changes would happen half as fast
CState changes would happen twice as fast
DState would change randomly
💡 Hint
Think about how interval affects the timing condition in execution_table.
Concept Snapshot
Timing-based state machines use a timer to control when states change.
Check elapsed time by subtracting last change time from current time.
Change state only if elapsed time >= interval.
Update last change time after state change.
Cycle states using modulo for repeated sequences.
Full Transcript
This example shows how an Arduino program uses timing to control state changes. It keeps track of the last time the state changed and compares it to the current time. If enough time has passed (the interval), it updates the state and resets the timer. This way, the program cycles through states at regular time intervals without blocking other code. The execution table traces each step, showing when the condition to change state is true or false, and how variables update. Key moments clarify why the state doesn't change too early, why the timer resets, and how the state cycles. The quiz tests understanding of these timing checks and state updates.

Practice

(1/5)
1.

What is the main purpose of using millis() in a timing-based state machine on Arduino?

easy
A. To pause the program for a fixed time
B. To reset the Arduino board
C. To track elapsed time without stopping the program
D. To read analog sensor values

Solution

  1. Step 1: Understand what millis() does

    millis() returns the number of milliseconds since the Arduino started running. It keeps counting without stopping the program.
  2. Step 2: Connect millis() to timing-based state machines

    Using millis() lets the program check how much time passed and change states without pausing or blocking other tasks.
  3. Final Answer:

    To track elapsed time without stopping the program -> Option C
  4. Quick Check:

    millis() tracks time without delay [OK]
Hint: Remember: millis() never stops your code [OK]
Common Mistakes:
  • Thinking millis() pauses the program
  • Confusing millis() with delay()
  • Using millis() to reset Arduino
2.

Which of the following is the correct way to check if 1000 milliseconds have passed using millis()?

unsigned long previousMillis = 0;
unsigned long interval = 1000;

void loop() {
  unsigned long currentMillis = millis();
  // What condition checks if interval passed?
  if (__________) {
    // do something
    previousMillis = currentMillis;
  }
}
easy
A. previousMillis + currentMillis <= interval
B. previousMillis - currentMillis >= interval
C. currentMillis + previousMillis >= interval
D. currentMillis - previousMillis >= interval

Solution

  1. Step 1: Understand elapsed time calculation

    Elapsed time is current time minus previous time: currentMillis - previousMillis.
  2. Step 2: Check if elapsed time reached interval

    We compare if elapsed time is greater or equal to the interval: currentMillis - previousMillis >= interval.
  3. Final Answer:

    currentMillis - previousMillis >= interval -> Option D
  4. Quick Check:

    Elapsed time = current - previous [OK]
Hint: Subtract previous from current time to get elapsed [OK]
Common Mistakes:
  • Reversing subtraction order
  • Adding times instead of subtracting
  • Using <= instead of >=
3.

What will be the output of this Arduino code snippet that uses a timing-based state machine?

unsigned long previousMillis = 0;
const long interval = 2000;
int ledState = LOW;

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    digitalWrite(13, ledState);
    Serial.println(ledState);
  }
}
medium
A. Prints alternating 0 and 1 every 2 seconds
B. Prints 1 continuously every 2 seconds
C. Prints 0 continuously every 2 seconds
D. No output because of syntax error

Solution

  1. Step 1: Understand the timing and state toggle

    Every 2000 ms, the code toggles ledState between LOW (0) and HIGH (1).
  2. Step 2: Check output printed

    Each toggle prints the current ledState (0 or 1) to Serial, alternating every 2 seconds.
  3. Final Answer:

    Prints alternating 0 and 1 every 2 seconds -> Option A
  4. Quick Check:

    State toggles and prints 0,1 alternately [OK]
Hint: Toggle state and print inside timed if-block [OK]
Common Mistakes:
  • Assuming constant output without toggle
  • Confusing HIGH/LOW with 1/0
  • Missing update of previousMillis
4.

Identify the bug in this timing-based state machine code and choose the fix.

unsigned long previousMillis = 0;
const long interval = 1000;
int ledState = LOW;

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

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    ledState = !ledState;
    digitalWrite(13, ledState);
  }
}
medium
A. Add previousMillis = currentMillis; inside the if-block
B. Change int ledState to bool ledState
C. Replace ! with ~ in toggle
D. Remove the if condition to toggle every loop

Solution

  1. Step 1: Check timing update logic

    The code never updates previousMillis, so the condition stays true forever after first pass.
  2. Step 2: Fix by updating previousMillis

    Adding previousMillis = currentMillis; inside the if-block resets the timer for the next interval.
  3. Final Answer:

    Add previousMillis = currentMillis; inside the if-block -> Option A
  4. Quick Check:

    Update previousMillis to reset timer [OK]
Hint: Always update previousMillis after interval check [OK]
Common Mistakes:
  • Forgetting to update previousMillis
  • Using bitwise NOT (~) instead of logical NOT (!)
  • Removing timing check causes fast toggling
5.

You want to create a state machine that cycles through three LED states: OFF, RED, GREEN. Each state lasts 3 seconds. Which code snippet correctly implements this using millis()?

unsigned long previousMillis = 0;
const long interval = 3000;
int state = 0;

void setup() {
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    state = (state + 1) % 3;
    switch(state) {
      case 0:
        digitalWrite(RED_PIN, LOW);
        digitalWrite(GREEN_PIN, LOW);
        break;
      case 1:
        digitalWrite(RED_PIN, HIGH);
        digitalWrite(GREEN_PIN, LOW);
        break;
      case 2:
        digitalWrite(RED_PIN, LOW);
        digitalWrite(GREEN_PIN, HIGH);
        break;
    }
  }
}
hard
A. Does not change states due to missing update
B. Correctly cycles OFF, RED, GREEN every 3 seconds
C. Cycles states every 1 second instead of 3
D. Cycles only RED and GREEN, skipping OFF

Solution

  1. Step 1: Check timing and state update

    The code uses millis() to check 3 seconds passed, then updates state cycling 0,1,2 with modulo 3.
  2. Step 2: Verify LED outputs per state

    State 0 turns both LEDs off, 1 turns RED on, 2 turns GREEN on. This matches the required cycle.
  3. Final Answer:

    Correctly cycles OFF, RED, GREEN every 3 seconds -> Option B
  4. Quick Check:

    State cycles with modulo and timing [OK]
Hint: Use modulo (%) to cycle states smoothly [OK]
Common Mistakes:
  • Forgetting to update previousMillis
  • Incorrect modulo causing wrong cycles
  • Not turning off LEDs in OFF state