button hell

So I need some help with a function that does not seem to be working as expected. I am trying to write something that will create a 4 digit pin number string. I want to use 4 buttons to do this: up, down, enter and back. The up and down buttons should increment or decement the digit and pressing enter moves the input position to the next digit. At digit 4 the function exits. Pressing back button erases the pin number and exits the function.
The circuit is 4 push buttons active high connected to PWM 's 2-5.
In the code the pin number is a global string modified by the getPin function with an initial value of "". I am using Bounce2 library to debounce the input. I create 4 bounce objects and set the intervals to 50 on each. The getPin function runs an infinite loop where it reads the buttons and checks the debounced value. It is supposed to exit the loop when the position in the string gets past 4 or when the used presses the back button by calling return. I put serial statements in the code to see what it is doing inside the function. Here is the output when I press the up button once:

Location: 0 Value: 1
Location: 0 Value: 2
Location: 0 Value: 3
Location: 0 Value: 4
Location: 0 Value: 5
Location: 0 Value: 6
Location: 0 Value: 7
Location: 0 Value: 8
Location: 0 Value: 9
Location: 0 Value: 9
Location: 0 Value: 9

Its executing the right code but doing it way more than I would have anticipated. Changing the delay period helps in that it does a few less cycles, but still its is executing more than the number of pushes on the button. If I go to a high enough delay interval it just doesn't respond at all. Suggestions??? Or code if you have had any success with this.

#include <Bounce2.h>

const int buttonDown = 1; //Value expected on pin when button down.
const int interv = 50; //Delay interval for debounce object.

const int pushButtonUp = 2; //Pins used.
const int pushButtonDn = 3;
const int pushButtonEnt = 4; //Enter
const int pushButtonBk = 5;  //Back

// Debounced pin objects from library.
Bounce BounceUp = Bounce();
Bounce BounceDn = Bounce();
Bounce BounceEnt = Bounce();
Bounce BounceBk  = Bounce();

//Variables
String pinNum; //Global

void setup()
{
  Serial.begin(9600);
  
  BounceUp.interval(interv);
  BounceDn.interval(interv);
  BounceEnt.interval(interv);
  BounceBk.interval(interv);
  
  BounceUp.attach(pushButtonUp);
  BounceDn.attach(pushButtonDn);
  BounceEnt.attach(pushButtonEnt);
  BounceBk.attach(pushButtonBk);
  
  pinNum = "";
  
  getPin();
  
}

void getPin()

//Function to create pin number...
// Value of pinNum is set within this function.

{
  

static int pinLoc = 0; //location within the string pinNum
static int locVal = 0; // numeric value at each digit of pin number.
  
 
  
/* Loop */
 while (true)
 {
    BounceUp.update();
    BounceDn.update();
    BounceEnt.update();
    BounceBk.update();
      
         
   if (BounceUp.read() == buttonDown)
      {
       //If Up button... 
       
       if (locVal < 9)
        {locVal ++; //increment digit
           }
       Serial.println("Location: "+String(pinLoc) + " Value: "+ String(locVal));
       delay(1000);  
     }
      
    
      
   if (BounceDn.read() == buttonDown)
      {
       //If Down button...
       if (locVal > 0)
        {locVal --;} //decrement digit
             Serial.println("Location: "+String(pinLoc) + " Value: "+ String(locVal));
        delay(1000);
        
      }   
      
         
   if (BounceEnt.read() == buttonDown)
      {
       //If Enter button...
       if (pinLoc < 4)
       {
          pinNum = pinNum + String(locVal); //add previuos digit.
          pinLoc ++; //increment location
          locVal = 0; //Set digit value.
        
          Serial.println("Pin Number: " + pinNum);
          delay(1000);
          
        }
       else
       {
            // Pin entery complete exit procedure
            return;
       }
      }
     
   
  
      
   if (BounceBk.read() == buttonDown)
      {
       //If Back button... 
        pinNum = "";
               
        return;
      }    
   
    
 }//while true
  
}//getPin()  



void loop()

{
   
  getPin();
  Serial.println("Pin Number: "+pinNum);
  delay(1000);
  
}

Where do you set the input pin modes to INPUT_PULLUP?

It looks to me as though you are reading the buttons and if you find one of them pressed you are acting upon that press. Whilst that sounds OK think what happens if the button remains pressed after the actions are complete. They will, of course, repeat. What you need to do is to act when a button becomes pressed rather than is currently pressed. There is a state change detection example in the IDE.

Incidentally, why do you have a while loop that will never exit in your code ? As far as I can see, once the getPin() function is called it will never end.

Bounce BounceUp = Bounce();

Rubbish. You NEVER call a constructor directly.

Bounce bounceUp;

is the correct way to create an instance of the class. Variable names should NOT start with capital letters.

// Instantiate a Bounce object
Bounce debouncer = Bounce();

Above is a line from the example program that comes with Bounce2. Apparently he doesn't know... Maybe you should tell him.

Apparently he doesn't know... Maybe you should tell him.

There is plenty of code on the internet that is wrong. This is one example.

So thanks for the comment on button state. That turned out to be a key insight. For anyone who might be struggling with the same problem. This works (so far):

if (bounceUp.update() && (bounceUp.read() == HIGH))
      {
       //If Up button...

I'd appreciate anyone who has more in depth knowledge about why this works to enlighten me further. Thanks.

Your above code will activate when the button is released. This is so you get a single UP, instead of UP UP UP UP.... while holding the button.

Check out the examples included with the bounce2 library.

"Rubbish. You NEVER call a constructor directly."
PaulS, read a book or two on C++, a constructor can be called directly, it uses the operator=() to initialize the class. It isn't the only way to do it, but it is a valid construction. I grant you it's unusual, and redundant the way it is used, since the default initalizer is already doing the same thing. It's more frequently done when the default initializer is not used, such as

CSomeClass AClass();

SomeFunction()
{
...
AClass = CSomeClass(var1, Var2);

As for your assertion that a variable NEVER should start with a capital letter, that is a matter of style. (and it's a class object, not a variable) There are as many people using capitals as not, especially on class instances, just because you have settled on a style you like does not mean everyone else is wrong.

If I felt like spending the time, I could show you several books that disagree on both points you make, all of them well respected in the field. Look around your own library, or possibly increase your library to open your mind to other possibilities.