Class accepting another class instance does not have access to vars

I'm making a fancy timer for airsoft capture the hill games. The device has two buttons, a buzzer, and LEDs. It's function is to be placed on a playfield at a capturable control point. Players must run up to the device, and press a color coded button which "captures" the point for their team.

When the unit starts up, the user is able to configure game parameters such as the cumulative time it takes for a team to capture the hill.

The device has two buttons, and I needed a way to provide three input actions to allow for a complex menu. For example, the left button decrements the displayed menu option, the right button increments the displayed menu uption, and holding both buttons together selects.

I have written some code to handle this menu. I created a Button class which is instantiated twice, one for each button. The Button class instances are then passed to a second class, ButtonManager, which queries the two Button instances once per loop, detecting if both Buttons are held simultaneously.

The problem I'm experiencing is in the ButtonManager class. It seems like the way I am passing the two Button instances to ButtonManager is flawed (I am not well trained in C++), such that the Button class instances inside ButtonManager do not have access to the Button's private variables. Because of this, the Button class instances inside ButtonManger cannot accurately compute the button status. That code can see when the button is pressed, and released, but it isn't able to detect a "held" state (when the button is pressed for >= 1 second.) due to Button::_lastPressTime seemingly not being accessible.

For test purposes, I ignored ButtonManager for a moment and instead put the desired state detection code in the main loop. The state detection code was able to accurately compute when a button was held. This drives me to the assumption that the way I am passing the Button class instances to ButtonManger is faulty.

Here is the relevant project code. Unfortunately I'm over the 9000 character limit, so I will just post the main sketch, Button.h, ButtonManager.h, and ButtonManager.cpp. I posted the full code to a gist

// device.ino

#include "Button.h"
#include "ButtonManager.h"
#include "Phase.h"
#include "Sound.h"

// pin definitions
#define button0Pin 4
#define button1Pin 7
#define buzzerPin 9

Phase phase = Phase();
Button team0Button = Button(0, button0Pin, &phase);
Button team1Button = Button(1, button1Pin, &phase);
ButtonManager buttonManager = ButtonManager(team0Button, team1Button, &phase);
Sound sound = Sound(buzzerPin, &phase);


void setup() {
  // put your setup code here, to run once:
  pinMode(buzzerPin, OUTPUT);
}

void loop() {
  team0Button.update();
  team1Button.update();
  buttonManager.update();

  sound.update();
  phase.update();

  
  /**
   * Below is the code in question.
   * 
   * The code is OK in the main loop. State 2 is observable because the code within 
   * Button::getState() has access to Button::_lastPressTime
   * 
   * But the issue is that I need to run this code inside ButtonManager.cpp, not the main loop!
   * This same code inside ButtonManager.cpp runs but there it cannot access Button::_lastPressTime
   */

//  if (team0Button.getState() == 0) {
//    digitalWrite(9, HIGH);
//    delay(10);
//    digitalWrite(9, LOW);
//    delay(60);
//  }
//
//  else if (team0Button.getState() == 1) {
//    digitalWrite(9, HIGH);
//    delay(50);
//    digitalWrite(9, LOW);
//    delay(60);
//  }
//
//  else if (team0Button.getState() == 2) {
//    digitalWrite(9, HIGH);
//    delay(300);
//    digitalWrite(9, LOW);
//    delay(60);
//  }
  
}
// Button.h

#ifndef Button_h
#define Button_h

#if defined(ARDUINO) && ARDUINO >= 100
    #include "Arduino.h"
#else
    #include "WProgram.h"
#endif
#include "Phase.h"


class Button
{
  public:
    Button(bool teamNumber, uint8_t buttonPin, Phase* phase);
    void update();
    void processPress();
    void processRelease();
    void processHold();
    int getState();
  private:
    bool _wasHeld;
    bool _wasPressed;
    bool _teamNumber;
    uint8_t _buttonPin;
    uint32_t _lastPressTime;
    uint32_t _lastReleaseTime;
    Phase* _phase;
};


#endif
// ButtonManager.cpp

#if defined(ARDUINO) && ARDUINO >= 100
    #include "Arduino.h"
#else
    #include "WProgram.h"
#endif
#include "ButtonManager.h"
#include "Button.h"



ButtonManager::ButtonManager(Button team0Button, Button team1Button, Phase* phase)
: _team0Button(team0Button), _team1Button(team1Button)
{
  _team0Button = team0Button;
  _team1Button = team1Button;
}


