Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Nabadip Nath
Published © GPL3+

Smart Puzzle Game for Kids

Using Arduino UNO, RFID reader and tags, Touch Sensor, LEDs, Buzzer, OLED display

IntermediateFull instructions provided12 hours318
Smart Puzzle Game for Kids

Things used in this project

Hardware components

Mini RFID Unit RC522 Module Sensor
M5Stack Mini RFID Unit RC522 Module Sensor
×1
Arduino UNO
Arduino UNO
×1
Grove - I2C Touch Sensor(MPR121)
Seeed Studio Grove - I2C Touch Sensor(MPR121)
×1
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Yellow
5 mm LED: Yellow
×1
5 mm LED: Green
5 mm LED: Green
×1
SparkFun MicroView - OLED Arduino Module
SparkFun MicroView - OLED Arduino Module
×1
Buzzer
Buzzer
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Power Supply Accessory, Battery Module Cable
Power Supply Accessory, Battery Module Cable
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Schematics

Smart Puzzle Game for Kids

Basic Circuit Diagram

Code

Smart Puzzle Game for Kids

C/C++
Project 1
#include <Adafruit_GFX.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Tone.h>

// Pin Definitions
#define SCL_PIN A1
#define SDA_PIN A0
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define RST_PIN 9
#define SS_PIN 10
#define BUZZER_PIN 5
#define RED_PIN 2
#define ORANGE_PIN 3
#define BLUE_PIN 4
#define YELLOW_PIN 6
#define TOUCH_SENSOR_PIN 7 // Connect KY-036 Touch Sensor to this pin

#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62

// MFRC522 setup
MFRC522 mfrc522(SS_PIN, RST_PIN);

// Game States
enum State {
  IDLE,
  GAME_RUNNING,
  GAME_OVER_DISPLAY,
};

State currentState = IDLE;
unsigned long lastActionTime = 0;
const unsigned long actionInterval = 10000;  // 10 seconds per shape
int scoreX = 0;
int scoreH = 0;
int scoreA = 0;

bool isTouchSensorTouched = false;
bool isGameRunning = false;
bool isFirstTouch = true;
bool isCountdownRestarting = false;

unsigned long gameStartTime = 0;
const unsigned long gameDuration = 30000;  // 30 seconds per game
unsigned long countdownStartTime = 0;

byte currentShapeUID[4];
char currentShape; // Declare currentShape globally

// Shapes and their corresponding RFID UIDs
char shapes[] = {'X', 'H', 'A'};
byte shapeUIDs[][4] = {
  {0x93, 0x33, 0x63, 0xF5},
  {0x03, 0xF6, 0x0B, 0xF5},
  {0xC3, 0x53, 0x52, 0xF5}
};

Tone buzzer;
int melodyPins[] = {8, 11, 12}; // Pins for generating melody tones

int twinkleMelody[] = {
  NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4,
};
int twinkleDurations[] = {
  250, 250, 250, 250, 250, 250
};

int happyBirthdayMelody[] = {
  NOTE_C4, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_F4, NOTE_E4,
};
int happyBirthdayDurations[] = {
  200, 200, 400, 200, 200, 200
};

int janaGanaManaMelody[] = {
  NOTE_E5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5
};
int janaGanaManaDurations[] = {
  250, 250, 250, 250, 250
};

char previousShape = ' '; // Initialize previousShape to an empty character

void setup(void) {
  Serial.begin(115200);
  Serial.println("Hello!");

  pinMode(SCL_PIN, OUTPUT);
  pinMode(SDA_PIN, OUTPUT);
  digitalWrite(SCL_PIN, HIGH);
  digitalWrite(SDA_PIN, HIGH);

  Wire.begin();
  SPI.begin();
  mfrc522.PCD_Init();

  pinMode(RED_PIN, OUTPUT);
  pinMode(ORANGE_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
  pinMode(YELLOW_PIN, OUTPUT);

  buzzer.begin(BUZZER_PIN);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 10);
  display.println("Smart Puzzle Game");
  display.println("for Kids");
  display.display();
  display.clearDisplay();

  pinMode(TOUCH_SENSOR_PIN, INPUT); // Define TOUCH_SENSOR_PIN as an input
}

void controlLEDs(int red, int orange, int blue, int yellow) {
  digitalWrite(RED_PIN, red);
  digitalWrite(ORANGE_PIN, orange);
  digitalWrite(BLUE_PIN, blue);
  digitalWrite(YELLOW_PIN, yellow);
}

void playMelody(int shapeIndex) {
  int *notes;
  int *durations;
  int numNotes;

  switch (shapeIndex) {
    case 0: // 'X'
      notes = twinkleMelody;
      durations = twinkleDurations;
      numNotes = sizeof(twinkleMelody) / sizeof(twinkleMelody[0]);
      break;
    case 1: // 'H'
      notes = happyBirthdayMelody;
      durations = happyBirthdayDurations;
      numNotes = sizeof(happyBirthdayMelody) / sizeof(happyBirthdayMelody[0]);
      break;
    case 2: // 'A'
      notes = janaGanaManaMelody;
      durations = janaGanaManaDurations;
      numNotes = sizeof(janaGanaManaMelody) / sizeof(janaGanaManaMelody[0]);
      break;
    default:
      return; // Invalid shapeIndex
  }

  for (int i = 0; i < numNotes; i++) {
    int noteDuration = durations[i];
    if (notes[i] != 0) {
      buzzer.play(notes[i], noteDuration);
      delay(noteDuration);
      buzzer.stop();
    } else {
      delay(noteDuration);
    }
    delay(50);
  }
}

