siliconchris
Created March 16, 2018 © CC BY-NC

Ye Olde Crate

Ye Olde Crate - an RFID locked treasure hunt chest with sound, lights and even a antique style handle .

IntermediateShowcase (no instructions)36
Ye Olde Crate

Things used in this project

Hardware components

Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
I presume any Arduino will do, but I used a 5V Arduino Pro Mini
×1
Adafruit AudioFX Mini Sound Board
×1
Adafruit Powerboost 500 Basic
×1
RFID reader (generic)
I reckon any TX/RX capable RFID Reader will do
×1
Servos (Tower Pro MG996R)
×2
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×2
Big Red Dome Button
SparkFun Big Red Dome Button
×1
Resistor 100k ohm
Resistor 100k ohm
×3
Resistor 221 ohm
Resistor 221 ohm
×3
LEDs
1 Red, 1 Green, 1 Blue LED
×3
4xAA battery holder
4xAA battery holder
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Schematics

Fritzing file for the schematics of the electronic components

Code

YeOldeCrate

Arduino
The main program of Ye Olde Crate
/* 
  Ye Olde Crate - YOC
  
  Ye Olde Crate is an RFID locked treasure hunt chest.
  .

         #############################################
        #                                          # #
       #              +------+                    #  #
      #               | RFID |                   #   #
     #                +------+                  #    #
    #                                          #     #
    ############################################     #
    #                                          #     #
    #  Find the gems, place them on the chest  #     #
    #           and claim your prize           #     #
    #                                          #     #
    #             LOCKED    OPEN               #     #
    #                ^                         #    #
    #                 \                        #   #
    #                  \                       #  #
    #                                          # #
    #############################################


  The idea is quite simple and has been made probably a hundred times. Nevertheless I wanted to build 
  my own. 

  The game behind Ye Olde Crate is like a treaure hunt. People get clues as to where to find items,
  say pieces of paper with riddles on them. Attached to these riddle-papers is an RFID tag. After 
  successfully retrieving all riddle papers the last clue shall direct the players to the crate. So 
  this first part is what we call a "Schnitzeljagd" in Germany.

  When the players finally find the crate, the second part begins. The players must now scan each paper
  (actually the attached RFID Tag) in the correct order on the crate to open it. For younger players 
  the crate features a simpler game mode in where simply all tags need to be scanned in any order.
  
  Additionally the crate provides three different levels of gameplay:
  * Beginner: 3 tags need to be scanned - either in consecuitve order or random
  * Advanced: 6 tags need to be scanned - either in consecuitve order or random
  * Export:   9 tags need to be scanned - either in consecuitve order or random
  
  Each scanned tag is acknowledged by a blue light, an arrow handle moving to the word "U Found It"
  and a sound. Once all required tags are scanned and the box unlocks, a green light is lit up, the 
  arrow handle moves to the word "Open" and a final sound is played.

  To start or stop the game there is a 10th RFID tag, the so called Master Tag. Upon scanning of the
  Master Tag, the game starts. Once the game is running, you can use the Master Tag to instantly open it.
  

  This all works because inside Ye Olde Crate a couple of devices are installed:
  
  1.  Arduino Pro Mini    - one might say the brain
  2.  RFID Reader         - to scan the tags
  3.  Adafruit AudioFX    - to play sounds
  4.  Lock Servo          - to lock/unlock the chest
  5.  Status Servo        - to move the arrow handle to one of three positions:
                            "Locked", "Open", "U found it"
  6.  Red LED             - indicating craete is locked
  7.  Green LED           - indicating crate is unlocked
  8.  Blue LED            - indicating Tag Found
  9.  Game Mode Button    - to change the game mode from consecutive order to random scan 
  10. Game Level Button   - to change the number of required tags: 3, 6 or 9 tags
  11. Sound Button        - to switch between 5 different sound sets: Mario, Zelda, Indiana Jones,
                            Evil or Comic
  
  
  
  USED PINS:
  Servo motors for lock and mechanical handle to indicate status:
  lockServoPin              9     locks the chest
  chestStateServoPin        10    indicates locking and tag found status


  Audio FX Board to play sounds. Fr this there are 2 different methods:
  The preferred method is to use the Adafruit AudioFX board with software serial. This mode allows for 
  multiple sound sets (5 are posssible in current implementation) and uses only 1 Pin on the Arduino.
  SFX_TX                    4     transmits data to arduino
  SFX_RX                    5     receives commands from arduino 
  SFX_RST                   6     NOT CONNECTED and NOT USED


  The second mode to use the AudioFX Board requires 6 pins on the Arduino as each pin is connected
  to one specific sound to play. I would suggest to NOT use this mode as izt takes up more pins and also
  lacks the ability to choose different sound sets. Finally this mode does not allow the crate to voice
  game mode and level.
  gameStartSoundPin         4
  tagScannedSoundPin        5
  wrongTagScannedSoundPin   7
  masterTagScannedPin       A7
  gameWonSoundPin           8
  gameLostSoundPin          A6
  
  
  RFID Reader board
  RFID_TX                   8     transmits tag data to Arduino
  RFID_RX                   7     NOT CONNECTED and NOT USED


  LED Lights
  chestIsLockedLedPin       A5    a red light indicating that the chest is locked
  chestIsOpenLedPin         A4    a green led indicating that the chest is unlocked
  tagScannedLedPin          A3    a blue led indicates that a correct tag was scanned
  

  Buttons
  gameLevelSwitchPin        A1    switch between 3, 6 or 9 tags to be scanned to open the chest
  gameVariantSwitchPin      A2    switch between consecutive or random tag scan order
  soundSetSelectSwitch      A0    switch between 5 different sound sets
  
  Based on Swee by BARRAGAN <http://barraganstudio.com>
*/

