Pushing button once, twice, three times... Hold - do action

Hello,
I´m still trying to finish my multiple plant watering project.
It´s working for automatic watering.

Now I´m about to try to read and set the desired value of the moisture for each sensor.

What I want:
Pushing a button once, then hold it for reading the first sensor, then set the actual value as desired value.
It´s working for one sensor right know.

I like to choose (click through) the sensor which I want to set the value for by in example:
Pushing the button three times (led should flash three times after letting go for a really short while) then holding the button for three seconds to set the value of sensor 3.

And so on for each sensor.

I hope its understandable.
Thank you for your help!

Heres my (not working) code so far:

//Drylimit set manually

    if (digitalRead(setButton) == HIGH) //read moisture value setButton
    {
      
      if (firstPress == true)
      {
        buttonCounter++;        // count one up 
        Serial.println(buttonCounter);
        btSerial.println(buttonCounter);
        buttonStartTime = millis(); //Start timer to count how long the button is held
        firstPress = false; //set flag to skip this if statement until after the button has been released
      }
        else if ((setButton == HIGH) && (buttonCounter == 1))
        {
          onLED1(flashBlue1); //flash once
          }
         else if ((setButton == HIGH) && (buttonCounter == 2))
          {
            onLED1(flashBlue2); //flash twice
          }
         else if ((setButton == HIGH) && (buttonCounter == 3))
          {
            onLED1(flashBlue3); //flash three times
          }
         else if ((setButton == HIGH) && (buttonCounter == 4))
          {
            onLED1(flashBlue4); //flash four times
          }
      
      //if button is held for more than menuPressTime (in milliseconds) reset desired moisture value to current moisture
      if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==1))
      {
        timedRelay(); //call for moisture value read
        dryLimit1 = moist1Value; //reset desired moisture value
        Serial.println("Sensor 1 now watering at: ");
        Serial.println(dryLimit1);
        btSerial.println("Sensor 1 now watering at: ");
        btSerial.println(dryLimit1);
        onLED1(blue);
        buttonCounter == 0; //reset button Counter
      }
      else if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==2))
      {
        timedRelay(); //call for moisture value read
        dryLimit2 = moist2Value; //reset desired moisture value
        Serial.println("Sensor 2 now watering at: ");
        Serial.println(dryLimit1);
        btSerial.println("Sensor 2 now watering at: ");
        btSerial.println(dryLimit2);
        onLED1(blue);
        buttonCounter == 0;
      }
      else if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==3))
      {
        timedRelay(); //call for moisture value read
        dryLimit3 = moist3Value; //reset desired moisture value
        Serial.println("Sensor 3 now watering at: ");
        Serial.println(dryLimit1);
        btSerial.println("Sensor 3 now watering at: ");
        btSerial.println(dryLimit3);
        onLED1(blue);
        buttonCounter == 0;
      }
      else if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==4))
      {
        timedRelay(); //call for moisture value read
        dryLimit4 = moist4Value; //reset desired moisture value
        Serial.println("Sensor 4 now watering at: ");
        Serial.println(dryLimit4);
        btSerial.println("Sensor 4 now watering at: ");
        btSerial.println(dryLimit4);
        onLED1(blue);
        buttonCounter == 0;
      }
    
    else firstPress = true; //reset button flag
    }
}

The following test won't work because setButton is a pin number:

  else if ((setButton == HIGH) && (buttonCounter == 1))

How are your buttons wired? Per your code I will assume there is a pull-down resistor and button is wired to +5V. You are testing the button state and NOT the change in state of the button. See the following tutorial: StateChangeDetection

If you want to count button pushes you must detect when the button is released. I would also recommend creating a sketch with nothing but button processing and Serial.print statements in order to debug your button behavior then add everything else once your buttons work like you want.

Hey, thanks for your replay.

Yes I´m using a pulldown resistor and its wired from 5V to Arduino Digital Pin.

I tried the buttonstate thing before I posted my code here... But it also didnt worked. I just counted up as long as I held the button.

I´ll try again tomorrow :slight_smile:

Floduino:
Hey, thanks for your replay.

Yes I´m using a pulldown resistor and its wired from 5V to Arduino Digital Pin.

I tried the buttonstate thing before I posted my code here... But it also didnt worked. I just counted up as long as I held the button.

I´ll try again tomorrow :slight_smile:

I just loaded the example code from the link I gave you and it counts button pushes perfectly. Please load the code from the example and let us know how it works.

Also, post your entire sketch. You only posted a fragment.

So I changed it to this (I had it before) but the serial Monitor shows me that its still counting up while I push the button :confused:

I´ll try just the code from the example next.

I didnt wanted to confuse anybody about the rest of my code :slight_smile:

//EDIT: I tried just the example and it workes fine… I don´t know where I made the mistake :confused:
//EDIT2: Counting is working now thank you :slight_smile: now i just have to implement the rest :smiley: I´ll let you know if i need help.

