Fixed #2 Persist counters

Added initialisation sketch and reset state
This commit is contained in:
Mark van Renswoude 2016-12-10 12:01:43 +01:00
parent e79cbc265a
commit 211740d51a
9 changed files with 317 additions and 5 deletions

View File

@ -2,6 +2,7 @@
#include "Globals.h"
#include "StateHandler.h"
#include "LowPower.h"
#include "EEPROM.h"
SegmentDisplay* display;
@ -15,6 +16,13 @@ uint32_t hits = 0;
unsigned long currentTime;
AbstractStateHandler* currentState;
byte shotCounterOffset;
byte hitCounterOffset;
#define counterTreshold (uint32_t)10000
uint32_t lastShotCounterIteration;
uint32_t lastHitCounterIteration;
void setCurrentState(AbstractStateHandler* value)
{
@ -36,7 +44,7 @@ void update()
// Count the shot no matter which state we're in
if (buttonArmed->changed() && !buttonArmed->pressed())
shots++;
addShot();
}
@ -72,4 +80,89 @@ void sleep()
buttonB->update(currentTime);
buttonB->block();
}
}
#define counterLocation(offset) 2 + (offset * sizeof(uint32_t))
void readCounters()
{
shotCounterOffset = EEPROM.read(0);
hitCounterOffset = EEPROM.read(1);
EEPROM.get(counterLocation(shotCounterOffset), shots);
EEPROM.get(counterLocation(hitCounterOffset), hits);
lastShotCounterIteration = shots / counterTreshold;
lastHitCounterIteration = hits / counterTreshold;
}
void setShots(uint32_t value)
{
shots = value;
uint32_t newShotCounterIteration = shots / counterTreshold;
if (newShotCounterIteration != lastShotCounterIteration || value == 0)
{
shotCounterOffset++;
if (counterLocation(shotCounterOffset) > EEPROM.length() - sizeof(uint32_t))
{
shotCounterOffset = 0;
if (shotCounterOffset == hitCounterOffset)
shotCounterOffset++;
}
EEPROM.write(0, shotCounterOffset);
lastShotCounterIteration = newShotCounterIteration;
}
EEPROM.write(counterLocation(shotCounterOffset), shots);
}
void addShot()
{
setShots(shots + 1);
}
void setHits(uint32_t value)
{
hits = value;
uint32_t newHitCounterIteration = hits / counterTreshold;
if (newHitCounterIteration != lastHitCounterIteration || value == 0)
{
hitCounterOffset++;
if (counterLocation(hitCounterOffset) > EEPROM.length() - sizeof(uint32_t))
{
hitCounterOffset = 0;
if (hitCounterOffset == shotCounterOffset)
hitCounterOffset++;
}
EEPROM.write(1, hitCounterOffset);
lastHitCounterIteration = newHitCounterIteration;
}
EEPROM.write(counterLocation(hitCounterOffset), hits);
}
void addHit()
{
setHits(hits + 1);
}
void resetShots()
{
setShots(0);
}
void resetHits()
{
setHits(0);
}

View File

@ -21,4 +21,10 @@ void setCurrentState(AbstractStateHandler* value);
void update();
void sleep();
void readCounters();
void addShot();
void addHit();
void resetShots();
void resetHits();
#endif

View File

@ -20,6 +20,7 @@ void setup()
display->setLatchPin(NerfLatchPin);
display->setDigits(NerfDigits);
display->begin();
display->clear();
// Configure buttons
@ -33,6 +34,7 @@ void setup()
buttonArmed->init(NerfButtonArmed, LOW);
readCounters();
setCurrentState(new DefaultState());
}

View File

@ -46,4 +46,11 @@
#define NerfDefaultSleepTime 10000
/**
* Reset state configuration
**/
#define NerfResetIntroTime 1000
#define NerfResetOutroTime 2000
#endif

View File

