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:
parent
b4e971cc4a
commit
ba58eb93c0
48
Button.cpp
Normal file
48
Button.cpp
Normal 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
37
Button.h
Normal 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
23
Globals.cpp
Normal 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
21
Globals.h
Normal 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
|
121
NerfStatTrek.ino
121
NerfStatTrek.ino
@ -1,110 +1,45 @@
|
|||||||
#include "SegmentDisplay.h"
|
#include "NerfStatTrekConfig.h"
|
||||||
|
#include "Globals.h"
|
||||||
|
|
||||||
#define ASCIIUppercaseA 65
|
#include "StateHandler.h"
|
||||||
#define ASCIIUppercaseZ 90
|
#include "StateDefault.h"
|
||||||
|
|
||||||
SegmentDisplay* display;
|
|
||||||
|
|
||||||
char* text = new char[7];
|
|
||||||
|
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
pinMode(2, INPUT_PULLUP);
|
// Configure display
|
||||||
pinMode(A3, INPUT);
|
|
||||||
|
|
||||||
display = new SegmentDisplay();
|
display = new SegmentDisplay();
|
||||||
//display->setClockSpeed(4000000UL);
|
|
||||||
display->setClockPin(12);
|
#ifdef SDUseSPI
|
||||||
display->setDataPin(11);
|
display->setClockSpeed(NerfClockSpeed);
|
||||||
display->setLatchPin(10);
|
#else
|
||||||
display->setDigits(6);
|
display->setClockPin(NerfClockPin);
|
||||||
|
display->setDataPin(NerfDataPin);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
display->setLatchPin(NerfLatchPin);
|
||||||
|
display->setDigits(NerfDigits);
|
||||||
display->begin();
|
display->begin();
|
||||||
|
|
||||||
text[0] = '\0';
|
|
||||||
text[1] = '\0';
|
// Configure buttons
|
||||||
text[2] = '\0';
|
buttonA = new Button();
|
||||||
text[3] = '\0';
|
buttonA->init(NerfButtonA);
|
||||||
text[4] = '\0';
|
|
||||||
text[5] = '\0';
|
buttonB = new Button();
|
||||||
text[6] = '\0';
|
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()
|
void loop()
|
||||||
{
|
{
|
||||||
currentTime = millis();
|
currentTime = millis();
|
||||||
|
|
||||||
buttonValue = digitalRead(2);
|
buttonA->update(currentTime);
|
||||||
if (buttonValue == LOW)
|
buttonB->update(currentTime);
|
||||||
{
|
|
||||||
if (!buttonDown && (currentTime - lastButtonChange) > 50)
|
|
||||||
{
|
|
||||||
buttonDown = true;
|
|
||||||
showChars = !showChars;
|
|
||||||
lastButtonChange = currentTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (buttonDown && (currentTime - lastButtonChange) > 50)
|
|
||||||
{
|
|
||||||
buttonDown = false;
|
|
||||||
lastButtonChange = currentTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showChars)
|
currentState->loop();
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
44
NerfStatTrekConfig.h
Normal file
44
NerfStatTrekConfig.h
Normal 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
|
@ -86,11 +86,13 @@ void SegmentDisplay::writeTextLeft(char* value)
|
|||||||
|
|
||||||
void SegmentDisplay::writeTextCenter(char* value)
|
void SegmentDisplay::writeTextCenter(char* value)
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SegmentDisplay::writeTextRight(char* value)
|
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
|
// 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
|
// power leaking through (it's probably an issue in my circuit or with the
|
||||||
|
@ -58,7 +58,6 @@ class SegmentDisplay
|
|||||||
protected:
|
protected:
|
||||||
void writeDigit(byte value, byte digitMask);
|
void writeDigit(byte value, byte digitMask);
|
||||||
void writeChar(char value, byte digitMask);
|
void writeChar(char value, byte digitMask);
|
||||||
public:
|
|
||||||
void writeDisplay(byte characterMask, byte digitMask);
|
void writeDisplay(byte characterMask, byte digitMask);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*
|
*
|
||||||
* If not defined, the order is reversed.
|
* If not defined, the order is reversed.
|
||||||
**/
|
**/
|
||||||
//#define SDPushSegmentsFirst
|
#define SDPushSegmentsFirst
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
82
StateDefault.cpp
Normal file
82
StateDefault.cpp
Normal 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
33
StateDefault.h
Normal 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
11
StateHandler.h
Normal 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
19
StateLoseTheGame.cpp
Normal 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
17
StateLoseTheGame.h
Normal 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
19
StateUserUnknown.cpp
Normal 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
19
StateUserUnknown.h
Normal 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
|
Loading…
Reference in New Issue
Block a user