[SOLVED]Help debugging my button class

I’ve written a class for detecting the button states for the power windows, door lock and latch buttons for my car.

It is set up for 2 button types:

Button Type 0: Door Locks/Latches

returns 3 States, Inactive(0), Pressed(1), Released(4)

Button Type 1: Power Windows

returns 5 States, Inactive(0), Pressed(1), Quick Press(2), Long Press Hold(3), Long Press Release(4)

It is working great except for one bug(so far).
When in state 3(long press hold) and I wiggle my finger on the button(causing bouncing I assume) it will return other states (1,2) randomly. I have tried several things to fix it, but have been unsuccessful in resolving the issue.

Hoping that one of the coding guru’s here can point out my mistake.

/*
    Button State Detection for Automotive Power Windows, Door Locks/Latches

    Button Type 0: Door Lock/Latch
      Detects 3 States
        Inactive(0), Pressed(1), Released(4)

    Button Type 1: Power Windows 
      Detects 5 States
        Inactive(0), Pressed(1), Quick Press(2), Long Press Hold(3), Long Press Release(4)
        
    April 2016 by Les Hawkins
*/


#define DEBUG true                         // Set to true to enable Serial Debug
#define Serial if (DEBUG)Serial            // WARNING: Use only when Serial is used for Debugging only (THIS REMOVES Serial COMPLETLY!!!)


class PWButton
{
    byte buttonPin;

    byte buttonType;
    byte buttonRead;
    byte oldButtonRead;
    byte buttonPressed;
    byte buttonReleased;
    byte buttonState;

    unsigned long currentTime;
    unsigned long startTime;
    unsigned long debounceTime;
    unsigned long quickPressThreshold;

  public:
    PWButton(byte pin, byte type, unsigned long bounce, unsigned long qpThresh)
    {
      buttonPin = pin;
      pinMode (buttonPin, INPUT_PULLUP);

      buttonType = type;
      debounceTime = bounce;
      quickPressThreshold = qpThresh;
    }


    byte check()
    {
      currentTime = millis();
      buttonRead = digitalRead(buttonPin);

      if (buttonRead != oldButtonRead)
      {
        oldButtonRead = buttonRead;
        startTime = currentTime;
      }

      if (buttonPressed == 0 && buttonReleased == 0)
      {
        buttonState = 0;                                    // Button Inactive
      }

      if (buttonRead == oldButtonRead && currentTime - startTime >= debounceTime)
      {
        if (buttonRead == LOW)                              // Button has been Pressed
        {
          buttonPressed = 1;
          buttonState = 1;                                  
        }
        else                                                // Button has been Released
        {
          if (buttonPressed == 1)
          {
            buttonReleased = 1;
          }
        }
      }

      if (buttonType == 1)     // Power Window Buttons
      {
        if (buttonState == 1 && buttonReleased == 1 && currentTime - startTime <= quickPressThreshold)
        {
          buttonState = 2;                                  // Quick Press
          buttonPressed = 0;
          buttonReleased = 0;
        }

        if (buttonState == 1 && buttonReleased == 0 && currentTime - startTime > quickPressThreshold)
        {
          buttonState = 3;                                  // Long Press Hold
        }

        if (buttonState == 3 && buttonReleased == 1 && currentTime - startTime > quickPressThreshold)
        {
          buttonState = 4;                                  // Long Press Released
          buttonPressed = 0;
          buttonReleased = 0;
        }
      }

      
     if (buttonType == 0)              // Door Lock/Latch Buttons
      {
        if (buttonPressed == 1 && buttonReleased == 1)
        {
          buttonState = 4;
          buttonPressed = 0;
          buttonReleased = 0;
        }
      }

      return buttonState;  // 0= inactive, 1= pressed, 2= quick press, 3= long press held, 4= released
    }
};      // END of Class

        // BUTTON OBJECTS
PWButton pwbutton1 (9, 1, 40, 250);    // ( pin(pin#), button Type(0 or 1), debounce time(millis), quick press threshold(millis) )
PWButton lockbutton1 (8, 0, 40, 0);



byte readbutton1;
byte readbutton2;
byte oldreadbutton1;
byte oldreadbutton2;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  readbutton1 = pwbutton1.check();
  if (readbutton1 != oldreadbutton1)
  {
    oldreadbutton1 = readbutton1;
    Serial.println (readbutton1);
  }

  readbutton2 = lockbutton1.check();
  if (readbutton2 != oldreadbutton2)
  {
    oldreadbutton2 = readbutton2;
    Serial.println (readbutton2);
  }
  
  
  
}

Hutkikz: I've written a class for detecting the button states for the power windows, door lock and latch buttons for my car.

your constructor sets up the object like this:

PWButton(byte pin, byte type, unsigned long bounce, unsigned long qpThresh)

but here, you set qpThresh to a very small number:

PWButton lockbutton1 (8, 0, 40, 0);

shouldn't you be calculating the threshold based on debounce time?

If it were me, I would look at recording millis() on a button press, and measuring the time pressed once debounce interval is achieved.

I'm not sure but it just feels like you are keeping too many variables for a button press scheme.

why does the program care if it was just pressed or unpressed?

Also, you may want to look at creating enums for easier comprehension of your code:

enum ButtonType{
  DOOR_LOCK,
  POWER_WINDOW
};

and use constructor like this:

PWButton lockbutton1 (8, DOOR_LOCK, 40, 0);

Power windows and door locks are different paradigms, so you can create your check() member function like this:

