Darth Coder
Darth Coder

Reputation: 1798

Using a PushButton as a trigger in Arduino

I'm trying to work with a simple Arduino circuit that increments a counter variable on pressing a push button in the circuit (connected as INPUT to PIN 8). My code is as simple as follows:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int c = 0, btnPIN, btnVal;

void setup ()
{
    btnPIN = 8;
    pinMode(btnPIN, INPUT); 

    lcd.begin(16,2);
    lcd.clear();
}
void void loop()
{
    btnVal = digitalRead(btnPIN);
    if (btnVal == LOW)
    {
        c++;
        lcd.clear();
        lcd.print(c);
    }
}

Problem is the counter increases by more than 1 every time I push the button. A little bit of printing on the serial monitor indicates that each time the button is pressed and the voltage is LOW, the conditional code is executed multiple times and the counter increases by multiple times instead of 1.

Maybe, I need to write some logic that checks if the button was initially unpressed, then pushed and then released again and then these steps would trigger the required action.

The solution I'm currently working with is as under (which works just fine):

int btnStatus = 0;
void loop()
{
    btnVal = digitalRead(btnPIN);

    if (btnVal == LOW)
        btnStatus = 1;
    if(btnStatus == 1 && btnVal == HIGH)    
    {
        c++;
        lcd.clear();
        lcd.print(c);
        btnStatus = 0;
    }
}

I'm not sure if there's a simpler solution available or if this approach is wrong for other reasons? Any advice would be most welcome!

Upvotes: 1

Views: 4966

Answers (2)

Clukester
Clukester

Reputation: 132

Another problem you you may be having is that mechanical buttons bounce. That is, they jump between two positions several times quickly before settling to a final position. This is standard operation so it is necessary to "debounce" the button.

There are very many ways to do this, but Here is a tutorial using an Arduino.

The main issue, as you probably figured out, is that the loop function is getting called multiple times while the button is down. This is what is fixed by your code, and yours looks to be a good solution and I don't really see a simpler way. For another way, though, perhaps you could try just adding a call to delay at the end of loop to slow it down a bit. You would have to play with the delay amount a bit, but it could work. Really though, your solution looks just fine.

Upvotes: 2

Michael
Michael

Reputation: 701

Your idea is correct, you need to track the previous state of the button to know if it is a new press or if it is simply being held down. Your code could be rewritten to look more like a state machine, however:

typedef enum {
    BTN_STATE_RELEASED,
    BTN_STATE_PRESSED
} ButtonState;

ButtonState btnStatus = BTN_STATE_RELEASED;

void increment_counter()
{
    c++;
    lcd.clear();
    lcd.print(c);
}

void loop()
{
    btnVal = digitalRead(btnPIN);

    switch(btnStatus)
    {
    case BTN_STATE_PRESSED:
        // Handle button release
        if(btnVal == HIGH)
        {
            btnStatus = BTN_STATE_RELEASED;
        }

        break;

    case BTN_STATE_RELEASED:
        // Handle button press
        if(btnVal == LOW)
        {
            increment_counter();
            btnStatus = BTN_STATE_PRESSED;
        }
        break;
    }
}

This was rewritten to use an enum to track the state of the system (a bit overkill for a simple button, but an important concept to know in case your system grows more complex).

The code to update the display was also moved into its own function to make it better separated between the display change and the actual update of the state.

Upvotes: 1

Related Questions