Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* Latest Build: 11/11/19
- * Includes: Keypad puzzle, 7-segment countdown, Morse Code puzzle, Rotary Puzzle,
- * MP3 sounds, Missile Toggle, Patch Cable Puzzle
- *
- * Patch cable puzzle modified from Playful Technologies:
- * https://www.patreon.com/playfultech/
- *
- * Images of the final build here:
- * https://imgur.com/gallery/J6HdM0v
- *
- * Video demonstration of the electrical components here:
- * https://www.youtube.com/watch?v=WK6nwx5pwIw
- */
- //-----Defines-----//
- #define numberOfSeconds(_time_) ((_time_ / 1000) % 60) // For Countdown
- #define numberOfMinutes(_time_) (((_time_ / 1000) / 60) % 60) //For Countdown
- #define Password_Length 7 // For Keypad Passcode
- //#define DEBUG
- //#define STATEDEBUG;
- //-----Includes-----//
- #include <Wire.h> // Segment Display
- #include <LiquidCrystal.h> // LCD
- #include <Keypad.h> // Keypad
- #include <Adafruit_NeoPixel.h> // Neopixels
- #include "Arduino.h" // MP3 Player
- #include "SoftwareSerial.h" // MP3 Player
- #include "DFRobotDFPlayerMini.h" // MP3 Player
- #include <TM1637Display.h> // Segment Display
- #include <SimpleRotary.h> // Rotary Switch
- #include "TM1637.h" // Rotary 7-segment Display
- //-----Patch Puzzle Setup-----//
- int sA = 36; // 0
- int sB = 34; // 1
- int sC = 32; // 2
- int sD = 30; // 3
- int sE = 28; // 4
- int sF = 26; // 5
- int sG = 24; // 6
- int sH = 22; // 7
- int s1 = 37; // 8
- int s2 = 35; // 9
- int s3 = 33; // 10
- int s4 = 31; // 11
- int s5 = 29; // 12
- int s6 = 27; // 13
- int s7 = 25; // 14
- int s8 = 23; // 15
- const byte numSockets = 16;
- const byte signalPins[numSockets] = { sA, sB, sC, sD, sE, sF, sG, sH, s1, s2, s3, s4, s5, s6, s7, s8 };
- const byte numConnections = 6;
- const byte connections[numConnections][2] = { {3,14}, {5,8}, {7,12}, {1,13}, {6,9}, {2,11} }; // D7, F1, H5, B6, G2, C4
- bool lastState[numConnections] = { false, false, false, false, false, false };
- enum PuzzleState {Initialising, Running, Solved};
- PuzzleState puzzleState = Initialising;
- bool Patch_Puzzle_is_good = false;
- //-----Missle Toggle Setup-----//
- const int MissleLED = 4; // Missle LED pin
- int MissleLEDState = LOW; // ledState used to set the LED
- unsigned long MissleLEDpreviousMillis = 0; // will store last time LED was updated
- const long MissleLEDinterval = 200; // interval at which to blink (milliseconds)
- const int MissleSwitch = 2; // MissleSwitch pin
- int MissleSwitchState = 1;
- //-----BarrelSwitch Setup-----//
- const int BarrelSwitch = 5;
- int BarrelSwitchState = 1;
- //-----DFPlayer Setup-----//
- SoftwareSerial mySoftwareSerial(11, 10); // RX, TX
- DFRobotDFPlayerMini myDFPlayer;
- void printDetail(uint8_t type, int value);
- //-----Neopixel: Setup 7 LEDs on Pin 3-----//
- Adafruit_NeoPixel strip = Adafruit_NeoPixel (7, 3, NEO_RGB + NEO_KHZ800);
- //-----KeyPad Setup-----//
- int signalPin = A0; // Signal pin when password is correct
- char KeypadData[Password_Length];
- char KeypadMaster[Password_Length] = "012798"; //PASSWORD
- byte Keypad_data_count = 0, Keypad_master_count = 0;
- bool KeypadPass_is_good = false;
- char customKey; //Keypad Button Name
- const byte ROWS = 4;
- const byte COLS = 3;
- char hexaKeys[ROWS][COLS] = {
- {'1', '2', '3'},
- {'4', '5', '6'},
- {'7', '8', '9'},
- {'*', '0', '#'}};
- byte rowPins[ROWS] = {50, 40, 42, 46}; // Keypad Row Location
- byte colPins[COLS] = {48, 52, 44}; // Keypad Column Location
- Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
- //-----LCD Setup-----//
- const int rs = 17, en = 18, d4 = 39, d5 = 41, d6 = 43, d7 = 45;
- LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
- int lcdbacklight = 12;
- //-----Segment Countdown Setup-----//
- const uint8_t OFF[] = {0, 0, 0, 0};
- // Byte order is .GFEDCBA
- const uint8_t PLAY[] = {B01110011, B00111000, B01011111, B01101110};
- const uint8_t DEAD[] = {0x5E, 0x79, 0x77, 0x5E};
- const uint8_t colon = 0b01000000;
- TM1637Display countdown1(7, 6); // Display 1 (Clock pin, Data pin)
- // 1000ms in 1sec, 60secs in 1min, 60mins in 1hr. So, 1000x60x60 = 3600000ms = 1hr
- unsigned long timeLimit = 3600000;
- unsigned long timeRemaining = timeLimit - millis();
- unsigned long countdowndelay;
- //-----Morse Code Setup-----//
- int morseunit = 250;
- int dash = (3.3*morseunit);
- int dot = (1.4*morseunit);
- int shortspace = (1.3*morseunit);
- int longspace = (4*morseunit);
- int morsetiming[] =
- {longspace, dash, shortspace, dot, shortspace, dash, shortspace, dot, // "C" -.-.
- longspace, dot, shortspace, dash, // "A" .-
- longspace, dot, shortspace, dot, shortspace, dot, shortspace, dash, // "V" ...-
- longspace, dot, // "E" .
- longspace, dot, shortspace, dot, shortspace, dot, shortspace, // "S" ...
- longspace, longspace, longspace}; // Long pause
- unsigned long currentMorseMillis = 0;
- unsigned long startMorseMillis = 0;
- int morsestep = 0;
- //-----7 Segment on 5 (CLK), 4 (D10)-----//
- TM1637 rotaryLCD(A0, A1);
- //-----Pin A, Pin B, Button Pin-----//
- SimpleRotary rotary(14, 15, 16);
- int rotarycount = 0;
- int rotaryposition = 0;
- char rotarynumbers[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- //-----Rotary Password Setup-----//
- int Rotary_data[4];
- int Master_Password = 4295;
- bool Rotary_pass_is_good = false;
- //-----Puzzle States------//
- bool puzzleStart = false;
- bool puzzleComplete = false;
- //-------------------------------------------------------------SETUP-----------------------------------------------//
- void setup(){
- //-----Missle Switch-----//
- pinMode(MissleLED, OUTPUT);
- pinMode(MissleSwitch, INPUT_PULLUP);
- //-----Barrel Switch-----//
- pinMode(BarrelSwitch, INPUT_PULLUP);
- //-----Patch Puzzle-----//
- for (int i=0; i<numSockets; i++){
- pinMode(signalPins[i], INPUT_PULLUP);}
- #ifdef DEBUG
- Serial.begin(9600);
- Serial.println(F("Serial communication started"));
- #endif
- puzzleState = Running;
- //-----LCD's number of columns and rows-----//
- lcd.begin(16, 2);
- pinMode(signalPin, OUTPUT);
- pinMode(lcdbacklight, OUTPUT);
- //-----Clear Programmable LEDs-----//
- strip.begin(); // Reset LEDs
- strip.show();
- //---------DFPlayer Setup Options-----------//
- mySoftwareSerial.begin(9600);
- Serial.begin(9600);
- //-----Initialization-----//
- #ifdef STATEDEBUG
- Serial.println();
- Serial.println(F("DFRobot DFPlayer Mini Demo"));
- Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
- #endif
- //-----Debug-----//
- if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
- #ifdef STATEDEBUG
- Serial.println(F("Unable to begin:"));
- Serial.println(F("1.Please recheck the connection!"));
- Serial.println(F("2.Please insert the SD card!"));
- #endif
- while(true);}
- #ifdef STATEDEBUG
- Serial.println(F("DFPlayer Mini online."));
- #endif
- myDFPlayer.setTimeOut(500); //Set serial communictaion time out 500ms
- //----Set volume and EQ----//
- myDFPlayer.volume(25); //Set volume value (0~30)
- myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
- myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
- //-----Neopixel Setup-----//
- // ClearLEDs();
- //---------Segment Display Setup-----------//
- countdown1.setBrightness(0x0c); // Set brightness
- countdown1.setSegments(OFF); // Clear the display
- //---------Rotary and 7-seg Display Setup-----------//
- rotaryLCD.init(); // Clears the display
- rotaryLCD.set(2); // TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
- rotary.setTrigger(HIGH); // Set trigger (Default: HIGH)
- rotary.setDebounceDelay(5); // Set debounce (Default: 2)
- rotary.setErrorDelay(200); // Set error correction (Default: 200)
- }
- //--------------------------------------------------------LOOP------------------------------------------------------//
- void loop(){
- BarrelSwitchState = digitalRead(BarrelSwitch);
- MissleSwitchState = digitalRead(MissleSwitch);
- #ifdef STATEDEBUG
- STATEDEBUGPRINT();
- #endif
- if (BarrelSwitchState == 0) {
- Serial.println("In the Start Up if statement");
- puzzleStart = true;
- digitalWrite(lcdbacklight, HIGH);
- rotaryLCD.display(0, 0);
- rotaryLCD.display(1, 0);
- rotaryLCD.display(2, 0);
- rotaryLCD.display(3, 0);
- countdowndelay = millis();
- lcd.setCursor(0,0);
- lcd.print("Enter Password:");
- myDFPlayer.playMp3Folder(6); // Power, 60 Minutes (SD/Mp3/0001.mp3)
- }
- while (puzzleStart == true && Patch_Puzzle_is_good == false) {
- #ifdef STATEDEBUG
- STATEDEBUGPRINT();
- Serial.println("In the Puzzle While statement");
- delay(2000);
- #endif
- if (KeypadPass_is_good == false) {
- KeypadPuzzle();}
- countdowntimer();
- RotaryEncoderPuzzle();
- PatchPuzzle();
- if (KeypadPass_is_good == true){
- //Serial.println("KeypadPass is good");
- MorsePuzzle();}
- }
- while (Patch_Puzzle_is_good == true){
- #ifdef STATEDEBUG
- STATEDEBUGPRINT();
- Serial.println("In the Final if statement");
- Serial.println(MissleSwitchState);
- delay(2000);
- #endif
- countdowntimer();
- MissleLEDBlink();
- MissleSwitchState = digitalRead(MissleSwitch);
- if (MissleSwitchState == 0) {
- puzzleComplete = true;
- myDFPlayer.playMp3Folder(10); // Final Disarm (SD/Mp3/0001.mp3)
- }
- while (puzzleComplete == true) {
- digitalWrite (MissleLED, HIGH);
- lcd.clear();
- lcd.setCursor(0,0);
- lcd.print("** EXPLOSIVES **");
- lcd.setCursor(0,1);
- lcd.print("** Disarmed **");
- FlashGreenLEDs ();
- #ifdef STATEDEBUG
- STATEDEBUGPRINT();
- Serial.println("***** Game Over!!! *****");
- delay(10000);
- #endif
- }
- }
- }
- //---------------------------------------------------SUBROUTINES-------------------------------------------------//
- void KeypadPuzzle(){
- customKey = customKeypad.getKey();
- if (customKey){
- KeypadData[Keypad_data_count] = customKey;
- lcd.setCursor(Keypad_data_count,1);
- lcd.print(KeypadData[Keypad_data_count]);
- Keypad_data_count++; }
- if(Keypad_data_count == Password_Length-1 && KeypadPass_is_good == false){
- lcd.clear();
- //ClearPasskeyLEDs();
- if(!strcmp(KeypadData, KeypadMaster)){
- lcd.setCursor(0,0);
- lcd.print("Code OK: ");
- lcd.print(KeypadMaster);
- lcd.setCursor(0,1);
- lcd.print("D7");
- myDFPlayer.playMp3Folder(11); //Correct Code (SD/Mp3/0001.mp3)
- KeypadPass_is_good = true;
- }
- else{
- lcd.print("INCORRECT");
- myDFPlayer.playMp3Folder(9); //incorrect Code (SD/Mp3/0001.mp3)
- countdowndelay = countdowndelay - 60000; // add 1 minute penalty
- delay(500);
- lcd.clear();
- clearData();
- lcd.setCursor(0,0);
- lcd.print("Enter Password:");}
- }
- }
- void clearData(){
- while(Keypad_data_count !=0){
- KeypadData[Keypad_data_count--] = 0;}
- return;
- //ClearLEDs ();
- }
- void ClearLEDs () { // Turns all LEDs off
- for (int i = 0; i < 6; i++) {
- strip.setPixelColor(i, 0);}
- strip.show();
- }
- void ClearPasskeyLEDs () { // Turns all LEDs off
- for (int i = 0; i < 6; i++) {
- strip.setPixelColor(i, 0);}
- strip.show();
- }
- void FlashGreenLEDs () { // Flashes all LEDs Green
- int repeat = 3; // Number of flashes
- for (int x = 0; x < repeat; x++) {
- for (int i = 0; i < 6; i++) {
- strip.setPixelColor(i, 0, 35, 0);
- strip.show();}
- delay(500);
- ClearLEDs();
- delay(500);
- }
- for (int i = 0; i < 7; i++) {
- strip.setPixelColor(i, 0, 35, 0);}
- strip.show();
- }
- void FlashRedLEDs () {
- int repeat = 3; // Number of flashes
- for (int x = 0; x < repeat; x++) {
- for (int i = 0; i < 7; i++) {
- strip.setPixelColor(i, 35, 0, 0);
- strip.show();}
- ClearPasskeyLEDs();
- }
- }
- void MorsePuzzle () {
- currentMorseMillis = millis();
- if (currentMorseMillis - startMorseMillis <= (morsetiming[morsestep])) {}
- else {
- startMorseMillis = currentMorseMillis;
- morsestep++;
- }
- if ((morsetiming[morsestep]) == dash) {
- MorseLEDon(); //Serial.println("Dash");
- }
- else if ((morsetiming[morsestep]) == dot) {
- MorseLEDon(); //Serial.println("Dot");
- }
- else if ((morsetiming[morsestep]) == longspace) {
- MorseLEDoff(); //Serial.println("Longspace");
- }
- else if ((morsetiming[morsestep]) == shortspace) {
- MorseLEDoff(); //Serial.println("Shortspace");
- }
- if (morsestep == 31) {
- morsestep = 0;
- }
- }
- void MorseLEDon() {
- strip.setPixelColor(6, 0, 65, 0);
- strip.show();
- //Serial.println("On");
- }
- void MorseLEDoff() {
- strip.setPixelColor(6, 0, 0, 0);
- strip.show();
- //Serial.println("Off");
- }
- void countdowntimer() {
- if(timeRemaining > 0 && timeRemaining < 3602000) {
- int seconds = numberOfSeconds(timeRemaining);
- int minutes = numberOfMinutes(timeRemaining);
- countdown1.showNumberDecEx(seconds, 0, true, 2, 2); // Displays seconds in last two places
- countdown1.showNumberDecEx(minutes, 0b01000000, true, 2, 0); // Minutes first two places w/ colon
- timeRemaining = timeLimit - millis() + countdowndelay; // Update the time remaining
- //Serial.println (timeRemaining/1000);
- }
- else {
- countdown1.setSegments(DEAD);
- delay(500);
- countdown1.setSegments(OFF);
- delay(500);
- }
- if(timeRemaining > 2699000 && timeRemaining < 2700000) {
- myDFPlayer.playMp3Folder(100); // 45 minutes remaining
- }
- if(timeRemaining > 1801000 && timeRemaining < 1802000) {
- myDFPlayer.playMp3Folder(101); // 30 minutes remaining
- }
- if(timeRemaining > 1201000 && timeRemaining < 1202000) {
- myDFPlayer.playMp3Folder(102); // 20 minutes remaining
- }
- if(timeRemaining > 601000 && timeRemaining < 602000) {
- myDFPlayer.playMp3Folder(103); // 10 minutes remaining
- }
- if(timeRemaining > 301000 && timeRemaining < 302000) {
- myDFPlayer.playMp3Folder(104); // 5 minutes remaining
- }
- if(timeRemaining > 61000 && timeRemaining < 62000) {
- myDFPlayer.playMp3Folder(105); // 1 minute remaining
- }
- if(timeRemaining > 31000 && timeRemaining < 32000) {
- myDFPlayer.playMp3Folder(106); // 30 seconds remaining
- }
- if(timeRemaining > 11000 && timeRemaining < 12000) {
- myDFPlayer.playMp3Folder(107); // 10 seconds remaining
- }
- if(timeRemaining > 6000 && timeRemaining < 7000) {
- myDFPlayer.playMp3Folder(108); // 5 4 3 2 1... Final boom
- }
- }
- void displayText() {
- countdown1.setSegments(PLAY);
- delay(2000);
- }
- void RotaryEncoderPuzzle() {
- if (Rotary_pass_is_good == false) {
- byte r;
- byte p;
- r = rotary.rotate(); // CW = 1, CCW = 2
- p = rotary.push(); // 0 = not pushed, 1 = pushed
- if (r == 1 ) {
- rotarycount++;
- if (rotarycount == 10) {
- rotarycount = 0;}
- rotaryLCD.display(rotaryposition, rotarycount);
- Rotary_data[rotaryposition] = rotarycount;
- }
- if (r == 2) {
- rotarycount--;
- if (rotarycount == -1) {
- rotarycount = 9;}
- rotaryLCD.display(rotaryposition, rotarycount);
- Rotary_data[rotaryposition] = rotarycount;
- }
- if (p == 1) {
- #ifdef STATEDEBUG
- Serial.println(Rotary_data[rotaryposition]);
- #endif
- rotaryposition++;
- rotarycount = 0;
- rotaryLCD.display(rotaryposition, rotarycount);
- }
- if (rotaryposition == 4) {
- int password = (Rotary_data[0] * 1000) + (Rotary_data[1] * 100) + (Rotary_data[2] * 10) + (Rotary_data[3] * 1);
- #ifdef STATEDEBUG
- Serial.print("Entered Password: ");
- Serial.println(password);
- #endif
- if (password - Master_Password == 0) {
- #ifdef STATEDEBUG
- Serial.println("Password Match");
- #endif
- Rotary_pass_is_good = true;
- rotaryLCD.init();
- rotaryLCD.display(1, 15); // "F"
- rotaryLCD.display(2, 1); // "1"
- myDFPlayer.playMp3Folder(11); // Correct Code (SD/Mp3/0001.mp3)
- }
- else {
- #ifdef STATEDEBUG
- Serial.println("Password Wrong");
- #endif
- myDFPlayer.playMp3Folder(9); // Incorrect Code (SD/Mp3/0001.mp3)
- countdowndelay = countdowndelay - 60000; // Add 1 minute penalty
- //else below this for not matching passcode
- rotaryposition = 0;
- rotaryLCD.display(0, 0);
- rotaryLCD.display(1, 0);
- rotaryLCD.display(2, 0);
- rotaryLCD.display(3, 0);
- rotarycount = 0;
- rotaryLCD.display(rotaryposition, rotarycount);
- }
- }
- }
- }
- void PatchPuzzle() {
- if (puzzleState == Solved) {
- //MissleLEDBlink ();
- }
- // Assume that all wires are correct
- bool AllWiresCorrect = true;
- // Assume that the puzzle state has not changed since last reading
- bool stateChanged = false;
- // Check each connection in turn
- for (int i=0; i<numConnections; i++){
- // Get the pin numbers that should be connected by this wire
- byte pin1 = signalPins[connections[i][0]];
- byte pin2 = signalPins[connections[i][1]];
- // Test whether the appropriate pins are correctly connected
- bool currentState = isConnected(pin1, pin2);
- if (currentState != lastState[i]) { // Has the connection changed since last reading?
- strip.setPixelColor(i, 0, 65, 0); // turn on NeoPixel in order
- strip.show();
- myDFPlayer.playMp3Folder(i); // Patch Sequence Order (SD/Mp3/[i].mp3)
- stateChanged = true; // Show state of the puzzle has changed
- lastState[i] = currentState; // Update the stored connection state
- }
- // If any connection is incorrect, puzzle is not solved
- if(currentState == false) {
- AllWiresCorrect = false;
- }
- }
- // If a connection has been made/broken since last time we checked
- if(stateChanged) {
- #ifdef DEBUG
- // Dump to serial the current state of all connections
- for (uint8_t i=0; i<numConnections; i++) {
- Serial.print(F("Pin#"));
- Serial.print(signalPins[connections[i][0]]);
- Serial.print(F(" - Pin#"));
- Serial.print(signalPins[connections[i][1]]);
- Serial.print(F(" : "));
- Serial.println(lastState[i] ? "CONNECTED" : "NOT CONNECTED");
- }
- Serial.println(F("----------------"));
- #endif
- }
- // If the state of the puzzle has changed
- if (AllWiresCorrect && puzzleState == Running){
- onSolve();
- }
- // If the state of the puzzle has changed
- else if (!AllWiresCorrect && puzzleState == Solved){
- onUnsolve();
- }
- }
- void onSolve() {
- #ifdef DEBUG
- Serial.println(F("The puzzle has been solved!"));
- #endif
- Patch_Puzzle_is_good = true;
- puzzleState = Solved;
- }
- void onUnsolve(){
- #ifdef DEBUG
- Serial.println(F("The puzzle is no longer solved!"));
- #endif
- digitalWrite(MissleLED, LOW);
- puzzleState = Running;
- }
- bool isConnected(byte OutputPin, byte InputPin) {
- pinMode(OutputPin, OUTPUT);
- pinMode(InputPin, INPUT_PULLUP);
- digitalWrite (OutputPin, LOW);
- bool isConnected = !digitalRead(InputPin);
- pinMode(OutputPin, INPUT_PULLUP);
- return isConnected;
- }
- void MissleLEDBlink () { // Based on blink without delay
- unsigned long MissleLEDcurrentMillis = millis();
- if (MissleLEDcurrentMillis - MissleLEDpreviousMillis >= MissleLEDinterval) {
- MissleLEDpreviousMillis = MissleLEDcurrentMillis;
- if (MissleLEDState == LOW) {
- MissleLEDState = HIGH;
- } else {
- MissleLEDState = LOW;
- }
- digitalWrite(MissleLED, MissleLEDState);
- }
- }
- void STATEDEBUGPRINT () {
- Serial.print("Puzzle start is: ");
- Serial.print(puzzleStart);
- Serial.print(" Patch Puzzle is: ");
- Serial.print(Patch_Puzzle_is_good);
- Serial.print(" Barrel Switch is: ");
- Serial.print(BarrelSwitchState);
- Serial.print(" Missle Switch is: ");
- Serial.print(MissleSwitchState);
- Serial.print(" Puzzle complete is: ");
- Serial.println(puzzleComplete);
- delay(2000);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement