#include <RTClib.h>
#include <LiquidCrystal.h>
#include <Eventually.h>
#include <EEPROM.h>
EvtManager mgr; // From Eventually
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // From LiquidCrystal
TimeSpan StopwatchTime; // From RTCLib
#define BUTTONPIN A0
#define btnRIGHT 5
#define btnUP 4
#define btnDOWN 3
#define btnLEFT 2
#define btnSELECT 1
#define btnNONE (-1)
//Wokwi
#define SELECT_PIN 10
#define UP_PIN 12
#define DOWN_PIN 3
#define LEFT_PIN 2
#define RIGHT_PIN 11
#define BUTTONUP 0
#define BUTTONDOWN 1
#define NRLAPTIMES 10 // This allows us to easily change the number of laps we can handle (up to 99)
// EEPROM Layout
#define MODEAddress 0
#define MODE_HMS 0
#define MODE_Sec 1
TimeSpan LapTime[NRLAPTIMES];
int lapNr = 0; // Stores the index of the next EMPTY LapTime
int lapToView = -1; // Stores the currently displayed LapTime or -1 is none
int buttonState = BUTTONUP;
int click = btnNONE;
#define STOPPED 0
#define RUNNING 1
int stopwatchState = STOPPED;
void setup() {
// wokwi
pinMode(SELECT_PIN, INPUT_PULLUP);
pinMode(UP_PIN, INPUT_PULLUP);
pinMode(DOWN_PIN, INPUT_PULLUP);
pinMode(LEFT_PIN, INPUT_PULLUP);
pinMode(RIGHT_PIN, INPUT_PULLUP);
// Serial.begin(115200);
lcd.begin(16,2);
// Cells never used are always set to 0xFF by default
if(EEPROM.read(MODEAddress) == 0xFF)
EEPROM.update(MODEAddress, MODE_HMS); // Set to HMS by default
// mgr.addListener(new EvtTimeListener(1000, true, (EvtAction)tick));
mgr.addListener(new EvtTimeListener(50, true, (EvtAction)buttons));
display();
}
void display() {
char time[30];
if(EEPROM.read(MODEAddress) == MODE_HMS)
sprintf(time, "%02d:%02d:%02d",
StopwatchTime.hours(),
StopwatchTime.minutes(),
StopwatchTime.seconds());
else
sprintf(time, "%d", StopwatchTime.totalseconds());
lcd.clear();
lcd.print(time);
if(lapToView >= 0) // lapToView will be -1 if there are no laps stored
{
// Display lap number as lapToView + 1 because lap index starts at zero and us humans generally count from 1
if(EEPROM.read(MODEAddress) == MODE_HMS)
sprintf(time, "Lap %2d %02d:%02d:%02d", lapToView + 1,
LapTime[lapToView].hours(),
LapTime[lapToView].minutes(),
LapTime[lapToView].seconds());
else
sprintf(time, "Lap %2d %d", lapToView + 1,
LapTime[lapToView].totalseconds());
lcd.setCursor(0, 1);
lcd.print(time);
}
}
bool tick() {
if(stopwatchState == RUNNING)
{
StopwatchTime = StopwatchTime + TimeSpan(1);
display();
}
return false; // Allow the event chain to continue
}
bool buttons()
{
static int count = 0;
if(++count == 20)
{
count = 0;
tick();
}
int button = read_LCD_buttons();
switch(buttonState)
{
case BUTTONUP:
if(button != btnNONE)
{
click = button;
buttonState = BUTTONDOWN;
}
break;
case BUTTONDOWN:
if(button == btnNONE)
{
click = btnNONE;
buttonState = BUTTONUP;
}
break;
}
switch(click)
{
case btnRIGHT:
if(stopwatchState == STOPPED)
stopwatchState = RUNNING;
else
stopwatchState = STOPPED;
break;
case btnLEFT:
if(stopwatchState == STOPPED)
{
StopwatchTime = TimeSpan();
lapNr = 0; // Reset lapNr and lapToView
lapToView = -1;
}
else
{
if(lapNr < NRLAPTIMES)
{
LapTime[lapNr] = StopwatchTime;
lapToView = lapNr++;
}
}
display();
break;
case btnUP :
if(lapToView > 0)
{ // Greater than the first lap. Scroll up.
lapToView--;
display();
}
break;
case btnDOWN :
if(lapToView < (lapNr - 1))
{ // Less than the last lap. Scroll down.
lapToView++;
display();
}
break;
case btnSELECT :
if(EEPROM.read(MODEAddress) == MODE_HMS)
EEPROM.update(MODEAddress, MODE_Sec);
else
EEPROM.update(MODEAddress, MODE_HMS);
display();
break;
}
click = btnNONE;
return false;
}
int read_LCD_buttons(){
if(!buttonRead(RIGHT_PIN))
return btnRIGHT;
if(!buttonRead(UP_PIN))
return btnUP;
if(!buttonRead(DOWN_PIN))
return btnDOWN;
if(!buttonRead(LEFT_PIN))
return btnLEFT;
if(!buttonRead(SELECT_PIN))
return btnSELECT;
return btnNONE;
}
bool buttonRead(int fPin)
{
int before = digitalRead(fPin);
delay(5);
if(digitalRead((fPin) == before))
return before;
return HIGH;
}
/*
int read_LCD_buttons(){
int adc_key_in = 0;
adc_key_in = analogRead(BUTTONPIN);
delay(5);
int k = (analogRead(BUTTONPIN) - adc_key_in);
if (abs(k) > 5)
return btnNONE;
// Allow +- 50 tolerance
if (adc_key_in > 1000) return btnNONE;
if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 195) return btnUP;
if (adc_key_in < 380) return btnDOWN;
if (adc_key_in < 555) return btnLEFT;
if (adc_key_in < 790) return btnSELECT;
return btnNONE;
}
*/
USE_EVENTUALLY_LOOP(mgr) // Use this instead of your loop() function.