@ -3,6 +3,7 @@
#include "Globals.h"
#include "StateLoseTheGame.h"
#include "StateUserUnknown.h"
#include "StateReset.h"
bool DefaultState::ShowHits = true;
@ -24,10 +25,10 @@ void DefaultState::loop()
return;
}
if (buttonA->changed() && !buttonA->pressed())
// A short (triggered on release)
if (buttonA->changed() && !buttonA->pressed() && buttonA->pressedMillis(currentTime) <= NerfButtonLongPressThreshold)
{
// A (triggered on release)
hits++;
addHit();
if (!DefaultState::ShowHits)
setShowHits(true);
@ -35,6 +36,12 @@ void DefaultState::loop()
lastAction = currentTime;
}
// A long
if (buttonA->pressed() && buttonA->pressedMillis(currentTime) > NerfButtonLongPressThreshold)
{
setCurrentState(new ResetState());
}
// B short (triggered on release)
if (buttonB->changed() && !buttonB->pressed() && buttonB->pressedMillis(currentTime) <= NerfButtonLongPressThreshold)
{

View File

@ -10,7 +10,8 @@
* Start by showing the current counter mode (hits or shots),
* then display the value.
*
* A: Add hit
* A short: Add hit
* A long: Go to reset state
* B short: Toggle between hits and shots and restart state
* A + B: Display "Error user unknwn"
* B long: Display "You lost the game" animation (losethegame.com - you're welcome!)

114
StateReset.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "StateReset.h"
#include "Globals.h"
#include "NerfStatTrakConfig.h"
#include "StateDefault.h"
void ResetState::setup()
{
intro = true;
introStart = currentTime;
outro = false;
selected = 0;
}
void ResetState::loop()
{
if (intro)
{
if (currentTime - introStart >= NerfResetIntroTime)
intro = false;
char text[] = "Reset";
display->writeTextLeft(text);
return;
}
if (outro)
{
loopOutro();
return;
}
if (buttonA->changed() && !buttonA->pressed())
{
switch (selected)
{
case 0:
setCurrentState(new DefaultState());
return;
case 1:
resetHits();
break;
case 2:
resetShots();
break;
case 3:
resetHits();
resetShots();
break;
}
outroStart = currentTime;
outro = true;
return;
}
if (buttonB->changed() && !buttonB->pressed())
{
selected++;
if (selected > 3)
selected = 0;
}
switch (selected)
{
case 0:
{
char text[] = "Cancel";
display->writeTextLeft(text);
break;
}
case 1:
{
char text[] = "Hits";
display->writeTextLeft(text);
break;
}
case 2:
{
char text[] = "Shots";
display->writeTextLeft(text);
break;
}
case 3:
{
char text[] = "Both";
display->writeTextLeft(text);
break;
}
}
}
void ResetState::loopOutro()
{
if (currentTime - outroStart >= NerfResetOutroTime)
{
setCurrentState(new DefaultState());
return;
}
char text[] = "Done";
display->writeTextLeft(text);
}

31
StateReset.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef StateReset_h
#define StateReset_h
#include "StateHandler.h"
#include "Arduino.h"
/**
* Reset state
*
* Shows a menu providing reset options. A confirms the
* selection, B switches to the next.
**/
class ResetState : public AbstractStateHandler
{
private:
bool intro;
unsigned long introStart;
bool outro;
unsigned long outroStart;
byte selected;
protected:
void loopOutro();
public:
void setup();
void loop();
};
#endif

View File

@ -0,0 +1,51 @@
#include "EEPROM.h"
/*
How the EEPROM is used:
First byte: shot counter offset
Second byte: hit counter offset
Each counter is a 32-bit unsigned integer (4 bytes). When it reaches a treshold
which is < 100,000 (the recommended EEPROM cell write limit) a new offset is calculated.
This way we ensure no cell is written over 100,000 times (assuming the EEPROM is
new) and you can click away until you run out of EEPROM.
For initialisation set ShotCounterOffset and HitCounterOffset to
different values. Keep in mind that Offset * 4 must not be greater than
the amount of available EEPROM!
Shot/HitCounterInitial defines the default value to write for
each counters.
*/
#define ShotCounterOffset 0
#define HitCounterOffset 1
#define ShotCounterInitial 0
#define HitCounterInitial 0
#define counterLocation(offset) 2 + (offset * sizeof(uint32_t))
void setup()
{
EEPROM.update(0, ShotCounterOffset);
EEPROM.update(1, HitCounterOffset);
EEPROM.put(counterLocation(ShotCounterOffset), (uint32_t)ShotCounterInitial);
EEPROM.put(counterLocation(HitCounterOffset), (uint32_t)HitCounterInitial);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}