Moved away from proof of concepts, started prototyping for the final software

- Button library
- State handling and transitions
- Default state: dual counter display
- User unknown state: static for now
- Lost the game state: static for now
This commit is contained in:
Mark van Renswoude 2016-12-04 19:53:43 +01:00
parent b4e971cc4a
commit ba58eb93c0
16 changed files with 405 additions and 96 deletions

48
Button.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "Button.h"
void Button::init(uint8_t pin, int offState, bool usePullUp)
{
this->pin = pin;
this->offState = offState;
this->lastState = offState;
if (offState == HIGH && usePullUp)
pinMode(pin, INPUT_PULLUP);
else
pinMode(pin, INPUT);
}
void Button::update(unsigned long referenceTime)
{
unsigned long currentTime = getTime(referenceTime);
int currentState = digitalRead(pin);
if (lastChangedTime != 0)
{
if (currentState != lastState && (currentTime - lastChangedTime) > debounceMillis)
{
lastChanged = true;
if (currentState != offState)
lastPressedTime = currentTime;
else if (blocked)
{
blocked = false;
lastChanged = false;
}
lastChangedTime = currentTime;
lastState = currentState;
return;
}
}
else
{
lastChangedTime = currentTime;
lastState = currentState;
}
lastChanged = false;
}

37
Button.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef Button_h
#define Button_h
#include "Arduino.h"
class Button
{
public:
void init(uint8_t pin, int offState = HIGH, bool usePullUp = true);
void update(unsigned long referenceTime = 0);
void block() { if (lastState != offState) blocked = true; }
bool changed() { return !blocked && lastChanged; }
bool pressed() { return !blocked && lastState != offState; }
unsigned long pressedMillis(unsigned long referenceTime = 0) { return getTime(referenceTime) - lastPressedTime; }
unsigned long getDebounceMillis() { return debounceMillis; }
void setDebounceMillis(unsigned long value) { debounceMillis = value; }
protected:
inline unsigned long getTime(unsigned long referenceTime) { return referenceTime == 0 ? millis() : referenceTime; };
private:
uint8_t pin = 1;
int offState = HIGH;
unsigned long debounceMillis = 50;
unsigned long lastChangedTime = 0;
unsigned long lastPressedTime = 0;
int lastState;
bool lastChanged;
bool blocked = false;
};
#endif

23
Globals.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "Globals.h"
#include "StateHandler.h"
SegmentDisplay* display;
Button* buttonA;
Button* buttonB;
uint32_t shots = 0;
uint32_t hits = 0;
unsigned long currentTime;
AbstractStateHandler* currentState;
void setCurrentState(AbstractStateHandler* value)
{
buttonA->block();
buttonB->block();
currentState = value;
currentState->setup();
}

21
Globals.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef Globals_h
#define Globals_h
#include "SegmentDisplay.h"
#include "SegmentDisplayConfig.h"
#include "Button.h"
#include "StateHandler.h"
extern SegmentDisplay* display;
extern Button* buttonA;
extern Button* buttonB;
extern uint32_t shots;
extern uint32_t hits;
extern unsigned long currentTime;
extern AbstractStateHandler* currentState;
void setCurrentState(AbstractStateHandler* value);
#endif

View File