byte check()
{
  if (buttonType == DOOR_LOCK)
  {
    //do this
  }
  else if (buttonType == POWER_WINDOW)
  {
    //do this instead
  }
  //etc...

I think you should only be returning three numbers for a lock and three numbers for a window: lock: a short press happened a long press happened nothing happened

window: holding down holding up (different input pin) nothing

lots to consider even before you get into the timing code...

finally, you are not guaranteed to set pinMode correctly in a constructor called outside of your setup function, your Arduino may not be done with what it was supposed to do to get ready for inputs and outputs.

you are urged to put that into a member function called within setup()... something like

PWButton::begin(int pin, blah...)
{
  buttonPin = pin;
  pinMode(buttonPin, INPUT_PULLUP);
  // blah blah..
}

your constructor sets up the object like this:

PWButton(byte pin, byte type, unsigned long bounce, unsigned long qpThresh)

but here, you set qpThresh to a very small number:

PWButton lockbutton1 (8, 0, 40, 0);

Button type 0(lock buttons) does not use qpThresh but if I don’t put something there the compiler complains.

Also, you may want to look at creating enums for easier comprehension of your code:

I really like that idea, however I’ve yet to learn how to use enum’s properly. Here’s my attempt that shows this error on line 37: use of enum ‘type’ without previous declaration.

/*
    Button State Detection for Automotive Power Windows, Door Locks/Latches

    Button Type 0: Door Lock/Latch
      Detects 3 States
        Inactive(0), Pressed(1), Released(4)

    Button Type 1: Power Windows 
      Detects 5 States
        Inactive(0), Pressed(1), Quick Press(2), Long Press Hold(3), Long Press Release(4)
        
    April 2016 by Les Hawkins
*/


#define DEBUG true                         // Set to true to enable Serial Debug
#define Serial if (DEBUG)Serial            // WARNING: Use only when Serial is used for Debugging only (THIS REMOVES Serial COMPLETLY!!!)


class PWButton
{
    byte buttonPin;
    
    enum buttonType{DOOR_LOCK, POWER_WINDOW};    
    byte buttonRead;
    byte oldButtonRead;
    byte buttonPressed;
    byte buttonReleased;
    byte buttonState;

    unsigned long currentTime;
    unsigned long startTime;
    unsigned long debounceTime;
    unsigned long quickPressThreshold;

  public:
    PWButton(byte pin, enum type, unsigned long bounce, unsigned long qpThresh)
    {
      buttonPin = pin;
      pinMode (buttonPin, INPUT_PULLUP);

      buttonType = type;
      debounceTime = bounce;
      quickPressThreshold = qpThresh;
    }


    byte check()
    {
      currentTime = millis();
      buttonRead = digitalRead(buttonPin);

      if (buttonRead != oldButtonRead)
      {
        oldButtonRead = buttonRead;
        startTime = currentTime;
      }

      if (buttonPressed == 0 && buttonReleased == 0)
      {
        buttonState = 0;                                    // Button Inactive
      }

      if (buttonRead == oldButtonRead && currentTime - startTime >= debounceTime)
      {
        if (buttonRead == LOW)                              // Button has been Pressed
        {
          buttonPressed = 1;
          buttonState = 1;                                  
        }
        else                                                // Button has been Released
        {
          if (buttonPressed == 1)
          {
            buttonReleased = 1;
          }
        }
      }

      if (buttonType == POWER_WINDOW)     // Power Window Buttons
      {
        if (buttonState == 1 && buttonReleased == 1 && currentTime - startTime <= quickPressThreshold)
        {
          buttonState = 2;                                  // Quick Press
          buttonPressed = 0;
          buttonReleased = 0;
        }

        if (buttonState == 1 && buttonReleased == 0 && currentTime - startTime > quickPressThreshold)
        {
          buttonState = 3;                                  // Long Press Hold
        }

        if (buttonState == 3 && buttonReleased == 1 && currentTime - startTime > quickPressThreshold)
        {
          buttonState = 4;                                  // Long Press Released
          buttonPressed = 0;
          buttonReleased = 0;
        }
      }

      
     if (buttonType == DOOR_LOCK)              // Door Lock/Latch Buttons
      {
        if (buttonPressed == 1 && buttonReleased == 1)
        {
          buttonState = 4;
          buttonPressed = 0;
          buttonReleased = 0;
        }
      }

      return buttonState;  // 0= inactive, 1= pressed, 2= quick press, 3= long press held, 4= released
    }
};      // END of Class

        // BUTTON OBJECTS
PWButton pwbutton1 (9, POWER_WINDOW, 40, 250);    // ( pin(pin#), button Type(0 or 1), debounce time(millis), quick press threshold(millis) )
PWButton lockbutton1 (8, DOOR_LOCK, 40, 0);



byte readbutton1;
byte readbutton2;
byte oldreadbutton1;
byte oldreadbutton2;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  readbutton1 = pwbutton1.check();
  if (readbutton1 != oldreadbutton1)
  {
    oldreadbutton1 = readbutton1;
    Serial.println (readbutton1);
  }

  readbutton2 = lockbutton1.check();
  if (readbutton2 != oldreadbutton2)
  {
    oldreadbutton2 = readbutton2;
    Serial.println (readbutton2);
  }
  
  
  
}

Power windows and door locks are different paradigms, so you can create your check() member function like this:

I did do that at the point where the logic diverges. This should be easier to spot in the updated code in this post.(if I can get the enum working).

I think you should only be returning three numbers for a lock and three numbers for a window:
lock:
a short press happened
a long press happened
nothing happened

window:
holding down
holding up (different input pin)
nothing

A door latch it needs to know when the button is pressed and when it is released. A door lock really only needs to know when a press happens but I didn’t think it necessary to have a 3rd type.

For the windows I wanted to know when the button became pressed so I could start the motor right away before a long or short press is determined. a short press will drive the window to it’s limit. a long press will drive it till the button is released. up and down are handled as separate buttons and are otherwise dealt with outside of the class

finally, you are not guaranteed to set pinMode correctly in a constructor called outside of your setup function, your Arduino may not be done with what it was supposed to do to get ready for inputs and outputs.

you are urged to put that into a member function called within setup()… something like

PWButton::begin(int pin, blah...)

{
  buttonPin = pin;
  pinMode(buttonPin, INPUT_PULLUP);
}

Noted, I will try to do this.

