Created
February 16, 2023 23:03
-
-
Save asafmatan/f4b95a6b3cd1bd3757fd052a7789cd86 to your computer and use it in GitHub Desktop.
Crack The Code Purim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Code Breaker | |
//Source Michael Klements Arduino Uno code | |
//Converted to ESP32 - Asaf Matan | |
#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 | |
#include "AiEsp32RotaryEncoder.h" | |
#define SCREEN_WIDTH 128 // OLED display width, in pixels | |
#define SCREEN_HEIGHT 64 // OLED display height, in pixels | |
#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 | |
#define OLED_RESET 4 // 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) | |
Servo lockServo; // create servo object to control a servo | |
#define ROTARY_ENCODER_A_PIN 5 | |
#define ROTARY_ENCODER_B_PIN 15 | |
#define ROTARY_ENCODER_BUTTON_PIN 23 | |
#define ROTARY_ENCODER_STEPS 4 | |
byte correctNumLEDs[4] = {27,14,33,18}; //Pin numbers for correct number LEDs (Indicate a correct digit) | |
byte correctPlaceLEDs[4] = {12,26,25,32}; //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 | |
byte encoderPos = 0; //Current value of encoder position, digit being input form 0 to 9 | |
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ROTARY_ENCODER_A_PIN, ROTARY_ENCODER_B_PIN, ROTARY_ENCODER_BUTTON_PIN, -1, ROTARY_ENCODER_STEPS); | |
int buttonState = 0; | |
void IRAM_ATTR readEncoderISR() | |
{ | |
rotaryEncoder.readEncoder_ISR(); | |
} | |
/* Local Functions decleration */ | |
void startupAni (); | |
void updateDisplayCode(); | |
void generateNewCode(); | |
void inputCodeGuess(); | |
void checkCodeGuess(); | |
void updateLEDs (int corNum, int corPla); | |
void setup() | |
{ | |
Serial.begin(115200); //Starts the Serial monitor for debugging | |
Serial.println(F("Starting...")); | |
rotaryEncoder.begin(); | |
rotaryEncoder.setup(readEncoderISR); | |
rotaryEncoder.setBoundaries(0, 9, false); //minValue, maxValue, circleValues true|false (when max go to min and vice versa) | |
rotaryEncoder.setAcceleration(250); | |
// Let Oled diplay power up | |
delay(3000); | |
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { | |
Serial.println(F("SSD1306 allocation failed")); | |
for(;;); // Don't proceed, loop forever | |
} | |
display.clearDisplay(); //Clear display | |
display.setTextColor(SSD1306_WHITE); //Set the text colour to white | |
lockServo.attach(19); | |
for(int i=0 ; i<=3 ; i++) //Define pin modes for the LEDs | |
{ | |
pinMode(correctNumLEDs[i], OUTPUT); | |
pinMode(correctPlaceLEDs[i], OUTPUT); | |
digitalWrite(correctPlaceLEDs[i], LOW); | |
digitalWrite(correctNumLEDs[i], LOW); | |
} | |
pinMode(ROTARY_ENCODER_A_PIN, INPUT_PULLUP); //Set pinA as an input, pulled HIGH to the logic voltage | |
pinMode(ROTARY_ENCODER_B_PIN, INPUT_PULLUP); //Set pinB as an input, pulled HIGH to the logic voltage | |
delay(2000); // Pause for 2 seconds | |
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(45); //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 | |
while(!lock) //While button is not pressed, wait for it to be pressed | |
{ | |
if (rotaryEncoder.isEncoderButtonClicked()) | |
{ | |
Serial.println("button pressed"); | |
lockServo.write(140); //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 | |
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: "); | |
code[0]=0; | |
code[1]=5; | |
code[2]=0; | |
code[3]=2; | |
for (int i=0 ; i<= 3 ; i++) //Loops through the four digits and assigns a random number to each | |
{ | |
// esp_random(); | |
// code[i] = random(0,9); //Generate a random number for each digit | |
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 | |
//rotaryEncoder.setEncoderValue(0); | |
updateDisplayCode(); | |
while(!confirmed) //While the user has not confirmed the digit input | |
{ | |
if (rotaryEncoder.encoderChanged()) | |
{ | |
Serial.println(rotaryEncoder.readEncoder()); | |
encoderPos = rotaryEncoder.readEncoder(); //Store encoder position | |
codeGuess[i] = encoderPos; //If the button is pressed, accept the current digit into the guessed code | |
updateDisplayCode(); | |
} | |
if (rotaryEncoder.isEncoderButtonClicked()) | |
{ | |
codeGuess[i] = encoderPos; //If the button is pressed, accept the current digit into the guessed code | |
updateDisplayCode(); | |
confirmed=true; //Update the code being displayed | |
} | |
} | |
} | |
} | |
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 | |
} | |
Serial.print("Correct Place: "); | |
Serial.println(correctPlace); | |
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 | |
{ | |
Serial.print("Turn correct place led number: "); | |
Serial.println(correctPlaceLEDs[k]); | |
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(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment