#include <U8x8lib.h>
#include <avr/sleep.h> // Sleep Modes
#include <avr/wdt.h>
#include <EEPROM.h> //We will store 11 games plus the current game
#define WAITTIME 80
#define TIMEOUT 2500
#define GAMESIZE 42
U8X8_SSD1306_128X32_UNIVISION_SW_I2C u8x8(/* clock=*/ 2, /* data=*/ 0, /* reset=*/ 5);
struct FullGame //42 bytes per game
{
byte date[3] = {1, 1, 24}; //month, day, year
byte hole = 1;
byte total = 0;
byte mull = 0;
byte par[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //par for each hole
byte score[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //score for each hole
};
const uint8_t custFont[930] U8X8_FONT_SECTION("custom") =
" ~\1\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\0\0\0\0\0\0\0"
"\33\0\0\0\0\0\0<\0<\0\0\0\0\0\0\0\0\0\0\0\0@\370@\370@\0\0\0\2\37"
"\2\37\2\0\0`\220\20\370\20`\0\0\14\20\21\77\21\16\0\0\60HH\360 \30\0\0\30\4\17"
"\22\22\14\0\0\0p\210\210p\0\0\0\16\21\21\12\14\23\0\0\0\0\0<\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\300\60\10\4\0\0\0\0\7\30 @\0\0\0\4\10\60\300\0\0\0\0@ "
"\30\7\0\0\0\0@\200\340\200@\0\0\0\4\2\17\2\4\0\0\0\0\0\340\0\0\0\0\0\1\1"
"\17\1\1\0\0\0\0\0\0\0\0\0\0\0\0H\70\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1"
"\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\20\70\20\0\0\0\0\0\0\300\60\14\0\0`\30\6"
"\1\0\0\0\0\340\20\10\10\20\340\0\0\7\10\20\20\10\7\0\0\0 \20\370\0\0\0\0\0\20\20"
"\37\20\20\0\0\60\10\10\10\210p\0\0\20\30\24\22\21\20\0\0\10\10\210\310\250\30\0\0\14\20\20"
"\20\20\17\0\0\0\200`\20\370\0\0\0\6\5\4\4\37\4\0\0\370HHHH\210\0\0\14\20\20"
"\20\20\17\0\0\340\20\210\210\210\0\0\0\17\21\20\20\20\17\0\0\10\10\10\210h\30\0\0\0\30\6"
"\1\0\0\0\0\60H\210\210H\60\0\0\16\21\20\20\21\16\0\0\360\10\10\10\210\360\0\0\14\21\21"
"\21\10\7\0\0\0\0@\340@\0\0\0\0\0\10\34\10\0\0\0\0\0\300\300\0\0\0\0\0\0$"
"\34\0\0\0\0\0\0\200@ \20\0\0\0\1\2\4\10\20\0\0@@@@@@\0\0\2\2\2"
"\2\2\2\0\0\0\20 @\200\0\0\0\0\20\10\4\2\1\0\0\60\10\10\210H\60\0\0\0\0\0"
"\33\0\0\0\0\340\20\310((\360\0\0\7\10\23\24\24\27\0\0\340\20\10\10\20\340\0\0\37\1\1"
"\1\1\37\0\0\370\210\210\210P \0\0\37\20\20\20\11\6\0\0\360\10\10\10\10\60\0\0\17\20\20"
"\20\20\14\0\0\370\10\10\10\20\340\0\0\37\20\20\20\10\7\0\0\370\210\210\210\10\10\0\0\37\20\20"
"\20\20\20\0\0\370\210\210\210\10\10\0\0\37\0\0\0\0\0\0\0\360\10\10\10\10\60\0\0\17\20\20"
"\21\11\37\0\0\370\200\200\200\200\370\0\0\37\0\0\0\0\37\0\0\0\10\10\370\10\10\0\0\0\20\20"
"\37\20\20\0\0\0\0\0\10\370\10\0\0\14\20\20\20\17\0\0\0\370\200@ \20\10\0\0\37\0\1"
"\2\4\30\0\0\370\0\0\0\0\0\0\0\37\20\20\20\20\20\0\0\370\60\300\300\60\370\0\0\37\0\0"
"\0\0\37\0\0\370`\200\0\0\370\0\0\37\0\0\1\6\37\0\0\360\10\10\10\10\360\0\0\17\20\20"
"\20\20\17\0\0\370\10\10\10\10\360\0\0\37\1\1\1\1\0\0\0\360\10\10\10\10\360\0\0\17\22\22"
"\24\70O\0\0\370\10\10\10\10\360\0\0\37\1\1\3\5\30\0\0p\210\210\10\10\60\0\0\14\20\20"
"\21\21\16\0\0\10\10\10\370\10\10\10\0\0\0\0\37\0\0\0\0\370\0\0\0\0\370\0\0\17\20\20"
"\20\20\17\0\0x\200\0\0\200x\0\0\0\3\34\34\3\0\0\0\0\370\0\0\0\370\0\0\0\17\20"
"\16\20\17\0\0\30`\200\200`\30\0\0\30\6\1\1\6\30\0\0\0\70\300\0\300\70\0\0\0\0\0"
"\37\0\0\0\0\10\10\10\310(\30\0\0";
FullGame currentGame;
byte OldGame = 255;
byte count = 0;
bool buttonFree[4] = {true, true, true, true};
unsigned int addCounter = 0;
unsigned int plusCounter = 0;
byte editCounter = 0;
byte edit = 0;
byte page = 0;
byte statCounter = 18;
byte statPage = 1;
byte holeCounter = 0;
byte gameVal = 0;
byte totalGames = 0;
unsigned int powerState = 0;
byte editFrom;
void setup(void)
{
u8x8.begin();
pinMode(1, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
u8x8.setFont(custFont);
refresh(count);
EEPROM.get(0, totalGames);
if (totalGames > 11)
{
totalGames = 0;
EEPROM.put(0, totalGames);
}
EEPROM.get(1, gameVal);
if (gameVal > 11)
{
gameVal = 0;
EEPROM.put(1, gameVal);
}
}
void resetValues() {
edit = 0;
statCounter = 18;
statPage = 1;
holeCounter = 0;
page = 0;
OldGame = 255;
}
void reset()
{
resetValues();
currentGame.total = 0;
currentGame.mull = 0;
count = 0;
currentGame.hole = 1;
refresh(count);
for (byte i = 0; i < 18; i++)
currentGame.score[i] = 0;
}
void loop()
{
byte button = digitalRead(1);
if (button == LOW) //Plus Button
{
buttonFree[0] = false;
if (plusCounter++ == WAITTIME && edit == 0)
changeVal(-1);
else if (plusCounter == 3 * WAITTIME)
{
edit = 10;
u8x8.clearDisplay();
u8x8.drawString(0, 0, "END GAME?");
printOption();
}
}
else if (button == HIGH && !buttonFree[0])
{
buttonFree[0] = true;
if (powerState > TIMEOUT && edit != 0)
goto plusOut;
if (plusCounter < WAITTIME)
changeVal(1);
plusOut:
powerState = 0;
plusCounter = 0;
}
button = digitalRead(4);
if (button == LOW) { //Add Button
buttonFree[1] = false;
if (++addCounter == 3 * WAITTIME && totalGames > 0)
{
if (OldGame != 255)
{
reloadGame();
}
else
{
OldGame = count;
loadLastGame();
}
}
}
else if (button == HIGH && !buttonFree[1])
{
buttonFree[1] = true;
if (powerState > TIMEOUT)
{
goto addOut;
}
if (addCounter < (3 * WAITTIME))
{
if (edit > 0)
changeVal(-1);
else if (count != 0)
{
addScore();
}
}
addOut:
addCounter = 0;
powerState = 0;
}
button = digitalRead(3);
if (button == LOW) //Edit Button
{
buttonFree[2] = false;
if (++editCounter == WAITTIME || (editCounter == 1 && edit > 0 && edit <= 4))
{
if (editCounter == WAITTIME)
editFrom = edit;
if (edit % 2 == 0 && edit > 0 && edit <= 4)
{
edit = editFrom;
editCounter = WAITTIME+1;
goto refresh;
}
if (edit == 0 || edit == 11)
{
edit = page;
}
if (++edit > 4)
edit = 0;
refresh:
refreshPage();
}
}
else if (button == HIGH && !buttonFree[2])
{
buttonFree[2] = true;
if (powerState > TIMEOUT)
{
goto editOut;
}
switch (edit) {
case 5:
case 6:
edit++;
break;
case 7:
statCounter += 1;
endGame();
break;
case 8:
endGame();
break;
case 0:
case 11:
if (editCounter < WAITTIME)
nextPage();
break;
}
editOut:
editCounter = 0;
powerState = 0;
}
if (powerState++ > TIMEOUT)
sleep();
delay(10);
}
void endGame()
{
edit = 11;
endGameStats();
}
void refreshPage() {
if (page == 0)
{
printHole();
printPar();
}
else
{
printTotal();
printMull();
}
}
void refresh(byte value)
{
refreshPage();
printLarge(12, value);
}
void increment(byte *value, byte upperlim, byte lowerlim, int amount)
{
byte lowRange = lowerlim -1;
if (lowerlim == 0) lowRange = 255;
*value += amount;
if (*value == lowRange)
{
*value = upperlim;
}
else if (*value > upperlim)
{
*value = lowerlim;
}
}
void changeVal(int amount)
{
switch (edit)
{
case 0:
count += amount;
if (count > 18)
count = 0;
else if (count == 255)
count = 0;
printLarge(12, count);
break;
case 1:
increment(¤tGame.hole, 18, 1, amount);
// currentGame.hole += amount;
// if (currentGame.hole > 18)
// currentGame.hole = 1;
// else if (currentGame.hole < 1)
// currentGame.hole = 18;
printHole();
count = currentGame.score[currentGame.hole-1];
printLarge(12, count);
break;
case 2:
increment(¤tGame.par[currentGame.hole-1], 7, 0, amount);
// currentGame.par[currentGame.hole-1] += amount;
// if (currentGame.par[currentGame.hole-1] == 255)
// currentGame.par[currentGame.hole-1] = 0;
// else if (currentGame.par[currentGame.hole-1] > 7)
// currentGame.par[currentGame.hole-1] = 7;
printPar();
break;
case 3:
currentGame.total += amount;
if (currentGame.total == 255)
currentGame.total = 0;
printTotal();
break;
case 4:
currentGame.mull += amount;
if (currentGame.mull > 99)
currentGame.mull = 0;
else if (currentGame.mull == 255)
currentGame.mull = 0;
printMull();
break;
case 5:
increment(¤tGame.date[0], 12, 1, amount);
printLarge(0, currentGame.date[0]);
break;
case 6:
increment(¤tGame.date[1], 31, 1, amount);
printLarge(6, currentGame.date[1]);
break;
case 7:
increment(¤tGame.date[2], 99, 0, amount);
printLarge(12, currentGame.date[2]);
break;
case 8:
increment(&gameVal, totalGames, 1, amount);
loadGame();
break;
case 9:
if (amount > 0)
statCounter += 1;
else if (amount < 0)
statCounter = 24;
endGameStats();
break;
case 10:
if (amount > 0)
{
endGame();
}
else
{
edit = 0;
page = 0;
u8x8.clearDisplay();
refresh(count);
}
break;
case 11:
statCounter += amount;
if (statCounter == 255)
statCounter = 0;
else if (statCounter <= 17)
{
currentGame.hole = statCounter + 1;
if (statCounter == 17)
u8x8.clearDisplay();
refresh(currentGame.score[currentGame.hole-1]);
break;
}
else if (statCounter > 24)
statCounter = 18;
endGameStats();
break;
case 12:
if (amount < 0)
{
holeCounter -= 8;
if (holeCounter > 240)
holeCounter = 1;
statPage -= 1;
}
if (statPage == 0)
{
holeCounter = 1;
statCounter -= 1;
endGame();
}
else
printHoleVal(holeCounter);
break;
}
}
void nextPage(void) {
page = (page + 2) % 4;
u8x8.draw2x2String(0, 0, " ");
refreshPage();
}
void reloadGame() {
count = OldGame;
resetValues();
EEPROM.get(469, currentGame);
u8x8.clearDisplay();
refresh(count);
}
void saveGame() {
EEPROM.get(0, totalGames);
if (totalGames < 11)
{
totalGames += 1;
EEPROM.put(0, totalGames);
}
EEPROM.get(1, gameVal);
gameVal = (gameVal % 11) + 1;
EEPROM.put(1, gameVal);
EEPROM.put(2 + ((gameVal-1) * GAMESIZE), currentGame);
}
void loadLastGame() {
EEPROM.put(469, currentGame);
EEPROM.get(0, totalGames);
EEPROM.get(1, gameVal);
edit = 8;
loadGame();
}
void loadGame() {
EEPROM.get(2 + ((gameVal-1) * GAMESIZE), currentGame);
printDate();
}
void addScore() {
u8x8.clearDisplay();
currentGame.total += count;
currentGame.score[currentGame.hole-1] = count;
if (currentGame.par[currentGame.hole-1] != 0)
{
switch (currentGame.score[currentGame.hole-1] - currentGame.par[currentGame.hole-1])
{
case 3:
u8x8.draw2x2String(0, 0, "3X BOGEY");
break;
case 2:
u8x8.draw2x2String(0, 0, "2X BOGEY");
break;
case 1:
u8x8.draw2x2String(0, 0, "BOGEY");
break;
case 0:
u8x8.draw2x2String(0, 0, "PAR");
break;
case -1:
u8x8.draw2x2String(0, 0, "BIRDIE");
break;
case -2:
u8x8.draw2x2String(0, 0, "EAGLE");
break;
}
delay(750);
u8x8.clearDisplay();
}
count = 0;
if (++currentGame.hole > 18)
{
endGame();
}
else
{
refresh(currentGame.score[currentGame.hole-1]);
}
}
void endGameStats() {
u8x8.clearDisplay();
switch (statCounter)
{
case 18:
u8x8.draw2x2String(0, 0, "SUMMARY");
break;
case 19:
printTotal();
printMull();
break;
case 20:
printHoleVal(1);
edit = 12;
break;
case 21:
if (OldGame != 255)
{
reloadGame();
break;
}
edit = 9;
u8x8.drawString(0, 0, "SAVE GAME?");
printOption();
break;
case 22:
edit = 5;
printDate();
break;
case 23:
saveGame();
case 24:
reset();
break;
}
}
void printLarge(byte x, byte value) {
char output[2] = " ";
itoa(value, output + (value < 10), 10);
u8x8.draw2x2String(x, 0, output);
}
void printTotal() {
printVal(0, 0, 3, currentGame.total, "TOTAL: ");
}
void printHole() {
printVal(0, 0, 1, currentGame.hole, "HOLE: ");
}
void printPar() {
printVal(0, 18, 2, currentGame.par[currentGame.hole-1], "PAR: ");
}
void printMull() {
printVal(0, 18, 4, currentGame.mull, "MULLS: ");
}
void printDate() {
printLarge(0, currentGame.date[0]);
u8x8.draw2x2String(4, 0, "/");
printLarge(6, currentGame.date[1]);
u8x8.draw2x2String(10, 0, "/");
printLarge(12, currentGame.date[2]);
}
void printOption()
{
u8x8.drawString(15, 18, "Y");
u8x8.drawString(8, 18, "N");
}
void printHoleVal(byte holeNum) {
u8x8.clearDisplay();
statPage = holeNum;
if (holeCounter >= 19)
{
statCounter += 1;
endGame();
}
int8_t counter = -1;
while (holeNum <= 18)
{
if (currentGame.score[holeNum-1] == 0)
{
holeNum++;
continue;
}
if (++counter >= 4)
break;
char holeName[5] = "H";
itoa(holeNum, holeName + 1, 10);
strcat(holeName, ": ");
printVal((counter > 1) * 8, (counter % 2) * 18, 99, currentGame.score[holeNum-1], holeName);
holeNum++;
}
holeCounter = holeNum;
}
void printVal(byte xLocation, byte yLocation, byte editnum, byte value, char output[])
{
byte outputSize = strlen(output);
if (edit != editnum)
u8x8.drawString(xLocation, yLocation, output);
char temp[5];
itoa(value, temp, 10);
if (edit == editnum)
strcat(temp, "<");
strcat(temp, " ");
u8x8.drawString(xLocation + outputSize, yLocation, temp);
}
void sleep()
{
u8x8.setPowerSave(1);
GIMSK |= _BV(PCIE); // Enable Pin Change Interrupts
PCMSK |= _BV(PCINT1); // Use PB1 as interrupt pin
PCMSK |= _BV(PCINT3); // Use PB3 as interrupt pin
PCMSK |= _BV(PCINT4); // Use PB4 as interrupt pin
ADCSRA &= ~_BV(ADEN); // ADC off
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // replaces above statement
sleep_enable(); // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
sei(); // Enable interrupts
sleep_cpu(); // sleep
cli(); // Disable interrupts
PCMSK &= ~_BV(PCINT1); // Turn off PB1 as interrupt pin
PCMSK &= ~_BV(PCINT3); // Turn off PB3 as interrupt pin
PCMSK &= ~_BV(PCINT4); // Turn off PB4 as interrupt pin
sleep_disable(); // Clear SE bit
ADCSRA |= _BV(ADEN); // ADC on
sei(); // Enable interrupts
u8x8.setPowerSave(0);
} // sleep
ISR(PCINT0_vect)
{
// This is called when the interrupt occurs, but I don't need to do anything in it
}