Skip to content

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.

Try it yourself: free lesson

“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.

Try it yourself: free lesson

“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 needed

Practice questions

Three quick questions from the lessons. Pick an answer to check it.

  1. Question 1 of 3The kill button is held down for a whole second. With a toggle on the HIGH-to-LOW change, stopped flips…
    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.
  2. Question 2 of 3A toggle flips stopped on every pass while the pin reads LOW, not on the change. You hold KILL for a quarter of a second. stopped ends up…
    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.
  3. Question 3 of 3While stopped, what does the knob do?
    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.
Keep practising: free lesson

“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.

Every topic