Button "Timeout" issue (still)

I've spent a few hours moving code around here... Trying to get this timer to ignore subsequent button presses if the time interval between presses is greater than 5 seconds.
3 presses ON, 3 presses OFF works - but will always work regardless of time between presses.
What am I missing here???


// MPS Intent Switch v2.0
// 3 Press ON, 3 Press OFF (if 3 presses within 5 seconds)
// c.2022-24 by Jerry Tucker

#define button1 3  //Push button1 on D3
#define RELAY 2    //RELAY on D2

//-----
const unsigned long Interval = 5000;
unsigned long lastpressTime = 0;
int state = 0;          //integer to hold current state
int old = 0;            //integer to hold last state
int button1Poll = 0;    //integer to hold button1 state
int lastPressTime = 0;  //integer to hold last press time


//-----
void setup() {
  pinMode(button1, INPUT_PULLUP);  //button1 set as input
  pinMode(RELAY, OUTPUT);          // RELAY as output

  digitalWrite(RELAY, HIGH);  //set RELAY initial state as OFF
}
//-----
void loop() {
  unsigned long currentTime = millis();
  //debouncing routine to read button
  button1Poll = digitalRead(button1);    //poll the state of button1
  if (button1Poll == 1) {                //check if it has been pressed
    delay(50);                           //wait 50ms
    button1Poll = digitalRead(button1);  //poll button1 again
    if (button1Poll == 0) {              // if 0 - considered one press
      state = old + 1;                   //increase state by 1
    }
  } else {      //if button1 has not been pressed
    delay(50);  //wait 50ms
  }
  (old = 0);


  switch (state) {                //react to button1 press & state
    case 1:                       //if state is 1
      digitalWrite(RELAY, HIGH);  //RELAY off
      old = state;                //set old state as current state
      break;

      //if button1 has not been pressed within 5 seconds
      if ((currentTime)-lastPressTime > Interval)
        ;
      (state = 0);  //set 'state' to zero;
      {
        case 2:
          digitalWrite(RELAY, HIGH);
          old = state;
          break;
        case 3:
          digitalWrite(RELAY, LOW);  //RELAY on
          old = state;
          break;
        case 4:
          digitalWrite(RELAY, LOW);  //RELAY on
          old = state;               //set old state as current state
          break;
        case 5:
          digitalWrite(RELAY, LOW);
          old = state;
          break;
        case 6:
          digitalWrite(RELAY, HIGH);  //RELAY off
          old = 0;
          break;
        default:                      //if state is not 1,2,3,4,5,6
          digitalWrite(RELAY, HIGH);  //RELAY OFF
          old = 0;                    //reset RELAY to  off/state
          break;
      }
  }
}

You should e using unsigned long for ALL!!!!! millisecond storage variables!

Your switch/case looks garbled:

  switch (state) {                //react to button1 press & state
    case 1:                       //if state is 1
      digitalWrite(RELAY, HIGH);  //RELAY off
      old = state;                //set old state as current state
      break;

      //if button1 has not been pressed within 5 seconds
      if ((currentTime)-lastPressTime > Interval)
        ;
      (state = 0);  //set 'state' to zero;
      {
        case 2:
          digitalWrite(RELAY, HIGH);
          old = state;
          break;
        case 3:
          digitalWrite(RELAY, LOW);  //RELAY on
          old = state;
          break;
        case 4:
          digitalWrite(RELAY, LOW);  //RELAY on
          old = state;               //set old state as current state
          break;
        case 5:
          digitalWrite(RELAY, LOW);
          old = state;
          break;
        case 6:
          digitalWrite(RELAY, HIGH);  //RELAY off
          old = 0;
          break;
        default:                      //if state is not 1,2,3,4,5,6
          digitalWrite(RELAY, HIGH);  //RELAY OFF
          old = 0;                    //reset RELAY to  off/state
          break;
      }
  }

This is unreachable, will never run, besides being perhaps not what you meant:

      //if button1 has not been pressed within 5 seconds
      if ((currentTime)-lastPressTime > Interval)
        ;
      (state = 0);  //set 'state' to zero;

as it is exactly the same as:

      state = 0;  //set 'state' to zero;

BTW usually statements are not ( inside parentheses )...

Also, I don't think you need old, which look s to be in the middle of this mess. Usually state would just be set dir3ctly by case code that decided the state should change, viz:

        case 5:
          digitalWrite(RELAY, LOW);
          state = 6;   // move to next state for next time in the switch/case
          break;

So I haven't yet looked at the logic or reviewed your goal.

Maybe later.

a7

Thank you, I'll work on my coding some more.

We've looked at your code, then we played with the general idea.

Some of us have convinced others that unless you keep track of the history, that is to say the times when the button got pressed, you will not be able to reliably detect and react to three button presses within five seconds.

Press, wait one second, press wait one second and press is straightforward and easy.

But look at

Press wait three seconds, press wait three seconds, press twice more within 2 more seconds.

It is the second, third and fourth presses that are within five seconds of each other.

You will have to store the times of the previous two presses and when the next one comes in, compare its time of arrival to that of the second most recent press.

As a new press comes along, the oldest press is no longer important. So you will, when it's all cranking away, be discarding the oldest press time, and adding the newly pressed time to to your tracking.

I will mention that in a case like this, an array will help, and using such an array as a circular buffer will make things a bit easier.

I just googled

 circular buffer

to see if it turns up what I was hoping. It did.

a7

Interesting information, thanks!
I will say that this circuit has been working perfectly (and still does) for a couple of years without the 5 second timeout...
It is for an electronic hand control accelerator for disabled drivers... I started playing around with the "5-second rule" just to try to avoid accidental engagement or disengagement because of fumbling sound hitting the button 3 times.
I can hit the button very rapidly, or hit... wait... hit... wait...hit and have it activate or deactivate the relay.

Now I think it may be something I missed.

Should one isolated press of the button do something? If so, you should then set a timer for five seconds, and ignore (don't even look at) the button until it expires:

  if time since last press is > 5 seconds
    see if the button is pressed and
      do whatever that should do
      note the time of this, the last button press

Or do you want to make someone press three times close together to get something to happen, and what you don't want is three presses over twenty minutes?

Then tracking them as I outkined seems necessary. Unless it's just a tricky UI thing, where saying "N presses within M seconds" is not literally the rule, and some tolerance is available for missing three press events which would qualify under a strict interpretation.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.