Embedded Systems
How to cycle through modes with one button on Arduino
To cycle through modes with one button on an Arduino, keep the mode in one number, mode, and move it on with each press: mode = (mode + 1) % 3 for three modes. The % remainder brings it back to 0 after the last mode, so the presses go round and round. Then give each mode its own rule, checked on every pass of loop().
A number for the mode
Number the modes from 0: 0 is off, 1 is full, 2 is half. One variable, declared above setup(), holds the current one, and the board starts in mode 0 at every power-up.
Why not three true-or-false flags? Because flags can disagree: a bug could leave "full" and "half" both true. A single number is always exactly one mode.
Round again with %
% gives the remainder after dividing. Adding 1 to the last mode, 2, gives 3, and 3 % 3 is 0: back to the first. From any other mode, the remainder is just the next number.
| mode | mode + 1 | (mode + 1) % 3 |
|---|---|---|
| 0 | 1 | 1 |
| 1 | 2 | 2 |
| 2 | 3 | 0 |
Live experiment
Press round the modes
Pip is the lamp on pin 9. Each press moves the mode on with (mode + 1) % 3 and the board says the new mode's name: off, full, half, and round again. Then give the lamp a fourth mode, dim, and watch the remainder become % 4.
“Modes” is lesson 6 of 6 in Clockwork Cove, Embedded Systems World 2. Free with an account. Lessons unlock in order, so you’ll do the 12 before it first.
The sketch
int mode = 0; // 0 off, 1 full, 2 half
bool last = HIGH;
unsigned long changedAt = 0;
void setup() {
pinMode(2, INPUT_PULLUP); // button to GND
pinMode(9, OUTPUT); // lamp on a PWM pin
Serial.begin(9600);
}
void loop() {
bool now = digitalRead(2);
if (now != last && millis() - changedAt > 50) {
changedAt = millis();
last = now;
if (now == LOW) { // a new press
mode = (mode + 1) % 3;
if (mode == 0) { Serial.println("off"); }
else if (mode == 1) { Serial.println("full"); }
else { Serial.println("half"); }
}
}
if (mode == 0) { analogWrite(9, 0); }
else if (mode == 1) { analogWrite(9, 255); }
else { analogWrite(9, 128); }
}
The press code only acts on the change from HIGH to LOW, ignoring 50 ms of bounce after each change, so one press moves exactly one mode. The rules at the bottom run on every pass, so the lamp always shows the current mode, however it got there. The names are printed inside the press's if, once per change.
Where you'll meet it
A bike light's steady, flashing and off, a fan's speed button, a phone's ring, vibrate and silent: each is a mode number and a rule per mode. Add rules for how modes change on their own, like a timer, and the same pattern becomes a full state machine.
Free lesson
Now build it yourself
Each lesson comes in three short parts: see the idea, try it on a live board, then build it yourself, with quick checks as you go.
“Modes” is lesson 6 of 6 in Clockwork Cove, Embedded Systems World 2. Free with an account. Lessons unlock in order, so you’ll do the 12 before it first.
Or start from the very first lesson, no account neededPractice questions
Three quick questions from the lessons. Pick an answer to check it.
Show the answer
Answer: Goes back to the first mode. The modes go round in a circle. After the last one, a press brings the first back, like a remainder clock.
- Why not “Turns the board off”? The board keeps running; only the mode number changes. A mode that turns the lamp off doesn't turn the board off.
- Why not “Does nothing”? Every press moves the mode on. From the last one, the remainder brings it round to the first.
- Why not “Adds a new mode”? The modes are written into the code. A press can only move between them, never invent a new one.
Show the answer
Answer: only when the tests before it were false. The chain tries each test in turn and runs the first block whose test is true. Later tests are skipped once one has matched.
- Why not “only when the first test was true”? That's backwards: if the first test is true, its block runs and every else if after it is skipped.
- Why not “never”? It runs whenever every test before it was false, which in a mode chain happens on most passes.
- Why not “on every pass”? It runs only when the chain gets that far. Once an earlier test is true, the rest are skipped.
Show the answer
Answer: One number can only be one mode at a time, so modes can't clash. With flags, two could be true at once. A single mode number is always exactly one mode.
- Why not “No reason”? There's a good reason: with separate flags, two modes could end up on at once.
- Why not “Numbers are faster”? Speed isn't the point; both are quick. One number simply can't hold two modes at once.
- Why not “Flags use more pins”? Variables don't use pins at all. The difference is that flags can clash and one number can't.
“Modes” is lesson 6 of 6 in Clockwork Cove, Embedded Systems World 2. Free with an account. Lessons unlock in order, so you’ll do the 12 before it first.
Questions people ask
How do I add another mode?
Raise the number in the remainder, % 3 to % 4, and add one more rule for the new mode number. The press code doesn't change, because it only ever moves the number on and wraps it round.
Should I use switch or if / else if for modes?
Either works. A switch statement with one case per mode number reads neatly when there are many modes; an if / else if chain is just as good for three. What matters is that each mode has exactly one rule.
How do I make the Arduino remember the mode after power off?
Save it in EEPROM, the chip's small non-volatile memory, whenever it changes, and read it back in setup(). EEPROM.update() only writes when the value differs, which saves wear: each byte survives about 100,000 writes.
Related topics
- How to count button presses on an ArduinoCount the change, not the hold, keep the count above setup(), and use % for every Nth press.
- How to program a robot as a state machineOne state at a time, each with its own job and its own ways out.
- Arduino analogWrite values: from percent to 0–255analogWrite counts to 255, not 100: turning a percentage into a value, and what the LED really gets.
- How to debounce a button on ArduinoWhy one press counts as four, and the 50 ms fixes that make it count once.