// comment out to suppress logging to serial console
#define DEBUG 1


// to activate the servo that opens/closes the lock, uncomment the following line
// this servo drives a little handle that moves a bar in and out of a hole in the chest and thus
// opens or locks the chest. It can move to 2 different positions
//    0 = locked         - servo position to close the lock
//  180 = open/unlocked  - servo position to open the lock
#define LOCKSERVO 1

// to activate the second servo motor that controls the mechanical arrow, uncomment the following line
// this servo motor handle is used to indicate open / locked state and also to show when a tag is found.
// It is attached to a little arrow handle and can move to 3 different positions: 
//   45 = open       - the arrow should point to a marking for open
//   90 = tag found  - the arrow should point to a marking for tag found
//  135 = locked     - the arrow should point to a marking for locked
//#define CHESTSTATESERVO 1


// to activate the Adafruit AudioFX Board to sound certain states, uncomment one of the next two opetions
// The Adafruit AudioFX Board should then contain 6 sounds
//    1.  Game is Started (spoken Game Variant and Level) - t01.wav in case of TRGAUDIOFX
//    2.  Tag scanned (a short sound)                     - t02.wav in case of TRGAUDIOFX
//    3.  Wrong Tag scanned (you like evil laugh?)        - t03.wav in case of TRGAUDIOFX
//    4.  Master Tag scanned (a march)                    - t04.wav in case of TRGAUDIOFX
//    5.  Game is won (maybe a fanfare)                   - t05.wav in case of TRGAUDIOFX
//    6.  Game is lost (even more evil laugh?)            - t06.wav in case of TRGAUDIOFX
// TO USE PINS TO CONTROL WHICH SOUND TO PLAY USE THIS LINE
//#define TRGAUDIOFX 1

// you can also go for software serial for the AudioFX Board control. In this case uncomment this line.
// To see how the sound files are constructed see below in section SSAUDIOFX 
// TO USE THE SOFTWARE SERIAL CONTROL OF THE BOARD USE THIS LINE
#define SSAUDIOFX 1

// in case you go with the SSAUDIOFX variant - means controlling audio playback with software serial
// you can also provide filenames to the player. That gives the capability to have different sound sets.
// To switch between those, you can have a button in the chest - if so, activate this line
//#define SOUNDSETSELECTSWITCH 1


// to activate the RFID reader, uncomment the following line
#define RFIDREADER 1


// to activate the LED that shows the chest locked state, uncomment the following line
#define CLSLIGHT 1
// to activate the LED that shows the chest open state, uncomment the following line
#define OPNLIGHT 1
// to activate the LED that shows when a tag is scanned, uncomment the following line
#define TAGLIGHT 1


// if you have the switch to choose the level (beginner, advanced or expert) uncomment this line
//#define GAMELEVELSWITCH 1

// if you have the on/off switch to choose the game version (tags must be scanned in random or consecutive order) uncomment this line
#define GAMETAGORDERSWITCH 1






#ifdef LOCKSERVO
  #include <Servo.h>  
  Servo lockServo;  // create servo object to control the lock servo
  const byte lockServoPin = 9;
#endif

#ifdef CHESTSTATESERVO
  #ifndef LOCKSERVO
    #include <Servo.h>    // in case the lock is disabled, we need to include the servo lib here as it is not done above
  #endif
  Servo chestStateServo; // create servo object to control the servo that show the chest state
  const byte chestStateServoPin = 10;
  
  // POSITIONS TO INDICATE LOCK-STATE AND FOUND TAG WITH THE ARROW
  const byte chestStateOpenPos = 45;
  const byte chestStateTagFoundPos = 90;
  const byte chestStateLockPos = 135;
