Push or hold for different outputs

hey, im trying to get the button when pushed shortly to make the button count 1 and a 2 if held.

#include "pitches.h"

int buzzer = 11;


int melody[] = {
  NOTE_G4, NOTE_C5, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_E4, NOTE_E4, 
  NOTE_A4, NOTE_G4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_C4, 
  NOTE_D4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, 
  NOTE_E5, NOTE_D5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_G4, 
  NOTE_C5, NOTE_B4, NOTE_A4, NOTE_B4, NOTE_E4, NOTE_E4, 
  NOTE_A4, NOTE_G4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_C4, 
  NOTE_C5, NOTE_B4, NOTE_A4, NOTE_G4, NOTE_B4, NOTE_C5, NOTE_D5, 
  NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_G4, NOTE_G4, NOTE_B4, NOTE_C5, NOTE_D5,
  NOTE_C5, NOTE_B4, NOTE_A4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_E4, NOTE_E4, NOTE_G4, NOTE_A4, NOTE_B4,
  NOTE_C5, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_C5, NOTE_F5,
  NOTE_F5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_C5,
  NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_A4,
  NOTE_C5, NOTE_B4, NOTE_A4, NOTE_G4, NOTE_C4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5
};

int noteDurations[] = {
  8, 4, 6, 16, 4, 8, 8, 
  4, 6, 16, 4, 8, 8, 
  4, 8, 8, 4, 8, 8, 4, 8, 8, 2,
  4, 6, 16, 4, 8, 8, 
  4, 6, 16, 4, 8, 8, 
  4, 6, 16, 4, 6, 16, 
  4, 6, 16, 8, 8, 8, 8, 
  2, 8, 8, 8, 8, 3, 8, 8, 8, 8, 8,
  2, 8, 8, 8, 8, 3, 8, 8, 8, 8, 8,
  4, 6, 16, 4, 6, 16, 4, 8, 8, 2,
  2, 8, 8, 8, 8, 3, 8, 2,
  2, 8, 8, 8, 8, 3, 8, 2,
  4, 6, 16, 4, 4, 2, 4, 4, 1
};

#include <dht_nonblocking.h>

#define DHT_SENSOR_TYPE DHT_TYPE_11
//#define DHT_SENSOR_TYPE DHT_TYPE_21
//#define DHT_SENSOR_TYPE DHT_TYPE_22

//Define the pin connection
int CLK = 2;//CLK->D2
int DT = 3;//DT->D3
int SW = 4;//SW->D4
const int interrupt0 = 0;// Interrupt 0 在 pin 2 上
int count = 0;//Define the count
int lastCLK = 0;//CLK initial value

static const int DHT_SENSOR_PIN = 8;
DHT_nonblocking dht_sensor( DHT_SENSOR_PIN, DHT_SENSOR_TYPE );
int btncount;
 int btn =7;                    // switch pin

int relayPin = 10;

unsigned long currentMillis;          // Variabele to store the number of milleseconds since the Arduino has started 

int buttonStatePrevious = HIGH;                      // previousstate of the switch
static const int buttonPin =7;                    // switch pin
unsigned long minButtonLongPressDuration = 3000;    // Time we wait before we see the press as a long press
unsigned long buttonLongPressMillis;                // Time in ms when we the button was pressed
bool buttonStateLongPress = false;                  // True if it is a long press

const int intervalButton = 50;                      // Time between two readings of the button state
unsigned long previousButtonMillis;                 // Timestamp of the latest reading

unsigned long buttonPressDuration;                  // Time the button is pressed in ms



void setup()
{
    Serial.begin(9600);
 
  pinMode(SW, INPUT);
  digitalWrite(SW, HIGH);
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
 attachInterrupt(interrupt0, ClockChanged, CHANGE);//Set the interrupt 0 handler, trigger level change
 pinMode(buzzer, OUTPUT);
  noTone(buzzer);
 pinMode(relayPin, OUTPUT);
 currentMillis = millis();    // store the current time
  readButtonState();      

}


//The interrupt handlers
void ClockChanged()
{  
  int clkValue = digitalRead(CLK);//Read the CLK pin level
  int dtValue = digitalRead(DT);//Read the DT pin level
  if (lastCLK != clkValue)
  {
    lastCLK = clkValue;
    count += (clkValue != dtValue ? 1 : -1);//CLK and inconsistent DT + 1, otherwise - 1

    Serial.print("count:");
    Serial.println(count);
      delay(200);
  } 
}


// Function for reading the button state
void readButtonState() {

  // If the difference in time between the previous reading is larger than intervalButton
  if(currentMillis - previousButtonMillis > intervalButton) {
    
    // Read the digital value of the button (LOW/HIGH)
    int buttonState = digitalRead(buttonPin);    

    // If the button has been pushed AND
    // If the button wasn't pressed before AND
    // IF there was not already a measurement running to determine how long the button has been pressed
    if (buttonState == LOW && buttonStatePrevious == HIGH && !buttonStateLongPress) {
      buttonLongPressMillis = currentMillis;
      buttonStatePrevious = LOW;
      Serial.println("Button pressed");
    }

    // Calculate how long the button has been pressed
    buttonPressDuration = currentMillis - buttonLongPressMillis;

    // If the button is pressed AND
    // If there is no measurement running to determine how long the button is pressed AND
    // If the time the button has been pressed is larger or equal to the time needed for a long press
    if (buttonState == LOW && !buttonStateLongPress && buttonPressDuration >= minButtonLongPressDuration) {
      buttonStateLongPress = true;
      Serial.println("Button long pressed");
    }
      
    // If the button is released AND
    // If the button was pressed before
    if (buttonState == HIGH && buttonStatePrevious == LOW) {
      
      buttonStatePrevious = HIGH;
      buttonStateLongPress = false;
      Serial.println("Button released");

      // If there is no measurement running to determine how long the button was pressed AND
      // If the time the button has been pressed is smaller than the minimal time needed for a long press
 
      if (buttonPressDuration < minButtonLongPressDuration) {
        Serial.println("Button pressed shortly");
      }
    }
    
    // store the current timestamp in previousButtonMillis
    previousButtonMillis = currentMillis;

  }
}