@ -1,110 +1,45 @@
#include "SegmentDisplay.h"
#include "NerfStatTrekConfig.h"
#include "Globals.h"
#define ASCIIUppercaseA 65
#define ASCIIUppercaseZ 90
SegmentDisplay* display;
char* text = new char[7];
#include "StateHandler.h"
#include "StateDefault.h"
void setup()
{
pinMode(2, INPUT_PULLUP);
pinMode(A3, INPUT);
// Configure display
display = new SegmentDisplay();
//display->setClockSpeed(4000000UL);
display->setClockPin(12);
display->setDataPin(11);
display->setLatchPin(10);
display->setDigits(6);
#ifdef SDUseSPI
display->setClockSpeed(NerfClockSpeed);
#else
display->setClockPin(NerfClockPin);
display->setDataPin(NerfDataPin);
#endif
display->setLatchPin(NerfLatchPin);
display->setDigits(NerfDigits);
display->begin();
text[0] = '\0';
text[1] = '\0';
text[2] = '\0';
text[3] = '\0';
text[4] = '\0';
text[5] = '\0';
text[6] = '\0';
// Configure buttons
buttonA = new Button();
buttonA->init(NerfButtonA);
buttonB = new Button();
buttonB->init(NerfButtonB);
setCurrentState(new DefaultState());
}
unsigned long currentTime;
unsigned long lastCounterTime = 0;
uint32_t counter = 0;
uint32_t delayValue;
bool showChars = true;
byte character = ASCIIUppercaseA - 1;
int buttonValue;
bool buttonDown = false;
unsigned long lastButtonChange = 0;
void loop()
{
currentTime = millis();
buttonValue = digitalRead(2);
if (buttonValue == LOW)
{
if (!buttonDown && (currentTime - lastButtonChange) > 50)
{
buttonDown = true;
showChars = !showChars;
lastButtonChange = currentTime;
}
}
else
{
if (buttonDown && (currentTime - lastButtonChange) > 50)
{
buttonDown = false;
lastButtonChange = currentTime;
}
}
buttonA->update(currentTime);
buttonB->update(currentTime);
if (showChars)
{
if (lastCounterTime == 0 || currentTime - lastCounterTime > 250UL)
{
character++;
if (character > ASCIIUppercaseZ)
character = ASCIIUppercaseA;
lastCounterTime = currentTime;
delayValue = ((uint32_t)analogRead(A3) * 100);
display->setDigitDelayMicroseconds(delayValue);
}
for (byte c = 0; c < 6; c++)
{
if (character + c > ASCIIUppercaseZ)
text[c] = ' ';
else
text[c] = character + c;
}
display->writeTextLeft(text);
}
else
{
if (currentTime - lastCounterTime > 100UL)
{
counter++;
if (counter > 999999)
counter = 0;
lastCounterTime = currentTime;
delayValue = ((uint32_t)analogRead(A3) * 100);
display->setDigitDelayMicroseconds(delayValue);
}
display->writeNumber(counter);
}
currentState->loop();
}

44
NerfStatTrekConfig.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef NerfStatTrekConfig_h
#define NerfStatTrekConfig_h
/**
* Display configuration
**/
#define NerfDigits 6
/**
* Display pin configuration
*
* Clock and data are used only when not using SPI,
* clock speed is only for SPI
**/
#define NerfLatchPin 10
#define NerfDataPin 11
#define NerfClockPin 12
#define NerfClockSpeed 1600000UL
/**
* Button configuration
**/
#define NerfButtonLongPressThreshold 1000
/**
* Button pin configuration
**/
#define NerfButtonA 3
#define NerfButtonB 2
#define NerfButtonArmed 4
/**
* Default state configuration
**/
#define NerfDefaultIntroTime 1000
#endif

View File

