Button click vs hold with a twist

Hi All,

Firstly, I am new to the Arduino (bet you love hearing that!) and of course to the forum also...

I am trying to have my Uno control a peristaltic pump, the hardware bit I have sorted with a bit of YouTube and searching these forums, thanks for that!

What I would like it to do, and am struggling with, is if the button is just pressed to then run the pump for 5 seconds but if it is held, to then run for as long as the button is held.

I have got this to work, but there is a problem, two days of reading and trying things and now I am here asking for your help!

When the button is pressed and released it waits and then runs for 5 seconds, just as planned. If the button is held it waits and then runs for as long as its held, again just as planned. I can do this over and over again as much as I like and everything seems to be working just fine. However, if I hold the button and let it run for more than five or six seconds and release it, it does what it is suppose to but then after two seconds it starts the five second run unprompted by me.

I have found many threads on getting two different functions out of a button depending on the interaction with it, however I can't get them to run the pump for as long as it is held if it is held.

Below is the code I have so far, please excuse me if it appears a bit messy, it's been ripped apart and put back together more times than I care to think about.

const int buttonPin = 2;
const int pumpPin = 3;

unsigned long keyPressTime = 0;
unsigned long fillTime = 0;

int filling = LOW;
int keyPressed = LOW;
int longRun = LOW;

void setup() {
}

void loop() {

int currKeyState = digitalRead(buttonPin);

if ((keyPressed == LOW) && (longRun == LOW) && (filling == LOW) && (currKeyState == HIGH)) {
    keyPressTime = millis();
    keyPressed = HIGH;
}

if (((millis() - keyPressTime) > 2000) && (keyPressed) == HIGH) {
    if (currKeyState == HIGH){
        keyPressTime = millis();
        analogWrite(pumpPin, 255);
        longRun = HIGH;
    } else if ((longRun == LOW) && filling == LOW) {
        keyPressTime = millis();
        analogWrite(pumpPin, 255);
        filling = HIGH;
        fillTime = millis();
    }
}

if ((keyPressed == HIGH) && (currKeyState == LOW) && (filling == LOW) && (longRun == HIGH)) {
    digitalWrite(pumpPin, LOW);
    keyPressed = LOW;
    longRun = LOW;
}

if (((millis() - fillTime) > 5000) && (filling == HIGH)) {
   fillTime = 0;
   digitalWrite(pumpPin, LOW);
   keyPressed = LOW;
   filling = LOW;
}

}

Any help is very much appreciated. As it is my first post please excuse me if I missed something out.

Many thanks!

Max

Edit: After more tinkering I noticed it is running the five second fill cycle when the top up cycle is held for LESS than two seconds (not more than five as stated above). This absolutely makes sense to me now as I have asked it do that in the code, if pressed for less than two seconds run the fill cycle, it just didn't occur to me! Now to stop it from doing that! Again any help is much appreciated!!!!

The 3 variables keyPressed, filling and longRun work together to define a "State". Try to draw all of those states on a page. Start with the low-low-low state. Draw a circle around that. Now what action causes it to leave that state? Draw that as an arrow. Write the input which made it leave that state and write what it does to the outputs.

Now you're in the high-low-low state. Draw a circle around that. Maybe give it a better name. What causes it to leave that state? Well, it looks like there's a 2-second timer.

Once you think about it as states, your logic will be more obvious. The way it's written now, you don't really check all the variables every time and other states like high-high-low might become true on one of high-low-low tests.

Often it's better to have one state variable and use an enum or #define'd constants to give the states names.

I’ve created an example and writen up extensive comments on detecting short and long button presses here: https://www.baldengineer.com/detect-short-long-button-press.html

To simplify the logic, I created an enum to track the state of the buttons. Follow MorganS’s suggestion of drawing a state diagram and the code will be easier understand.

Hi Morgan and James,

Thank you both for your responses, lots more reading to do! :slight_smile:

I really appreciate you both not just fixing my errors but rather helping me have the ability to do it myself in the future.

With a sick dog and a serious lack of sleep I am struggling to come up with an appropriate response and certainly can't get my head around this just yet, but I felt I should say thank you.

I will come back once I have had a bit of sleep, a big cup of coffee and had another go :slight_smile:

Again, thanks to you both!

Max

Max, if you would diagram your logic on a time line you will realize your program needs to run the pump forever, if the switch is pressed. Then after 5 seconds has elapsed, begin to test if the button is not being pressed and if that is true, stop the pump.

Paul

This is nitpicking, but could be important. If I press button at 0 s and release it at 1 s, then press it at 4 s and release it at 6 s, what should happen? Should the pump run from 0 to 6 s or from 0 to 9 s?