Ciao!
quest'anno ho deciso di regalare un gioco DIY a mia figlia,
cercando su Instructables ho trovato questo:
Crack the Code Game (Arduino Based Puzzle Box) di thediylife
In breve il gioco consiste nel trovare la giusta combinazione (viene generata random) nel minor numero possibile di tentativi, 8 led aiuteranno nel capire quanti numeri sono stati indovinati e se sono stati indovinati nella posizione giusta.
Ho trovato il gioco molto carino sia esteticamente che funzionalmente quindi ho deciso di replicarlo,
ho eseguito alcune modifiche però:
- ho modificato il codice in modo che funzioni con numeri da 0 a 4 anzichè 0-9 come l'originale, questo per semplificare il gioco per renderlo più adatto ad una bambina di 6 anni (nulla vieta in futuro di riprogrammarlo)
- ho utilizzato un clone Arduino Nano che avevo già a disposizione
- l'ho alimentato con una batteria lipo ed un lipo-charger che potete facilmente acquistare online (evitando di dover sostituire la batteria ogni volta).
Ecco alcune foto utili della mia replica:
Ed ecco il codice modificato per avere i numeri da 0 a 4 invece che da 0 a 9:
- //Code Breaker
- //Michael Klements
- //The DIY Life
- //15 May 2020
- //Encoder interrupt routine adapted from Simon Merrett's example code
- #include <SPI.h> //Import libraries to control the OLED display
- #include <Wire.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_SSD1306.h>
- #include <Servo.h> //Import library to control the servo
- Servo lockServo; //Create a servo object for the lock servo
- #define SCREEN_WIDTH 128 // OLED display width, in pixels
- #define SCREEN_HEIGHT 32 // OLED display height, in pixels
- #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
- Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
- static int pinA = 2; //Hardware interrupt digital pin 2
- static int pinB = 3; //Hardware interrupt digital pin 3
- volatile byte aFlag = 0; //Rising edge on pinA to signal that the encoder has arrived at a detent
- volatile byte bFlag = 0; //Rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
- volatile byte encoderPos = 0; //Current value of encoder position, digit being input form 0 to 9
- volatile byte prevEncoderPos = 0; //To track whether the encoder has been turned and the display needs to update
- volatile byte reading = 0; //Stores direct value from interrupt pin
- const byte buttonPin = 4; //Pin number for encoder push button
- byte oldButtonState = HIGH; //First button state is open because of pull-up resistor
- const unsigned long debounceTime = 10; //Debounce delay time
- unsigned long buttonPressTime; //Time button has been pressed for debounce
- byte correctNumLEDs[4] = {9,7,12,11}; //Pin numbers for correct number LEDs (Indicate a correct digit)
- byte correctPlaceLEDs[4] = {8,6,13,10}; //Pin numbers for correct place LEDs (Indicate a correct digit in the correct place)
- byte code[4] = {0,0,0,0}; //Create an array to store the code digits
- byte codeGuess[4] = {0,0,0,0}; //Create an array to store the guessed code digits
- byte guessingDigit = 0; //Tracks the current digit being guessed
- byte numGuesses = 0; //Tracks how many guesses it takes to crack the code
- boolean correctGuess = true; //Variable to check whether the code has been guessed correctly, true initially to generate a new code on startup
- void setup()
- {
- Serial.begin(9600); //Starts the Serial monitor for debugging
- if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) //Connect to the OLED display
- {
- Serial.println(F("SSD1306 allocation failed")); //If connection fails
- for(;;); //Don't proceed, loop forever
- }
- display.clearDisplay(); //Clear display
- lockServo.attach(5); //Assign the lock servo to pin 5
- for(int i=0 ; i<=3 ; i++) //Define pin modes for the LEDs
- {
- pinMode(correctNumLEDs[i], OUTPUT);
- pinMode(correctPlaceLEDs[i], OUTPUT);
- }
- pinMode(pinA, INPUT_PULLUP); //Set pinA as an input, pulled HIGH to the logic voltage
- pinMode(pinB, INPUT_PULLUP); //Set pinB as an input, pulled HIGH to the logic voltage
- attachInterrupt(0,PinA,RISING); //Set an interrupt on PinA
- attachInterrupt(1,PinB,RISING); //Set an interrupt on PinB
- pinMode (buttonPin, INPUT_PULLUP); //Set the encoder button as an input, pulled HIGH to the logic voltage
- randomSeed(analogRead(0)); //Randomly choose a starting point for the random function, otherwise code pattern is predictable
- display.setTextColor(SSD1306_WHITE); //Set the text colour to white
- startupAni(); //Display the startup animation
- }
- void loop()
- {
- if(correctGuess) //Code between games to reset if the guess is correct, initially true to open safe and then generate new code
- {
- lockServo.write(140); //Unlock the safe
- delay(300);
- updateLEDs (0,4); //Flashing LED sequence
- delay(300);
- updateLEDs (4,0);
- delay(300);
- updateLEDs (0,4);
- delay(300);
- updateLEDs (4,0);
- delay(300);
- updateLEDs (4,4); //Turn all LEDs on
- if(numGuesses >= 1) //Check that its not the start of the game
- {
- display.clearDisplay(); //Clear the display
- display.setTextSize(1); //Set the display text size to small
- display.setCursor(35,10); //Set the display cursor position
- display.print(F("In ")); //Set the display text
- display.print(numGuesses); //Set the display text
- display.setCursor(35,20); //Set the display cursor position
- display.print(F("Attempts")); //Set the display text
- display.display(); //Output the display text
- delay(5000);
- }
- display.clearDisplay(); //Clear the display
- display.setTextSize(1); //Set the display text size to small
- display.setCursor(35,10); //Set the display cursor position
- display.print(F("Push To")); //Set the display text
- display.setCursor(35,20); //Set the display cursor position
- display.print(F("Lock Safe")); //Set the display text
- display.display(); //Output the display text
- display.setTextSize(2); //Set the display text size back to large
- boolean lock = false; //Safe is initially not locked
- boolean pressed = false; //Keeps track of button press
- while(!lock) //While button is not pressed, wait for it to be pressed
- {
- byte buttonState = digitalRead (buttonPin);
- if (buttonState != oldButtonState)
- {
- if (millis () - buttonPressTime >= debounceTime) //Debounce button
- {
- buttonPressTime = millis (); //Time when button is pressed
- oldButtonState = buttonState; //Remember button state
- if (buttonState == LOW)
- {
- pressed = true; //Records button has been pressed
- }
- else
- {
- if (pressed == true) //Makes sure that button is pressed and then released before continuing in the code
- {
- lockServo.write(45); //Lock the safe
- display.clearDisplay(); //Clear the display
- display.setCursor(30,10); //Set the display cursor position
- display.print(F("Locked")); //Set the display text
- display.display(); //Output the display text
- lock = true;
- }
- }
- }
- }
- }
- generateNewCode(); //Calls function to generate a new random code
- updateLEDs (0,0);
- correctGuess = false; //The code guess is initially set to incorrect
- numGuesses = 0; //Reset the number of guesses counter
- }
- inputCodeGuess(); //Calls function to allow the user to input a guess
- numGuesses++; //Increment the guess counter
- checkCodeGuess(); //Calls function to check the input guess
- encoderPos = 0; //Reset the encoder position
- guessingDigit = 0; //Reset the digit being guessed
- codeGuess[0] = 0; //Reset the first digit of the code
- updateDisplayCode(); //Update the displayed code
- }
- void updateDisplayCode() //Function to update the display with the input code
- {
- String temp = ""; //Temporary variable to concatenate the code string
- if(!correctGuess) //If the guess is not correct then update the display
- {
- for (int i=0 ; i<guessingDigit ; i++) //Loops through the four digits to display them
- {
- temp = temp + codeGuess[i];
- }
- temp = temp + encoderPos;
- for (int i=guessingDigit+1 ; i<=3 ; i++)
- {
- temp = temp + "0";
- }
- Serial.println(temp); //Output to Serial monitor for debugging
- display.setTextSize(2); //Set the display text size
- display.clearDisplay(); //Clear the display
- display.setCursor(40,10); //Set the display cursor position
- display.println(temp); //Set the display text
- display.display(); //Update the display
- }
- }
- void generateNewCode() //Function to generate a new random code
- {
- Serial.print("Code: ");
- for (int i=0 ; i<= 3 ; i++) //Loops through the four digits and assigns a random number to each
- {
- code[i] = random(1,4); //Generate a random number for each digit (was 0,9)
- Serial.print(code[i]); //Display the code on Serial monitor for debugging
- }
- Serial.println();
- }
- void inputCodeGuess() //Function to allow the user to input a guess
- {
- for(int i=0 ; i<=3 ; i++) //User must guess all four digits
- {
- guessingDigit = i;
- boolean confirmed = false; //Both used to confirm button push to assign a digit to the guess code
- boolean pressed = false;
- encoderPos = 0; //Encoder starts from 0 for each digit
- while(!confirmed) //While the user has not confirmed the digit input
- {
- byte buttonState = digitalRead (buttonPin);
- if (buttonState != oldButtonState)
- {
- if (millis () - buttonPressTime >= debounceTime) //Debounce button
- {
- buttonPressTime = millis (); //Time when button was pushed
- oldButtonState = buttonState; //Remember button state for next time
- if (buttonState == LOW)
- {
- codeGuess[i] = encoderPos; //If the button is pressed, accept the current digit into the guessed code
- pressed = true;
- }
- else
- {
- if (pressed == true) //Confirm the input once the button is released again
- {
- updateDisplayCode(); //Update the code being displayed
- confirmed = true;
- }
- }
- }
- }
- if(encoderPos!=prevEncoderPos) //Update the displayed code if the encoder position has changed
- {
- updateDisplayCode();
- prevEncoderPos=encoderPos;
- }
- }
- }
- }
- void checkCodeGuess() //Function to check the users guess against the generated code
- {
- int correctNum = 0; //Variable for the number of correct digits in the wrong place
- int correctPlace = 0; //Variable for the number of correct digits in the correct place
- int usedDigits[4] = {0,0,0,0}; //Mark off digits which have been already identified in the wrong place, avoids counting repeated digits twice
- for (int i=0 ; i<= 3 ; i++) //Loop through the four digits in the guessed code
- {
- for (int j=0 ; j<=3 ; j++) //Loop through the four digits in the generated code
- {
- if (codeGuess[i]==code[j]) //If a number is found to match
- {
- if(usedDigits[j]!=1) //Check that it hasn't been previously identified
- {
- correctNum++; //Increment the correct digits in the wrong place counter
- usedDigits[j] = 1; //Mark off the digit as been identified
- break; //Stop looking once the digit is found
- }
- }
- }
- }
- for (int i=0 ; i<= 3 ; i++) //Compares the guess digits to the code digits for correct digits in correct place
- {
- if (codeGuess[i]==code[i]) //If a correct digit in the correct place is found
- correctPlace++; //Increment the correct place counter
- }
- updateLEDs(correctNum, correctPlace); //Calls a function to update the LEDs to reflect the guess
- if(correctPlace==4) //If all 4 digits are correct then the code has been cracked
- {
- display.clearDisplay(); //Clear the display
- display.setCursor(20,10); //Set the display cursor position
- display.print(F("Cracked")); //Set the display text
- display.display(); //Output the display text
- correctGuess = true;
- }
- else
- correctGuess = false;
- }
- void updateLEDs (int corNum, int corPla) //Function to update the LEDs to reflect the guess
- {
- for(int i=0 ; i<=3 ; i++) //First turn all LEDs off
- {
- digitalWrite(correctNumLEDs[i], LOW);
- digitalWrite(correctPlaceLEDs[i], LOW);
- }
- for(int j=0 ; j<=corNum-1 ; j++) //Turn on the number of correct digits in wrong place LEDs
- {
- digitalWrite(correctNumLEDs[j], HIGH);
- }
- for(int k=0 ; k<=corPla-1 ; k++) //Turn on the number of correct digits in the correct place LEDs
- {
- digitalWrite(correctPlaceLEDs[k], HIGH);
- }
- }
- void startupAni ()
- {
- display.setTextSize(2); //Set the display text size
- display.setCursor(35,10); //Set the display cursor position
- display.println(F("Crack")); //Set the display text
- display.display(); //Output the display text
- delay(500);
- display.clearDisplay(); //Clear the display
- display.setCursor(45,10);
- display.println(F("The"));
- display.display();
- delay(500);
- display.clearDisplay();
- display.setCursor(40,10);
- display.println(F("Code"));
- display.display();
- delay(500);
- display.clearDisplay();
- }
- void PinA() //Rotary encoder interrupt service routine for one encoder pin
- {
- cli(); //Stop interrupts happening before we read pin values
- reading = PIND & 0xC; //Read all eight pin values then strip away all but pinA and pinB's values
- if(reading == B00001100 && aFlag) //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
- {
- if(encoderPos>1)
- encoderPos --; //Decrement the encoder's position count
- else
- encoderPos = 4; //Go back to 9 after 0
- bFlag = 0; //Reset flags for the next turn
- aFlag = 0; //Reset flags for the next turn
- }
- else if (reading == B00000100) //Signal that we're expecting pinB to signal the transition to detent from free rotation
- bFlag = 1;
- sei(); //Restart interrupts
- }
- void PinB() //Rotary encoder interrupt service routine for the other encoder pin
- {
- cli(); //Stop interrupts happening before we read pin values
- reading = PIND & 0xC; //Read all eight pin values then strip away all but pinA and pinB's values
- if (reading == B00001100 && bFlag) //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
- {
- if(encoderPos<4)
- encoderPos ++; //Increment the encoder's position count
- else
- encoderPos = 1; //Go back to 0 after 9
- bFlag = 0; //Reset flags for the next turn
- aFlag = 0; //Reset flags for the next turn
- }
- else if (reading == B00001000) //Signal that we're expecting pinA to signal the transition to detent from free rotation
- aFlag = 1;
- sei(); //Restart interrupts
- }
Nessun commento:
Posta un commento
Lascia un commento qui sotto, ti risponderò il prima possibile!