#endif


#ifdef TRGAUDIOFX
  // We need six pins to trigger the sounds
  const byte gameStartSoundPin        = 4;  //  1.  Game is Started (maybe a fanfare)             t01.wav
  const byte tagScannedSoundPin       = 5;  //  2.  Tag scanned (a short sound - maybe Zelda)     t02.wav
  const byte wrongTagScannedSoundPin  = 7;  //  3.  Wrong Tag scanned (you like evil laugh?)      t03.wav
  const byte masterTagScannedPin      = A7; //  4.  The Master Tag was scanned (something cool)   t04.wav
  const byte gameWonSoundPin          = 8;  //  5.  Game is won (could be we are the chaompions)  t05.wav
  const byte gameLostSoundPin         = A6; //  6.  Game is lost (even more evil laugh)           t06.wav
#endif

#ifdef SSAUDIOFX
  #ifndef SoftwareSerial_h
    #include <SoftwareSerial.h>
  #endif
  
  #include "Adafruit_Soundboard.h"
  
  // Choose any two pins that can be used with SoftwareSerial to RX & TX
  #define SFX_TX 4
  #define SFX_RX 5
  
  // Connect to the RST pin on the Sound Board
  #define SFX_RST 6   // NOT USED
  
  // we'll be using software serial
  SoftwareSerial ssaudiofx = SoftwareSerial(SFX_TX, SFX_RX);
  
  // pass the software serial to Adafruit_soundboard, the second
  // argument is the debug port (not used really) and the third 
  // arg is the reset pin
  Adafruit_Soundboard sfx = Adafruit_Soundboard(&ssaudiofx, NULL, SFX_RST);
  // can also try hardware serial with
  // Adafruit_Soundboard sfx = Adafruit_Soundboard(&Serial1, NULL, SFX_RST);

  // next define some nice tunes we can play - I decided to opt for a switch to choose between different styles
  #ifdef SOUNDSETSELECTSWITCH
    const byte soundSetSelectPin = A0; // the pin where the push button to choose the sound set is connected to
  #endif

  // these constants are used to construct the final sound file name
  // I have 5 pre defined sound sets
  //   1. Super Mario
  //   2. Legend of Zelda
  //   3. Indiana Jones
  //   4. Evil Laughing and sounds
  //   5. Comic Style
  // The final filename will be of the form:
  //
  //   s[sound set number]_t0[sound number].wav
  //
  // For example, the startup sound of Indiana Jones will be in the form of
  //
  //   s3_t01.wav
  //
  const char soundSetPrefix = 's';    // the prefix for the sound set
  byte soundSetNumber = 1;            // the sound set itself - if you do not have the switch this is hardcoded on compile time
  const byte trackPrefix[] = "_t0";   // the prefix for the sound number / in the call to playSound()
  const char fileExtension[] = ".wav";// the .wav extension
#endif


#ifdef RFIDREADER
  // based on work by Michael Schoeffler, http://www.mschoeffler.de
  #ifndef SoftwareSerial_h
    #include <SoftwareSerial.h>
  #endif

  #define RFID_TX 8
  #define RFID_RX 7  // NOT USED
    
  // RFID DATA FRAME FORMAT: 1byte head (value: 2), 10byte data (2byte version + 8byte tag), 2byte checksum, 1byte tail (value: 3)
  const byte BUFFER_SIZE = 14; 
  const uint8_t DATA_TAG_SIZE = 8; // 8byte tag
  
  SoftwareSerial ssrfid = SoftwareSerial(RFID_TX, RFID_RX); 
  
  uint8_t buffer[BUFFER_SIZE]; // used to store an incoming data frame 
  uint16_t buffer_index = 0;
#endif


#ifdef CLSLIGHT
  const byte chestIsLockedLedPin = A5;
#endif
#ifdef OPNLIGHT
  const byte chestIsOpenLedPin = A3;
#endif
#ifdef TAGLIGHT
  const byte tagScannedLedPin = A4;
#endif


#ifdef GAMELEVELSWITCH
  // pin to which the turn dial switch to select the difficulty level is connected to
  const byte gameLevelSwitchPin = A1;   
#endif

#ifdef GAMETAGORDERSWITCH
  // pin to which the turn push on/off switch to select between random and consecutive tag-order is connected to
  const byte gameVariantSwitchPin = A2;
  byte lastGameVariantSelect;
#endif