//Drylimit set manually

    buttonState = digitalRead(setButton);
    
    //if (digitalRead(setButton) == HIGH) //read moisture value setButton
    if (buttonState != lastButtonState)
    {
      
      //if (firstPress == true)
      if (buttonState == HIGH)
      {
        buttonCounter++;        // count one up 
        Serial.println(buttonCounter);
        btSerial.println(buttonCounter);
        buttonStartTime = millis(); //Start timer to count how long the button is held
        firstPress = false; //set flag to skip this if statement until after the button has been released
      }
        else if ((setButton == HIGH) && (buttonCounter == 1))
        {
          onLED1(flashBlue1); //flash once
          }
         else if ((setButton == HIGH) && (buttonCounter == 2))
          {
            onLED1(flashBlue2); //flash twice
          }
         else if ((setButton == HIGH) && (buttonCounter == 3))
          {
            onLED1(flashBlue3); //flash three times
          }
         else if ((setButton == HIGH) && (buttonCounter == 4))
          {
            onLED1(flashBlue4); //flash four times
          }
      
      //if button is held for more than menuPressTime (in milliseconds) reset desired moisture value to current moisture
      if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==1))
      {
        timedRelay(); //call for moisture value read
        dryLimit1 = moist1Value; //reset desired moisture value
        Serial.println("Sensor 1 now watering at: ");
        Serial.println(dryLimit1);
        btSerial.println("Sensor 1 now watering at: ");
        btSerial.println(dryLimit1);
        onLED1(blue);
        buttonCounter == 0; //reset button Counter
      }
      else if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==2))
      {
        timedRelay(); //call for moisture value read
        dryLimit2 = moist2Value; //reset desired moisture value
        Serial.println("Sensor 2 now watering at: ");
        Serial.println(dryLimit1);
        btSerial.println("Sensor 2 now watering at: ");
        btSerial.println(dryLimit2);
        onLED1(blue);
        buttonCounter == 0;
      }
      else if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==3))
      {
        timedRelay(); //call for moisture value read
        dryLimit3 = moist3Value; //reset desired moisture value
        Serial.println("Sensor 3 now watering at: ");
        Serial.println(dryLimit1);
        btSerial.println("Sensor 3 now watering at: ");
        btSerial.println(dryLimit3);
        onLED1(blue);
        buttonCounter == 0;
      }
      else if (digitalRead(setButton) == HIGH && (millis() - buttonStartTime) > menuPressTime && firstPress == false &&(buttonCounter ==4))
      {
        timedRelay(); //call for moisture value read
        dryLimit4 = moist4Value; //reset desired moisture value
        Serial.println("Sensor 4 now watering at: ");
        Serial.println(dryLimit4);
        btSerial.println("Sensor 4 now watering at: ");
        btSerial.println(dryLimit4);
        onLED1(blue);
        buttonCounter == 0;
      }
    
    else firstPress = true; //reset button flag
    }
}

Code.txt (16.8 KB)

You also need to debounce for button. You can make your code easy by using the following structure:

#include <ezButton.h>

ezButton button(7);  // create ezButton object that attach to pin 7;
unsigned long lastCount = 0;
unsigned long count = 0;

void setup() {
  Serial.begin(9600);
  button.setDebounceTime(50); // set debounce time to 50 milliseconds
  button.setCountMode(COUNT_FALLING);
}

void loop() {
  button.loop(); // MUST call the loop() function first

  count = button.getCount();

  if (count != lastCount) {
    Serial.println(count);

    int countIn3 = count % 3 + 1;

    switch (countIn3) {
      case 1:
        // TO DO
        break;

      case 2:
        // TO DO
        break;

      case 3:
        // TO DO
        break;
    }

    lastCount = count;
  }
}

Note that the above code used this button library. The library already used internal pull-up resistor and supports debounce

Floduino:
So I changed it to this (I had it before) but the serial Monitor shows me that its still counting up while I push the button :confused:

I´ll try just the code from the example next.

I didnt wanted to confuse anybody about the rest of my code :slight_smile:

//EDIT: I tried just the example and it workes fine… I don´t know where I made the mistake :confused:
//EDIT2: Counting is working now thank you :slight_smile: now i just have to implement the rest :smiley: I´ll let you know if i need help.

You still have the issue I pointed out in reply #1. Also, where are you setting lastButtonState?

The way you want your buttons to work you could easily implement with a state machine.

Floduino:
What I want:
Pushing a button once, then hold it for reading the first sensor, then set the actual value as desired value.
It´s working for one sensor right know.

I like to choose (click through) the sensor which I want to set the value for by in example:
Pushing the button three times (led should flash three times after letting go for a really short while) then holding the button for three seconds to set the value of sensor 3.

And so on for each sensor.

To my mind that sounds like a system that is very nice in theory (and capable of being programmed) but would be a nightmare to use in practice. The user would be constantly getting mixed up.

...R

Here is a state machine version of your button configuration scheme. Run it standalone then add your code. I don't normally write code for people but it was a fun little challenge.

