// #include <TM1637Display.h>
// #include <Servo.h>
// #include <Wire.h>
// #include <LiquidCrystal_I2C.h>
// #include <IRremote.h>
// // --- PIN DEFINITIONS ---
// const int CLK = 2;
// const int DIO = 3;
// #define SERVO1 5
// #define SERVO2 6
// int receiver_pin = 9;
// // --- SETTINGS ---
// const int correctCode[4] = {1, 2, 3, 4};
// int enteredCode[4] = {0, 0, 0, 0};
// int codeIndex = 0;
// int currentAngle = 180;
// bool systemOn = false;
// bool codeValidated = false;
// // --- MOVEMENT & TIMING ---
// unsigned long lastActionTime = 0;
// unsigned long lastIRTime = 0;
// int movingDirection = 0; // 0 = stop, 1 = opening, -1 = closing
// TM1637Display display(CLK, DIO);
// Servo servo1;
// Servo servo2;
// LiquidCrystal_I2C lcd(0x27, 16, 2);
// void setup() {
// display.setBrightness(7);
// servo1.attach(SERVO1);
// servo2.attach(SERVO2);
// lcd.begin(16, 2);
// IrReceiver.begin(receiver_pin, ENABLE_LED_FEEDBACK);
// setDoorAngle(180);
// updatePowerState();
// }
// void loop() {
// // 1. CHECK IR RECEIVER
// if (IrReceiver.decode()) {
// int key = IrReceiver.decodedIRData.command;
// IrReceiver.resume();
// lastIRTime = millis(); // Track when we last saw ANY signal
// // POWER BUTTON (162)
// if (key == 162 ) {
// systemOn = !systemOn;
// updatePowerState();
// return;
// }
// if (systemOn) {
// // MASTER CLEAR (176)
// if (key == 176) { resetSystem(); return; }
// if (codeValidated) {
// delay(1000);
// lastActionTime = millis();
// if (key == 168) { autoOpenClose(); } // PLAY
// else if (key == 2) { movingDirection = 1; } // PLUS -> Start Opening
// else if (key == 152) { movingDirection = -1; } // MINUS -> Start Closing
// }
// else {
// int digit = mapKeyToDigit(key);
// if (digit != -1 && codeIndex < 4) {
// lastActionTime = millis();
// enteredCode[codeIndex] = digit;
// display.showNumberDec(digit, false, 1, codeIndex);
// lcd.setCursor(codeIndex, 1);
// lcd.print(digit);
// codeIndex++;
// if (codeIndex == 4) checkCode();
// }
// }
// //
// }
// }
// if (systemOn && codeValidated) {
// // Stop moving if the remote stops sending signals for more than 200ms
// if (millis() - lastIRTime > 200) {
// movingDirection = 0;
// }
// if (movingDirection == 1 && currentAngle > 90) {
// currentAngle -= 1; // Smooth step
// setDoorAngle(currentAngle);
// delay(15); // Controls speed
// }
// else if (movingDirection == -1 && currentAngle < 180) {
// currentAngle += 1;
// setDoorAngle(currentAngle);
// delay(15);
// }
// }
// // 3. AUTO-RESET & AUTO-CLOSE (5 seconds of inactivity)
// // Trigger if: 1. System is On AND
// // 2. (The code is already correct OR the user has started typing) AND
// // 3. The door isn't currently moving
// if (systemOn && (codeValidated || codeIndex > 0) && movingDirection == 0) {
// if (millis() - lastActionTime > 5000) {
// if (codeValidated && currentAngle < 180) {
// autoCloseSequence(); // Close the door if it was open
// } else {
// resetSystem(); // Just clear the screen if they were mid-typing
// }
// }
// }
// }
// void autoCloseSequence() {
// lcd.setCursor(0, 1);
// lcd.print("Auto-Closing... ");
// while (currentAngle < 180) {
// currentAngle += 1;
// setDoorAngle(currentAngle);
// delay(20);
// }
// resetSystem();
// }
// void autoOpenClose() {
// lcd.clear();
// lcd.print("Mode: PLAY");
// lcd.setCursor(0, 1);
// lcd.print("Auto-Opening... ");
// //setDoorAngle(90);
// while (currentAngle > 90) {
// currentAngle -= 1;
// setDoorAngle(currentAngle);
// delay(20);
// }
// currentAngle = 90;
// for (int i = 5; i > 0; i--) {
// display.showNumberDec(i);
// delay(1000);
// }
// autoCloseSequence();
// }
// void checkCode() {
// bool match = true;
// for(int i = 0; i < 4; i++) {
// if(enteredCode[i] != correctCode[i]) match = false;
// }
// if (match) {
// codeValidated = true;
// lastActionTime = millis();
// lcd.clear();
// lcd.print("Access Granted");
// lcd.setCursor(0, 1);
// lcd.print("PLAY / +/- Keys");
// } else {
// lcd.clear();
// lcd.print("Wrong Code!");
// display.showNumberDec(8888);
// delay(2000);
// resetSystem();
// }
// }
// void setDoorAngle(int angle) {
// servo1.write(angle);
// servo2.write(180 - angle);
// // Show 0-100% on 7-segment
// int percent = map(angle, 180, 90, 0, 100);
// display.showNumberDec(percent);
// }
// void updatePowerState() {
// if (systemOn) {
// lcd.backlight();
// resetSystem();
// } else {
// lcd.clear();
// lcd.noBacklight();
// display.clear();
// codeValidated = false;
// currentAngle = 180;
// setDoorAngle(180);
// }
// }
// void resetSystem() {
// codeIndex = 0;
// codeValidated = false;
// movingDirection = 0;
// for (int i = 0; i < 4; i++) { enteredCode[i] = 0; }
// //if (systemOn) {
// lcd.clear();
// lcd.print("Enter Code:");
// uint8_t dashes[] = {0x40, 0x40, 0x40, 0x40};
// display.setSegments(dashes);
// // }
// }
// int mapKeyToDigit(int key) {
// if (key == 48) return 1;
// if (key == 24) return 2;
// if (key == 122) return 3;
// if (key == 16) return 4;
// if (key == 56) return 5;
// if (key == 90) return 6;
// if (key == 66) return 7;
// if (key == 74) return 8;
// if (key == 82) return 9;
// if (key == 104) return 0;
// return -1;
// }
#include <TM1637Display.h>
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <IRremote.h>
// --- PIN DEFINITIONS ---
const int CLK = 2;
const int DIO = 3;
#define SERVO1 5
#define SERVO2 6
int receiver_pin = 9;
// --- SETTINGS ---
const int correctCode[4] = {1, 2, 3, 4};
int enteredCode[4] = {0, 0, 0, 0};
int codeIndex = 0;
int currentAngle = 180;
bool systemOn = false;
bool codeValidated = false;
// --- MOVEMENT & TIMING ---
unsigned long lastActionTime = 0;
unsigned long lastIRTime = 0;
unsigned long lastServoMove = 0; // For non-blocking movement
int movingDirection = 0; // 0 = stop, 1 = opening, -1 = closing
TM1637Display display(CLK, DIO);
Servo servo1;
Servo servo2;
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
display.setBrightness(7);
servo1.attach(SERVO1);
servo2.attach(SERVO2);
lcd.begin(16, 2);
IrReceiver.begin(receiver_pin, ENABLE_LED_FEEDBACK);
setDoorAngle(180);
updatePowerState();
}
void loop() {
// 1. CHECK IR RECEIVER
if (IrReceiver.decode()) {
int key = IrReceiver.decodedIRData.command;
IrReceiver.resume();
lastIRTime = millis();
if (key == 162 ) {
systemOn = !systemOn;
updatePowerState();
return;
}
if (systemOn) {
if (key == 176) { resetSystem(); return; }
if (codeValidated) {
lastActionTime = millis();
if (key == 168) { autoOpenClose(); }
else if (key == 2) { movingDirection = 1; }
else if (key == 152) { movingDirection = -1; }
}
else {
int digit = mapKeyToDigit(key);
if (digit != -1 && codeIndex < 4) {
lastActionTime = millis();
enteredCode[codeIndex] = digit;
display.showNumberDec(digit, false, 1, codeIndex);
lcd.setCursor(codeIndex, 1);
lcd.print(digit);
codeIndex++;
if (codeIndex == 4) checkCode();
}
}
}
}
// 2. CONTINUOUS MOVEMENT LOGIC
if (systemOn && codeValidated) {
// Increased timeout to 400ms to prevent jitter/stopping
if (millis() - lastIRTime > 400) {
movingDirection = 0;
}
// Non-blocking movement timer
if (movingDirection != 0 && (millis() - lastServoMove > 15)) {
lastServoMove = millis();
if (movingDirection == 1 && currentAngle > 90) {
currentAngle -= 1;
setDoorAngle(currentAngle);
}
else if (movingDirection == -1 && currentAngle < 180) {
currentAngle += 1;
setDoorAngle(currentAngle);
}
}
}
// 3. AUTO-RESET & AUTO-CLOSE
if (systemOn && (codeValidated || codeIndex > 0) && movingDirection == 0) {
if (millis() - lastActionTime > 5000) {
if (codeValidated && currentAngle < 180) {
autoCloseSequence();
} else if (!codeValidated) {
resetSystem();
}
}
}
}
void autoCloseSequence() {
lcd.setCursor(0, 1);
lcd.print("Auto-Closing... ");
while (currentAngle < 180) {
currentAngle += 1;
setDoorAngle(currentAngle);
delay(20);
}
resetSystem();
}
void autoOpenClose() {
lcd.clear();
lcd.print("Mode: PLAY");
lcd.setCursor(0, 1);
lcd.print("Auto-Opening... ");
while (currentAngle > 90) {
currentAngle -= 1;
setDoorAngle(currentAngle);
delay(20);
}
currentAngle = 90;
for (int i = 5; i > 0; i--) {
display.showNumberDec(i);
delay(1000);
}
autoCloseSequence();
}
void checkCode() {
bool match = true;
for(int i = 0; i < 4; i++) {
if(enteredCode[i] != correctCode[i]) match = false;
}
if (match) {
codeValidated = true;
lastActionTime = millis();
lcd.clear();
lcd.print("Access Granted");
lcd.setCursor(0, 1);
lcd.print("PLAY / +/- Keys");
} else {
lcd.clear();
lcd.print("Wrong Code!");
display.showNumberDec(8888);
delay(2000);
resetSystem();
}
}
void setDoorAngle(int angle) {
servo1.write(angle);
servo2.write(180 - angle);
int percent = map(angle, 180, 90, 0, 100);
display.showNumberDec(percent);
}
void updatePowerState() {
if (systemOn) {
lcd.backlight();
resetSystem();
} else {
lcd.clear();
lcd.noBacklight();
display.clear();
codeValidated = false;
currentAngle = 180;
setDoorAngle(180);
}
}
void resetSystem() {
codeIndex = 0;
codeValidated = false;
movingDirection = 0;
for (int i = 0; i < 4; i++) { enteredCode[i] = 0; }
lcd.clear();
lcd.print("Enter Code:");
uint8_t dashes[] = {0x40, 0x40, 0x40, 0x40};
display.setSegments(dashes);
}
int mapKeyToDigit(int key) {
if (key == 48) return 1;
if (key == 24) return 2;
if (key == 122) return 3;
if (key == 16) return 4;
if (key == 56) return 5;
if (key == 90) return 6;
if (key == 66) return 7;
if (key == 74) return 8;
if (key == 82) return 9;
if (key == 104) return 0;
return -1;
}servo1
servo2