/**
Arduino Electronic Safe
Copyright (C) 2020, Uri Shaked.
Released under the MIT License.
*/
/**
Arduino Electronic Safe (I2C) - Versão com Alarme e Auto-Power Off
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
#include "SafeState.h"
#include "icons.h"
/* Configurações de Hardware */
#define SERVO_PIN 6
#define BUZZER_PIN 8
#define SERVO_LOCK_POS 20
#define SERVO_UNLOCK_POS 90
/* Configurações de Standby */
unsigned long lastInteractionTime = 0;
const unsigned long DISPLAY_TIMEOUT = 10000; // 10 segundos
bool displayIsOn = true;
Servo lockServo;
LiquidCrystal_I2C lcd(0x27, 16, 2);
SafeState safeState;
int wrongAttempts = 0;
/* Teclado */
const byte KEYPAD_ROWS = 4;
const byte KEYPAD_COLS = 4;
byte rowPins[KEYPAD_ROWS] = {5, 4, 3, 2};
byte colPins[KEYPAD_COLS] = {A3, A2, A1, A0};
char keys[KEYPAD_ROWS][KEYPAD_COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS);
// --- FUNÇÕES AUXILIARES ---
void updateIdleTimer() {
lastInteractionTime = millis();
if (!displayIsOn) {
lcd.backlight();
displayIsOn = true;
}
}
void playTone(int freq, int duration) {
tone(BUZZER_PIN, freq, duration);
delay(duration);
}
void alarm() {
lcd.backlight();
displayIsOn = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SISTEMA BLOQUEAD");
lcd.setCursor(0, 1);
lcd.print("Aguarde 30 seg");
for (int i = 0; i < 30; i++) {
tone(BUZZER_PIN, 800); delay(500);
tone(BUZZER_PIN, 500); delay(500);
}
noTone(BUZZER_PIN);
wrongAttempts = 0;
updateIdleTimer();
}
// --- CONTROLE DO COFRE ---
void lock() {
lockServo.write(SERVO_LOCK_POS);
safeState.lock();
}
void unlock() {
lockServo.write(SERVO_UNLOCK_POS);
wrongAttempts = 0;
}
String inputSecretCode() {
lcd.setCursor(5, 1);
lcd.print("[____]");
lcd.setCursor(6, 1);
String result = "";
while (result.length() < 4) {
char key = keypad.getKey();
// Verifica se deve desligar o LCD enquanto espera a tecla
if (displayIsOn && (millis() - lastInteractionTime > DISPLAY_TIMEOUT)) {
lcd.noBacklight();
displayIsOn = false;
}
if (key >= '0' && key <= '9') {
updateIdleTimer(); // Acorda o LCD e reseta o timer
playTone(1000, 50);
lcd.print('*');
result += key;
}
}
return result;
}
void showWaitScreen(int delayMillis) {
lcd.setCursor(2, 1);
lcd.print("[..........]");
lcd.setCursor(3, 1);
for (byte i = 0; i < 10; i++) {
delay(delayMillis);
lcd.print("=");
}
}
bool setNewCode() {
updateIdleTimer();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Novo Codigo:");
String newCode = inputSecretCode();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Confirme:");
String confirmCode = inputSecretCode();
if (newCode.equals(confirmCode)) {
safeState.setCode(newCode);
playTone(1500, 400);
return true;
} else {
lcd.clear();
lcd.print("Erro: Diferentes");
playTone(300, 500);
delay(2000);
return false;
}
}
void safeUnlockedLogic() {
updateIdleTimer();
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(ICON_UNLOCKED_CHAR);
lcd.print(" # para Trancar");
if (safeState.hasCode()) {
lcd.setCursor(0, 1);
lcd.print(" A = Novo Codigo");
}
char key = NO_KEY;
while (key != 'A' && key != '#') {
key = keypad.getKey();
if (displayIsOn && (millis() - lastInteractionTime > DISPLAY_TIMEOUT)) {
lcd.noBacklight();
displayIsOn = false;
}
if (key) updateIdleTimer();
}
bool readyToLock = (key == '#');
if (key == 'A') readyToLock = setNewCode();
if (readyToLock) {
lcd.clear();
lcd.setCursor(5, 0);
lcd.write(ICON_RIGHT_ARROW);
lcd.write(ICON_LOCKED_CHAR);
safeState.lock();
lock();
showWaitScreen(100);
}
}
void safeLockedLogic() {
// O LCD só mostra o "Locked" se estiver ligado
if (displayIsOn) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(ICON_LOCKED_CHAR);
lcd.print(" Cofre Fechado ");
lcd.write(ICON_LOCKED_CHAR);
}
String userCode = inputSecretCode(); // O inputSecretCode já gerencia o sono
bool unlockedSuccessfully = safeState.unlock(userCode);
showWaitScreen(200);
if (unlockedSuccessfully) {
updateIdleTimer();
lcd.clear();
lcd.print("Acesso Liberado!");
playTone(1500, 200);
unlock();
} else {
wrongAttempts++;
updateIdleTimer();
lcd.clear();
lcd.print("Senha Incorreta!");
lcd.setCursor(0, 1);
lcd.print("Tentativa: "); lcd.print(wrongAttempts);
playTone(200, 600);
if (wrongAttempts >= 3) {
alarm();
} else {
delay(2000);
}
}
}
// --- SETUP E LOOP ---
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
lcd.init();
lcd.backlight();
init_icons(lcd);
lockServo.attach(SERVO_PIN);
if (safeState.locked()) lock(); else unlock();
lastInteractionTime = millis();
lcd.setCursor(4, 0);
lcd.print("Iniciando...");
delay(1500);
}
void loop() {
// Gerencia o desligamento automático no loop principal
if (displayIsOn && (millis() - lastInteractionTime > DISPLAY_TIMEOUT)) {
lcd.noBacklight();
displayIsOn = false;
}
if (safeState.locked()) {
safeLockedLogic();
} else {
safeUnlockedLogic();
}
}