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
|
||||
#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
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)
|
||||
{
|
||||
// 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
|
||||
|
@ -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:
|
||||
|
@ -21,7 +21,7 @@
|
||||
*
|
||||
* 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