#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//SCREEN
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
//IO
#define turnTableDirPin 19
#define turnTableStepPin 17
#define cardShooterDirPin 5
#define cardShooterStepPin 18
#define enterBtn 33
#define upBtn 32
#define dnBtn 25
#define backBtn 26
//BUTTONS
bool upBtnState = HIGH;
bool upBtnLastState = HIGH;
unsigned long upBtnLastDebounceTime = 0;
unsigned long upBtnDebounceDelay = 20;
bool dnBtnState = HIGH;
bool dnBtnLastState = HIGH;
unsigned long dnBtnLastDebounceTime = 0;
unsigned long dnBtnDebounceDelay = 20;
bool enterBtnState = HIGH;
bool enterBtnLastState = HIGH;
unsigned long enterBtnLastDebounceTime = 0;
unsigned long enterBtnDebounceDelay = 20;
bool backBtnState = HIGH;
bool backBtnLastState = HIGH;
unsigned long backBtnLastDebounceTime = 0;
unsigned long backBtnDebounceDelay = 20;
//INTERNAL
uint8_t pulseWidthMicros = 20;
uint8_t numberOfRounds = 1;
uint8_t numberOfRoundsRemainder = 1;
uint8_t localCardsPerRound = 1;
uint8_t localNumberOfRounds = 1;
uint8_t localnumberOfStepsTurnTable = 1;
int i0, i1, i2, i3;
String rummyModeStr = "";
bool processStart = LOW;
bool inMainMenu = HIGH;
int8_t mainMenuLevelNr = 0;
const int8_t mainMenuLevelNrMax = 2;
const char* menuItems[mainMenuLevelNrMax] = {"Start", "Settings"};
bool inSettingsMenu = LOW;
int8_t settingsMenuLevelNr = 0;
const int8_t settingsMenuLevelNrMax = 8;
int8_t settingsValueToSet = 0;
bool settingsSetValueEn = LOW;
int8_t settingsSetValueNr = 0;
int enterPressedCounter = 0;
//USER
uint8_t numberOfPlayers = 4;
uint8_t numberOfCards = 3;
uint8_t maxCardsPerRound = 2;
bool rummyMode = LOW;
uint8_t turnTableSpeed = 25; //100 lassu
uint8_t cardShooterSpeed = 2;
uint8_t numberOfStepsTurnTable = 200; // 200 egesz kor
uint8_t numberOfStepsCardShooter = 200;
void setup() {
Serial.begin(9600);
pinMode(turnTableDirPin, OUTPUT);
pinMode(turnTableStepPin, OUTPUT);
pinMode(cardShooterDirPin, OUTPUT);
pinMode(cardShooterStepPin, OUTPUT);
pinMode(enterBtn, INPUT_PULLUP);
pinMode(backBtn, INPUT_PULLUP);
pinMode(upBtn, INPUT_PULLUP);
pinMode(dnBtn, INPUT_PULLUP);
digitalWrite(turnTableDirPin, HIGH);
digitalWrite(cardShooterDirPin, HIGH);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
display.setTextColor(SSD1306_WHITE);
}
void loop() {
upBtnLastState = debounceBtn(upBtn, &upBtnState, &upBtnLastState, &upBtnLastDebounceTime, upBtnDebounceDelay, "UP");
dnBtnLastState = debounceBtn(dnBtn, &dnBtnState, &dnBtnLastState, &dnBtnLastDebounceTime, dnBtnDebounceDelay, "DOWN");
enterBtnLastState = debounceBtn(enterBtn, &enterBtnState, &enterBtnLastState, &enterBtnLastDebounceTime, enterBtnDebounceDelay, "ENTER");
backBtnLastState = debounceBtn(backBtn, &backBtnState, &backBtnLastState, &backBtnLastDebounceTime, backBtnDebounceDelay, "BACK");
if (rummyMode){
numberOfCards = 14;
maxCardsPerRound = 3;
}
upperAndLowerLimit(numberOfCards,20,1,LOW);
upperAndLowerLimit(numberOfPlayers,20,1,LOW);
upperAndLowerLimit(maxCardsPerRound,20,1,LOW);
upperAndLowerLimit(turnTableSpeed,200,1,LOW);
upperAndLowerLimit(cardShooterSpeed,200,1,LOW);
upperAndLowerLimit(maxCardsPerRound,20,1,LOW);
upperAndLowerLimit(numberOfStepsTurnTable,400,1,LOW);
upperAndLowerLimit(numberOfStepsCardShooter,400,1,LOW);
if (numberOfCards < maxCardsPerRound){
maxCardsPerRound = numberOfCards;
}
numberOfRounds = numberOfCards / maxCardsPerRound;
numberOfRoundsRemainder = numberOfCards % maxCardsPerRound;
if (numberOfRoundsRemainder != 0){
localNumberOfRounds = numberOfRounds + 1;
}
else{
localNumberOfRounds = numberOfRounds;
}
if (!processStart && inMainMenu && !inSettingsMenu){
updateMainScreen();
}
else if (!processStart && !inMainMenu && inSettingsMenu){
updateSettingsScreen();
}
if(processStart){
updateProcessScreen();
for (i0 = 0; i0 < localNumberOfRounds; i0++){
localCardsPerRound = maxCardsPerRound;
if ((i0 == (localNumberOfRounds - 1)) && (numberOfRoundsRemainder != 0)){
localCardsPerRound = numberOfRoundsRemainder;
}
for(i1 = 0; i1 < numberOfPlayers; i1++) {
localnumberOfStepsTurnTable = numberOfStepsTurnTable;
if ((numberOfStepsTurnTable % numberOfPlayers != 0) && (i1 == (numberOfPlayers - 1))){
localnumberOfStepsTurnTable = numberOfStepsTurnTable + (numberOfStepsTurnTable % numberOfPlayers) * numberOfPlayers;
}
for(i2 = 0; i2 < (localnumberOfStepsTurnTable / numberOfPlayers); i2++) {
if (i2 % ((i2 + 1) * (numberOfStepsTurnTable / numberOfPlayers)) == 0){
stepperMotorControlForCycle(numberOfStepsCardShooter * localCardsPerRound, cardShooterStepPin, cardShooterSpeed);
}
if (1 < numberOfPlayers){
stepperMotorControlNoCycle(turnTableStepPin, turnTableSpeed);
}
}
}
}
if (rummyMode){
stepperMotorControlForCycle(numberOfStepsCardShooter, cardShooterStepPin, cardShooterSpeed);
}
processStart = LOW;
}
}
void stepperMotorControlForCycle(int limit, int stepPin, int speed){
for(i3 = 0; i3 < limit; i3++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(pulseWidthMicros);
digitalWrite(stepPin, LOW);
delay(speed);
}
}
void stepperMotorControlNoCycle(int stepPin, int speed){
digitalWrite(stepPin, HIGH);
delayMicroseconds(pulseWidthMicros);
digitalWrite(stepPin, LOW);
delay(speed);
}
bool debounceBtn(int pin, bool* state, bool *lastState, unsigned long* lastDebounceTime,unsigned long debounceDelay, String message){
if (digitalRead(pin) != *lastState) {
*lastDebounceTime = millis();
}
if ((millis() - *lastDebounceTime) > debounceDelay) {
if (digitalRead(pin) != *state) {
*state = digitalRead(pin);
if (*state == LOW) {
Serial.println(message);
switch (pin){
case upBtn:
if (!processStart && inMainMenu && !inSettingsMenu && !settingsSetValueEn){
mainMenuLevelNr--;
}
if (!processStart && !inMainMenu && inSettingsMenu && !settingsSetValueEn){
settingsMenuLevelNr--;
}
settingsValueToSet = 1;
handleUpDownButton();
break;
case dnBtn:
if (!processStart && inMainMenu && !inSettingsMenu && !settingsSetValueEn){
mainMenuLevelNr++;
}
if (!processStart && !inMainMenu && inSettingsMenu && !settingsSetValueEn){
settingsMenuLevelNr++;
}
settingsValueToSet = -1;
handleUpDownButton();
break;
case enterBtn:
handleEnterBtn();
break;
case backBtn:
handleBackBtn();
break;
default: break;
}
}
}
}
return digitalRead(pin);
}
void handleEnterBtn(){
if (0 < mainMenuLevelNr){
inMainMenu = LOW;
}
if (inMainMenu && (mainMenuLevelNr == 0)){
processStart = HIGH;
}
if (inSettingsMenu){
settingsSetValueNr = settingsMenuLevelNr;
enterPressedCounter++;
}
settingsSetValueEn = (enterPressedCounter % 2 != 0) && inSettingsMenu;
inSettingsMenu = mainMenuLevelNr == 1;
}
void handleBackBtn(){
inMainMenu = HIGH;
inSettingsMenu = LOW;
enterPressedCounter = 0;
settingsSetValueEn = LOW;
settingsSetValueNr = 0;
settingsValueToSet = 0;
mainMenuLevelNr = 0;
settingsMenuLevelNr = 0;
}
void handleUpDownButton() {
if (!processStart && inMainMenu && !inSettingsMenu && !settingsSetValueEn){
if (mainMenuLevelNr < 0){
mainMenuLevelNr = mainMenuLevelNrMax - 1;
}
if ((mainMenuLevelNrMax - 1) < mainMenuLevelNr){
mainMenuLevelNr = 0;
}
}
else if (!processStart && !inMainMenu && inSettingsMenu && !settingsSetValueEn){
if (settingsMenuLevelNr < 0){
settingsMenuLevelNr = settingsMenuLevelNrMax - 1;
}
if ((settingsMenuLevelNrMax - 1) < settingsMenuLevelNr){
settingsMenuLevelNr = 0;
}
}
else if (!processStart && !inMainMenu && inSettingsMenu && settingsSetValueEn){
switch (settingsMenuLevelNr){
case 0:
numberOfPlayers += settingsValueToSet;
break;
case 1:
numberOfCards += settingsValueToSet;
break;
case 2:
maxCardsPerRound += settingsValueToSet;
break;
case 3:
rummyMode += settingsValueToSet;
break;
case 4:
turnTableSpeed += settingsValueToSet;
break;
case 5:
numberOfStepsTurnTable += settingsValueToSet;
break;
case 6:
numberOfStepsCardShooter += settingsValueToSet;
break;
case 7:
cardShooterSpeed += settingsValueToSet;
break;
}
}
}
void upperAndLowerLimit(int value, int limitUpper, int limitLower, int onlyUpper){
if (limitUpper < value){
value = limitUpper;
}
if (value < limitLower && !onlyUpper){
value = limitLower;
}
}
void updateMainScreen() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
for (int i = 0; i < mainMenuLevelNrMax; ++i) {
display.setCursor(0, i * 8);
if (i == mainMenuLevelNr) {
display.print("> ");
} else {
display.print(" ");
}
display.print(menuItems[i]);
}
display.display();
}
void updateSettingsScreen(){
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
rummyModeStr = rummyMode ? "True" : "False";
String settingsItems[settingsMenuLevelNrMax] = {"Players: " + String(numberOfPlayers),
"Cards: " + String(numberOfCards),
"Cards/round: " + String(maxCardsPerRound),
"Rummy mode: " + rummyModeStr,
"Turn speed: " + String(turnTableSpeed),
"Steps: " + String(numberOfStepsTurnTable),
"Disp speed: " + String(cardShooterSpeed),
"Steps: " + String(numberOfStepsCardShooter)};
for (int i = 0; i < settingsMenuLevelNrMax; ++i) {
display.setCursor(0, i * 8);
if (i == settingsMenuLevelNr && !settingsSetValueEn) {
display.print("> ");
} else if (i == settingsSetValueNr && settingsSetValueEn) {
display.print(">>>");
} else {
display.print(" ");
}
display.print(settingsItems[i]);
}
display.display();
}
void updateProcessScreen(){
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.display();
}