Skip to content

Instantly share code, notes, and snippets.

@insanity54
Created January 30, 2018 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save insanity54/b3efe0339bc4a155d4da7b65a55a2537 to your computer and use it in GitHub Desktop.
Save insanity54/b3efe0339bc4a155d4da7b65a55a2537 to your computer and use it in GitHub Desktop.
Class accepting another class instance does not have access to vars
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Button.h"
#include "Phase.h"
Button::Button(bool teamNumber, uint8_t buttonPin, Phase* phase)
{
pinMode(buttonPin, INPUT);
_teamNumber = teamNumber;
_buttonPin = buttonPin;
_phase = phase;
_wasPressed = 0;
_wasHeld = 0;
_lastPressTime = 0;
_lastReleaseTime = 0;
}
/**
* Button.update()
*
* If the button is pressed, update the controlling team in Score
*/
void Button::update() {
// if the button was pressed last tick, and it is also pressed this tick, do nothing.
if (digitalRead(_buttonPin) && _wasPressed) {
// if the button is pressed and has been for 1 second, mark as held
if (millis() - _lastPressTime > 1000 && !_wasHeld) {
processHold();
}
return;
}
// if the button was not pressed last tick, but is pressed this tick, process press.
else if (digitalRead(_buttonPin) && !_wasPressed) {
processPress();
}
// if the button is not pressed this tick, but was pressed last tick, process release.
else if (!digitalRead(_buttonPin) && _wasPressed) {
processRelease();
}
}
/**
* Do the action that the button should perform based on the current phase.
*/
void Button::processPress() {
// set the wasPressed boolean to TRUE which will be queried in later ticks to see if the button state was changed since last tick
_wasPressed = 1;
// set a start press time to later be used to determine if the button is being held (multi-button press&hold functionality)
_lastPressTime = millis();
/**
* Phase 0-- test phase. Button should advance to next phase when pressed
*/
if (_phase->getCurrentPhase() == 0) {
_phase->advance();
return;
}
/**
* Phase 1-- Hello phase. Button should do nothing when pressed
*/
else if (_phase->getCurrentPhase() == 1) {
return;
}
/**
* Phase 2-- Programming > Game mode.
* The phase where the type of game is chosen.
* Red button cycles through game modes.
* Green button selects game mode.
*/
else if (_phase->getCurrentPhase() == 2) {
// Red button cDycles through game modes
if (_teamNumber == 0) {
// @TODO
}
else if (_teamNumber == 1) {
// @TODO
}
}
/**
* Phase 3-- Programming > Domination > duration.
* This is the phase when the user chooses the total cumulative time a team needs to control the point to win.
* Green button increments the time by 1 minute (up to a maximum of 595 hours)
* Red button decrements the time by 1 minute (down to a minimum of 1 second)
*/
else if (_phase->getCurrentPhase() == 3) {
if (_teamNumber == 0) {
//_controller->incrementTimeToWin(60000);
}
else {
//_controller->decrementTimeToWin(60000);
}
return;
}
/**
* Phase 4-- Domination > Run
* This is the phase where the Domination game is in progress.
* Red button sets team 0 as controlling
* Green button sets team 1 as controlling
* Button should set _wasPressed to TRUE, set controlling team in _score, and timestamp now as the button's last press time.
*/
else if (_phase->getCurrentPhase() == 4) {
//_controller->triggerButtonPress(_teamNumber);
}
/**
* Phase 5-- Domination > Pause
* Domination game mode is paused.
* Red button should not respond to presses or releases.
* Green button should not respond to presses or releases.
* Red and green buttons simultaneously pressed & held for 5 seconds should resume game (switch to Phase 4)
*/
else if (_phase->getCurrentPhase() == 5) {
// @todo
}
/**
* Phase 6-- Domination > Win
* A team has won the game.
* Red button should not respond to presses or releases
* Green button should not respond to presses or releases
* Red and green buttons simultaneously pressed & held for 5 seconds should...
* * reset game scores
* * Go to phase 2
*/
else if (_phase->getCurrentPhase() == 6) {
// @todo
}
}
/**
* Do the release action that the button should perform based on the current phase.
*/
void Button::processRelease() {
_wasPressed = 0;
_wasHeld = 0;
_lastReleaseTime = millis();
}
/**
* processHold
*
* the action to do when the button is held
*
* This function is for single-button holds only.
* multi-button holds are handled in ButtonManager.
*/
void Button::processHold() {
_wasHeld = 1;
/**
* Phase 2-- Programming > Game mode.
* The phase where the type of game is chosen.
* Holding button 0
*/
if (_phase->getCurrentPhase() == 2) {
//
}
}
/**
* getState
*
* 0 released
* 1 pressed
* 2 held
*/
int Button::getState() {
// if the latest press was more recent than the latest release, the button is physically pressed
if (digitalRead(_buttonPin) == HIGH) {
// if the button has been pressed for less than 1000 ms, consider it pressed
if (millis() - _lastPressTime < 1000) {
return 1;
}
// if the button has been pressed for greater than or equal to 1000 ms, consider it held
else {
return 2;
}
}
// if the latest release was more recent than the latest press, the button is released.
else {
return 0;
}
}
// 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
#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);
}
}
#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
#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);
// }
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Phase.h"
/**
* PHASES
* * Phases are a combination of preparatory programming steps, and game modes.
* * Phases determine how the discreet classes (Button.cpp, Domination.cpp, LED.cpp)
* react to changes in game state.
*
* Phase 0-- Test phase
* A test sequence runs, lighting up every LED and testing the buzzer.
* Allows user to verify that all LED & sound hardware is functioning properly.
*
* Phase 1-- Hello phase.
* D3VICE network syncronization. D3VICE finds an existing game on the XBee network
* if one exists. If no game is found, D3VICE switches to standalone mode and enters programming phase.
*
* Phase 2-- Programming > Game mode
* D3VICE buttons act as input for choosing the game mode
*
* Phase 3-- Programming > Domination > duration
* D3VICE buttons act as input for choosing the total accumulated time a team needs to control the point to win
*
* Phase 4-- Domination > Run
* Phase 5-- Domination > Pause
* Phase 6-- Domination > Win
*
*/
Phase::Phase()
{
_phase = 0;
_isSwitchedLastTick = 1; // starts as 1 so other classes know phase 0 just started
_isPhaseAdvanceQueued = 0;
}
/**
* advance
*
* queues a phase advancement to happen during the next run of Phase::update()
*/
void Phase::advance()
{
_isPhaseAdvanceQueued = 1;
return;
}
uint8_t Phase::getCurrentPhase()
{
return _phase;
}
bool Phase::getWasSwitchedLastTick()
{
return _isSwitchedLastTick;
}
void Phase::update()
{
if (_isSwitchedLastTick == 1) {
_isSwitchedLastTick = 0;
}
if (_isPhaseAdvanceQueued == 1) {
_isPhaseAdvanceQueued = 0;
_advance();
}
}
/**
* Phase::_advance()
*
* Do the natural phase advancement.
* Do not call Phase::_advance() function from other classes. Instead call Phase::advance()
*
* It is possible that phase advancments are not in order (ex: from 1 to 2)
* It is possible that order may be more like (ex: 1 to 7)
* This is because more phases may be added later on which fall outside natural integer progression.
* In this function we can code any order we want, for cases in which advance() would be called from other classes.
* It may make more sense to add a separate function for explicitly switching to a specific phase. (ex: Phase::goToPhase(20);)
*
*/
void Phase::_advance()
{
_isSwitchedLastTick = 1;
if (_phase == 6) {
_phase = 0;
}
else {
_phase += 1;
}
return;
}
#ifndef Phase_h
#define Phase_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
/**
* For a good time https://www.youtube.com/user/thirdphaseofmoon/
*/
class Phase
{
public:
Phase();
void advance();
uint8_t getCurrentPhase();
bool getWasSwitchedLastTick();
void update();
private:
uint8_t _phase;
void _advance();
bool _isSwitchedLastTick;
bool _isPhaseAdvanceQueued;
};
#endif
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Sound.h"
#include "Phase.h"
Sound::Sound(uint8_t buzzerPin, Phase* phase)
{
_buzzerPin = buzzerPin;
_phase = phase;
}
void Sound::update()
{
// if the buzzer is buzzing
// if the buzzer needs to stop buzzing
// stop the buzzer
asyncBeep();
// if this is the first tick of a new phase
// start beeping
if (_phase->getWasSwitchedLastTick() == 1) {
asyncBeep(100);
}
}
void Sound::asyncBeep(uint32_t duration)
{
// if the function was supplied a duration, set a timer for that duration and start beeping.
_asyncBeepStartTime = millis();
_asyncBeepDuration = duration;
digitalWrite(_buzzerPin, HIGH);
return;
}
void Sound::asyncBeep()
{
// if the function was not supplied a duration, continue beeping if duration has not expired
// otherwise, stop beeping
if (millis() - _asyncBeepStartTime > _asyncBeepDuration) {
digitalWrite(_buzzerPin, LOW);
}
}
#ifndef Sound_h
#define Sound_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Phase.h"
class Sound
{
public:
Sound(uint8_t buzzerPin, Phase* phase);
void asyncMorse(char character);
void asyncBeep(uint32_t duration);
void asyncBeep();
void update();
private:
uint8_t _buzzerPin;
uint32_t _asyncBeepStartTime;
uint32_t _asyncMorseStartTime;
Phase* _phase;
bool _isAsyncMorseComplete;
bool _isAsyncBeepComplete;
uint32_t _morseDitDuration;
uint32_t _morseDahDuration;
uint32_t _asyncBeepDuration;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment