Thursday, May 14, 2009

Click for A, Press and Hold for B

I planned to add a time setting mode to the alarm clock later, as gravy-- thought I wouldn't need one since the clock chip keeps time-- but with the board not yet built, I keep accidentally disconnecting the DS1307's battery and needing to reset it with a special sketch.

That's a pain, so I'm adding it to the PPAC program next, and to keep the interface simple-- jog/shuttle and a button-- a press and hold event seems appropriate for switching into an otherwise hidden mode, like shutting down a MacBook by holding down the button instead of just clicking it.

Despite a few posted examples on debouncing a button press (explicitly or with a library), I couldn't find any press+hold examples, so I took a stab at it and got a test sketch working pretty quickly with a button and two indicator LEDs. The code is posted below, as a comment.

7 comments:

  1. /* Click and Press+Hold Test Sketch
    By Jeff Saltzman
    To keep input interfaces simple, I want to use a single button to:
    1) click (fast press and release) for regular button use, and
    2) press and hold to enter a configuration mode.
    */

    #define buttonPin 19 // analog input pin to use as a digital input
    #define ledPin1 9 // digital output pin for LED 1 indicator
    #define ledPin2 8 // digital output pin for LED 2 indicator

    #define debounce 20 // ms debounce period to prevent flickering when pressing or releasing the button
    #define holdTime 2000 // ms hold period: how long to wait for press+hold event

    // Button variables
    int buttonVal = 0; // value read from button
    int buttonLast = 0; // buffered value of the button's previous state
    long btnDnTime; // time the button was pressed down
    long btnUpTime; // time the button was released
    boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered

    // LED variables
    boolean ledVal1 = false; // state of LED 1
    boolean ledVal2 = false; // state of LED 2


    //=================================================


    void setup()
    {

    // Set button input pin
    pinMode(buttonPin, INPUT);
    digitalWrite(buttonPin, HIGH );

    // Set LED output pins
    pinMode(ledPin1, OUTPUT);
    digitalWrite(ledPin1, ledVal1);
    pinMode(ledPin2, OUTPUT);
    digitalWrite(ledPin2, ledVal2);

    }


    //=================================================


    void loop()
    {

    // Read the state of the button
    buttonVal = digitalRead(buttonPin);

    // Test for button pressed and store the down time
    if (buttonVal == LOW && buttonLast == HIGH && (millis() - btnUpTime) > long(debounce))
    {
    btnDnTime = millis();
    }

    // Test for button release and store the up time
    if (buttonVal == HIGH && buttonLast == LOW && (millis() - btnDnTime) > long(debounce))
    {
    if (ignoreUp == false) event1();
    else ignoreUp = false;
    btnUpTime = millis();
    }

    // Test for button held down for longer than the hold time
    if (buttonVal == LOW && (millis() - btnDnTime) > long(holdTime))
    {
    event2();
    ignoreUp = true;
    btnDnTime = millis();
    }

    buttonLast = buttonVal;

    }


    //=================================================
    // Events to trigger by click and press+hold

    void event1()
    {
    ledVal1 = !ledVal1;
    digitalWrite(ledPin1, ledVal1);
    }

    void event2()
    {
    ledVal2 = !ledVal2;
    digitalWrite(ledPin2, ledVal2);
    }

    ReplyDelete
    Replies
    1. how do you have it wired. i tried on my arduino uno and i get nothing.

      Delete
  2. very, very nice! I think a lot of 'duino with LCD's just got nested menus because of you!

    ReplyDelete
  3. Gotta say that's a great idea. Seems so simply obvious to do that now that you've stated it. :-)

    I think that's begging to become a compliment to AlphaBeta's Button abstraction layer: http://www.arduino.cc/playground/Code/Button.

    We could start coding all that in two lines like this, and get down to the fun stuff:

    Button pressMe = button(2, PULLUP);
    if (pressMe.held(1000)) someFunction();

    ReplyDelete
  4. I used something like this in a project I was working on. Though I think this could be compacted further. If you use a simple counter 'while' or 'for' loop, you can have the led turn on, or the function occur right when the threshold between a 'press' and a 'hold' is crossed.

    Attach this function to an interrupt pin

    int ButtonCheck(){
    count = 0;
    while(digitalRead(But)==HIGH){
    delay(10);
    count++;
    }
    if (count > 100){ /button held
    return 2; /button held
    else if count > 0
    return 1; /button pressed
    }
    }

    ReplyDelete
  5. What about a double press?

    ReplyDelete
  6. I'm working on a similar code but with a sensor instead. I've been trying for days to convert the code but no luck.
    Here's the code I have for the sensor to turn off a single led.
    void loop() {
    val = analogRead(LDR);

    if (val2 < 28)
    {digitalWrite(ledPin, HIGH);
    //delay(3000);
    } else {
    digitalWrite(ledPin, LOW); // turn the ledPin off
    }
    //delay(1000);
    }

    Thanks, 12 year old from Toronto

    ReplyDelete