void loop()
{

    if (btncount <1 && currentMillis <= 2867 )
    {
       Serial.print("push 2 statr;       ");
       Serial.println("off");
        delay(1000);
        Serial.println("rotate to set temp (0 to 34)");
      delay(1000);
    btncount = 0; 
          delay(1000);
         Serial.println(count);
   if (buttonPressDuration < minButtonLongPressDuration) {
        Serial.println("Button pressed shortly");
      }

    }    
  
   if (digitalRead( btn ) == LOW) //check if button is pressed
     {
    ++ btncount; // if pressed add one to variable btncount
    Serial.println("on");
    delay(1000);

If you use the OneButton library there is already capability for detecting single press or long press and take action, that would make your code easier to write (and read/maintain)

is there a way to do it with no library, i wana further my understanding of code :~)

You can study the library :slight_smile:

The basics are relatively easy.
When you detect that a button became (!!) pressed, you 'start' a timer (that is, store the value of millis() in a variable).
Next you keep on checking millis() against the stored value plus a preset duration. If the button is released during that period, it's a short press, else it's held.

the author has a good explanation of how his state machine works. read Arduino OneButton Library

It seems far more complex than it is. Essentially it is the same millis timing that we all use for “blink without delay” type scenarios. I would tend to start a new sketch for buttons and get it working in simple functions and then combine it when working. Essentially you could write your own library but you don’t need to abstract into other files etc as it is simple enough to combine with your main sketch.

Think about the concepts:
Detecting when a button is pushed
Debouncing
Detecting when a button is released
Millis timing and comparators
State machines

These are all very basic concepts that when used together can achieve a lot of things. A library is just when you abstract your functions off into classes in another file so you don’t have it cluttering up your main code.

It is very good to understand it as then you can have long press, short press, double press, any press you like

The Arduino Toggle Library can combine any press action (long press, short press, double-click, tripple-click, etc, up to 225 unique combinations) by using the pressCode() function.

This debounce algorithm is inherent.

Try pressCode on Wokwi. (very responsive)

Here's the state machine:

uint8_t Toggle::pressCode(bool debug) {
  static uint8_t pCode = 0, code = 0;
  static uint32_t elapsedMs = 0;

  switch (_state) {
    case PB_DEFAULT:
      elapsedMs = getElapsedMs();
      if (pCode && isReleased() && (elapsedMs > (CLICK::DONE))) _state = PB_DONE;
      if (onChange()) clearTimer();
      if (onPress()) {
        _state = PB_ON_PRESS;
      }
      if (onRelease()) {
        if (debug) {
          Serial.print(F(" Pressed for:\t")); Serial.print(elapsedMs); Serial.println(" ms");
        }
        _state = PB_ON_RELEASE;
      }
      break;

    case PB_ON_PRESS:
      _state = PB_DEFAULT;
      break;

    case PB_ON_RELEASE:
      if (elapsedMs > CLICK::FAST && (!pCode || pCode >= 0x10)) _state = PB_LONG_CLICKS;
      else if (elapsedMs < CLICK::FAST && pCode >= 0x10) _state = PB_SHORT_CLICKS;
      else if (elapsedMs < CLICK::FAST && !pCode) _state = PB_FAST_CLICKS;
      else  _state = PB_SHORT_CLICKS;
      break;

    case PB_FAST_CLICKS:
      pCode |= 0xF0;
      if ((pCode & 0x0F) < 0x0F) pCode += 1;
      _state = PB_DEFAULT;
      break;

    case PB_SHORT_CLICKS:
      if ((pCode & 0x0F) < 0x0F) pCode += 1;
      _state = PB_DEFAULT;
      break;

    case PB_LONG_CLICKS:
      if ((pCode & 0xE0) < 0xE0) pCode += 0x10;
      _state = PB_DEFAULT;
      break;

    case PB_DONE:
      if (debug) {
        Serial.print(F(" Code:\t\t")); Serial.println(pCode, HEX); Serial.println();
      }
      code = pCode;
      pCode = 0;
      _state = PB_DEFAULT;
      if (code) return code;
      break;

    default:
      _state = PB_DEFAULT;
      break;
  }
  return 0;
}

Only 7-states are needed and only 2 timing values are checked ...

    enum CLICK : uint16_t {FAST = 200, DONE = 500};
    enum fsm_t : uint8_t {  // finite state machine
      PB_DEFAULT = 0,
      PB_ON_PRESS = 1,
      PB_ON_RELEASE = 2,
      PB_FAST_CLICKS = 3,
      PB_SHORT_CLICKS = 4,
      PB_LONG_CLICKS = 5,
      PB_DONE = 6
    };

right - I keep forgetting about your very capable library

if anyone reads this in the future and is new like me here is the easiest way to do it

 if ( digitalRead( btn ) == LOW )
  {
    const int button_threshold_ms = 2000;
      
    int button_press_time_ms = 0;
    int button_release_time_ms = 0;
    int button_hold_time_ms = 0;
    
    // Store millisecond count when button was pressed
    button_press_time_ms = millis();

    // Wait while button is pressed
    while ( digitalRead( btn ) == LOW ) {}

    // Store millisecond count when button was released
    button_release_time_ms = millis();
    
    // Calculate button hold time
    button_hold_time_ms = button_release_time_ms - button_press_time_ms;

    // Determine if button press was longer than threshold
    if ( button_hold_time_ms >= button_threshold_ms )
    {
      Serial.println("Long press detected: ");
      Serial.println(button_hold_time_ms);
      Serial.println("ms");

      // Execute long press code here or set flag

    }
    else
    {
      Serial.println("Short press detected: ");
      Serial.println(button_hold_time_ms);
      Serial.println("ms");

      // Execute short press code here or set flag


Great. Your entire sketch comes to a screeching halt if the button is held down. No thanks.

While{} is equal to delay but indefinite based on the condition. Better to continue to loop fast and just check each time to see if bouncing is over and press is stable.

Also you can define On as LOW and make it more readable