Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
In busy restaurant environments, customers often experience frustration during extended food preparation times. Traditional wait times lack engagement, leading to customer dissatisfaction and perceived longer delays.
In busy restaurant environments, customers often experience frustration during extended food preparation times. Traditional wait times lack engagement, leading to customer dissatisfaction and perceived longer delays.The Solution
The Smart Dining Kiosk transforms passive restaurant wait times into an engaging, interactive experience. Built around the XIAO ESP32S3 Sense microcontroller, an 0.96-inch SSD1306 OLED display, and an analog thumb joystick, this tabletop device allows customers to:
Track Orders: Manually select their order (Biriyani, Porotta, Mandhi) to display real-time prep time estimates.
- Track Orders: Manually select their order (
Biriyani,Porotta,Mandhi) to display real-time prep time estimates.
Play Retro Arcade Games: Features fully playable, custom-coded versions of Snake Xenzia, Dino Run, and a Lights-Out Reaction Time Test.
- Play Retro Arcade Games: Features fully playable, custom-coded versions of Snake Xenzia, Dino Run, and a Lights-Out Reaction Time Test.
Earn Gamified Discounts: Customers can play a randomized 5-question food trivia quiz (chosen from a 15-question preloaded database). For every correct answer, they earn a 1% bill discount—incentivizing interaction.
- Earn Gamified Discounts: Customers can play a randomized 5-question food trivia quiz (chosen from a 15-question preloaded database). For every correct answer, they earn a 1% bill discount—incentivizing interaction.
Visual Feedback: Dual indicator LEDs (Correct/Success and Wrong/Failure) provide immediate physical feedback during trivia and gaming.
- Visual Feedback: Dual indicator LEDs (
Correct/SuccessandWrong/Failure) provide immediate physical feedback during trivia and gaming.
The Smart Dining Kiosk transforms passive restaurant wait times into an engaging, interactive experience. Built around the XIAO ESP32S3 Sense microcontroller, an 0.96-inch SSD1306 OLED display, and an analog thumb joystick, this tabletop device allows customers to:Track Orders: Manually select their order (Biriyani,Porotta,Mandhi) to display real-time prep time estimates.Play Retro Arcade Games: Features fully playable, custom-coded versions of Snake Xenzia, Dino Run, and a Lights-Out Reaction Time Test.Earn Gamified Discounts: Customers can play a randomized 5-question food trivia quiz (chosen from a 15-question preloaded database). For every correct answer, they earn a 1% bill discount—incentivizing interaction.Visual Feedback: Dual indicator LEDs (Correct/SuccessandWrong/Failure) provide immediate physical feedback during trivia and gaming.
Smart Dining Kiosk - Circuit Schematic & Wiring Diagram
How to read/use it:
- Controller: Seeed Studio XIAO ESP32S3 Sense.
- Display: 0.96" I2C SSD1306 OLED connected to SDA (D4/GPIO5) and SCL (D5/GPIO6), powered via 3V3.
- Input: Two-axis analog joystick connected to D0 (VRx), D1 (VRy), and D2 (SW/Select Button with internal pull-up).
- Feedback LEDs: Green LED on D3 (Correct Answer / Success) and Red LED on D6 (Wrong Answer / Game Over), both wired to GND through 220Ω current-limiting resistors.
- Power: All modules share common 3.3V (VCC) and GND rails.

