Editorial Solution

We have to detect singledouble, and long press events from the push button

Control LED1 and LED2 based on the press events

  • Single press: Toggle LED1
  • Double press: Toggle LED2
  • Long press (over 1 second): Turn off both LED1 and LED2

Hardware Connection

  • Push button → Pin 2 (using internal pull-up)
  • LED1 → Pin 5 
  • LED2 → Pin 6 
  • Use a 330 Ohm resistor for each LED to limit current to 10mA

 

Circuit Connection


Firmware

What do we have to achieve?

  • The system detects singledouble, and long button presses using an Arduino UNO.
  • Toggles LED1 and LED2 based on the type of press.
  • Use timers and interrupts for button press detection and debounce logic.

Logic to detect button presses

 

What is Debouncing logic?

  • When a button is pressed
    • Ideally, Wait for 50 ms → check if the button is still pressed → click++. 
    • But, the maximum limit timer_2 can count is 16 ms. Thus, we will loop 3 times to complete the 48ms wait time.
    • After waiting 48ms → check if the button is pressed → if yes, we will increment the button-click count.
  • If a noise spike occurs, Timer_2 is reset to 0 again.
  • Thus, button-press count will be updated only after a stable 48ms button press is detected.

How do we detect the type of button press?

  • When a button is pressed
    • Start Timer_1 for 500 ms.
  • In this 500 ms, we are monitoring constantly for a button press. If a button press occurs we update the button-press count.

There are 2 cases after 500ms occurs

  • After 500ms (button released)
    • If the button is pressed 1 time → register single click
    • If the button is pressed more than 1 time → register double click
  • After 500ms (button still pressed)
    • Wait for another 500ms
    • After 500ms, if the button still pressed → register as long press

 

Code


#define LED1_PIN 5    // Pin for LED1
#define LED2_PIN 6    // Pin for LED2
#define BUTTON_PIN 2  // Pin for the button

volatile bool isT1running = false;  // Flag to indicate if Timer1 is running
volatile bool isT2running = false;  // Flag to indicate if Timer2 is running
volatile int clicksCount = 0;       // Counter for button clicks
volatile int t1Counter = 0;         // Counter for Timer1 compare events
volatile int t2Counter = 0;         // Counter for Timer2 compare events

void setup() {
  pinMode(LED1_PIN, OUTPUT);          // Set LED1 pin as output
  pinMode(LED2_PIN, OUTPUT);          // Set LED2 pin as output
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // Set button pin as input with pull-up resistor

  // Configure Timer1 for a 500ms timeout
  TCCR1A = 0;              // Set Timer1 to Normal mode
  TCCR1B = 0;              // Stop Timer1
  OCR1A = 31249;           // Set compare value for 500ms
  TCNT1 = 0;               // Reset Timer1 counter
  TIMSK1 = (1 << OCIE1A);  // Enable Timer1 Compare Match A interrupt

  // Configure Timer2 for debounce logic
  TCCR2A = (1 << WGM21);   // Set Timer2 to CTC mode
  TCCR2B = 0;              // Stop Timer2
  OCR2A = 255;             // Set compare value for debounce duration
  TCNT2 = 0;               // Reset Timer2 counter
  TIMSK2 = (1 << OCIE2A);  // Enable Timer2 Compare Match A interrupt

  // Configure external interrupt for the button (INT0)
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), ISR_INT0, FALLING);

  // Enable global interrupts
  sei();
}

void loop() {
  while (true) {
    //In this loop Microcontroller is busy in monitoring and performing important tasks constantly.
  }
}

// Interrupt Service Routine for button press (INT0)
void ISR_INT0() {
  // If Timer1 is not running and no clicks are counted yet, start Timer1
  if (!isT1running && clicksCount == 0) {
    TCCR1B = (1 << WGM12) | (1 << CS12);  // Start Timer1 
    isT1running = true;
  }

  // Start Timer2 for debounce logic if it's not running
  if (!isT2running) {
    TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20);  // Start Timer2
    isT2running = true;
  }
}