// POSITIONS TO LOCK / UNLOCK THE CHEST
const byte lockClosePos = 0;                  // servo position to close the lock
const byte lockOpenPos = 180;                 // servo position to open the lock
uint16_t pos = lockOpenPos;                   // variable to store the servo position


const byte expertLevel = 9;                   // go get em all
const byte advancedLevel = 6;                 // intermediate play
const byte beginnerLevel = 3;                 // for kids or very short rounds
byte numberOfTagsToOpenLock = beginnerLevel;  // how many tags are needed for this round (set via dial switch)
// can be c = consecutive order (in which case the tags must be scanned in the order 1 2 3 ...) or r = random
char gameVariant = 'r';

byte numberOfTagsFound = 0;                   // how many tags did the player find? If this equals numberOfTagsToOpenLock, th elock opens
byte lastNumberOfTagsFound = 0;               // used in the loop to check if we want to light up the tag found light, or sound this event
                                              // only in case it differs from the var numberOfTagsFound, the light/sound/arrow is triggered


//TODO: check if we can put this into PROGMEM to save space
//const uint16_t knownTags[] = {7680128, 7728564, 7668489, 7719795, 7695644, 3229021, 7691044, 7707761, 3247061, 7867919};

const uint16_t knownTag1 = (unsigned char) 7680128;     // Tag 1 
const uint16_t knownTag2 = (unsigned char) 7728564;     // Tag 2
const uint16_t knownTag3 = (unsigned char) 7668489;     // Tag 3
const uint16_t knownTag4 = (unsigned char) 7719795;     // Tag 4
const uint16_t knownTag5 = (unsigned char) 7695644;     // Tag 5
const uint16_t knownTag6 = (unsigned char) 3229021;     // Tag 6
const uint16_t knownTag7 = (unsigned char) 7691044;     // Tag 7
const uint16_t knownTag8 = (unsigned char) 7707761;     // Tag 8
const uint16_t knownTag9 = (unsigned char) 3247061;     // Tag 9
const uint16_t knownTagX = (unsigned char) 7867919;     // Master Tag - used to close the lock. Also instantly opens the lock

boolean tag1Found = false;
boolean tag2Found = false;
boolean tag3Found = false;
boolean tag4Found = false;
boolean tag5Found = false;
boolean tag6Found = false;
boolean tag7Found = false;
boolean tag8Found = false;
boolean tag9Found = false;
boolean tagXFound = false;

unsigned long gameStartTimeInMillis = 0;      // timer for millis, when the last tag was scanned - to avoid rereading the same tag over and over again
unsigned long gameRunTimeInSeconds = 0;       // how long in secnds took it the players to open the chest
const uint16_t minimalGameTime = 30000;       // how long in millis between each scanning of a tag
unsigned long tagScannedInMillis = 0;         // time in millis when a tag was scanned, used for lighting up the tag scan light
const uint16_t keepTagScannedIndicator = 5000;// number of milliseconds until the light indicating a scanned tag is turned off again


boolean chestIsLocked = false;                // is set to true in case the chest is locked - operated by switchLockState() function
boolean gameIsStarted = false;                // this locks the box, turning it into a treasure chest


void setup() {
  #ifdef DEBUG
    Serial.begin(38400);
  #endif
  
  #ifdef CLSLIGHT
    pinMode(chestIsLockedLedPin, OUTPUT);
  #endif
  #ifdef OPNLIGHT
    pinMode(chestIsOpenLedPin, OUTPUT);
  #endif
  #ifdef TAGLIGHT
    pinMode(tagScannedLedPin, OUTPUT);
  #endif
  
  #ifdef GAMELEVELSWITCH
    pinMode(gameLevelSwitchPin, INPUT);
  #endif
  
  #ifdef GAMETAGORDERSWITCH
    pinMode(gameVariantSwitchPin, INPUT);
  #endif

  
  #ifdef LOCKSERVO
    lockServo.attach(lockServoPin);   // attaches the servo on pin 9 to the servo object
    lockServo.write(lockOpenPos);     // make sure lock is open
    delay(15);
  #endif

  #ifdef CHESTSTATESERVO
    chestStateServo.attach(chestStateServoPin); // attaches the servo on pin 10 to the servo object
    chestStateServo.write(chestStateOpenPos);
    delay(15);
  #endif

  
  #ifdef RFIDREADER
    ssrfid.begin(9600);
    ssrfid.listen(); 
  #endif
  
  
  #ifdef TRGAUDIOFX
    pinMode(gameStartSoundPin,        OUTPUT);
    pinMode(tagScannedSoundPin,       OUTPUT);
    pinMode(wrongTagScannedSoundPin,  OUTPUT);
    pinMode(masterTagScannedPin,      OUTPUT);
    pinMode(gameWonSoundPin,          OUTPUT);
    pinMode(gameLostSoundPin,         OUTPUT);
  #endif
  
  #ifdef SSAUDIOFX
    // softwareserial at 9600 baud
    ssaudiofx.begin(9600);
    // can also do Serial1.begin(9600)
  
    if (!sfx.reset()) {
      Serial.println("Not found");
      while (1);
    }

    #ifdef SOUNDSETSELECTSWITCH
      pinMode(soundSetSelectPin, INPUT);
    #endif
  #endif
  
  
  #ifdef DEBUG
    Serial.print(F("Close the chest and scan the Master tag. Then scan "));Serial.print(numberOfTagsToOpenLock);Serial.print(F(" tags")); 
    if (gameVariant == 'c') Serial.print(F(" in correct order")); 
    Serial.println(F(" to open the chest"));
  #endif
}