Smart Dining Kiosk - Main Firmware (ESP32 / C++)
C/C++What it does & how to use it:
- Order & Prep Tracking: Allows customers to select their food order (Biriyani, Porotta, Mandhi) and view estimated preparation times.
- Gamified Discounts: Generates a randomized 5-question food trivia quiz from an internal 15-question bank. Each correct answer awards a 1% bill discount.
- Arcade Games: Includes fully playable versions of Snake Xenzia, Dino Run, and a Lights-Out Reaction Time Test to entertain customers while waiting.
- Feedback System: Controls two indicator LEDs (Green on pin D3 for Correct/Success, Red on pin D6 for Wrong/Failure) and renders UI graphics on a 0.96" SSD1306 OLED display via I2C.
- Input & Control: Uses a 2-axis analog thumb joystick with built-in switch debouncing (via Bounce2 library) for menu navigation and gameplay.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Bounce2.h>
// ==========================================
// OLED DISPLAY SETTINGS
// ==========================================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ==========================================
// HARDWARE PINS (UPDATED FOR 2 LEDS)
// ==========================================
#define JOY_X_PIN D0
#define JOY_Y_PIN D1
#define JOY_SW_PIN D2
#define LED_CORRECT D3
#define LED_WRONG D6
Bounce2::Button joyButton = Bounce2::Button();
// ==========================================
// GAME STATE & VARIABLES
// ==========================================
enum GameState { WELCOME, FOOD_MENU, PREP_TIME, MENU, GAMES_MENU, INSTRUCTIONS, QUIZ, GAMEOVER, LEADERBOARD, REACTION_GAME, SNAKE_GAME, SNAKE_GAMEOVER, DINO_GAME, DINO_GAMEOVER };
GameState currentState = WELCOME;
// --- Menu Navigation ---
int foodMenuSelection = 0;
int mainMenuSelection = 0;
int gamesMenuSelection = 0;
// --- Food Data ---
String foodItems[3] = {"Biriyani", "Porotta", "Mandhi"};
int foodPrepTimes[3] = {20, 10, 25};
int activePrepTime = 0;
String activeFood = "";
// --- Quiz Variables ---
#define TOTAL_QUESTIONS 15
int currentQuestion = 0;
int quizScore = 0;
int quizSelection = 0;
int selectedQuestions[5];
String allQuestions[TOTAL_QUESTIONS] = {
"What is a tomato?", "Main part of Guacamole?", "Which is a root veg?", "Sushi wrapper name?", "Not a type of pasta?",
"What makes bread rise?", "Nut inside marzipan?", "Origin of French Fries?", "Main part of hummus?", "Tofu is made from?",
"Spice making curry yellow", "Is a pumpkin a fruit?", "Most stolen food?", "Feta cheese is from?", "What is calamari?"
};
String allOptions[TOTAL_QUESTIONS][3] = {
{"Fruit", "Vegetable", "Meat"}, {"Tomato", "Avocado", "Lime"}, {"Spinach", "Carrot", "Celery"}, {"Nori", "Wasabi", "Mochi"}, {"Penne", "Ravioli", "Baguette"},
{"Yeast", "Salt", "Sugar"}, {"Walnut", "Almond", "Cashew"}, {"France", "Belgium", "USA"}, {"Lentils", "Chickpeas", "Beans"}, {"Soy", "Wheat", "Milk"},
{"Cumin", "Turmeric", "Clove"}, {"Yes", "No", "It's a root"}, {"Cheese", "Meat", "Bread"}, {"Italy", "Greece", "Spain"}, {"Squid", "Octopus", "Clam"}
};
int allCorrectAnswers[TOTAL_QUESTIONS] = {
0, 1, 1, 0, 2, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0
};
// --- Reaction Game Variables ---
unsigned long sequenceStartTime = 0;
unsigned long waitStartTime = 0;
unsigned long randomDelay = 0;
unsigned long finalReactionTime = 0;
int lightsOn = 0;
int reactionSubState = 0;
// --- Snake Xenzia Variables ---
int snakeX[64];
int snakeY[64];
int snakeLength = 3;
int snakeDir = 1;
int foodX = 10, foodY = 10;
unsigned long lastSnakeMove = 0;
int snakeScore = 0;
// --- Dino Run Variables ---
float dinoY = 38;
float dinoVelocity = 0;
bool isJumping = false;
int cactusX = 128;
int dinoScore = 0;
// ==========================================
// SETUP & UTILITIES
// ==========================================
// New function for the Welcome blinking pattern
void playWelcomePattern() {
for (int i = 0; i < 3; i++) {
digitalWrite(LED_CORRECT, HIGH);
digitalWrite(LED_WRONG, LOW);
delay(150);
digitalWrite(LED_CORRECT, LOW);
digitalWrite(LED_WRONG, HIGH);
delay(150);
}
digitalWrite(LED_WRONG, LOW); // Turn both off at the end
}
// Helper to turn off all LEDs cleanly
void turnOffLEDs() {
digitalWrite(LED_CORRECT, LOW);
digitalWrite(LED_WRONG, LOW);
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(A3));
pinMode(LED_CORRECT, OUTPUT);
pinMode(LED_WRONG, OUTPUT);
turnOffLEDs();
joyButton.attach(JOY_SW_PIN, INPUT_PULLUP);
joyButton.interval(15);
joyButton.setPressedState(LOW);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for(;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 25);
display.println("SYSTEM BOOTING...");
display.display();
delay(1500);
// Play pattern before showing the first welcome screen
playWelcomePattern();
}
// ==========================================
// MAIN LOOP
// ==========================================
void loop() {
joyButton.update();
int joyX = analogRead(JOY_X_PIN);
int joyY = analogRead(JOY_Y_PIN);
switch (currentState) {
case WELCOME: handleWelcome(); break;
case FOOD_MENU: handleFoodMenu(joyY); break;
case PREP_TIME: handlePrepTime(joyX); break;
case MENU: handleMainMenu(joyY); break;
case GAMES_MENU: handleGamesMenu(joyY); break;
case INSTRUCTIONS: handleInstructions(joyX); break;
case QUIZ: handleQuiz(joyX, joyY); break;
case GAMEOVER: handleGameOver(); break;
case LEADERBOARD: handleLeaderboard(joyX); break;
case REACTION_GAME: handleReactionGame(); break;
case SNAKE_GAME: handleSnakeGame(joyX, joyY); break;
case SNAKE_GAMEOVER: handleSnakeGameOver(); break;
case DINO_GAME: handleDinoGame(); break;
case DINO_GAMEOVER: handleDinoGameOver(); break;
}
}
// ==========================================
// STATE: WELCOME SCREEN
// ==========================================
void handleWelcome() {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(20, 10);
display.println("WELCOME");
display.setTextSize(1);
display.setCursor(15, 35);
display.println("To The Food Hub!");
display.setCursor(10, 55);
display.println("[Press to Order]");
display.display();
if (joyButton.pressed()) {
foodMenuSelection = 0;
currentState = FOOD_MENU;
}
}
// ==========================================
// STATE: FOOD SELECTION MENU
// ==========================================
void handleFoodMenu(int joyY) {
if (joyY < 1000) {
foodMenuSelection--;
if (foodMenuSelection < 0) foodMenuSelection = 0;
delay(200);
} else if (joyY > 3000) {
foodMenuSelection++;
if (foodMenuSelection > 3) foodMenuSelection = 3;
delay(200);
}
display.clearDisplay();
display.setCursor(0, 0);
display.println("Select Your Order:");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
for (int i = 0; i < 3; i++) {
display.setCursor(10, 15 + (i * 12));
if (foodMenuSelection == i) display.print(">");
display.println(" " + foodItems[i]);
}
display.setCursor(10, 52);
if (foodMenuSelection == 3) display.print(">");
display.println(" <- Back");
display.display();
if (joyButton.pressed()) {
if (foodMenuSelection == 3) {
playWelcomePattern(); // Play pattern when returning to Welcome
currentState = WELCOME;
} else {
activeFood = foodItems[foodMenuSelection];
activePrepTime = foodPrepTimes[foodMenuSelection];
currentState = PREP_TIME;
}
}
}
// ==========================================
// STATE: ESTIMATED TIME SCREEN
// ==========================================
void handlePrepTime(int joyX) {
display.clearDisplay();
display.setCursor(0, 0);
display.println("Order Confirmed!");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(0, 20);
display.print("Preparing: ");
display.println(activeFood);
display.setTextSize(2);
display.setCursor(0, 35);
display.print(activePrepTime);
display.println(" mins");
display.setTextSize(1);
display.setCursor(0, 55);
display.println("[< Back] [Press Next]");
display.display();
if (joyButton.pressed()) currentState = MENU;
if (joyX < 1000) { currentState = FOOD_MENU; delay(300); }
}
// ==========================================
// STATE: THE MAIN MENU
// ==========================================
void handleMainMenu(int joyY) {
if (joyY < 1000) {
mainMenuSelection--;
if (mainMenuSelection < 0) mainMenuSelection = 0;
delay(200);
} else if (joyY > 3000) {
mainMenuSelection++;
if (mainMenuSelection > 3) mainMenuSelection = 3;
delay(200);
}
display.clearDisplay();
display.setCursor(0, 0);
display.print("Wait time: ");
display.print(activePrepTime);
display.println(" mins");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(10, 18);
if (mainMenuSelection == 0) display.print(">"); display.println(" Games");
display.setCursor(10, 30);
if (mainMenuSelection == 1) display.print(">"); display.println(" Quizzes");
display.setCursor(10, 42);
if (mainMenuSelection == 2) display.print(">"); display.println(" Leaderboard");
display.setCursor(10, 54);
if (mainMenuSelection == 3) display.print(">"); display.println(" <- Back to Food");
display.display();
if (joyButton.pressed()) {
if (mainMenuSelection == 0) { gamesMenuSelection = 0; currentState = GAMES_MENU; }
else if (mainMenuSelection == 1) currentState = INSTRUCTIONS;
else if (mainMenuSelection == 2) currentState = LEADERBOARD;
else if (mainMenuSelection == 3) currentState = FOOD_MENU;
}
}
// ==========================================
// STATE: THE GAMES SUB-MENU
// ==========================================
void handleGamesMenu(int joyY) {
if (joyY < 1000) {
gamesMenuSelection--;
if (gamesMenuSelection < 0) gamesMenuSelection = 0;
delay(200);
} else if (joyY > 3000) {
gamesMenuSelection++;
if (gamesMenuSelection > 3) gamesMenuSelection = 3;
delay(200);
}
display.clearDisplay();
display.setCursor(0, 0);
display.println("--- ARCADE ---");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(10, 15);
if (gamesMenuSelection == 0) display.print(">"); display.println(" Snake Xenzia");
display.setCursor(10, 27);
if (gamesMenuSelection == 1) display.print(">"); display.println(" Reaction Test");
display.setCursor(10, 39);
if (gamesMenuSelection == 2) display.print(">"); display.println(" Dino Run");
display.setCursor(10, 51);
if (gamesMenuSelection == 3) display.print(">"); display.println(" <- Back to Main");
display.display();
if (joyButton.pressed()) {
turnOffLEDs();
if (gamesMenuSelection == 0) initSnake();
else if (gamesMenuSelection == 1) { reactionSubState = 0; currentState = REACTION_GAME; }
else if (gamesMenuSelection == 2) initDino();
else if (gamesMenuSelection == 3) currentState = MENU;
}
}
// ==========================================
// STATE: QUIZ INSTRUCTIONS
// ==========================================
void handleInstructions(int joyX) {
display.clearDisplay();
display.setCursor(0, 0);
display.println("QUIZ RULES:");
display.println("- 5 Questions");
display.println("- 1% discount per");
display.println(" correct answer!");
display.setCursor(0, 50);
display.println("[< Back] [Press Start]");
display.display();
if (joyButton.pressed()) {
quizScore = 0;
currentQuestion = 0;
quizSelection = 0;
for (int i = 0; i < 5; i++) {
bool isDuplicate;
int candidate;
do {
isDuplicate = false;
candidate = random(0, TOTAL_QUESTIONS);
for (int j = 0; j < i; j++) {
if (selectedQuestions[j] == candidate) {
isDuplicate = true;
break;
}
}
} while (isDuplicate);
selectedQuestions[i] = candidate;
}
currentState = QUIZ;
}
if (joyX < 1000) { currentState = MENU; delay(300); }
}
// ==========================================
// STATE: THE QUIZ LOOP
// ==========================================
void handleQuiz(int joyX, int joyY) {
if (joyX < 1000) { currentState = MENU; delay(300); return; }
if (joyY < 1000) {
quizSelection--;
if (quizSelection < 0) quizSelection = 0;
delay(200);
} else if (joyY > 3000) {
quizSelection++;
if (quizSelection > 2) quizSelection = 2;
delay(200);
}
int actualQIndex = selectedQuestions[currentQuestion];
display.clearDisplay();
display.setCursor(0, 0);
display.print("Q"); display.print(currentQuestion + 1); display.print(": ");
display.println(allQuestions[actualQIndex]);
display.drawLine(0, 17, 128, 17, SSD1306_WHITE);
for (int i = 0; i < 3; i++) {
display.setCursor(10, 25 + (i * 12));
if (quizSelection == i) display.print(">");
display.println(allOptions[actualQIndex][i]);
}
display.display();
if (joyButton.pressed()) {
if (quizSelection == allCorrectAnswers[actualQIndex]) {
// Blink CORRECT LED
digitalWrite(LED_CORRECT, HIGH);
delay(300);
digitalWrite(LED_CORRECT, LOW);
quizScore++;
} else {
// Blink WRONG LED
digitalWrite(LED_WRONG, HIGH);
delay(300);
digitalWrite(LED_WRONG, LOW);
}
currentQuestion++;
quizSelection = 0;
if (currentQuestion >= 5) currentState = GAMEOVER;
}
}
// ==========================================
// STATE: QUIZ RESULTS
// ==========================================
void handleGameOver() {
display.clearDisplay();
display.setCursor(0, 0);
display.println("QUIZ COMPLETE!");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setTextSize(2);
display.setCursor(0, 20);
display.print("Score: "); display.print(quizScore); display.println("/5");
display.setTextSize(1);
display.setCursor(0, 45);
display.print("You won a "); display.print(quizScore); display.println("% discount!");
display.setCursor(0, 55); display.println("[Press for Leaderboard]");
display.display();
if (joyButton.pressed()) currentState = LEADERBOARD;
}
// ==========================================
// STATE: LEADERBOARD
// ==========================================
void handleLeaderboard(int joyX) {
display.clearDisplay();
display.setCursor(0, 0);
display.println("--- LEADERBOARD ---");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(10, 15); display.println("1. Table 4 - 5/5");
display.setCursor(10, 25); display.println("2. Table 9 - 4/5");
display.setCursor(10, 35); display.print("3. YOUR TEAM - "); display.print(quizScore); display.println("/5");
display.setCursor(0, 55); display.println("[< Back] [Press Reset]");
display.display();
if (joyButton.pressed()) {
turnOffLEDs();
mainMenuSelection = 0;
playWelcomePattern(); // Play pattern when restarting flow
currentState = WELCOME;
}
if (joyX < 1000) { currentState = MENU; delay(300); }
}
// ==========================================
// ARCADE GAMES (Snake, Dino, Reaction)
// ==========================================
void initDino() {
dinoY = 38; dinoVelocity = 0; isJumping = false; cactusX = 128; dinoScore = 0;
currentState = DINO_GAME;
}
void handleDinoGame() {
if (joyButton.pressed() && !isJumping) { dinoVelocity = -5.5; isJumping = true; }
dinoVelocity += 0.6; dinoY += dinoVelocity;
if (dinoY >= 38) { dinoY = 38; dinoVelocity = 0; isJumping = false; }
cactusX -= 4;
if (cactusX < -10) {
cactusX = 128; dinoScore++;
// Blink Correct LED for scoring a point
digitalWrite(LED_CORRECT, HIGH); delay(20); digitalWrite(LED_CORRECT, LOW);
}
if (cactusX < 24 && cactusX > 6 && dinoY > 26) { currentState = DINO_GAMEOVER; return; }
display.clearDisplay();
display.setCursor(100, 0); display.print(dinoScore);
display.drawLine(0, 50, 128, 50, SSD1306_WHITE);
display.fillRect(cactusX, 36, 6, 14, SSD1306_WHITE); display.fillRect(cactusX - 3, 40, 3, 6, SSD1306_WHITE);
int intY = (int)dinoY;
display.fillRect(14, intY, 10, 8, SSD1306_WHITE); display.drawPixel(20, intY + 2, SSD1306_BLACK); display.fillRect(10, intY + 8, 12, 10, SSD1306_WHITE);
if (!isJumping && (millis() / 100) % 2 == 0) {
display.drawLine(12, intY + 18, 12, intY + 22, SSD1306_WHITE); display.drawLine(16, intY + 18, 18, intY + 20, SSD1306_WHITE);
} else {
display.drawLine(12, intY + 18, 10, intY + 20, SSD1306_WHITE); display.drawLine(18, intY + 18, 18, intY + 22, SSD1306_WHITE);
}
display.display(); delay(20);
}
void handleDinoGameOver() {
display.clearDisplay(); display.setCursor(0, 0); display.println("EXTINCTION!"); display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setTextSize(2); display.setCursor(0, 25); display.print("Score: "); display.println(dinoScore); display.setTextSize(1);
display.setCursor(0, 50); display.println("[Press to Menu]"); display.display();
if (joyButton.pressed()) { gamesMenuSelection = 0; currentState = GAMES_MENU; }
}
void initSnake() {
snakeLength = 3; snakeDir = 1; snakeScore = 0;
snakeX[0] = 16; snakeY[0] = 8; snakeX[1] = 15; snakeY[1] = 8; snakeX[2] = 14; snakeY[2] = 8;
foodX = random(2, 30); foodY = random(2, 14); currentState = SNAKE_GAME;
}
void handleSnakeGame(int joyX, int joyY) {
if (joyY < 1000 && snakeDir != 2) snakeDir = 0;
else if (joyX > 3000 && snakeDir != 3) snakeDir = 1;
else if (joyY > 3000 && snakeDir != 0) snakeDir = 2;
else if (joyX < 1000 && snakeDir != 1) snakeDir = 3;
if (millis() - lastSnakeMove > 150) {
lastSnakeMove = millis();
for (int i = snakeLength - 1; i > 0; i--) { snakeX[i] = snakeX[i - 1]; snakeY[i] = snakeY[i - 1]; }
if (snakeDir == 0) snakeY[0]--; if (snakeDir == 1) snakeX[0]++; if (snakeDir == 2) snakeY[0]++; if (snakeDir == 3) snakeX[0]--;
if (snakeX[0] < 0 || snakeX[0] >= 32 || snakeY[0] < 0 || snakeY[0] >= 16) { currentState = SNAKE_GAMEOVER; return; }
for (int i = 1; i < snakeLength; i++) { if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { currentState = SNAKE_GAMEOVER; return; } }
if (snakeX[0] == foodX && snakeY[0] == foodY) {
if (snakeLength < 64) snakeLength++; snakeScore++;
foodX = random(1, 31); foodY = random(1, 15);
// Blink Correct LED for eating food
digitalWrite(LED_CORRECT, HIGH); delay(30); digitalWrite(LED_CORRECT, LOW);
}
}
display.clearDisplay(); display.fillRect(foodX * 4, foodY * 4, 4, 4, SSD1306_WHITE);
for (int i = 0; i < snakeLength; i++) { display.fillRect(snakeX[i] * 4, snakeY[i] * 4, 4, 4, SSD1306_WHITE); }
display.display();
}
void handleSnakeGameOver() {
display.clearDisplay(); display.setCursor(0, 0); display.println("SNAKE CRASHED!"); display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setTextSize(2); display.setCursor(0, 25); display.print("Apples: "); display.println(snakeScore); display.setTextSize(1);
display.setCursor(0, 50); display.println("[Press to Menu]"); display.display();
if (joyButton.pressed()) { gamesMenuSelection = 0; currentState = GAMES_MENU; }
}
void handleReactionGame() {
switch (reactionSubState) {
case 0:
display.clearDisplay(); display.setCursor(0, 0); display.println("--- LIGHTS OUT ---");
display.println("Press the button"); display.println("the exact moment"); display.println("both LEDs go dark!");
display.setCursor(0, 50); display.println("[Press to Ready]"); display.display();
if (joyButton.pressed()) { reactionSubState = 1; lightsOn = 0; sequenceStartTime = millis(); }
break;
case 1:
display.clearDisplay(); display.setCursor(30, 25); display.setTextSize(2); display.println("READY"); display.setTextSize(1); display.display();
if (millis() - sequenceStartTime > 1000) {
if (lightsOn == 0) {
digitalWrite(LED_CORRECT, HIGH); lightsOn++; sequenceStartTime = millis();
}
else if (lightsOn == 1) {
digitalWrite(LED_WRONG, HIGH); lightsOn++; sequenceStartTime = millis();
}
else {
reactionSubState = 2; randomDelay = random(500, 3500); waitStartTime = millis();
}
}
if (joyButton.pressed()) reactionSubState = 4;
break;
case 2:
if (millis() - waitStartTime > randomDelay) {
turnOffLEDs();
sequenceStartTime = millis(); reactionSubState = 3;
}
if (joyButton.pressed()) reactionSubState = 4;
break;
case 3:
display.clearDisplay(); display.setCursor(50, 25); display.setTextSize(2); display.println("GO!"); display.setTextSize(1); display.display();
if (joyButton.pressed()) { finalReactionTime = millis() - sequenceStartTime; reactionSubState = 5; }
break;
case 4:
turnOffLEDs();
display.clearDisplay(); display.setCursor(10, 20); display.setTextSize(2); display.println("JUMP START"); display.setTextSize(1);
display.setCursor(0, 50); display.println("[Press to Menu]"); display.display();
if (joyButton.pressed()) currentState = GAMES_MENU;
break;
case 5:
display.clearDisplay(); display.setCursor(0, 0); display.println("REACTION TIME:"); display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(0, 25); display.setTextSize(2); display.print(finalReactionTime); display.println(" ms"); display.setTextSize(1);
display.setCursor(0, 50); display.println("[Press to Menu]"); display.display();
if (joyButton.pressed()) currentState = GAMES_MENU;
break;
}
}















Comments