Control an LED Chaser with Button Hold and Double Click (Arduino Tutorial)

In this project, you’ll learn how to control a multi-LED chaser effect using just one button. This tutorial covers how to:

  • Run the LED chaser while holding the button
  • Enable auto-run mode with a double click
  • Stop the LED effect with another double click

It’s a fun way to explore INPUT_PULLUP, button debouncing, and LED animations — all with clean, beginner-friendly code.


Components Needed:

This post may contain affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you. It helps support this blog and keeps the projects coming—thanks for your support!


How It Works

The LED chaser moves from left to right, turning on one LED at a time. You can control it in two ways:

  • Hold the button: The LEDs will run as long as you keep pressing.
  • Double click: This toggles an “auto-run” mode, where the LEDs keep running even after you release the button.

Clicking the button twice again disables auto-run mode and turns off all LEDs.

The code uses:

  • millis() for non-blocking LED timing
  • Debounce logic for reliable double-click detection
  • INPUT_PULLUP to simplify button wiring

Circuit Wiring

  • Connect each LED:
    • Anode (long leg) → 220Ω resistor → Arduino digital pins 2 to 7
    • Cathode (short leg) → GND rail
  • Button wiring:
    • One side → GND
    • Other side → Arduino digital pin 8

Note: No external resistor needed because we’re using INPUT_PULLUP.


Arduino Code



const int ledCount = 6;
int ledPins[ledCount] = {2, 3, 4, 5, 6, 7};
const int buttonPin = 8;

int currentLED = 0;
unsigned long previousMillis = 0;
const unsigned long interval = 100;

bool autoRun = false;              // true if LED should run automatically
bool lastButtonState = HIGH;
unsigned long lastPressTime = 0;
int pressCount = 0;

void setup() {
  for (int i = 0; i < ledCount; i++) {
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW); // ensure all LEDs off at start
  }
  pinMode(buttonPin, INPUT_PULLUP); // use internal pull-up resistor
}

void loop() {
  handleButton();  // check for hold or double-click

  bool buttonHeld = (digitalRead(buttonPin) == LOW); // true when button is held

  // Run LEDs if button is held or in auto mode
  if (autoRun || buttonHeld) {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;

      // Turn off all LEDs
      for (int i = 0; i < ledCount; i++) {
        digitalWrite(ledPins[i], LOW);
      }

      // Turn on the current LED
      digitalWrite(ledPins[currentLED], HIGH);

      // Move to the next LED
      currentLED = (currentLED + 1) % ledCount;
    }
  } else {
    // If not holding and not auto-running → turn off all LEDs
    for (int i = 0; i < ledCount; i++) {
      digitalWrite(ledPins[i], LOW);
    }
  }
}

void handleButton() {
  bool currentState = digitalRead(buttonPin);

  // Detect button press (falling edge)
  if (currentState != lastButtonState) {
    if (currentState == LOW) {
      unsigned long now = millis();

      // Check for double click within 300ms
      if (now - lastPressTime < 300) {
        pressCount++;
        if (pressCount >= 2) {
          autoRun = !autoRun;    // toggle auto mode
          pressCount = 0;        // reset press counter
        }
      } else {
        pressCount = 1;          // start new click count
      }

      lastPressTime = now;
    }

    lastButtonState = currentState;
  }
}

Breaking Down the Code

Let’s go through the sketch step-by-step to understand how it works.

Step 1: Define constants and variables


const int ledCount = 6;
int ledPins[ledCount] = {2, 3, 4, 5, 6, 7};
const int buttonPin = 8;

int currentLED = 0;
unsigned long previousMillis = 0;
const unsigned long interval = 100;

bool autoRun = false;              // true if LED should run automatically
bool lastButtonState = HIGH;
unsigned long lastPressTime = 0;
int pressCount = 0;

– ledPins[] stores the digital pins connected to LEDs (D2 to D7).
– buttonPin is connected to a pushbutton (D8).
– autoRun is a flag that enables auto-run mode.
– Other variables are used to track button state, time, and number of presses.

Step 2: Setup all pins


void setup() {
  for (int i = 0; i < ledCount; i++) {
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW); // ensure all LEDs off at start
  }
  pinMode(buttonPin, INPUT_PULLUP); // use internal pull-up resistor
}

Initializes all LED pins as OUTPUT and sets them LOW. The button pin is set to INPUT_PULLUP so that pressing it brings the value to LOW.

Step 3: LED sequence logic


void loop() {
  handleButton();  // check for hold or double-click

  bool buttonHeld = (digitalRead(buttonPin) == LOW); // true when button is held

  if (autoRun || buttonHeld) {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;

      for (int i = 0; i < ledCount; i++) {
        digitalWrite(ledPins[i], LOW);
      }

      digitalWrite(ledPins[currentLED], HIGH);
      currentLED = (currentLED + 1) % ledCount;
    }
  } else {
    for (int i = 0; i < ledCount; i++) {
      digitalWrite(ledPins[i], LOW);
    }
  }
}

This function is called continuously. If autoRun is enabled or the button is held, it updates the current LED every 100ms. If not, it ensures all LEDs are off.

Step 4: Handle button presses


void handleButton() {
  bool currentState = digitalRead(buttonPin);

  if (currentState != lastButtonState) {
    if (currentState == LOW) {
      unsigned long now = millis();

      if (now - lastPressTime < 300) {
        pressCount++;
        if (pressCount >= 2) {
          autoRun = !autoRun;
          pressCount = 0;
        }
      } else {
        pressCount = 1;
      }

      lastPressTime = now;
    }

    lastButtonState = currentState;
  }
}

Detects a double-click on the button. If the user presses the button twice within 300 milliseconds, it toggles the autoRun flag on or off.

Testing It Out

  1. Upload the code to your Arduino
  2. Hold the button → LEDs chase while held
  3. Double click the button → LEDs run automatically
  4. Double click again → LEDs stop

Tips & Custom Ideas

  • Add another button to reverse direction
  • Use a potentiometer to control speed
  • Add more LEDs or change colors for effects

This project is perfect for building your button-handling skills and getting comfortable with non-blocking timing using millis().

Enjoy building it — and happy tinkering! 🔧✨

đź”§ Recommended Arduino Starter Kits for Beginners

If you’re just getting started with Arduino, these beginner-friendly kits will help you learn faster and avoid the headache of missing parts. They all include essential components like LEDs, resistors, jumper wires, and an Arduino-compatible board.

  • Arduino Official Starter Kit
    Includes a genuine Arduino UNO board, project book, and components for 15+ tutorials.
    👉 Check it on Amazon

  • Elegoo UNO R3 Super Starter Kit
    Affordable and packed with sensors, LEDs, motors, and wires — great value.
    👉 View the Elegoo Kit

  • Freenove Ultimate Starter Kit
    Includes 200+ components, an Arduino-compatible board, and 50+ example projects.
    👉 See the Freenove Kit

đź’ˇ Tip: Choose a kit with a good variety of components so you can build multiple projects without buying extra parts later.

Leave a Reply

Your email address will not be published. Required fields are marked *