void loop() {
  // ONLY WHEN THE MASTER TAG WAS SWIPED, THE GAME STARTS AND FROM THEN ON WE CHECK IF THE BOX SHALL BE OPENED
  if (gameIsStarted) {

    // a NEW tag was scanned
    if (lastNumberOfTagsFound != numberOfTagsFound) {
      #ifdef DEBUG
        Serial.print(F("found tag # "));Serial.println(numberOfTagsFound);
      #endif
      
      #ifdef TAGLIGHT
        digitalWrite(tagScannedLedPin, HIGH);
      #endif

      #ifdef CHESTSTATESERVO
        chestStateServo.write(chestStateTagFoundPos);        // indicate tag found status with the arrow
      #endif

      // sound 2 is the track for a found tag ;-)
      playSound(2);
            
      lastNumberOfTagsFound = numberOfTagsFound;
    }
    
    if (numberOfTagsFound == numberOfTagsToOpenLock || tagXFound) {
      gameRunTimeInSeconds = ( ( millis() - gameStartTimeInMillis ) / 1000 );
      #ifdef DEBUG
        Serial.print(F("You found the "));if (tagXFound) Serial.print(F("master")); else Serial.print(F("last")); 
        Serial.print(F(" tag! The price is yours after "));
        Serial.print(gameRunTimeInSeconds);Serial.println(F(" seconds"));
      #endif
      
      switchLockState();  // open the chest
      indicateChestStatus();
      
      // sound 4 is the track for game won
      playSound(4);
      
      delay(10000);
      resetChest();       // make sure to reset all vars to default
      delay(100000);
      stopTheGame();      // and finally stop the game
    }
  } else {
    // ONLY IN CASE THE GAME IS NOT YET STARTED, ONE MAY CHANGE LEVEL AND VARIANT
    // Game level switch --> beginner, advanced, expert
    #ifdef GAMELEVELSWITCH
      byte gLevel = digitalRead(gameLevelSwitchPin);
      delay(50);
      if (gLevel == HIGH) {
        #ifdef DEBUG
          Serial.print(F("Game Level Button pressed"));
        #endif
        if (numberOfTagsToOpenLock == beginnerLevel) {
          // ADVANCED
          #ifdef DEBUG
            Serial.println(F("  Advanced Level chosen"));
          #endif
          numberOfTagsToOpenLock = advancedLevel;
        } else if (numberOfTagsToOpenLock == advancedLevel) {
          // EXPERT
          #ifdef DEBUG
            Serial.println(F("  Expert Level chosen"));
          #endif
          numberOfTagsToOpenLock = expertLevel;
        } else {
          // BEGINNER
          #ifdef DEBUG
            Serial.println(F("  Beginner Level chosen"));
          #endif
          numberOfTagsToOpenLock = beginnerLevel;
        }
      }
    #endif

    
    #ifdef GAMETAGORDERSWITCH
      // check whether the game Variant switch is HIGH = consecutive order needed / or LOW = random order is ok
      byte gVariant = digitalRead(gameVariantSwitchPin);
      delay(50);
      if (gVariant != lastGameVariantSelect) {
        #ifdef DEBUG
          Serial.print(F("  gVariant: "));Serial.print(gVariant);Serial.print(F(" / lastGameVariantSelect: "));Serial.println(lastGameVariantSelect);
        #endif
        lastGameVariantSelect = gVariant;
        if (gVariant == HIGH) {
          gameVariant = 'c';
          #ifdef DEBUG
            Serial.println(F(" Consecutive Game-Vaiant selected"));
          #endif
        } else if (gVariant == LOW) { 
          gameVariant = 'r';
          #ifdef DEBUG
            Serial.println(F(" Random Game-Variant selected"));
          #endif
        }
      }
    #endif
    
    
    #ifdef SOUNDSETSELECTSWITCH
      // check whether the game Variant switch is HIGH = consecutive order needed / or LOW = random order is ok
      byte sSet = digitalRead(soundSetSelectPin);
      delay(50);
      if (sSet == HIGH) {
        if (soundSetNumber == 5) { soundSetNumber = 1; } else { soundSetNumber = soundSetNumber+1; } // cycle through the five sound sets
        #ifdef DEBUG
          Serial.print(F("  Sound Set Number "));Serial.print(soundSetNumber);Serial.println(F(" selected"));
        #endif
      }
    #endif
  }

  
  // SCAN FOR TAGS - THIS IS DONE REGARDLESS OF THE STATUS OF THE GAME (STARTED OR NOT)
  if (ssrfid.available() > 0){
    
    boolean call_extractTag = false;
    int ssvalue = ssrfid.read(); // read 
    if (ssvalue == -1) { // no data was read
      return;
    }

    if (ssvalue == 2) { // RDM630/RDM6300 found a tag => tag incoming 
      buffer_index = 0;
    } else if (ssvalue == 3) { // tag has been fully transmitted       
      call_extractTag = true; // extract tag at the end of the function call
    }

    if (buffer_index >= BUFFER_SIZE) { // checking for a buffer overflow (It's very unlikely that an buffer overflow comes up!)
      #ifdef DEBUG
        Serial.println("Error: Buffer overflow detected!");
      #endif
      return;
    }
    
    buffer[buffer_index++] = ssvalue; // everything is alright => copy current value to buffer

    if (call_extractTag == true) {
      if (buffer_index == BUFFER_SIZE) {
        unsigned tag = extractTag();
        checkTag(tag);
      } else { // something is wrong... start again looking for preamble (value: 2)
        buffer_index = 0;
        return;
      }
    }    
  }

  // if a tag was found, we turn on the blue light and after roughly 5 seconds we turn it off again
  if ((millis() - tagScannedInMillis) > keepTagScannedIndicator) {
    indicateChestStatus();
  }
}





