#include <Arduino.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>
#include <SSD1306Ascii.h>
#include <SSD1306AsciiWire.h>
#define INIT_BOARD1 858993459
#define INIT_BOARD2 3435973836
#define LATCH_PIN DDB1
#define DATA1_PIN DDB3
#define DATA2_PIN DDB4
#define CLK_PIN DDB5
#define CS_PIN 10
uint32_t oldOptionSwitch1 = 0;
uint32_t oldOptionSwitch2 = 0;
uint32_t setOptionSwitch1 = 0;
uint32_t setOptionSwitch2 = 0;
int white_player_time = 300; // 5 minutes in seconds
int black_player_time = 300; // 5 minutes in seconds
int *current_player_time = &black_player_time;
uint8_t rotate = 1; // 1 for black, 0 for white
uint8_t start = 0;
uint8_t picked = 255;
uint8_t put = 255;
int *expected_player = &white_player_time;
SSD1306AsciiWire oled;
File myFile;
const int pulseWidth = 50;
typedef enum {
INIT_BOARD,
WAIT_BLACK_START,
PICK,
MOVE,
MOVED,
ATE,
SAVE_GAME,
NUM_STATES
} state_t;
typedef state_t state_func_t(void);
state_t init_board(void);
state_t wait_black_start(void);
state_t pick(void);
state_t move(void);
state_t moved(void);
state_t ate(void);
state_t save_game(void);
state_func_t *const state_table[NUM_STATES] = {
init_board,
wait_black_start,
pick,
move,
moved,
ate,
save_game
};
state_t run_state(state_t state) {
return state_table[state]();
}
state_t current_state = INIT_BOARD;
uint8_t chess_to_switch[8][8] = {
{39, 35, 47, 43, 55, 51, 63, 59},
{38, 34, 46, 42, 54, 50, 62, 58},
{37, 33, 45, 41, 53, 49, 61, 57},
{36, 32, 44, 40, 52, 48, 60, 56},
{3, 7, 11, 15, 19, 23, 27, 31},
{2, 6, 10, 14, 18, 22, 26, 30},
{1, 5, 9, 13, 17, 21, 25, 29},
{0, 4, 8, 12, 16, 20, 24, 28}
};
uint8_t switch_to_chess[8][8] = {
{56, 48, 40, 32, 57, 49, 41, 33},
{58, 50, 42, 34, 59, 51, 43, 35},
{60, 52, 44, 36, 61, 53, 45, 37},
{62, 54, 46, 38, 63, 55, 47, 39},
{25, 17, 9, 1, 24, 16, 8, 0},
{27, 19, 11, 3, 26, 18, 10, 2},
{29, 21, 13, 5, 28, 20, 12, 4},
{31, 23, 15, 7, 30, 22, 14, 6}
};
void displayChessBoard(uint32_t optionSwitch1, uint32_t optionSwitch2) {
for (int i = 3; i >= 0; i--) {
for (int j = 0; j < 8; j++) {
if (bitRead(optionSwitch2, chess_to_switch[i][j] - 32) == LOW) {
Serial.print(0);
Serial.print(" ");
} else {
Serial.print(1);
Serial.print(" ");
}
}
Serial.println();
}
Serial.println("\nChess board: " + String(current_state));
for (int i = 7; i >= 4; i--) {
for (int j = 0; j < 8; j++) {
if (bitRead(optionSwitch1, chess_to_switch[i][j]) == LOW) {
Serial.print(0);
Serial.print(" ");
} else {
Serial.print(1);
Serial.print(" ");
}
}
Serial.println();
}
}
void ReadOneLevel(uint32_t *optionSwitch1, uint32_t *optionSwitch2, int shift);
void ReadBoard(uint32_t *optionSwitch1, uint32_t *optionSwitch2);
void displayTime(int time, int x, int y);
void TIMER1_init(void) {
TCCR1A = 0;
TCCR1B = (1 << WGM12);
OCR1A = 15624;
OCR1B = 0;
TCCR1B |= (1 << CS12) | (1 << CS10);
TIMSK1 = (1 << OCIE1A);
}
void WHITE_BLACK_BUTTONS_init(void) {
DDRD &= ~((1 << DDD2) | (1 << DDD3));
PORTD = 0;
PORTD |= (1 << PORTD2) | (1 << PORTD3);
EICRA = (1 << ISC01) | (1 << ISC11);
EIMSK = (1 << INT0) | (1 << INT1);
}
void SHIFT_REGISTERS_init(void) {
DDRB &= ~((1 << DATA1_PIN) | (1 << DATA2_PIN));
DDRB |= (1 << CLK_PIN) | (1 << LATCH_PIN);
PORTB = 0;
PORTB |= (1 << LATCH_PIN);
}
ISR(TIMER1_COMPA_vect) {
if (start) {
*current_player_time -= 1;
if (*current_player_time <= 0) {
rotate = 1 - rotate;
current_state = SAVE_GAME;
}
}
}
ISR(INT0_vect) {
start = 1;
if (current_player_time != &white_player_time) {
current_player_time = &white_player_time;
rotate = 0;
}
}
ISR(INT1_vect) {
if (start && current_player_time != &black_player_time) {
current_player_time = &black_player_time;
rotate = 1;
}
}
void printBits(uint32_t var) {
for (int i = 31; i >= 0; i--) {
Serial.print((var >> i) & 1);
if (i % 8 == 0) {
Serial.print(" ");
}
}
Serial.println();
}
void setup() {
Serial.begin(9600);
TIMER1_init();
WHITE_BLACK_BUTTONS_init();
SHIFT_REGISTERS_init();
sei();
// if (!SD.begin(CS_PIN)) {
// return;
// }
// myFile = SD.open("moves.txt", FILE_WRITE);
// if (!myFile) {
// Serial.println("Failed to open moves file!");
// return;
// }
// myFile.println("New game started");
// myFile.close();
Wire.begin();
oled.begin(&Adafruit128x32, 0x3C);
oled.setFont(System5x7);
oled.clear();
oled.println("Setup complete");
delay(2000);
ReadBoard(&oldOptionSwitch1, &oldOptionSwitch2);
}
void loop() {
oled.clear();
if (rotate == 1) {
oled.displayRemap(true);
} else {
oled.displayRemap(false);
}
displayTime(*current_player_time, 23, 28);
current_state = run_state(current_state);
_delay_ms(1000);
}
state_t init_board(void) {
uint32_t optionSwitch1 = 0;
uint32_t optionSwitch2 = 0;
ReadBoard(&optionSwitch1, &optionSwitch2);
displayChessBoard(optionSwitch1, optionSwitch2);
if (optionSwitch1 != INIT_BOARD1 || optionSwitch2 != INIT_BOARD2) {
return INIT_BOARD;
}
oldOptionSwitch1 = optionSwitch1;
oldOptionSwitch2 = optionSwitch2;
return WAIT_BLACK_START;
}
state_t wait_black_start(void) {
if (start == 0) {
return WAIT_BLACK_START;
}
return PICK;
}
state_t pick(void) {
uint32_t optionSwitch1 = 0;
uint32_t optionSwitch2 = 0;
ReadBoard(&optionSwitch1, &optionSwitch2);
displayChessBoard(optionSwitch1, optionSwitch2);
if (optionSwitch1 == oldOptionSwitch1 && optionSwitch2 == oldOptionSwitch2) {
if (current_player_time == expected_player) {
return PICK;
} else {
if (picked == 255) {
return SAVE_GAME;
} else {
return MOVE;
}
}
}
for (int i = 0; i < 32; i++) {
if (bitRead(optionSwitch1, i) != bitRead(oldOptionSwitch1, i)) {
picked = i;
break;
}
if (bitRead(optionSwitch2, i) != bitRead(oldOptionSwitch2, i)) {
picked = i + 32;
break;
}
}
oldOptionSwitch2 = optionSwitch2;
oldOptionSwitch1 = optionSwitch1;
return MOVE;
}
state_t move(void) {
uint32_t optionSwitch1 = 0;
uint32_t optionSwitch2 = 0;
ReadBoard(&optionSwitch1, &optionSwitch2);
displayChessBoard(optionSwitch1, optionSwitch2);
if (optionSwitch1 == oldOptionSwitch1 && optionSwitch2 == oldOptionSwitch2) {
if (current_player_time == expected_player) {
return MOVE;
} else {
if (put == 255) {
return SAVE_GAME;
} else {
return MOVED;
}
}
}
for (int i = 0; i < 32; i++) {
if (bitRead(optionSwitch1, i) != bitRead(oldOptionSwitch1, i)) {
put = i;
if (bitRead(optionSwitch1, i) == LOW) {
setOptionSwitch1 = bitWrite(optionSwitch1, i, HIGH);
setOptionSwitch2 = optionSwitch2;
return ATE;
}
break;
}
if (bitRead(optionSwitch2, i) != bitRead(oldOptionSwitch2, i)) {
put = i + 32;
if (bitRead(optionSwitch2, i) == LOW) {
setOptionSwitch1 = optionSwitch1;
setOptionSwitch2 = bitWrite(optionSwitch2, i, HIGH);
return ATE;
}
break;
}
}
oldOptionSwitch2 = optionSwitch2;
oldOptionSwitch1 = optionSwitch1;
return MOVED;
}
state_t moved(void) {
uint32_t optionSwitch1 = 0;
uint32_t optionSwitch2 = 0;
ReadBoard(&optionSwitch1, &optionSwitch2);
displayChessBoard(optionSwitch1, optionSwitch2);
if (current_player_time != expected_player) {
uint8_t board_pick = switch_to_chess[picked / 8][picked % 8];
uint8_t board_put = switch_to_chess[put / 8][put % 8];
if (expected_player == &white_player_time) {
Serial.print("White moves: ");
expected_player = &black_player_time;
} else {
Serial.print("Black moves: ");
expected_player = &white_player_time;
}
char buffer[7];
sprintf(buffer, "%c%d -> %c%d", 'H' - (board_pick % 8), 8 - (board_pick / 8), 'H' - (board_put % 8), 8 - (board_put / 8));
Serial.println(buffer);
// myFile = SD.open("moves.txt", FILE_WRITE);
// if (myFile) {
// myFile.println(buffer);
// myFile.close();
// } else {
// Serial.println("Error opening moves.txt");
// }
picked = 255;
put = 255;
return PICK;
}
return MOVED;
}
state_t ate(void) {
uint32_t optionSwitch1 = 0;
uint32_t optionSwitch2 = 0;
ReadBoard(&optionSwitch1, &optionSwitch2);
displayChessBoard(optionSwitch1, optionSwitch2);
if (current_player_time != &white_player_time && optionSwitch1 == setOptionSwitch1 && optionSwitch2 == setOptionSwitch2) {
uint8_t board_pick = switch_to_chess[picked / 8][picked % 8];
uint8_t board_put = switch_to_chess[put / 8][put % 8];
if (expected_player == &white_player_time) {
Serial.print("White moves: ");
expected_player = &black_player_time;
} else {
Serial.print("Black moves: ");
expected_player = &white_player_time;
}
char buffer[7];
sprintf(buffer, "%c%d -> %c%d", 'H' - (board_pick % 8), 8 - (board_pick / 8), 'H' - (board_put % 8), 8 - (board_put / 8));
Serial.println(buffer);
// myFile = SD.open("moves.txt", FILE_WRITE);
// if (myFile) {
// myFile.println(buffer);
// myFile.close();
// } else {
// Serial.println("Error opening moves.txt");
// }
picked = 255;
put = 255;
oldOptionSwitch1 = optionSwitch1;
oldOptionSwitch2 = optionSwitch2;
return PICK;
}
return ATE;
}
state_t save_game(void) {
if (rotate == 1) {
Serial.println("Black wins");
// myFile = SD.open("moves.txt", FILE_WRITE);
// if (myFile) {
// myFile.println("Black wins");
// myFile.close();
// } else {
// Serial.println("Error opening moves.txt");
// }
} else {
Serial.println("White wins");
// myFile = SD.open("moves.txt", FILE_WRITE);
// if (myFile) {
// myFile.println("White wins");
// myFile.close();
// } else {
// Serial.println("Error opening moves.txt");
// }
}
cli();
start = 0;
white_player_time = 300;
black_player_time = 300;
current_player_time = &black_player_time;
expected_player = &white_player_time;
rotate = 1;
sei();
return INIT_BOARD;
}
void ReadOneLevel(uint32_t *optionSwitch1, uint32_t *optionSwitch2, int shift) {
uint8_t ret1 = 0;
uint8_t ret2 = 0;
for (int i = 7; i >= 0; i--) {
if (PINB & (1 << DATA1_PIN)) ret1 |= (1 << i);
if (PINB & (1 << DATA2_PIN)) ret2 |= (1 << i);
PORTB |= (1 << CLK_PIN);
_delay_us(pulseWidth);
PORTB &= ~(1 << CLK_PIN);
}
*optionSwitch1 |= (uint32_t)ret1 << shift;
*optionSwitch2 |= (uint32_t)ret2 << shift;
}
void ReadBoard(uint32_t *optionSwitch1, uint32_t *optionSwitch2) {
PORTB &= ~(1 << LATCH_PIN);
_delay_us(pulseWidth);
PORTB |= (1 << LATCH_PIN);
for (int i = 24; i >= 0; i -= 8) {
ReadOneLevel(optionSwitch1, optionSwitch2, i);
}
}
void displayTime(int time, int x, int y) {
oled.setFont(lcdnums14x24);
oled.setCursor(x, y);
uint8_t minutes = time / 60;
uint8_t seconds = time % 60;
char buffer[6];
sprintf(buffer, "%02d:%02d", minutes, seconds);
oled.print(buffer);
}