Maintenance: Project Hub will be unavailable on Wednesday 25 (9AM to 6PM CET) while we deploy critical improvements
Electronic Puzzle Box Game
Players complete timed challenges to beat the game.
Components and supplies
Push Button
Breadboard - 400 contacts
Multicolored Dupont Wire
Box of 100 pcs M.O. 10 VAL. trimmer resistors from 500 to 1 Mohm
Arduino Nano
Panel Mount 1K potentiometer (Breadboard Friendly) - 1K Linear
matrix 4x4 keypad
LED(Light Emitting Diode) White,Green,Red,Yellow,Blue Color
Mini breadboard - White
piezo speaker
16x2 LCD display with I²C interface
Tools and machines
Soldering kit
Hot Glue Gun
Apps and platforms
tinkercad circuits
Arduino IDE
Project description
Code
Electronic Puzzle Box
cpp
1// C++ code 2// 3 4#include <Keypad.h> 5 6 7#include <LiquidCrystal_I2C.h> 8 9LiquidCrystal_I2C lcd_1(0x27, 16, 2); 10 int start = 0; 11 12 bool lose = 0; 13 14 long startMillis = millis(); 15 long currentMillis; 16 int const period = 500; 17 18 long startMillis2 = millis(); 19 long currentMillis2; 20 int const period2 = 1000; 21 22 int seconds = 59; 23 int minutes = 1; 24 25 int lives = 3; 26 int tasks = 11; 27 28 int speakerPin = 13; 29 30 int pressure = 100; 31 int buttonTimer = 0; 32 33const int buttonPin = A0; // Pin where the button is connected 34 35int potentiometerPin = A1; 36 37int lockCode; 38int lockScore = 0; 39int ledLockPin = 10; 40int lockPeriod = 2500; 41int lockDistance; 42 43int lockstartMillis = millis(); 44 45const int ROW_NUM = 4; //four rows 46const int COLUMN_NUM = 4; //four columns 47 48const int stringLength = 5; // Change this to generate a longer or shorter string 49char result[stringLength + 1]; // +1 for the null terminator 50 51const int morseSpeakerPin = 11; 52const unsigned long dotDuration = 200; // 200 ms 53const unsigned long dashDuration = 600; // 600 ms 54 55bool morseActive = false; 56unsigned long morseStartTime = 0; 57unsigned long morseDuration = 0; 58 59int startTime2 = millis(); 60 61char inputBuffer[6]; // You can adjust size as needed 62int inputIndex = 0; 63 64 65int failures = 0; 66 67int pauseCounter = 0; 68 69char keys[ROW_NUM][COLUMN_NUM] = { 70 {'1','2','3', 'A'}, 71 {'4','5','6', 'B'}, 72 {'7','8','9', 'C'}, 73 {'*','0','#', 'D'} 74}; 75 76byte pin_rows[ROW_NUM] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad 77byte pin_column[COLUMN_NUM] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad 78 79Keypad keypad = Keypad( makeKeymap(keys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM ); 80 81void setup() 82{ 83 lcd_1.backlight(); 84 pinMode(buttonPin, INPUT); 85 pinMode(speakerPin, OUTPUT); 86 pinMode(12, OUTPUT); 87 pinMode(potentiometerPin, INPUT); 88 pinMode(ledLockPin, OUTPUT); 89 90 randomSeed(analogRead(A3)); 91 92 lcd_1.begin(16, 2); 93 Serial.begin(9600); 94 95 lcd_1.setCursor(0, 0); 96 lcd_1.print(minutes); 97 lcd_1.print(":"); 98 lcd_1.print(seconds); 99 100 101 lcd_1.setCursor(9, 0); 102 lcd_1.print("Lives:"); 103 lcd_1.print(lives); 104 long startTime2 = millis(); 105 106 const char charset[] = "0123456789ABCD"; 107 int charsetLength = sizeof(charset) - 1; 108 109 for (int i = 0; i < stringLength; i++) { 110 int index = random(charsetLength); 111 result[i] = charset[index]; 112 } 113 114 result[stringLength] = '\0'; // Null-terminate the string 115 116 Serial.println("Generated random string:"); 117 Serial.println(result); 118 119 lockCode = random(1, 1024); 120 121 122 123 124} 125 126 127void loop() 128{ 129 130 131 while(start == 0){ 132 lcd_1.clear(); 133 lcd_1.setCursor(0,0); 134 lcd_1.print("Press button to"); 135 lcd_1.setCursor(0,1); 136 lcd_1.print("start."); 137 delay(100); 138 lcd_1.clear(); 139 int reading = digitalRead(buttonPin); 140 if(reading== HIGH){ 141 start++; 142 } 143 } 144 145 146 for(int i = 0; i < stringLength; i++){ 147 if(result[i] == 'A'){ 148 morseDot(); 149 morseDash(); 150 nonDelay(); 151 nonDelay(); 152 } 153 if(result[i] == 'B'){ 154 morseDash(); 155 morseDot(); 156 morseDot(); 157 morseDot(); 158 nonDelay(); 159 nonDelay(); 160 } 161 if(result[i] == 'C'){ 162 morseDash(); 163 morseDot(); 164 morseDash(); 165 morseDot(); 166 nonDelay(); 167 nonDelay(); 168 } 169 if(result[i] == 'D'){ 170 morseDash(); 171 morseDot(); 172 morseDot(); 173 nonDelay(); 174 nonDelay(); 175 } 176 if(result[i] == '0'){ 177 morseDash(); 178 morseDash(); 179 morseDash(); 180 morseDash(); 181 morseDash(); 182 nonDelay(); 183 nonDelay(); 184 } 185 if(result[i] == '1'){ 186 morseDot(); 187 morseDash(); 188 morseDash(); 189 morseDash(); 190 morseDash(); 191 nonDelay(); 192 nonDelay(); 193 } 194 if(result[i] == '2'){ 195 morseDot(); 196 morseDot(); 197 morseDash(); 198 morseDash(); 199 morseDash(); 200 nonDelay(); 201 nonDelay(); 202 } 203 if(result[i] == '3'){ 204 morseDot(); 205 morseDot(); 206 morseDot(); 207 morseDash(); 208 morseDash(); 209 nonDelay(); 210 nonDelay(); 211 } 212 if(result[i] == '4'){ 213 morseDot(); 214 morseDot(); 215 morseDot(); 216 morseDot(); 217 morseDash(); 218 nonDelay(); 219 nonDelay(); 220 } 221 if(result[i] == '5'){ 222 morseDot(); 223 morseDot(); 224 morseDot(); 225 morseDot(); 226 morseDot(); 227 nonDelay(); 228 nonDelay(); 229 } 230 if(result[i] == '6'){ 231 morseDash(); 232 morseDot(); 233 morseDot(); 234 morseDot(); 235 morseDot(); 236 nonDelay(); 237 nonDelay(); 238 } 239 if(result[i] == '7'){ 240 morseDash(); 241 morseDash(); 242 morseDot(); 243 morseDot(); 244 morseDot(); 245 nonDelay(); 246 nonDelay(); 247 } 248 if(result[i] == '8'){ 249 morseDash(); 250 morseDash(); 251 morseDash(); 252 morseDot(); 253 morseDot(); 254 nonDelay(); 255 nonDelay(); 256 } 257 if(result[i] == '9'){ 258 morseDash(); 259 morseDash(); 260 morseDash(); 261 morseDash(); 262 morseDot(); 263 nonDelay(); 264 nonDelay(); 265 } 266 267 pauseCounter++; 268 Serial.println(pauseCounter); 269 270 if(pauseCounter >= 5){ 271 nonDelay(); 272 nonDelay(); 273 nonDelay(); 274 pauseCounter = 0; 275 } 276 277 278 } 279} 280 281void morseDot() { 282 tone(morseSpeakerPin, 440, 200); 283 nonDelay(); 284} 285 286void morseDash() { 287 tone(morseSpeakerPin, 220, 400); 288 nonDelay(); 289} 290 291void nonDelay() { 292 293 294 if(tasks == 0){ 295 296 lcd_1.clear(); 297 lcd_1.print("Bomb defused!"); 298 delay(500); 299 300 while(tasks == 0){ 301 302 int reading = digitalRead(buttonPin); 303 if(reading == HIGH){ 304 305 randomSeed(analogRead(A3)); 306 307 minutes= 1; 308 seconds =59; 309 lives = 3; 310 pressure = 100; 311 tasks = 11; 312 lockScore = 0; 313 314 lcd_1.clear(); 315 lcd_1.setCursor(0, 0); 316 lcd_1.print(minutes); 317 lcd_1.print(":"); 318 319 lcd_1.print(seconds); 320 321 322 lcd_1.setCursor(9, 0); 323 lcd_1.print("Lives:"); 324 lcd_1.print(lives); 325 long startTime2 = millis(); 326 327 const char charset[] = "0123456789ABCD"; 328 int charsetLength = sizeof(charset) - 1; 329 330 for (int i = 0; i < stringLength; i++) { 331 int index = random(charsetLength); 332 result[i] = charset[index]; 333 } 334 335 result[stringLength] = '\0'; // Null-terminate the string 336 337 Serial.println("Generated random string:"); 338 Serial.println(result); 339 340 lockCode = random(1, 1024); 341 } 342 } 343 344 } 345 346 if(lose==1){ 347 lcd_1.clear(); 348 lcd_1.print("You blew up!"); 349 lcd_1.setCursor(0,1); 350 lcd_1.print("Restart?"); 351 352 while(lose == 1){ 353 int reading = digitalRead(buttonPin); 354 if(reading == HIGH){ 355 lose = 0; 356 randomSeed(analogRead(A3)); 357 358 minutes= 1; 359 seconds =59; 360 lives = 3; 361 pressure = 100; 362 tasks = 11; 363 lockScore = 0; 364 365 lcd_1.clear(); 366 lcd_1.setCursor(0, 0); 367 lcd_1.print(minutes); 368 lcd_1.print(":"); 369 370 lcd_1.print(seconds); 371 372 373 lcd_1.setCursor(9, 0); 374 lcd_1.print("Lives:"); 375 lcd_1.print(lives); 376 long startTime2 = millis(); 377 378 const char charset[] = "0123456789ABCD"; 379 int charsetLength = sizeof(charset) - 1; 380 381 for (int i = 0; i < stringLength; i++) { 382 int index = random(charsetLength); 383 result[i] = charset[index]; 384 } 385 386 result[stringLength] = '\0'; // Null-terminate the string 387 388 Serial.println("Generated random string:"); 389 Serial.println(result); 390 391 lockCode = random(1, 1024); 392 } 393 } 394 } 395 396 if(lives == 0){ 397 lose=1; 398 } 399 400 if(pressure>=1000){ 401 lose=1; 402 } 403 404 if(pressure<=0){ 405 lose=1; 406 delay(500); 407 } 408 409 currentMillis2 = millis(); 410 if (currentMillis2 - startMillis2 >= period2) //test whether the period has elapsed 411 { 412 seconds = seconds - 1; 413 lcd_1.setCursor(0,0); 414 lcd_1.print(minutes); 415 lcd_1.print(":"); 416 if(seconds < 10){ 417 lcd_1.print("0"); 418 } 419 lcd_1.print(seconds); 420 startMillis2 = currentMillis2; 421 422 if(seconds == 0){ 423 minutes--; 424 seconds=60; 425 } 426 } 427 428 lcd_1.setCursor(9, 0); 429 lcd_1.print("Lives:"); 430 lcd_1.print(lives); 431 432 currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started) 433 if (currentMillis - startMillis >= period) //test whether the period has elapsed 434 { 435 digitalWrite(speakerPin, !digitalRead(speakerPin)); //if so, change the state of the LED. Uses a neat trick to change the state 436 startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state. 437 } 438 439 440 unsigned long waitTime = 600; 441 unsigned long startTime = millis(); 442 443 444 445 446 //Serial.println(lockScore); 447 //Serial.println(lockDistance); 448 449 450 lcd_1.setCursor(9,1); 451 452 lcd_1.print("Mod:"); 453 lcd_1.print(tasks); 454 455 if(tasks<10){ 456 lcd_1.setCursor(14, 1); 457 lcd_1.print(" "); 458 } 459 460 lcd_1.setCursor(0,1); 461 lcd_1.print("Pres:"); 462 463 lcd_1.print(pressure); 464 if(pressure < 100){ 465 lcd_1.setCursor(7,1); 466 lcd_1.print(" "); 467 } 468 if(pressure < 10){ 469 lcd_1.setCursor(6,1); 470 lcd_1.print(" "); 471 } 472 473 while (millis() - startTime < waitTime) { 474 int lockInput = analogRead(potentiometerPin); 475 int lockDistance = abs(lockCode-lockInput); 476 int brightness = (1024 - lockDistance); 477 478 479 if(minutes<0){ 480 lose=1; 481 } 482 483 brightness = map(brightness, 0, 1024, 0, 255); 484 485 analogWrite(ledLockPin, brightness); 486 487 488 if(lockDistance <= 100){ 489 int lockcurrentMillis = millis(); 490 491 if(lockcurrentMillis - lockstartMillis >= lockPeriod){ 492 if (lockScore <10){ 493 lockScore++; 494 tasks--; 495 } 496 lockCode = random(1, 1024); 497 lockstartMillis = lockcurrentMillis; 498 } 499 } 500 501 502 int reading = digitalRead(buttonPin); 503 //Serial.println(reading); 504 505 506static unsigned long lastPressureInc = 0; 507static unsigned long lastPressureDec = 0; 508 509unsigned long now = millis(); 510 511if (reading == HIGH) { 512 if (now - lastPressureDec >= 20) { // e.g., reduce pressure once per second while held 513 pressure--; 514 lastPressureDec = now; 515 } 516} else { 517 if (now - lastPressureInc >= 30) { // e.g., increase pressure every 2 sec if not pressed 518 pressure++; 519 lastPressureInc = now; 520 } 521} 522 523 524 char key = keypad.getKey(); // non-blocking read 525 if (key) { 526 //Serial.print("Interrupt Key Pressed: "); 527 528 529 //Serial.println(key); 530 531 532 533 if (inputIndex < sizeof(inputBuffer) - 1) { // prevent overflow 534 inputBuffer[inputIndex++] = key; 535 inputBuffer[inputIndex] = '\0'; // keep string null-terminated 536 Serial.print("Current input buffer: "); 537 Serial.println(inputBuffer); 538 lcd_1.clear(); 539 lcd_1.print(inputBuffer); 540 delay(500); 541 lcd_1.clear(); 542 543 if (inputIndex == stringLength) { // Only check after full code entered 544 if (strcmp(inputBuffer, result) == 0) { 545 // Correct code 546 tasks--; 547 lcd_1.clear(); 548 lcd_1.setCursor(0, 0); 549 lcd_1.print("Correct Code!"); 550 delay(300); 551 lcd_1.clear(); 552 lcd_1.setCursor(0,0); 553 lcd_1.print(minutes); 554 lcd_1.print(":"); 555 if(seconds < 10){ 556 lcd_1.print("0"); 557 } 558 lcd_1.print(seconds); 559 560 } else { 561 // Incorrect code 562 lives--; 563 lcd_1.clear(); 564 lcd_1.setCursor(0, 0); 565 lcd_1.print("Wrong Code!"); 566 delay(300); 567 lcd_1.clear(); 568 lcd_1.setCursor(0,0); 569 lcd_1.print(minutes); 570 lcd_1.print(":"); 571 if(seconds < 10){ 572 lcd_1.print("0"); 573 } 574 lcd_1.print(seconds); 575 } 576 577 // Clear buffer for next attempt 578 inputIndex = 0; 579 inputBuffer[0] = '\0'; 580 } 581} 582 583 } 584 } 585}
Documentation
Circuit Schematic
Screenshot 2025-06-15 175239.png

Front side
The screw is used to hold the top panel in place.
IMG_7195.jpeg

Inside the box
IMG_7201.jpeg

Top side
The LCD and top is held by zip ties.
IMG_7198.jpeg

Wired up Arduino
IMG_7209.jpeg

Video
IMG_7217.mov
Comments
Only logged in users can leave comments