Saimon říká:


Hra „Simon říká“ realizovaná pomocí Arduina je skvělým projektem, jehož cílem je spojit zábavu s učením a rozvojem technických dovedností. Hráč se snaží zapamatovat a správně zopakovat sekvenci světelných nebo zvukových signálů, čímž si trénuje paměť, koncentraci a rychlé reakce. Arduino zde slouží jako řídicí jednotka, která ovládá LED diody, tlačítka a případně piezo reproduktor, a zároveň zajišťuje logiku celé hry. Projekt je ideální pro začátečníky i pokročilé, protože umožňuje naučit se základy elektroniky, programování v jazyce C/C++ a práci s mikrokontroléry. Navíc nabízí prostor pro kreativitu – lze přidávat nové funkce, měnit obtížnost nebo design, a tím si vytvořit vlastní unikátní verzi této klasické paměťové hry.

Schéma zapojení:

  • Napájení a základní deska:
    • Používá se Arduino (např. Uno nebo Nano), které řídí LED diody, tlačítka a reproduktor.
  • LED diody (výstup):
    • Červená LED je připojena na pin 10
    • Modrá LED je připojena na pin 11
    • Zelená LED je připojena na pin 9
    • Žlutá LED je připojena na pin 8
  • Tlačítka (vstup):
    • Červené tlačítko je připojeno na pin 6
    • Modré tlačítko je připojeno na pin 7
    • Zelené tlačítko je připojeno na pin 5
    • Žluté tlačítko je připojeno na pin 4
  • Reproduktor (výstup):
    • Piezo reproduktor je připojen na pin 12
    • Slouží k přehrávání tónů podle barvy (např. červená = 200 Hz, modrá = 400 Hz atd.)
  • Další poznámky:
    • Tlačítka by měla být zapojena s pull-down rezistorem nebo využít interní pull-up rezistor Arduina.
    • LED diody by měly mít sériový rezistor (např. 220 Ω) pro omezení proudu.
    • Reproduktor může být piezo typ, který zvládne jednoduché tóny pomocí funkce tone().

Kód pro Arduino:

Kód je napsán v C++ a používá třídy, pokud to uvidíte poprvé, nebojte se, jsou to takové kontejnery na funkce a proměnné. Pokud se chcete dozvědět více, vyhledejte výukovou lekci C++ o třídách.

class Game {
    private:
      int debounce(int last, int buttonPin);
      void playNote(int note, int noteSpeed) const;
      void flashLed(int led, int flashSpeed) const;
    public:
      static const int RED_PIN;
      static const int BLUE_PIN;
      static const int GREEN_PIN;
      static const int YELLOW_PIN;
      static const int MICROPHONE_PIN;
      static const int RED_BUTTON_PIN;
      static const int BLUE_BUTTON_PIN;
      static const int GREEN_BUTTON_PIN;
      static const int YELLOW_BUTTON_PIN;
      static const int RED_TONE;
      static const int BLUE_TONE;
      static const int GREEN_TONE;
      static const int YELLOW_TONE;
      static const int GAMEOVER_TONE;
      int gameLevel[200];
      int gameSpeed;
      int lastButtonValue;
      int currentLevel;
      int gameIsOver;
      double gameDifficulty;
      enum color { YELLOW, GREEN, RED, BLUE };
    public:
    Game();
    Game(int);
    void playLevel();
    int userInput();
    int gameOver();
    int getNote(int note) const;
    int pinToColorCode(int);
    int colorCodeToPin(int);
    int readButton(int buttonPin);
};

/* Pin settings */
static const int Game::MICROPHONE_PIN       = 12;
static const int Game::BLUE_PIN             = 11;
static const int Game::RED_PIN              = 10;
static const int Game::GREEN_PIN            = 9;
static const int Game::YELLOW_PIN           = 8;
static const int Game::BLUE_BUTTON_PIN      = 7;
static const int Game::RED_BUTTON_PIN       = 6;
static const int Game::GREEN_BUTTON_PIN     = 5;
static const int Game::YELLOW_BUTTON_PIN    = 4;
/* Tone frequencies */
static const int Game::RED_TONE             = 200;
static const int Game::BLUE_TONE            = 400;
static const int Game::YELLOW_TONE          = 600;
static const int Game::GREEN_TONE           = 800;
static const int Game::GAMEOVER_TONE        = 1000;

