Robotics
How to make an Arduino toggle button (a robot kill switch)
A toggle button turns one push-button into an on-off switch: press once to stop, press again to go. The trick is to act on the change, the moment the pin goes from HIGH to LOW, not on the pin being LOW. Remember last pass's reading, compare it with this one, and flip a bool only when they differ in that direction.
Level or edge?
With the button on pin 2 and INPUT_PULLUP, the pin reads LOW while it's held. A press lasts a tenth of a second or more, and loop() comes round every fraction of a millisecond, so this flips stopped hundreds of times per press:
if (digitalRead(2) == LOW) stopped = !stopped; // wrong: the level
Whether the robot ends up stopped is a coin toss. The fix is to keep a memory of the last reading:
bool now = digitalRead(2);
if (now == LOW && last == HIGH) { // a new press
stopped = !stopped;
}
last = now; // every pass
HIGH then LOW happens exactly once per press, however long it's held. last = now has to run on every pass, outside the if, or the next press never shows up as a change.
The stop always wins
Add a speed knob on A2, and put the stop after it, just before the wheels are written:
int speed = map(analogRead(A2), 0, 1023, 0, 255);
if (stopped) speed = 0;
analogWrite(9, speed);
analogWrite(10, speed);
Knob first, stop second, wheels last. Whatever the knob says, a stopped robot gets 0. A kill switch that the next line of code can overrule isn't a kill switch.
Live experiment
Turn the knob, press KILL
The knob on A2 sets both wheels through map(reading, 0, 1023, 0, 255). KILL on pin 2 flips stopped, and while stopped the speed is 0 whatever the knob says. Flip on the change from HIGH to LOW and each press is one flip. Flip while the pin reads LOW and a held press flips it hundreds of times.
“A knob and a kill switch” is lesson 6 of 6 in Rover Ridge, Robotics World 1. Free with an account. Lessons unlock in order, so you’ll do the 5 before it first.
Real buttons bounce
A real button's contacts bounce for a few milliseconds, making several quick edges per press. Count an edge only if 50 ms have passed since the last one:
if (now == LOW && last == HIGH && millis() - pressedAt >= 50) {
stopped = !stopped;
pressedAt = millis();
}
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.
“A knob and a kill switch” is lesson 6 of 6 in Rover Ridge, Robotics World 1. Free with an account. Lessons unlock in order, so you’ll do the 5 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: Once, at the moment of the press. The change from HIGH to LOW happens once per press, however long it's held. Testing the level instead would flip stopped on every pass and the rover would stutter.
- Why not “On release”? Release is the change from LOW to HIGH, which this test ignores. It flips on the press.
- Why not “Once per second”? Nothing in the rule depends on how long it's held. One press is one edge, so one flip.
- Why not “Thousands of times”? That's what happens when you test the level. The change from HIGH to LOW happens only once per press.
Show the answer
Answer: Wherever the count happens to end: a coin toss. loop() comes round every fraction of a millisecond, so a quarter-second press flips it hundreds or thousands of times: an odd count leaves it stopped, an even one going. On the change from HIGH to LOW, a press is exactly one flip.
- Why not “Always false”? It ends false only after an even number of flips, which is luck, not design.
- Why not “Always true”? It flips on every pass, so it lands on true only if the count happens to be odd, which you can't control.
- Why not “Unchanged”? It changes on every pass the pin reads LOW, so a quarter-second press flips it many times.
Show the answer
Answer: Nothing: speed is forced to 0 before the wheels are written. The stop overrides the knob. The knob is still read, but its value is replaced by 0 while stopped, so the wheels stay off until the next press.
- Why not “Sets the speed anyway”? The knob's speed is replaced by 0 after it's read, because the stop test comes later.
- Why not “Unstops the rover”? Only a new press of the button flips stopped. The knob never touches it.
- Why not “Reverses”? Nothing in the program reverses the wheels. The speed is held at 0.
“A knob and a kill switch” is lesson 6 of 6 in Rover Ridge, Robotics World 1. Free with an account. Lessons unlock in order, so you’ll do the 5 before it first.
Questions people ask
Why does my Arduino toggle button flicker or only work sometimes?
It's probably flipping on the level, every pass the pin reads LOW, instead of on the change. loop() runs thousands of times during one press, so the state flips thousands of times and ends up wherever the count stops. Flip only when the pin goes from HIGH to LOW.
Do I need to debounce a toggle button?
On a real button, yes. The contacts bounce for a few milliseconds, and each bounce is another HIGH-to-LOW change, so one press can flip the state two or three times. Ignore any new press for about 50 ms after one is counted.
What's the difference between a kill switch and a dead-man's switch?
A kill switch is a toggle: press to stop, press again to go. A dead-man's switch only lets the machine run while it's held, and stops it the moment it's released. In code, the dead-man's version is simply stopped = (pin == HIGH), with no memory.
Related topics
- How to debounce a button on ArduinoWhy one press counts as four, and the 50 ms fixes that make it count once.
- How the Arduino map() function worksRescale a reading from one range to another, and the traps that come with it.
- What is a pull-up resistor?Why an input pin needs a pull-up, and why a pressed button reads LOW.
- How to write Arduino robot code without delay()Swap the waits for a glance at the clock, and the robot hears its bumper mid-leg.