void playSound(byte trackNo) {
  // playing audio can be done either by connecting pins from the Arduino to the AudioFX Board
  // or by using a software serial interface and providing the filename to play
  //
  // the method is chosen at compile time by MAKRO switches at the top of the sketch: TRGAUDIOFX or SSAUDIOFX
  //
  //                                                    TRGAUDIOFX - connected pins   SSAUDIOFX - filename
  //  1.  Game is Started (maybe a fanfare)             gameStartSoundPin             t01.wav
  //  2.  Tag scanned (a short sound - maybe Zelda)     tagScannedSoundPin            t02.wav
  //  3.  Wrong Tag scanned (you like evil laugh?)      wrongTagScannedSoundPin       t03.wav
  //  4.  Master Tag Scanned                            masterTagScannedPin           t04.wav
  //  5.  Game Won (We are the champions)               gameWonSoundPin               t05.wav
  //  6.  Game is lost (even more evil laugh)           gameLostSoundPin              t06.wav
  
  #ifdef DEBUG
    Serial.print(F("playing track "));Serial.println(trackNo);
  #endif
  

  // playing audio is very simple if we use the input pins on the board, as we only need to pull the pin
  // that corresponds to the filename to play (t01.wav ... t06.wav) to low for at least 125ms. 
  // Afterwards we set it high again, to prevent repeated playing the file.
  #ifdef TRGAUDIOFX
    byte triggerAudioPin;
    switch (trackNo) {
      case 1: { triggerAudioPin = gameStartSoundPin;        break; }
      case 2: { triggerAudioPin = tagScannedSoundPin;       break; }
      case 3: { triggerAudioPin = wrongTagScannedSoundPin;  break; }
      case 4: { triggerAudioPin = masterTagScannedPin;      break; }
      case 5: { triggerAudioPin = gameWonSoundPin;          break; }
      case 6: { triggerAudioPin = gameLostSoundPin;         break; }
      
    }
    
    digitalWrite(triggerAudioPin, LOW);
    delay(150);
    digitalWrite(triggerAudioPin, HIGH);
  #endif

  // or one can play audio via software serial command and provide the filename to play
  #ifdef SSAUDIOFX
    char filename[13];
    memset(filename, 0, sizeof(filename));
    strcat(filename, soundSetPrefix);
    strcat(filename, soundSetNumber);
    strcat(filename, trackPrefix);
    strcat(filename, trackNo);
    strcat(filename, fileExtension);
    
    if (!sfx.playTrack(filename)) {
      #ifdef DEBUG
        Serial.println("Failed to play track?");
      #endif
    }
    
    /*if (!sfx.playTrack(uint8_t(trackNo))) {
      #ifdef DEBUG
        Serial.println("Failed to play track?");
      #endif
    }
    */
  #endif
}


