/*
Project: Tennis Scoreboard
Description: Use to keep score in a tennis match (see Rules.txt)
In a real circuit add resistors on each segment
and transistors on each digit! (Wokwi doesn't care...)
The gray wires are digits, colored are the segments.
Creation date: 5/21/23
Author: AnonEngineering
License: https://en.wikipedia.org/wiki/Beerware
*/
#include <LiquidCrystal_I2C.h>
const char PLAYER_1_NAME[] = {"Otto Bounds"};
const char PLAYER_2_NAME[] = {"Dennis Ball"};
const int PLAYER_BTN[2] = {A3, A2};
const int CLOCK_PIN = A1;
const int LATCH_PIN = A0;
const int DATA_PIN = 13;
// point scores
const uint8_t POINT_SCORES[4] = {0, 15, 30, 40};
// digit driver pins, 0 - 9
const uint8_t DIGIT_PINS[10] = {7, 8, 9, 10, 11, 2, 3, 4, 5, 6};
// lookup table, which segments are on for each number,
// hex values for each digit 0 - 9
const int SEG_BYTES[] = {
0xFC, /* 0 */
0x60, /* 1 */
0xDA, /* 2 */
0xF2, /* 3 */
0x66, /* 4 */
0xB6, /* 5 */
0xBE, /* 6 */
0xE0, /* 7 */
0xFE, /* 8 */
0xF6 /* 9 */
};
int sets[2] = {0, 0};
int games[2] = {0, 0};
int points[2] = {0, 0};
LiquidCrystal_I2C lcd(0x27, 16, 2);
void checkMatchWin() {
if (sets[0] >= 2 || sets[1] >= 2) {
if (sets[0] > sets[1]) {
Serial.println("Player 1 match");
sets[0] = 0;
sets[1] = 0;
}
if (sets[1] > sets[0]) {
Serial.println("Player 2 match");
sets[0] = 0;
sets[1] = 0;
}
}
}
void checkSetWin() {
if (games[0] >= 6 || games[1] >= 6) {
if (games[0] >= games[1] + 2) {
Serial.println("Player 1 set");
sets[0]++;
games[0] = 0;
games[1] = 0;
checkMatchWin();
return;
}
if (games[1] >= games[0] + 2) {
Serial.println("Player 2 set");
sets[1]++;
games[0] = 0;
games[1] = 0;
checkMatchWin();
return;
}
}
Serial.print("P1 games: ");
Serial.print(games[0]);
Serial.print("\tP2 games:");
Serial.println(games[1]);
}
void checkGameWin() {
if (points[0] >= 4 || points[1] >= 4) {
if (points[0] >= points[1] + 2) {
Serial.println("Player 1 game");
clearAdvantage();
games[0]++;
points[0] = 0;
points[1] = 0;
checkSetWin();
return;
}
if (points[1] >= points[0] + 2) {
Serial.println("Player 2 game");
clearAdvantage();
games[1]++;
points[0] = 0;
points[1] = 0;
checkSetWin();
return;
}
}
showPoint();
}
void showPoint() {
if (points[0] >= 3 || points[1] >= 3) {
clearAdvantage();
if (points[0] == points[1]) {
Serial.println("Deuce");
return;
}
}
if (points[0] >= 3 && points[1] >= 3) {
if (points[0] >= points[1] + 1) {
Serial.println("Player 1 Adv");
lcd.setCursor(13, 0);
lcd.print("ADV");
}
if (points[1] >= points[0] + 1) {
Serial.println("Player 2 Adv");
lcd.setCursor(13, 1);
lcd.print("ADV");
}
return;
}
Serial.print("P1 points: ");
Serial.print(points[0]);
Serial.print("\tP2 points:");
Serial.println(points[1]);
}
void clearAdvantage() {
lcd.setCursor(13, 0);
lcd.print(" ");
lcd.setCursor(13, 1);
lcd.print(" ");
}
void getPoints(int playerIndex) {
static int lastPlayerState[] = {1, 1};
int playerState[] = {0, 0};
playerState[playerIndex] = digitalRead(PLAYER_BTN[playerIndex]);
if (playerState[playerIndex] != lastPlayerState[playerIndex]) {
if (playerState[playerIndex] == LOW) {
points[playerIndex]++;
checkGameWin();
}
delay(20); // debounce
}
lastPlayerState[playerIndex] = playerState[playerIndex];
}
void updateSet() {
writeShiftDigit(0, sets[0]);
writeShiftDigit(5, sets[1]);
}
void updateGame() {
int gameHi[2] = {0, 0};
int gameLo[2] = {0, 0};
gameHi[0] = (games[0] % 100) / 10; // tens
gameHi[1] = (games[1] % 100) / 10; // tens
gameLo[0] = games[0] % 10; // ones
gameLo[1] = games[1] % 10; // ones
writeShiftDigit(1, gameHi[0]);
writeShiftDigit(2, gameLo[0]);
writeShiftDigit(6, gameHi[1]);
writeShiftDigit(7, gameLo[1]);
}
void updatePoint() {
int displayPoints[2] = {0, 0};
int displayPtHi[2] = {0, 0};
int displayPtLo[2] = {0, 0};
displayPoints[0] = points[0];
displayPoints[1] = points[1];
if (displayPoints[0] > 3) displayPoints[0] = 3;
if (displayPoints[1] > 3) displayPoints[1] = 3;
displayPoints[0] = POINT_SCORES[displayPoints[0]];
displayPoints[1] = POINT_SCORES[displayPoints[1]];
displayPtHi[0] = (displayPoints[0] % 100) / 10; // tens
displayPtHi[1] = (displayPoints[1] % 100) / 10; // tens
displayPtLo[0] = displayPoints[0] % 10; // ones
displayPtLo[1] = displayPoints[1] % 10; // ones
writeShiftDigit(3, displayPtHi[0]);
writeShiftDigit(4, displayPtLo[0]);
writeShiftDigit(8, displayPtHi[1]);
writeShiftDigit(9, displayPtLo[1]);
}
// for test, not called
void testDisplay() {
writeShiftDigit(0, 0);
writeShiftDigit(1, 1);
writeShiftDigit(2, 2);
writeShiftDigit(3, 3);
writeShiftDigit(4, 4);
writeShiftDigit(5, 5);
writeShiftDigit(6, 6);
writeShiftDigit(7, 7);
writeShiftDigit(8, 8);
writeShiftDigit(9, 9);
}
void writeShiftDigit(int digit, int number) {
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, SEG_BYTES[number]);
digitalWrite(LATCH_PIN, HIGH);
digitalWrite(DIGIT_PINS[digit], LOW);
digitalWrite(DIGIT_PINS[digit], HIGH);
}
void setup() {
Serial.begin(115200); // for test
lcd.init();
pinMode(PLAYER_BTN[0], INPUT_PULLUP);
pinMode(PLAYER_BTN[1], INPUT_PULLUP);
pinMode(DATA_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
for (int i = 0; i < 10; i++) {
pinMode(DIGIT_PINS[i], OUTPUT);
digitalWrite(DIGIT_PINS[i], HIGH);
}
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(PLAYER_1_NAME);
lcd.setCursor(0, 1);
lcd.print(PLAYER_2_NAME);
}
void loop() {
//testDisplay();
getPoints(0);
getPoints(1);
updatePoint();
updateGame();
updateSet();
}
Players
Sets
Games
Points