Hutkikz: Button type 0(lock buttons) does not use qpThresh but if I don't put something there the compiler complains.

you could overload the constructor, it would be prettier...

class PWButton
{
    byte buttonPin;
    
    enum buttonType{DOOR_LOCK, POWER_WINDOW};    
    byte buttonRead;
    byte oldButtonRead;
    byte buttonPressed;

try putting the enum outside the class (before it):

you could overload the constructor, it would be prettier...

Another thing to learn. I will look into it.

try putting the enum outside the class (before it):

Gives a different error: expected primary-expression before '==' token. if (buttonType == DOOR_LOCK)

you'll have to post your code but you need to declare the variable like this:

enum DeviceType{
  DOOR_LOCK, 
  POWER_WINDOW
};  

class PWButton
{
    byte buttonPin;
    DeviceType buttonType;   // enum is a 'datatype' now
    byte buttonRead;
    byte oldButtonRead;
    byte buttonPressed;
    ... blah blah blah

are you doing it like that?

Ok, I got the enum working.

/*
    Button State Detection for Automotive Power Windows, Door Locks/Latches

    Button Type 0: Door Lock/Latch
      Detects 3 States
        Inactive(0), Pressed(1), Released(4)

    Button Type 1: Power Windows 
      Detects 5 States
        Inactive(0), Pressed(1), Quick Press(2), Long Press Hold(3), Long Press Release(4)
        
    April 2016 by Les Hawkins
*/


#define DEBUG true                         // Set to true to enable Serial Debug
#define Serial if (DEBUG)Serial            // WARNING: Use only when Serial is used for Debugging only (THIS REMOVES Serial COMPLETLY!!!)

enum DeviceType{DOOR_LOCK, POWER_WINDOW};

class PWButton
{
    byte buttonPin;
    
    DeviceType buttonType;    
    byte buttonRead;
    byte oldButtonRead;
    byte buttonPressed;
    byte buttonReleased;
    byte buttonState;

    unsigned long currentTime;
    unsigned long startTime;
    unsigned long debounceTime;
    unsigned long quickPressThreshold;

  public:
    PWButton(byte pin, DeviceType type, unsigned long bounce, unsigned long qpThresh)
    {
      buttonPin = pin;
      pinMode (buttonPin, INPUT_PULLUP);

      buttonType = type;
      debounceTime = bounce;
      quickPressThreshold = qpThresh;
    }


    byte check()
    {
      currentTime = millis();
      buttonRead = digitalRead(buttonPin);

      if (buttonRead != oldButtonRead)
      {
        oldButtonRead = buttonRead;
        startTime = currentTime;
      }

      if (buttonPressed == 0 && buttonReleased == 0)
      {
        buttonState = 0;                                    // Button Inactive
      }

      if (buttonRead == oldButtonRead && currentTime - startTime >= debounceTime)
      {
        if (buttonRead == LOW)                              // Button has been Pressed
        {
          buttonPressed = 1;
          buttonState = 1;                                  
        }
        else                                                // Button has been Released
        {
          if (buttonPressed == 1)
          {
            buttonReleased = 1;
          }
        }
      }

      if (buttonType == POWER_WINDOW)     // Power Window Buttons
      {
        if (buttonState == 1 && buttonReleased == 1 && currentTime - startTime <= quickPressThreshold)
        {
          buttonState = 2;                                  // Quick Press
          buttonPressed = 0;
          buttonReleased = 0;
        }

        if (buttonState == 1 && buttonReleased == 0 && currentTime - startTime > quickPressThreshold)
        {
          buttonState = 3;                                  // Long Press Hold
        }

        if (buttonState == 3 && buttonReleased == 1 && currentTime - startTime > quickPressThreshold)
        {
          buttonState = 4;                                  // Long Press Released
          buttonPressed = 0;
          buttonReleased = 0;
        }
      }

      
     if (buttonType == DOOR_LOCK)              // Door Lock/Latch Buttons
      {
        if (buttonPressed == 1 && buttonReleased == 1)
        {
          buttonState = 4;
          buttonPressed = 0;
          buttonReleased = 0;
        }
      }

      return buttonState;  // 0= inactive, 1= pressed, 2= quick press, 3= long press held, 4= released
    }
};      // END of Class