@ -86,11 +86,13 @@ void SegmentDisplay::writeTextLeft(char* value)
void SegmentDisplay::writeTextCenter(char* value)
{
// TODO
}
void SegmentDisplay::writeTextRight(char* value)
{
// TODO
}
@ -120,7 +122,7 @@ inline void SegmentDisplay::writeChar(char value, byte digitMask)
}
inline void SegmentDisplay::writeDisplay(byte segmentMask, byte digitMask)
void SegmentDisplay::writeDisplay(byte segmentMask, byte digitMask)
{
// If no segments should be lit, clear the digit mask as well to prevent
// power leaking through (it's probably an issue in my circuit or with the

View File

@ -58,7 +58,6 @@ class SegmentDisplay
protected:
void writeDigit(byte value, byte digitMask);
void writeChar(char value, byte digitMask);
public:
void writeDisplay(byte characterMask, byte digitMask);
private:

View File

@ -21,7 +21,7 @@
*
* If not defined, the order is reversed.
**/
//#define SDPushSegmentsFirst
#define SDPushSegmentsFirst
/**

82
StateDefault.cpp Normal file
View File

@ -0,0 +1,82 @@
#include "StateDefault.h"
#include "NerfStatTrekConfig.h"
#include "Globals.h"
#include "StateLoseTheGame.h"
#include "StateUserUnknown.h"
bool DefaultState::ShowHits = true;
void DefaultState::setup()
{
introStart = currentTime;
}
void DefaultState::loop()
{
if (buttonA->pressed() && buttonB->pressed())
{
// A + B
setCurrentState(new LoseTheGameState());
return;
}
if (buttonA->changed() && !buttonA->pressed())
{
// A (triggered on release)
hits++;
if (!DefaultState::ShowHits)
setShowHits(true);
}
// B short (triggered on release)
if (buttonB->changed() && !buttonB->pressed() && buttonB->pressedMillis(currentTime) <= NerfButtonLongPressThreshold)
{
setShowHits(!DefaultState::ShowHits);
}
// B long
if (buttonB->pressed() && buttonB->pressedMillis(currentTime) > NerfButtonLongPressThreshold)
{
setCurrentState(new UserUnknownState());
return;
}
if (showIntro)
{
if (currentTime - introStart >= NerfDefaultIntroTime)
showIntro = false;
if (DefaultState::ShowHits)
{
// Prevents warning: deprecated conversion from string constant to 'char*'
char text[] = "Hits";
display->writeTextLeft(text);
}
else
{
char text[] = "Shots";
display->writeTextLeft(text);
}
}
else
{
if (DefaultState::ShowHits)
display->writeNumber(hits);
else
display->writeNumber(shots);
}
}
void DefaultState::setShowHits(bool value)
{
showIntro = true;
introStart = currentTime;
DefaultState::ShowHits = value;
}

33
StateDefault.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef StateDefault_h
#define StateDefault_h
#include "StateHandler.h"
/**
* Default state
*
* Start by showing the current counter mode (hits or shots),
* then display the value.
*
* A: Add hit
* B short: Toggle between hits and shots and restart state
* B long: Display "Error user unknwn"
* A + B: Display "You lost the game" animation (losethegame.com - you're welcome!)
**/
class DefaultState : public AbstractStateHandler
{
private:
bool showIntro = true;
unsigned long introStart;
void setShowHits(bool value);
public:
static bool ShowHits;
void setup();
void loop();
};
#endif

11
StateHandler.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef StateHandler_h
#define StateHandler_h
class AbstractStateHandler
{
public:
virtual void setup() = 0;
virtual void loop() = 0;
};
#endif

19
StateLoseTheGame.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "StateLoseTheGame.h"
#include "Globals.h"
#include "StateDefault.h"
void LoseTheGameState::setup()
{
}
void LoseTheGameState::loop()
{
if (buttonA->pressed() || buttonB->pressed())
{
setCurrentState(new DefaultState());
}
char text[] = "Lost";
display->writeTextLeft(text);
}

17
StateLoseTheGame.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef StateLoseTheGame_h
#define StateLoseTheGame_h
#include "StateHandler.h"
/**
* Lose the game state
**/
class LoseTheGameState : public AbstractStateHandler
{
public:
void setup();
void loop();
};
#endif

19
StateUserUnknown.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "StateUserUnknown.h"
#include "Globals.h"
#include "StateDefault.h"
void UserUnknownState::setup()
{
}
void UserUnknownState::loop()
{
if (buttonA->pressed() || buttonB->pressed())
{
setCurrentState(new DefaultState());
}
char text[] = "Unknwn";
display->writeTextLeft(text);
}

19
StateUserUnknown.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef StateUserUnknown_h
#define StateUserUnknown_h
#include "StateHandler.h"
/**
* "User unknown" state
*
* Displays "Error user unknwn" until a button is pressed.
**/
class UserUnknownState : public AbstractStateHandler
{
public:
void setup();
void loop();
};
#endif