void indicateChestStatus() {
  #ifdef DEBUG
    Serial.print(F("   gameIsStarted = "));Serial.print(gameIsStarted);Serial.print(F(" / chestIsLocked = "));Serial.println(chestIsLocked);
  #endif

  // START Condition
  if (gameIsStarted && chestIsLocked) {
    #ifdef CLSLIGHT
      digitalWrite(chestIsLockedLedPin, HIGH);
    #endif
    #ifdef OPNLIGHT
      digitalWrite(chestIsOpenLedPin, LOW);
    #endif
    #ifdef TAGLIGHT
      digitalWrite(tagScannedLedPin, LOW);
    #endif
    
    #ifdef CHESTSTATESERVO
      chestStateServo.write(chestStateLockPos);        // indicate locking status with the arrow
    #endif
  } else if (gameIsStarted && !chestIsLocked) {
    #ifdef CLSLIGHT
      digitalWrite(chestIsLockedLedPin, LOW);
    #endif
    #ifdef OPNLIGHT
      digitalWrite(chestIsOpenLedPin, HIGH);
    #endif
    #ifdef TAGLIGHT
      digitalWrite(tagScannedLedPin, LOW);
    #endif
    
    #ifdef CHESTSTATESERVO
      chestStateServo.write(chestStateOpenPos);       // indicate locking status with the arrow
    #endif
  } else if (!gameIsStarted) {
    #ifdef CLSLIGHT
      digitalWrite(chestIsLockedLedPin, LOW);
    #endif
    #ifdef OPNLIGHT
      digitalWrite(chestIsOpenLedPin, LOW);
   #endif
    #ifdef TAGLIGHT
      digitalWrite(tagScannedLedPin, LOW);
    #endif

    #ifdef CHESTSTATESERVO
      chestStateServo.write(chestStateOpenPos);       // indicate locking status with the arrow
    #endif
  }
}


void switchLockState(void) {
  if (chestIsLocked) {
    chestIsLocked = openCloseLock('o');
  } else {
    chestIsLocked = openCloseLock('c');
  }
  #ifdef DEBUG
    Serial.print(F("Chest is now "));if (chestIsLocked) Serial.println(F("locked")); else Serial.println(F("open"));
  #endif
}


boolean openCloseLock(char openClose) {
  if (openClose == 'o') { 
    #ifdef DEBUG
      Serial.print(F("OPENING put servo: "));Serial.print(lockClosePos);Serial.print(F(" -> "));Serial.print(lockOpenPos);
    #endif
    
    for (pos = lockClosePos; pos < lockOpenPos; pos++) { // goes from 0 degrees to 180 degrees
      #ifdef DEBUG 
        if ((pos % 5) == 0) Serial.print(F(".")); 
      #endif
      #ifdef LOCKSERVO
        lockServo.write(pos);               // tell servo to go to position in variable 'pos'
      #endif
      delay(15);                            // waits 15ms for the servo to reach the position
    }
    #ifdef DEBUG
      Serial.println(F("open")); 
    #endif
    
    return(false);
    
  } else if (openClose == 'c') {
    #ifdef DEBUG
      Serial.print(F("CLOSING put servo: "));Serial.print(lockOpenPos);Serial.print(F(" -> "));Serial.print(lockClosePos);
    #endif
    
    for (pos = lockOpenPos; pos > lockClosePos; pos--) { // goes from 180 degrees to 0 degrees
      #ifdef DEBUG
        if ((pos % 5) == 0) Serial.print(F(".")); 
      #endif
      #ifdef LOCKSERVO
        lockServo.write(pos);               // tell servo to go to position in variable 'pos'
      #endif
      delay(15);                            // waits 15ms for the servo to reach the position
    }
    #ifdef DEBUG
      Serial.println(F("closed")); 
    #endif
    
    return(true);
    
  } else {
    return(false);
  }
}


