#define I2C_ADDR 0x27 // Dirección I2C de la LCD (verificar en la pantalla LCD)
#define LCD_COLUMNS 20
#define LCD_LINES 4
#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
#include <DHT.h> // Incluir la librería DHT
#include <math.h> // Para isnan y abs (valor absoluto)
// Definición del layout del teclado y pines
const byte ROWS = 4; // Filas
const byte COLS = 4; // Columnas
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// Pines según diagram.json
byte rowPins[ROWS] = {15, 2, 4, 5}; // D15, D2, D4, D5
byte colPins[COLS] = {18, 19, 27, 23}; // D18, D19, D27, D23
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Definición del servo
Servo myServo;
const int servoPin = 14; // D14 según diagram.json
const int unlockPosition = -90; // Valor original del usuario para desbloquear
const int lockPosition = 90; // Valor original del usuario para bloquear
// Definición de la LCD
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
// Definición del sensor DHT22
const int dhtPin = 13; // Pin conectado al DHT22 (D13 según tu diagrama)
DHT dht(dhtPin, DHT22); // Objeto DHT para el sensor DHT22
// Variables globales
bool locked = true; // Estado de la cerradura (bloqueada/desbloqueada)
char enteredCode[5]; // Código de 4 dígitos + terminador nulo
int codeIndex = 0; // Índice para el código ingresado
const char correctCode[] = "1234"; // Código correcto
// Variables para temperatura y humedad
float temperature = NAN; // Inicializar con No-es-un-Número (Not-a-Number)
float humidity = NAN; // Inicializar con No-es-un-Número
// Variables para control por sensor
bool unlockedBySensor = false; // true si la puerta fue desbloqueada por sensor
const float TEMP_THRESHOLD_UNLOCK = 30.0;
const float HUM_THRESHOLD_UNLOCK = 50.0;
// Variables para tiempo
unsigned long lastActivityTime = 0; // Última vez que hubo actividad
const unsigned long timeoutDuration = 10000; // 10 segundos de inactividad para timeout
unsigned long lastDhtReadTime = 0; // Última vez que se leyó el DHT
const unsigned long dhtReadInterval = 2000; // Leer DHT cada 2 segundos
void setup() {
Serial.begin(9600);
Serial.println("Inicializando Sistema de Cerradura...");
// Inicializa el servo motor
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
myServo.setPeriodHertz(50);
myServo.attach(servoPin, 500, 2400);
myServo.write(lockPosition);
Serial.println("Servo motor inicializado en posición de bloqueo.");
// Inicializa la LCD
Wire.begin();
lcd.init();
lcd.backlight();
Serial.println("LCD inicializada.");
// Inicializa el sensor DHT22
dht.begin();
Serial.println("Sensor DHT22 inicializado.");
// Realizar una lectura inicial del DHT22
float initialTemp = dht.readTemperature();
float initialHum = dht.readHumidity();
if (!isnan(initialTemp)) {
temperature = initialTemp;
} else {
Serial.println("Fallo al leer temperatura inicial del DHT22!");
}
if (!isnan(initialHum)) {
humidity = initialHum;
} else {
Serial.println("Fallo al leer humedad inicial del DHT22!");
}
updateLockStatus();
lastActivityTime = millis();
Serial.println("Sistema de cerradura listo.");
}
void loop() {
char key = keypad.getKey();
if (key) {
Serial.print("Tecla presionada: ");
Serial.println(key);
lastActivityTime = millis();
handleKeypadInput(key);
}
// Verificar el timeout por inactividad
if (millis() - lastActivityTime > timeoutDuration) {
if (!locked) {
Serial.println("Inactividad detectada (puerta abierta). Bloqueando el sistema.");
lockDoor();
} else {
if (codeIndex > 0) {
Serial.println("Inactividad detectada (ingresando PIN). Reiniciando ingreso.");
codeIndex = 0;
memset(enteredCode, 0, sizeof(enteredCode));
updateLockStatus();
}
}
lastActivityTime = millis();
}
// Lectura no bloqueante y lógica de control por sensor DHT22
if (millis() - lastDhtReadTime > dhtReadInterval) {
float newTemperature = dht.readTemperature();
float newHumidity = dht.readHumidity();
bool tempChanged = false;
bool humChanged = false;
// Comprobar temperatura
if (isnan(newTemperature)) {
if (!isnan(temperature)) {
temperature = NAN;
tempChanged = true;
}
} else if (isnan(temperature) || abs(temperature - newTemperature) > 0.1) {
temperature = newTemperature;
tempChanged = true;
}
// Comprobar humedad
if (isnan(newHumidity)) {
if (!isnan(humidity)) {
humidity = NAN;
humChanged = true;
}
} else if (isnan(humidity) || abs(humidity - newHumidity) > 0.1) {
humidity = newHumidity;
humChanged = true;
}
if (tempChanged || humChanged) {
Serial.println("Datos del DHT actualizados.");
// Lógica de control por sensor
if (locked) {
// Condición para desbloquear por sensor
if ((!isnan(temperature) && temperature > TEMP_THRESHOLD_UNLOCK) || (!isnan(humidity) && humidity > HUM_THRESHOLD_UNLOCK)) {
Serial.println("Condiciones de sensor para DESBLOQUEO cumplidas.");
unlockDoor();
unlockedBySensor = true; // Marcar que fue desbloqueado por sensor
}
} else { // La puerta está desbloqueada
if (unlockedBySensor) { // Solo aplicar autobloqueo si fue desbloqueada por sensor
// Condición para bloquear automáticamente (si ya no se cumplen las de desbloqueo)
bool shouldLock = true; // Asumir que se debe bloquear
if (!isnan(temperature) && temperature > TEMP_THRESHOLD_UNLOCK) shouldLock = false;
if (!isnan(humidity) && humidity > HUM_THRESHOLD_UNLOCK) shouldLock = false;
if (shouldLock) {
Serial.println("Condiciones de sensor para AUTOBLOQUEO cumplidas.");
lockDoor();
}
}
}
// Actualizar LCD si no se está ingresando código, o si la puerta está desbloqueada,
// o si un cambio de sensor acaba de cambiar el estado de la cerradura (implícito por llamada a lock/unlockDoor)
if (codeIndex == 0 || !locked) {
updateLockStatus();
}
}
lastDhtReadTime = millis();
}
}
void updateLockStatus() {
lcd.clear();
// Primera línea: Temperatura
lcd.setCursor(0, 0);
lcd.print("Temp: ");
if (isnan(temperature)) {
lcd.print("Err");
} else {
lcd.print(temperature, 1);
lcd.print((char)223);
lcd.print("C");
}
// Segunda línea: Humedad
lcd.setCursor(0, 1);
lcd.print("Hum: ");
if (isnan(humidity)) {
lcd.print("Err");
} else {
lcd.print(humidity, 1);
lcd.print(" %");
}
// Tercera línea: Código Ingresado
lcd.setCursor(0, 2);
lcd.print("Codigo: ");
for (int i = 0; i < codeIndex; i++) {
lcd.print("*");
}
for (int i = codeIndex; i < (sizeof(enteredCode) - 1); i++) {
lcd.print(" ");
}
// Cuarta línea: Estado de la Puerta
lcd.setCursor(0, 3);
if (locked) {
lcd.print("Puerta Bloqueada ");
} else {
lcd.print("Puerta Desbloq.");
if (unlockedBySensor) {
lcd.print(" (Sensor)"); // Indica si fue desbloqueada por sensor
} else {
lcd.print(" (PIN) "); // Espacios para limpiar si antes decía (Sensor)
}
}
}
void handleKeypadInput(char key) {
if (locked) {
if (key == '#' && codeIndex > 0) {
enteredCode[codeIndex] = '\0';
Serial.print("Código ingresado: ");
Serial.println(enteredCode);
if (strcmp(enteredCode, correctCode) == 0) {
Serial.println("Código Correcto!");
unlockDoor();
unlockedBySensor = false; // IMPORTANTE: Indicar que fue desbloqueado por PIN
} else {
Serial.println("Código Incorrecto!");
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("PIN Incorrecto");
delay(1500);
codeIndex = 0;
memset(enteredCode, 0, sizeof(enteredCode));
updateLockStatus();
}
} else if (key == 'C' && codeIndex > 0) {
codeIndex--;
enteredCode[codeIndex] = '\0';
updateLockStatus();
} else if (isdigit(key) && codeIndex < (sizeof(enteredCode) - 1)) {
enteredCode[codeIndex] = key;
codeIndex++;
updateLockStatus();
} else if (key == '*' || key == 'A' || key == 'B' || key == 'D') {
Serial.print("Tecla no válida durante ingreso de PIN: ");
Serial.println(key);
}
} else { // Si está desbloqueado
if (key == '*') { // Usar '*' para bloquear la puerta manualmente
lockDoor();
}
}
}
void unlockDoor() {
locked = false;
myServo.write(unlockPosition);
Serial.println("Puerta Desbloqueada");
codeIndex = 0;
memset(enteredCode, 0, sizeof(enteredCode));
// No modificar 'unlockedBySensor' aquí directamente, se maneja en el contexto de la llamada
updateLockStatus();
}
void lockDoor() {
locked = true;
unlockedBySensor = false; // Siempre resetear esto al bloquear
myServo.write(lockPosition);
Serial.println("Puerta Bloqueada");
codeIndex = 0;
memset(enteredCode, 0, sizeof(enteredCode));
updateLockStatus();
}