How to make hold button function

I’m trying to make it so that when we hold the button for three seconds the servo will switch position. (we are designing a braking system for a wheelchair for a class and we need to make sure that the wheels don’t lock while they are moving if someone accidentally hits the button hence why we need the three second hold) Here is the code as we currently have it. I’m not sure what is wrong with it but the servo will not move at all.

#include <Servo.h>

#define KEY_PRESSED HIGH //state of key being pressed
#define KEY_NO_PRESS 0 //key not pressed
#define KEY_SHORT_PRESS 1 //key pressed short
#define KEY_LONG_PRESS 2 //key pressed long
#define KEY_DURATION 3000 //cycle count, minimum threshold to test for long presses
const int buttonPin = 8;
const int servoPin = 9;
int buttonState = 0;
int directionState = 0;
Servo servoOne;
int pos = 0;
int count = 0;

void setup() {
servoOne.attach(9);
servoOne.write(directionState);
pinMode(buttonPin, INPUT);
unsigned char key_read(unsigned char pin);
unsigned char count = 0;
}

void loop() {

if (digitalRead(8) != KEY_PRESSED) return KEY_NO_PRESS; //key not pressed
//key is pressed
while (digitalRead(8) == KEY_PRESSED) count+=1; //increment count if key is continuously pressed
if (count > KEY_DURATION) return KEY_LONG_PRESS;
else return KEY_SHORT_PRESS;

if (KEY_LONG_PRESS) {
if (buttonState == HIGH) {
directionState = 1;
for (pos = 0; pos < 180; pos = pos + 1) {
servoOne.write(pos);
delay(5);
}
}
} else if (KEY_LONG_PRESS) {
if (buttonState == HIGH) {
directionState = 0;
for (pos = 180; pos > 1; pos = pos - 1) {
servoOne.write(pos);
delay(5);
}
}
}
}

Does anyone know what we are doing wrong or how we could write this code in a different way to accomplish the same purpose? Some of the code in there might be irrelevant because I am trying to combine two sets of code that I found online that serves to make the servo move and serves to make a button held for a certain amount of time before it does something and I’m not sure it is all necessary. Thank you for any help!

The code in this link illustrates how to implement different button clicks.

Note that it will be quite impossible to achieve your goal if there are delay()s anywhere in your program. The functions delay() and delayMicroseconds() block the Arduino until they complete.

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

...R

have a look at the state change detection example in the IDE (File->examples->02.Digital->StateChangeDetection) to see how you can detect when your button is pressed and released. When these events occur, you need to note the time and then determine how long this has been going on. If it is long enough for a ‘LONG’ key press, then move the servo

untested

#include <Servo.h>

const int KEY_PRESSED = HIGH; //state of key being pressed
const unsigned long KEY_DURATION = 3000; //milliseconds, minimum threshold to test for long presses
const int buttonPin = 8;
const int servoPin = 9;
int buttonState = 0;
int lastButtonState = 0;

unsigned long buttonPressTime;
bool buttonDown = false;

int directionState = 0;

Servo servoOne;

void setup() {
  servoOne.attach(9);
  servoOne.write(directionState);
  pinMode(buttonPin, INPUT);
}

void loop() {

  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, react
    if (buttonState == KEY_PRESSED) {
      // The button just got pushed
      buttonPressTime = millis();
      buttonDown = true;
      Serial.println("on");
    } else {
      // the button just got released
      buttonDown = false;
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;

  if (buttonDown == true && millis() - buttonPressTime >= KEY_DURATION) {
    // we have a long key press
    buttonDown = false; // reset state so button has to be released before moving servo again
    if ( directionState == 1 ) {
      directionState = 0;
      for (int pos = 0; pos < 180; pos = pos + 1) {
        servoOne.write(pos);
        delay(5);
      }
    } else {
      directionState = 1;
      for (int pos = 180; pos > 1; pos = pos - 1) {
        servoOne.write(pos);
        delay(5);
      }
    }
  }
}

when you 'return' a value, execution of that function is terminated and the value is returned to whatever called it.

I would expect that this code would generate an error, or at least a warning - because you've advertised that loop does returns nothing (void), but here you are returning a value.

In any event, when a sketch runs, it calls setup() once, and then loop() repeatedly forever. Nothing is done with any value returned.

So what your code will do is get to the first return statement in loop, and then stop executing loop; this process will repeat infinitely, and it will never get to the code where you control the servos, because it will always hit a return statement first.

Secondly, you then test if(KEY_LONG_PRESS) - KEY_LONG_PRESS is #defined as 2. 2 is not equal to 0, so it is true, so that code will always execute.

Thirdly, you have an else if statement with the same test as the preceeding if statement. If that condition is true, the else will never be reached because the test of the first if will be true, so it will never get to the else clause. If that condition is false, the body if the initial if statement will not execute, it will go straight to the else condition. But the condition will still be false, so the body of the else if statement will also not be executed. You need to involve additional variables to track whether the brake is engaged or not (and also ensure that it waits for the button to be released before engaging/disengaging the break again, otherwise if the button is held down while it engages the brake, as soon as it finishes, it will see that the button has been pressed for long enough to trigger switching the brake and the brake is engaged, and immediately disengage the brake).

Finally, you are counting the duration that the switch is closed for based on the number of cycles of loop() that it was pressed for. But this time is not constant - depending on which code path it goes through, loop may take a different amount of time to execute. Use millis() to time it as blh64 suggested. My guess is that 3000 cycles would (with code corrected as suggested) take less than a tenth of a second most of the time.

So, overall, Instead of returning that value, set a variable to that value, and then check that value in those if statements (and correct the logic as noted above), and time the press properly.

In general, you do not appear to have much of an idea of how to write Arduino code, and are jumping to a whole project, rather than getting pieces working one at a time. Write it one piece at a time, outputting debug information to serial so you can make sure that the code is doing what you think; then, when that piece works, then add another piece. The less experience you have with writing arduino code, the smaller the chunks of code you write between testing your program should be.

Thank you that piece of code worked perfectly!

blh64:
have a look at the state change detection example in the IDE (File->examples->02.Digital->StateChangeDetection) to see how you can detect when your button is pressed and released. When these events occur, you need to note the time and then determine how long this has been going on. If it is long enough for a ‘LONG’ key press, then move the servo

untested

#include <Servo.h>

const int KEY_PRESSED = HIGH; //state of key being pressed
const unsigned long KEY_DURATION = 3000; //milliseconds, minimum threshold to test for long presses
const int buttonPin = 8;
const int servoPin = 9;
int buttonState = 0;
int lastButtonState = 0;

unsigned long buttonPressTime;
bool buttonDown = false;

int directionState = 0;

Servo servoOne;

void setup() {
  servoOne.attach(9);
  servoOne.write(directionState);
  pinMode(buttonPin, INPUT);
}

void loop() {

// read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

// compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, react
    if (buttonState == KEY_PRESSED) {
      // The button just got pushed
      buttonPressTime = millis();
      buttonDown = true;
      Serial.println(“on”);
    } else {
      // the button just got released
      buttonDown = false;
      Serial.println(“off”);
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;

if (buttonDown == true && millis() - buttonPressTime >= KEY_DURATION) {
    // we have a long key press
    buttonDown = false; // reset state so button has to be released before moving servo again
    if ( directionState == 1 ) {
      directionState = 0;
      for (int pos = 0; pos < 180; pos = pos + 1) {
        servoOne.write(pos);
        delay(5);
      }
    } else {
      directionState = 1;
      for (int pos = 180; pos > 1; pos = pos - 1) {
        servoOne.write(pos);
        delay(5);
      }
    }
  }
}