        // BUTTON OBJECTS
PWButton pwbutton1 (9, POWER_WINDOW, 40, 250);    // ( pin(pin#), button Type(0 or 1), debounce time(millis), quick press threshold(millis) )
PWButton lockbutton1 (8, DOOR_LOCK, 40, 0);



byte readbutton1;
byte readbutton2;
byte oldreadbutton1;
byte oldreadbutton2;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  readbutton1 = pwbutton1.check();
  if (readbutton1 != oldreadbutton1)
  {
    oldreadbutton1 = readbutton1;
    Serial.println (readbutton1);
  }

  readbutton2 = lockbutton1.check();
  if (readbutton2 != oldreadbutton2)
  {
    oldreadbutton2 = readbutton2;
    Serial.println (readbutton2);
  }
  
  
  
}

need to get the pinMode thing going yet, and still haven’t addressed the primary issue .
But Many thanks for your help so far.

Hutkikz: and still haven't addressed the primary issue .

once your class is easy to read and understand, it will make resolving the return values for check() a lot easier.

Plus, it will be easily extensible for your other button types.

:)

Thats a good point, I will study classes further and try to work with the layout you provided to do just that.

Hutkikz: Thats a good point, I will study classes further and try to work with the layout you provided to do just that.

try to get the code out of the class definition... put just the function prototypes only in within the class definition. It makes it easier to read as well.

It will also make it easier to convert to a header and implementation file, when you are ready to do that.

fun stuff!!

fun stuff!!

For Sure!!

As you can see I am just now tackling classes. I took the template for my class from this tutorial because it was the simplest easiest to understand implementation I had seen, even though I knew it wasn't fully the correct way.

The layout you provided based on my code has really made it click on how classes should be structured( you should post it here for others). I'll be back when I get this further

Hutkikz: The layout you provided based on my code has really made it click on how classes should be structured( you should post it here for others).

Not to dismiss the valuable and welcome contributions of Bulldog, but that layout is really just the accepted norm for OO in C++. It's well documented in various ways, in many places on the internet, and there is a lot of detail to it. So it makes no sense to post it here.

It's well documented in various ways, in many places on the internet, and there is a lot of detail to it.

I won't argue that point, It likely clicked for me because it was applied to code I had written. just curious how you knew since it has only been posted in PM.

Still looking for an answer to the original issue.

aarg:
Not to dismiss the valuable and welcome contributions of Bulldog, but that layout is really just the accepted norm for OO in C++. It’s well documented in various ways, in many places on the internet, and there is a lot of detail to it. So it makes no sense to post it here.

I agree with aarg, but in the interest of sharing here is where we left off… no secrets!

enum DeviceType { 
  DOOR_LOCK,
  POWER_WINDOW
};

class PWButton
{
  private:  // why not make it perfectly clear using the private access specifier?
    byte _buttonPin;  // you can pick a way to identify member variables... so you can easily distinguish them from other variables in a member function
    byte _buttonType;
    byte _lastState;
    unsigned long _lastMillis;
    unsigned long _debounceTime;

  public:  
    PWButton() {};  // just a plain old constructor; notice that I am placing function prototypes here only, no code!
    PWButton(DeviceType device);  // overloaded constructor.  See the syntax below, which lets you do something cool
    void begin(byte pin, DeviceType type, unsigned long bounce);  // added the begin() method to make sure that the pinMode() happened at the right time
    void begin(byte pin, unsigned long bounce); // hey let's overload begin() too, so that you can begin different DeviceTypes uniquely
    byte checkButton();  // more discriptive name for the method is better
};

// moving the functions out of the class when you are developing will make it a lot easier to make header(PWButton.h) and implementation (PWButton.cpp) files later.