void checkTag(unsigned tag) {
  switch (tag) {
    case knownTag1: {
      // regardless whether or not we need any number of tags or tags in a specific (consecutive) order, the first tag is always good
      if (!tag1Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 1 scanned"));
        #endif
        tag1Found = true;
        numberOfTagsFound = numberOfTagsFound+1;  
        tagScannedInMillis = millis();
      }
      break;
    }
    
    case knownTag2: {
      if (!tag2Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 2 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag1Found) || gameVariant == 'r') {
          tag2Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        }/* else if (gameVariant == 'r') { 
          tag2Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        }*/
      }
      break;
    }
    
    case knownTag3: {
      if (!tag3Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 3 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag2Found) || gameVariant == 'r') {
          tag3Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        } /*else if (gameVariant == 'r') { 
          tag3Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        }*/
      }
      break;
    }
    
    case knownTag4: {
      if (!tag4Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 4 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag3Found) || gameVariant == 'r') {
          tag4Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        } /*else if (gameVariant == 'r') { 
          tag4Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        }*/
      }
      break;
    }
    
    case knownTag5: {
      if (!tag5Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 5 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag4Found) || gameVariant == 'r') {
          tag5Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis(); 
        } /*else if (gameVariant == 'r') { 
          tag5Found = true;
          numberOfTagsFound = numberOfTagsFound+1; 
          tagScannedInMillis = millis(); 
        }*/
      }
      break;
    }
    
    case knownTag6: {
      if (!tag6Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 6 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag5Found) || gameVariant == 'r') {
          tag6Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();  
        } /*else if (gameVariant == 'r') { 
          tag6Found = true;
          numberOfTagsFound = numberOfTagsFound+1;  
          tagScannedInMillis = millis();
        }*/
      }
      break;
    }
    
    case knownTag7: {
      if (!tag7Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 7 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag6Found) || gameVariant == 'r') {
          tag7Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        } /*else if (gameVariant == 'r') { 
          tag7Found = true;
          numberOfTagsFound = numberOfTagsFound+1; 
          tagScannedInMillis = millis();
        }*/
      }
      break;
    }
    
    case knownTag8: {
      if (!tag8Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 8 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag7Found) || gameVariant == 'r') {
          tag8Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        } /*else if (gameVariant == 'r') { 
          tag8Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis(); 
        }*/
      }
      break;
    }
    
    case knownTag9: {
      if (!tag9Found) {
        #ifdef DEBUG
          Serial.println(F("Tag 9 scanned"));
        #endif
        // gameVariant can be c = consecutive order (in which case the previos tag must be scanned prior this one) or r = random
        if ((gameVariant == 'c' && tag8Found) || gameVariant == 'r') {
          tag9Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        } /*else if (gameVariant == 'r') { 
          tag9Found = true;
          numberOfTagsFound = numberOfTagsFound+1;
          tagScannedInMillis = millis();
        }*/
      }
      break;
    }
    
    case knownTagX: {
      // Master Tag was swiped
      // first check if the chest is open - in this case we want to close it and start the game
      if (!gameIsStarted) {
        startTheGame();
      } else {
        // second, in case game is already started, check if the minimal play time (30 seconds) is over
        // bevor using the master tag to open the chest
        if ((millis() - gameStartTimeInMillis) > minimalGameTime) {
          #ifdef DEBUG
            Serial.print(F("Master Tag scanned"));
            if (numberOfTagsFound > 0) {
              Serial.print(F(" - you already had ")); Serial.print(numberOfTagsFound); Serial.print(F(" of ")); Serial.print(numberOfTagsToOpenLock); Serial.println(F(" tags needed to open the chest."));
            } else {
              Serial.println(F("."));
            }
          #endif
          tagXFound = true;
          numberOfTagsFound = 0;
        }
      }
      break;
    }
  }
}


void stopTheGame() {
  #ifdef DEBUG
    Serial.println(F("stopping game."));
  #endif
  gameIsStarted = false;
  indicateChestStatus();
}


void startTheGame(){
  #ifdef DEBUG
    Serial.print(F("Chest is NOT locked and Master Tag ("));Serial.print(knownTagX);Serial.println(F(") scanned: close the chest within 4 seconds as the the game is about to start"));
  #endif
  
  #ifdef CLSLIGHT
    digitalWrite(chestIsLockedLedPin, HIGH);
  #endif
    #ifdef OPNLIGHT
    digitalWrite(chestIsOpenLedPin, LOW);
  #endif
  #ifdef TAGLIGHT
    digitalWrite(tagScannedLedPin, LOW);
  #endif
  delay(500);
  #ifdef CLSLIGHT
    digitalWrite(chestIsLockedLedPin, LOW);
  #endif
  #ifdef OPNLIGHT
    digitalWrite(chestIsOpenLedPin, HIGH);
  #endif
  #ifdef TAGLIGHT
    digitalWrite(tagScannedLedPin, LOW);
  #endif
  delay(500);
  #ifdef CLSLIGHT
    digitalWrite(chestIsLockedLedPin, LOW);
  #endif
...

This file has been truncated, please download it to see its full contents.

Credits

siliconchris
0 projects • 2 followers

Comments