// I like to choose (click through) the sensor which I want to set the value for by in example:
// Pushing the button three times (led should flash three times after letting go for a really short while) 
// then holding the button for three seconds to set the value of sensor 3.

const int setButton = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;      // the pin that the LED is attached to
const int maxSensors = 3;   // maximum number of sensors
const unsigned long confirmLEDTimeMs = 250; // on/off time for LED confirmation flash
const unsigned long selectTimeoutMs = 1000; // maximum time between button pushes for select
const unsigned long setTimeoutMs = 5000;    // maximum wait time before aborting set value
const unsigned long setHoldMs = 3000;      // button hold minimum time for setting sensor value

unsigned long lastMillis;

int sensorSelected = 0;   // Sensor selected counter
int lastButtonState;      // previous state of the button
int confirmFlashes;       // number of confirmation flashes

// Selection states
enum _state_enum
{
  IDLE,
  SELECTING,
  SELECT_CONFIRM,
  SET_WAIT,
  SET_VALUE
};

_state_enum state;

void setup() 
{
  pinMode(setButton, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  Serial.begin(9600);
  lastButtonState = digitalRead(setButton);
  state = IDLE;
}

bool isButtonPressed()
{
  // read the pushbutton input pin:
  int buttonState = digitalRead(setButton);
  bool pressed = false;
  
  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) 
  {
    if (buttonState == HIGH) pressed = true;
    delay(50); // debounce
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
  return pressed;
}

void loop() 
{
  bool buttonPressed = isButtonPressed();
  
  switch (state)
  {
    case IDLE:
      if (buttonPressed)
      {
        sensorSelected++;
        lastMillis = millis();
        state = SELECTING;
        Serial.println("Selecting the sensor");
      }
      break;
    
    case SELECTING:
      if ((millis() - lastMillis > selectTimeoutMs) || sensorSelected >= maxSensors)
      {
        lastMillis = millis();
        state = SELECT_CONFIRM;
        digitalWrite(ledPin, LOW);
        Serial.print("Sensor #");
        Serial.print(sensorSelected);
        Serial.println(" selected");
        confirmFlashes = sensorSelected * 2;
      }
      else if (buttonPressed)
      {
        lastMillis = millis();
        sensorSelected++;
      }
      break;
      
    case SELECT_CONFIRM:
      if (millis() - lastMillis > confirmLEDTimeMs)
      {
        digitalWrite(ledPin, !digitalRead(ledPin));
        lastMillis = millis();
        if (--confirmFlashes == 0)
        {
          digitalWrite(ledPin, LOW);
          state = SET_WAIT;
          Serial.print("Sensor #");
          Serial.print(sensorSelected);
          Serial.println(" confirmed");
          Serial.println("Wait for button hold...");
        }
      }
      break;
      
    case SET_WAIT:
      if (millis() - lastMillis > setTimeoutMs)
      {
        state = IDLE;
        sensorSelected = 0;
        Serial.println("Button hold not detected, set sensor aborted...");
      }
      else if (buttonPressed)
      {
        lastMillis = millis();
        state = SET_VALUE;
      }
	  break;
    
    case SET_VALUE:
      if (digitalRead(setButton) == HIGH)
      {
        if (millis() - lastMillis > setHoldMs)
        {
          // ***** TBD: Set the value of the sensor here!!! ****
          Serial.print("Sensor #");
          Serial.print(sensorSelected);
          Serial.println(" is set");
          state = IDLE;
          sensorSelected = 0;
        }
      }
      else
      {
        // The user let go of the button before the hold time expired.  Return to IDLE
        // without setting sensor.
        state = IDLE;
        sensorSelected = 0;
        Serial.println("Button hold too short, set sensor aborted...");
      }
      break;
  }
}

Floduino:
I like to choose (click through) the sensor which I want to set the value for by in example:
Pushing the button three times (led should flash three times after letting go for a really short while) then holding the button for three seconds to set the value of sensor 3.

There are also button libraries which offer long press/short press options for things like this.

Wow first at all thank you all for your help!
I found out about debouncing too yesterday but I was too tired to find a way to fix it.

I´ll work myself through your suggestions and let you know if anything doesn´t work.

@Robin2
Why is it a nightmare for the user?

Floduino:
@Robin2
Why is it a nightmare for the user?

I believe that in practice the user will frequently press the button too often or not often enough and find themselves in the wrong place and won't even know they are in the wrong place.

User interface design needs very careful consideration about how the user will actually use it.

...R

Thats the reason manuals are existing I think :smiley:
For normal use you don´t have to change any settings, it´s kind of an add on.

And you get response from the LED by flashing for showing of which sensor you have selected.
(this is the plan so far)

Floduino:
Thats the reason manuals are existing I think :smiley:

Ahh we've found him at last - the guy who reads manuals :slight_smile:

If it's only used occasionally that is even more reason to make it intuitive to use (without needing a manual)

...R