PWButton::PWButton(DeviceType device)  // use the Scope Qualifier :: to create member functions, but don't forget the return type on non-constructor types
{
  _buttonType = device;
}

void PWButton::begin(byte pin, DeviceType type, unsigned long bounce)  // sort-of universally accepted name, begin() for this type of work
{
  _buttonPin = pin;
  pinMode (_buttonPin, INPUT_PULLUP);
  _buttonType = type;
  _debounceTime = bounce;
}

void PWButton::begin(byte pin, unsigned long bounce)  // example of overloaded member function (they are actually separate functions, you see)
{
  _buttonPin = pin;
  pinMode (_buttonPin, INPUT_PULLUP);
  _debounceTime = bounce;
}

byte PWButton::checkButton()  // work still to do here
{
  unsigned long currentTime = millis();
  byte currentState = digitalRead(_buttonPin);
  if (currentState != _lastState && millis() - _lastMillis > _debounceTime)
  {
    _lastState = currentState; //record current state
    if (_buttonType == DOOR_LOCK)
    {
      if (currentState == HIGH)
      {
        return  (millis() - _lastMillis > 500UL) ? 2 : 1; // 2 for a longish press, 1 for a shortish press
      }
    }
    else if (_buttonType = POWER_WINDOW)
    {
      if(currentState == LOW)  // depressed
      {
        return 1;
      }
      else if (currentState == HIGH)
      {
        return 2;
      }
    }
    _lastMillis = currentTime;
  }
  return 0;
}

// <<<<<<<<<<<<<<<<<<<<<<<< Main Sketch >>>>>>>>>>>>>>>>>>>>>>//

// BUTTON OBJECTS
PWButton pwbutton1;    // ( pin(pin#), button Type(0 or 1), debounce time(millis), quick press threshold(millis) )
PWButton lockbutton1 = POWER_WINDOW;  // see here, this is pretty cool way to set a single arg in your constructor!

void setup()
{
  Serial.begin(9600);
  pwbutton1.begin(9, 40);  // (pin, debounce interval)  device type was already set in constructor
  lockbutton1.begin(8, DOOR_LOCK, 40);  // (pin, DeviceType, debounce interval)  // 
}

void loop()
{
  switch (pwbutton1.checkButton())
  {
    case 0:
      //  Nothing to do here
      break;
    case 1:
      Serial.println(F("Short Press Happened"));
      break;
    case 2:
      Serial.println(F("Long Press Happened"));
      break;
  }
  switch (lockbutton1.checkButton())
  {
    case 0:
      //  Nothing to do here
      break;
    case 1:
      Serial.println(F("Button Depressed"));
      break;
    case 2:
      Serial.println(F("Button Released"));
      break;
  }
}

Still haven’t answered your original questions!!! Ha!

PS I saw the Adafruit example and while nearly everything they do is great, personal stylistic choices make me want to do it differently.

So… can you explain the events you want to take action with?

by device type

for example;

POWER_WINDOW

I want to do something when a long press is recorded
I want to do something when a short press is recorded
I want to do something when the button is held down
yada yada

there should be an action (e.g. short press) and a reaction (e.g moveServo() )

Sure,

POWER WINDOW

ACTION RETURNED STATE REACTION

press button buttonState(1) start motor when a debounced press detected(separate function outside of class)

quick press buttonState(2) stop motor only after reaching limit(again separate function)

long press hold buttonState(3) motor continues but stopped by either reaching limit or button released(yes separate function)

long press release buttonState(4) motor stops(also separate)

no action buttonState(0) no reaction(Idle state)

the door lock/latches code works fine so no need to worry about that.

The logic I am currently using is that if after the debounce period the button is released before the quick press threshold time it is a quick press and if it is still held after qp threshold has passed it is a long press.

Hutkikz:
The logic I am currently using is that if after the debounce period the button is released before the quick press threshold time it is a quick press and if released after it is a long press.

I cannot see the need for a return of 3. if the button is held down, motor starts. if it is let up, it depends if it was a short press or a long press what you want to do:

