Skip to content

Instantly share code, notes, and snippets.

@asafmatan
Created February 16, 2023 23:03
Show Gist options
  • Save asafmatan/f4b95a6b3cd1bd3757fd052a7789cd86 to your computer and use it in GitHub Desktop.
Save asafmatan/f4b95a6b3cd1bd3757fd052a7789cd86 to your computer and use it in GitHub Desktop.
Crack The Code Purim
//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