void ButtonManager::update()
{


  /**
   * PROBLEM CODE
   * 
   * State 2 is never observed because the code in Button::getState() 
   * appears to not be able to access Button::_lastPressTime
   */
  if (_team0Button.getState() == 0) {
    digitalWrite(9, HIGH);
    delay(10);
    digitalWrite(9, LOW);
    delay(60);
  }

  else if (_team0Button.getState() == 1) {
    digitalWrite(9, HIGH);
    delay(50);
    digitalWrite(9, LOW);
    delay(60);
  }

  else if (_team0Button.getState() == 2) {
    digitalWrite(9, HIGH);
    delay(300);
    digitalWrite(9, LOW);
    delay(60);
  }

  
}
// ButtonManager.h

#ifndef ButtonManager_h
#define ButtonManager_h

#if defined(ARDUINO) && ARDUINO >= 100
    #include "Arduino.h"
#else
    #include "WProgram.h"
#endif
#include "Button.h"


class ButtonManager
{
  public:
    ButtonManager(Button team0Button, Button team1Button, Phase* phase);
    void update();
  private:
    Button _team0Button;
    Button _team1Button;
    Phase* _phase;
};

#endif

My questions are, why can't ButtonManager access Button::_lastPressTime? Is there a better way to pass the Button instances to ButtonManager?

...not your problem but here you are using the initializers and then setting the variable again, you merely need to pick one:

ButtonManager::ButtonManager(Button team0Button, Button team1Button, Phase* phase)
: _team0Button(team0Button), _team1Button(team1Button)
{
  _team0Button = team0Button;
  _team1Button = team1Button;
}

if you need your Button class to have access to ButtonManager functions, you either have to use inheritance of some type or declare the functions friends.

Where is the source for ButtonManager class?

I think you need to pass references (or pointers) in to ButtonManager. Then use pointer notation within the ButtonManager code. UNTESTED:

// ---------------- In ButtonManager.h ----------------------
class ButtonManager
{
  public:
    ButtonManager(Button &b0, Button &b1, Phase* p);
    void update();
  private:
    Button *_team0Button;
    Button *_team1Button;
    Phase* _phase;
};
#endif


// ---------------- In ButtonManager.cpp ----------------------
#include "ButtonManager.h"
#include "Button.h"

ButtonManager::ButtonManager(Button b0, Button b1, Phase* phase)
: _team0Button(&b0), _team1Button(&b1), _phase(p)
{}

void ButtonManager::update()
{
  if (_team0Button->getState() == 0) {
    digitalWrite(9, HIGH);
    delay(10);
    digitalWrite(9, LOW);
    delay(60);
  }
//
//
//
//
}

grimtech:
My questions are, why can't ButtonManager access Button::_lastPressTime?

Because it's private.

Thanks BulldogLowell, gfvalvo, and Jiggly-Ninja, your replies helped a lot.

BulldogLowell:
...not your problem but here you are using the initializers and then setting the variable again, you merely need to pick one:

Ah, so that's how that works! Today I learned.

BulldogLowell:
if you need your Button class to have access to ButtonManager functions, you either have to use inheritance of some type or declare the functions friends.

I couldn't think of any way I could work either of those patterns into this code, but friend functions are new to me and I will keep them in mind.

BulldogLowell:
Where is the source for ButtonManager class?

Whoops, here it is

  if (_phase->getCurrentPhase() == 2) {
    // handle both buttons pressed simultaneously
    if (_team0Button.getState() == 2 && _team1Button.getState() == 2) {
      _phase->advance();
    }
  }

That third line was what was giving me grief. Button::getState() never returned 2.

Jiggy-Ninja:
Because it's private.

LOL, I didn't even think of that. I just thought functions are public, variables are private. I did more reading on C++ Class Access Modifiers. I see what you're saying, private members cannot be accessed from outside classes. I have a better understanding of why getter functions are used. I was using a getter function Button::getState() to access the Button::_lastPressTime private member, but still getState() wasn't returning an accurate statae derived from Button::_lastPressTime.

gfvalvo:
I think you need to pass references (or pointers) in to ButtonManager. Then use pointer notation within the ButtonManager code.

I think that was it, thanks for that. I did some reading about Passing Parameters by References to get a better understanding. So I think what I was doing wrong was I was making a copy of Button, when I actually wanted to pass a reference.

class ButtonManager
{
  public:
    // GOOD. Passing a Button reference to ButtonManager, so ButtonManager can use the already initialized Button::getState()
    ButtonManager(Button& team0Button, Button& team1Button, Phase* phase);
    void update();
  private:
    Button& _team0Button;
    Button& _team1Button;
    Phase* _phase;
}
class ButtonManager
{
  public:
    // BAD. Passing a Button value to ButonManager. I think this makes a copy of the Button object, thereby making it's members not the same as the ones belonging to the Button instance initialized in the main sketch.
    ButtonManager(Button team0Button, Button team1Button, Phase* phase);
    void update();
  private:
    Button _team0Button;
    Button _team1Button;
    Phase* _phase;
};

Thanks again! It's performing as expected now with the Button instances passed as references.