Compiles/un-tested (for POWER_WINDOW testing only)

enum DeviceType { 
  DOOR_LOCK,
  POWER_WINDOW
};

class PWButton
{
  private:  // why not make it perfectly clear using the private access specifier?
    byte _buttonPin;  // you can pick a way to identify member variables... so you can easily distinguish them from other variables in a member function
    byte _buttonType;
    byte _lastState;
    unsigned long _lastMillis;
    unsigned long _debounceTime;

  public:  
    PWButton() {};  // just a plain old constructor; notice that I am placing function prototypes here only, no code!
    PWButton(DeviceType device);  // overloaded constructor.  See the syntax below, which lets you do something cool
    void begin(byte pin, DeviceType type, unsigned long bounce);  // added the begin() method to make sure that the pinMode() happened at the right time
    void begin(byte pin, unsigned long bounce); // hey let's overload begin() too, so that you can begin different DeviceTypes uniquely
    byte checkButton();  // more discriptive name for the method is better
};

// moving the functions out of the class when you are developing will make it a lot easier to make header(PWButton.h) and implementation (PWButton.cpp) files later.

PWButton::PWButton(DeviceType device)  // use the Scope Qualifier :: to create member functions, but don't forget the return type on non-constructor types
{
  _buttonType = device;
}

void PWButton::begin(byte pin, DeviceType type, unsigned long bounce)  // sort-of universally accepted name, begin() for this type of work
{
  _buttonPin = pin;
  pinMode (_buttonPin, INPUT_PULLUP);
  _buttonType = type;
  _debounceTime = bounce;
}

void PWButton::begin(byte pin, unsigned long bounce)  // example of overloaded member function (they are actually separate functions, you see)
{
  _buttonPin = pin;
  pinMode (_buttonPin, INPUT_PULLUP);
  _debounceTime = bounce;
}

byte PWButton::checkButton()  // work still to do here
{
  unsigned long currentTime = millis();
  byte buttonState = digitalRead(_buttonPin);
  if (buttonState != _lastState && millis() - _lastMillis > _debounceTime)
  {
    _lastState = buttonState; //record current state
    if (_buttonType == POWER_WINDOW)
    {
      if(buttonState == LOW)
      {
        _lastMillis = currentTime;
        return 1;
      }
      if (buttonState == HIGH)
      {
        _lastMillis = currentTime;
        return  (millis() - _lastMillis > 500UL) ? 4 : 2; // 2 for a longish press, 1 for a shortish press
      }
    }
//    else if (_buttonType = DOOR_LOCK)
//    {
//      if(buttonState == LOW)  // depressed
//      {
//        return 1;
//      }
//      else if (buttonState == HIGH)
//      {
//        return 2;
//      }
//    }
  }
  return 0;
}

// <<<<<<<<<<<<<<<<<<<<<<<< Main Sketch >>>>>>>>>>>>>>>>>>>>>>//

// BUTTON OBJECTS
PWButton powerWindow = POWER_WINDOW;    // ( pin(pin#), button Type(0 or 1), debounce time(millis), quick press threshold(millis) )

void setup()
{
  Serial.begin(9600);
  powerWindow.begin(9, 40);  // (pin, debounce interval)  device type was already set in constructor
}

void loop()
{
  switch (powerWindow.checkButton())
  {
    case 0:
      //  Nothing to do here
      break;
    case 1:
      Serial.println(F("Button Pressed, Motor Start"));
      break;
    case 2:
      Serial.println(F("Quick Press, motor continues"));
      break;
    case 3:
      Serial.println(F("Long Press, motor continues"));  // I cannot see a need and can't figure out how???
      break;
    case 4:
      Serial.println(F("Long Press Release, motor Stops"));
  }
}

EDITED

You’re Right!!! eliminating that state got rid of the problem and it functions correctly every time.

A Big Thanks, and a well earned karma point for ya!

EDIT: I will rewrite it in my style using the suggestions you made earlier and post it when finished.