// Construct and initialize the Game object.
Game::Game(int difficulty) : gameSpeed(1000), lastButtonValue(-1), currentLevel(0), gameDifficulty(difficulty), gameIsOver(0) {
    Serial.print("Constructing game object with difficulty: ");
    Serial.println(difficulty);
    pinMode(Game::MICROPHONE_PIN, OUTPUT);
    pinMode(Game::BLUE_PIN, OUTPUT);
    pinMode(Game::RED_PIN, OUTPUT);
    pinMode(Game::GREEN_PIN, OUTPUT);
    pinMode(Game::YELLOW_PIN, OUTPUT);
}

Game::Game() : gameSpeed(1000), lastButtonValue(-1), currentLevel(0), gameDifficulty(10), gameIsOver(0) {
    Serial.println("Constructing game object");
    pinMode(Game::MICROPHONE_PIN, OUTPUT);
    pinMode(Game::BLUE_PIN, OUTPUT);
    pinMode(Game::RED_PIN, OUTPUT);
    pinMode(Game::GREEN_PIN, OUTPUT);
    pinMode(Game::YELLOW_PIN, OUTPUT);
}

/*
 * Makes sure the button is pressed only once.
 */
int Game::debounce(int last, int buttonPin) {
      int current = digitalRead(buttonPin);
      if (last != current)
      {
        delay(5);
        current = digitalRead(buttonPin);
      }
      return current;
}

/*
 * Plays a note. 
 * Receives the button number and plays the corresponding note.
 */
void Game::playNote(int note, int noteSpeed) const {
    Serial.print("playNote: Playing note: ");
    Serial.print(note);
    Serial.print(" with speed: ");
    Serial.println(noteSpeed);
    
    note = Game::getNote(note);
    
    tone(Game::MICROPHONE_PIN, note, noteSpeed);  
}

/*
 * Returns the corresponding color code based on pin.
 */
int Game::colorCodeToPin(int value) {
    int ret_val = -1;
   
    switch(value) {
      case RED:
          ret_val = Game::RED_PIN;
          break;
      case GREEN:
          ret_val = Game::GREEN_PIN;
          break;
      case BLUE:
          ret_val = Game::BLUE_PIN;
          break;
      case YELLOW:
          ret_val = Game::YELLOW_PIN;
          break;
      default:
        Serial.println("colorCodeToPin: Invalid value!");
        delay(1000);
        exit(0);
    }

    return ret_val;
}

/*
 * Converts the button pin to a color code.
 */
int Game::pinToColorCode(int value) {
    int ret_val = -1;
    switch(value) {
        case Game::RED_BUTTON_PIN:
            ret_val = RED;
            break;
        case Game::GREEN_BUTTON_PIN:
            ret_val = GREEN;
            break;
        case Game::BLUE_BUTTON_PIN:
            ret_val = BLUE;
            break;
        case Game::YELLOW_BUTTON_PIN:
            ret_val = YELLOW;
            break;
        default:
          Serial.println("pinToColorCode: Invalid value!");
          delay(1000);
          exit(0);
    }

    return ret_val;
}

/*
 * The the corresponding note based on the color code it receives.
 */
int Game::getNote(int note) const {
    int return_value = -1;
    switch(note) {
      case YELLOW:
          return_value = Game::YELLOW_TONE;
          break;
      case GREEN:
          return_value = Game::GREEN_TONE;
          break;
      case RED:
          return_value = Game::RED_TONE;
          break;
      case BLUE:
          return_value = Game::BLUE_TONE;
          break;
      case 4:
          return_value = Game::GAMEOVER_TONE;
          break;        
      default:
        Serial.println("playNote: Error! Invalid note!");
        delay(1000);
        exit(0);
    }
    return return_value;
}

/*
 * Flashes a led. Receives the led code and sets it to the corresponding pin.
 */
void Game::flashLed(int led, int flashSpeed) const {
    Serial.print("flashLed: Flashing LED: ");
    Serial.print(led);
    Serial.print(" with speed: ");
    Serial.println(flashSpeed);

    led = Game::colorCodeToPin(led);

    digitalWrite(led, HIGH);
    delay(flashSpeed);
    digitalWrite(led, LOW);
}

/*
 * Plays the next level.
 */
void Game::playLevel() {
  Serial.print("playLevel: Playing on level: ");
  Serial.println(Game::currentLevel);
  Game::gameLevel[Game::currentLevel] = random(0, 4); // Create a random move every time. 0 to 4 exclusive.
  ++Game::currentLevel;
  int nextDificulty = Game::gameDifficulty * Game::currentLevel;
  if (Game::gameSpeed - nextDificulty >= 10) {
    Game::gameSpeed -= nextDificulty; // decrease the speed;
  }
  
  // Play all the moves
  for (int i = 0; i < Game::currentLevel; ++i) {
      Game::playNote(Game::gameLevel[i], Game::gameSpeed);
      Game::flashLed(Game::gameLevel[i], Game::gameSpeed);
  }
}

/*
 * Reads the button value and returns the following codes:
 * 0 - Yellow 1 - Green 2 - Red 3 - Blue
 */
int Game::readButton(int buttonPin) {
    int currentButtonValue = Game::debounce(Game::lastButtonValue, buttonPin);
    int return_value = -1;
    if (lastButtonValue == LOW & currentButtonValue > LOW) {
        return_value = Game::pinToColorCode(buttonPin);
    }
    Game::lastButtonValue = currentButtonValue;
    if (return_value >= 0) {
      Serial.print("readButton: Received signal from button number: ");
      Serial.println(return_value);
    }
    return return_value;
}

int Game::gameOver() {
    Serial.println("game_is_over: Checking if game is over!");
    if (Game::gameIsOver) {
      Serial.println("game_is_over: Game is over!");
    }
    return Game::gameIsOver;
}

/*
 * Gets the user button presses and checks them to see if they're good.
 */
int Game::userInput() {
    for (int i = 0; i < Game::currentLevel; ++i) {
      Serial.println("userInput: User is pressing.");
      int buttonPressed = -1;
      while(true) {
          buttonPressed = readButton(Game::RED_BUTTON_PIN);
          if (buttonPressed != -1) { break; }
          buttonPressed = readButton(Game::GREEN_BUTTON_PIN);
          if (buttonPressed != -1) { break; }
          buttonPressed = readButton(Game::YELLOW_BUTTON_PIN);
          if (buttonPressed != -1) { break; }
          buttonPressed = readButton(Game::BLUE_BUTTON_PIN);
          if (buttonPressed != -1) { break; }
      }

      if (buttonPressed != gameLevel[i]) {
          Game::playNote(4, 100); // game over note, and game over note speed.
          Game::flashLed(buttonPressed, 1000);
          return 0;
      }
      Game::playNote(buttonPressed, Game::gameSpeed);
      Game::flashLed(buttonPressed, Game::gameSpeed);
    }
    delay(500);
    return 1;
}

Game g(50); //  Constructs the game object.
void setup() {
  Serial.begin(9600);
  randomSeed(0);
}

void loop() {
  if (g.gameOver()) { 
    delay(1000); // Wait for serial to finish printing.
    /*
      On Arduino exit(0) disables the interrupts
      and goes in an infinite loop.
      On your PC exit(0) closes the program and
      tries to clean up resources.
    */
    exit(0);
  }
    g.playLevel();
    if (g.userInput() == 0) {
        g.gameIsOver = 1;
    }
}

Další podobné články

ROBOTICKÉ RAMENO

Stavebnice obsahuje všechny potřebné díly na sestavení robotnického ramene včetně spojovacího materiálu, pouze je nutné dokoupit čtyři kusy MIKRO SERV SG90. Dále je nutné dokoupit řídící jednotku já jsem použil domácí zásoby ARDUINO NANO a pro něho pak modul ARDUINO NANO IO SHIELD pro jednoduchost zapojení. Díly pro sestavení ramene jdou dobře tzv. vylamovat „vypadávají skoro sami. K servům pokud použijete nové tak doporučuji je před montáží odzkoušet zda jsou funkční v plném rozsahu tj. od 0° do 180°, po namontování a zjištění že servo nefunguje to pak opravdu dost zahýbá s nervy. 

Electronic TiltMaze

Cílem tohoto projektu je vytvoření jednoduchého ovládacího systému, který umožňuje naklápění dvou servomotorů pomocí analogového joysticku. Platforma řízená servomotory může simulovat pohyb například v ose X a Y — tedy naklánění doleva/doprava a dopředu/dozadu. Tento systém může sloužit jako základ pro různé aplikace:

- Manuální ovládání kamery nebo senzoru (např. na pohyblivé konstrukci nebo robotovi)
- Interaktivní ovládací panel pro školní projekty nebo herní ovladač