1

I would like to detect how long the state on/off is going on for. Is there a way to do that? I would like to do that for two reasons:

  1. I would be able to make a very simple toggle button. (I know about the other way with the old value and new value).
  2. I can make the button being pressed for a long time be like a return button.

Anyone got any ideas?

2

This needs a combination of patterns.

First, the button pin needs to be debounced. This can be done with the "blink-without-delay"-pattern where the button pin is sampled with a low period (40 ms in the example code below).

Second, a simple state-machine is needed to keep track of the button state but also if the return button action function, doReturnButton(), has already been called.

The button is assumed to be connected from GND to pin4. The button action function, doButton(), is called when the button is pushed (goes LOW).

const int buttonPin = 4;
const unsigned long debounceInterval = 40;
const unsigned long returnInverval = 400;
bool buttonState = true;
bool returnButtonState = false;
unsigned long buttonDownMillis = 0L;

void doButton()
{
  Serial.print(buttonDownMillis);
  Serial.println(F(":doButton"));
}

void doReturnButton()
{
  Serial.print(buttonDownMillis);
  Serial.println(F(":doReturnButton"));
}

void setup()
{
  Serial.begin(57600);
  while (!Serial);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop()
{
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0L;

  // Check if it is time to sample the button pin
  if (currentMillis - previousMillis >= debounceInterval) {
    previousMillis = currentMillis;

    // Did the button change
    if (digitalRead(buttonPin) != buttonState) {
      buttonState = !buttonState;

      // Was the button released
      if (buttonState) {
        returnButtonState = false;
      }

      // Was the button pushed
      else {
        buttonDownMillis = currentMillis;
        returnButtonState = true;
        doButton();
      }
    }

    // Check if button was pushed and time for return action
    else if (returnButtonState) {
      if (currentMillis - buttonDownMillis >= returnInverval) {
        returnButtonState = false;
        doReturnButton();
      }
    }
  }
}

What does the code do?

  1. It samples the button pin with a debounce interval.
  2. If the button state changes: a. High to Low it calls the doButton() function and captures a timestamp. It also allows detect of return time interval. b. Low to High. Return time interval is not allowed any more.
  3. It checks if the return time interval has elapsed and if so calls the doReturnButton() function. The detect is then not allowed any more.

The doButton() function is called when the button is pushed (transition high to low), only once. The doReturnButton() function is called if the return time limit exceeds and the button is still low. Both are only called once per push (transition high to low).

To avoid the multiple transitions that occur when a button is pushed a debounce time period is used. The can also be done in hardware with a RC-circuit.

With some abstraction it could be rewritten as:

#include <Timemark.h>

Timemark debounce(40);
Timemark buttonHold(400);

const int buttonPin = 4;
bool buttonState;

void doButton()
{
  Serial.println(F("doButton"));
}

void doReturnButton()
{
  Serial.println(F("doReturnButton"));
}

void setup()
{
  Serial.begin(9600);
  while (!Serial);
  Serial.println(F("started"));
  pinMode(buttonPin, INPUT_PULLUP);
  buttonState = digitalRead(buttonPin);
  debounce.start();
}

void loop()
{
  if (debounce.expired()) {
    bool currentState = digitalRead(buttonPin);
    if (currentState != buttonState) {
      buttonState = currentState;
      if (buttonState) {
        if (buttonHold.expired())
          doReturnButton();
        else
          doButton();
        buttonHold.stop();
      }
      else {
        buttonHold.start();
      }
    }
  }
}

This also demonstrates the callback on the release of the button. The Timemark class hides all the time intervals, expire calculations, etc.

Cheers!

  • Isn't this just making a toggle button? – Jack Feb 9 '16 at 12:26
  • It calls doButton when the button is pushed and doReturnButton if the button is still pressed after a time limit. – Mikael Patel Feb 9 '16 at 12:31
  • Ah sorry I just got to understand the code better hang on. – Jack Feb 9 '16 at 12:31
  • I am genuinely sorry but can you please explain each part of the code? it would really help me understand it as well as broaden my knowledge of arduino. – Jack Feb 9 '16 at 15:59
  • On a long press, you will doButton() and doReturnButton(). I do not thing this is what the OP wants, although the question is not clear on this regard. – Edgar Bonet Feb 9 '16 at 20:52
1
  1. Note the time the switch is pressed (with millis())
  2. Note the time the switch was released.
  3. When the switch is released calculate the difference. If it is a "long" time do one thing, otherwise do something else.
  • But won't millis() just detect how long the program was running? – Jack Feb 9 '16 at 12:25
  • @Jack Think about it. You're not looking at millis() directly, but at the difference between two consecutive calls. – Tom van der Zanden Feb 9 '16 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.