// Interrupt Service Routine for Timer2 (Debounce logic)
ISR(TIMER2_COMPA_vect) {
  t2Counter++;
  if (t2Counter >= 3) {  // After debounce duration (~3 cycles)
    if (digitalRead(BUTTON_PIN) == LOW && isT1running) {
      clicksCount++;  // Increment click count if button is still pressed
    }
    TCCR2B = 0;           // Stop Timer2
    isT2running = false;  // Reset debounce flag
    t2Counter = 0;        // Reset Timer2 counter
  }
}

// Interrupt Service Routine for Timer1 (Click detection logic)
ISR(TIMER1_COMPA_vect) {
  t1Counter++;

  if (t1Counter == 1) {  // First timeout (500ms after button press)
    if (clicksCount > 1) {
      digitalWrite(LED2_PIN, !digitalRead(LED2_PIN));  // Toggle LED2 on double-click
      t1Counter = 3;                                   // Skip further checks
    } else if (digitalRead(BUTTON_PIN) == HIGH && clicksCount == 1) {
      digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));  // Toggle LED1 on single click
      t1Counter = 3;                                   // Skip further checks
    }
  } else if (t1Counter == 2) {  // Second timeout (long press detected)
    if (digitalRead(BUTTON_PIN) == LOW && clicksCount == 1) {
      digitalWrite(LED1_PIN, LOW);  // Turn off LED1
      digitalWrite(LED2_PIN, LOW);  // Turn off LED2
    }
    t1Counter = 3;  // Ensure Timer1 stops
  }

  if (t1Counter >= 3) {   // Stop Timer1 after logic completion
    TCCR1B = 0;           // Stop Timer1
    isT1running = false;  // Reset Timer1 running flag
    clicksCount = 0;      // Reset click counter
    t1Counter = 0;        // Reset Timer1 counter
  }
}

 

Code Explanation

1. Global Variables:

  • isT1running and isT2running: Flags to track if Timer1 and Timer2 are active.
  • clicksCount: Counts the number of button clicks.
  • t1Counter and t2Counter: Counters for Timer1 and Timer2 interrupts loops. 

2. setup() Function

  • Button ConfigurationBUTTON_PIN is set as an input with a internal pull-up resistor.
  • Timer1 Configuration:
    • Configured for a 500ms timeout using a 16MHz clock and a prescaler of 256.
    • Compare value OCR1A is set to 31249 for 500 ms.
  • Timer2 Configuration:
    • Configured for debounce logic using a prescaler of 1024.
    • Compare value OCR2A is set to 255 for 16 ms.
  • External Interrupt:
    • INT0 is configured to trigger on the falling edge of the button press.
  • Global Interrupts: Enabled using sei().

3. loop() Function

  • The loop() performing important tasks and all the logic is handled by interrupts.

4. Interrupt Service Routines (ISRs)

Button Press ISR (ISR_INT0)

  • Start Timer1 if it is not already running and button-clicks = 0.
  • Used to detect single-click, double-click, and long-press events.
  • Start Timer2 if it is not already running.
  • Used for debouncing a button press.

Timer2 ISR (Debounce Logic)

  • Debounce Logic:
    • After 3 cycles (debounce duration), the button state is checked.
    • If the button is still pressed, clicksCount is incremented.
    • Timer2 is stopped, and its flags are reset.

Timer1 ISR (Click Detection Logic)

  • First Timeout (500ms):
    • If clicksCount > 1, it's a double-click: toggle LED2.
    • If clicksCount == 1 and the button is released, it's a single click: toggle LED1.
  • Second Timeout (1000ms):
    • If the button is still pressed and clicksCount == 1, it's a long press: turn off both LEDs.
  • Stop Timer1:
    • After completing the logic, Timer1 is stopped, and all flags are reset.

 

Output

Hardware Setup

 

Video


 

Submit Your Solution