void displayShape(char shape) {
  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(20, 10);

  switch (shape) {
    case 'X':
      display.println("X");
      break;
    case 'H':
      display.println("H");
      break;
    case 'A':
      display.println("A");
      break;
  }

  display.display();
}

void updateScore(int points) {
  if (currentShape == 'X') {
    scoreX += points;
  } else if (currentShape == 'H') {
    scoreH += points;
  } else if (currentShape == 'A') {
    scoreA += points;
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Score: ");
  display.println((currentShape == 'X') ? scoreX : (currentShape == 'H') ? scoreH : scoreA);
  display.display();
}

void displayTotalScore() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Game Ended");
  display.display();
  delay(2000);
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("Total Score: ");
  display.println(scoreX + scoreH + scoreA);
  display.display();
  delay(3000);

  currentState = GAME_OVER_DISPLAY;
  gameStartTime = millis();
  isCountdownRestarting = false;
}

void startGame() {
  scoreX = 0;
  scoreH = 0;
  scoreA = 0;
  gameStartTime = millis();
  countdownStartTime = millis();
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Game Started");
  display.display();
  delay(1000);
  isGameRunning = true;
  isFirstTouch = false;
  isCountdownRestarting = true;
  getNextRandomShape(); // Use the new function to get a random shape
}

void stopGame() {
  displayTotalScore();
  isGameRunning = false;
}

void getNextRandomShape() {
  char newShape;
  do {
    newShape = shapes[random(3)]; // Choose a random shape
  } while (newShape == previousShape); // Repeat until it's different from the previous shape

  previousShape = newShape; // Update the previousShape

  int selectedMelody;
  switch (newShape) {
    case 'X':
      selectedMelody = 0;
      break;
    case 'H':
      selectedMelody = 1;
      break;
    case 'A':
      selectedMelody = 2;
      break;
  }

  memcpy(currentShapeUID, shapeUIDs[selectedMelody], 4); // Set the current shape's UID
  currentShape = newShape; // Set the current shape
  displayShape(currentShape);

  controlLEDs(0, 0, 0, 0); // Turn off all LEDs
  if (currentShape == 'X') {
    controlLEDs(255, 0, 0, 0); // Red LED
    playMelody(0); // Play melody for 'X'
  } else if (currentShape == 'H') {
    controlLEDs(0, 255, 0, 0); // Green LED
    playMelody(1); // Play melody for 'H'
  } else if (currentShape == 'A') {
    controlLEDs(0, 0, 255, 0); // Blue LED
    playMelody(2); // Play melody for 'A'
  }

  currentState = GAME_RUNNING;
  lastActionTime = millis();
}

void displayGameInfo(unsigned long remainingTime, char shape, int currentScore) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Time Left: ");
  display.println(remainingTime / 1000); // Convert to seconds
  display.print("Shape: ");
  display.println(shape);
  display.print("Score: ");
  display.println(currentScore);
  display.display();
}

void loop(void) {
  if (isGameRunning) {
    unsigned long elapsedTime = millis() - gameStartTime;
    unsigned long remainingTime = max(0, gameDuration - elapsedTime);

    // Display time left, shape, and score for that shape
    displayGameInfo(remainingTime, currentShape, (currentShape == 'X') ? scoreX : (currentShape == 'H') ? scoreH : scoreA);

    // Check if the game has ended after 30 seconds
    if (elapsedTime >= gameDuration) {
      stopGame();
      return; // Exit the loop early to avoid playing the buzzer and LEDs again
    }
  }

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();

  if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
    Serial.println("Found an RFID card!");

    if (memcmp(mfrc522.uid.uidByte, currentShapeUID, 4) == 0) {
      controlLEDs(0, 0, 0, 0); // Turn off all LEDs
      buzzer.stop(); // Turn off the buzzer
      if (currentShape == 'X') {
        scoreX += 2;
      } else if (currentShape == 'H') {
        scoreH += 3;
      } else if (currentShape == 'A') {
        scoreA += 5;
      }
      // Proceed to the next shape immediately
      getNextRandomShape(); // Use the new function to get a random shape
    }
  }

  // Check if the KY-036 Touch Sensor is touched
  if (digitalRead(TOUCH_SENSOR_PIN) == LOW) {
    if (!isTouchSensorTouched) {
      isTouchSensorTouched = true;

      if (isFirstTouch) {
        startGame();
      } else if (currentState == GAME_OVER_DISPLAY && !isCountdownRestarting) {
        startGame();
      } else if (isGameRunning) {
        stopGame();
      }
    }
  } else {
    isTouchSensorTouched = false;
  }
}

Credits

Nabadip Nath

Nabadip Nath

1 project • 0 followers
Technologist; Sr. Technical Assistant, EPD, IIT Guwahati

Comments