#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_timer.h"
/* ==================== CONFIGURACION DE PINES ==================== */
#define LED_PIN GPIO_NUM_25 // LED: se enciende con codigo valido o durante todo Modo Programacion
#define BUZZER_PIN GPIO_NUM_26 // Buzzer: suena cuando el codigo ingresado es invalido
#define DOOR_PIN GPIO_NUM_27 // Señal a la puerta cuando el acceso es autorizado
// botones[0..3] = digitos 1,2,3,4 ; botones[4] = boton de Modo Programacion
int botones[] = {18, 19, 21, 22, 23};
#define NUM_BOTONES 5
#define PIN_MODO 23
#define DEBOUNCE_MS 350
/* ==================== MAQUINA DE ESTADOS ==================== */
typedef enum {
MODO_ESPERA, // Modo Acceso: espera ingreso de codigo o boton de Modo Programacion
MOD_ACCESO_ING_COD, // Modo Acceso: el usuario esta ingresando el codigo (4 digitos)
MOD_ACCESO_IN, // Modo Acceso: acceso autorizado, se activa LED + senal a la puerta
MOD_ACCESO_NEG, // Modo Acceso: acceso denegado, se activa el buzzer
MODO_PROG_ESPERA, // Se selecciono Modo Programacion, esperando el primer digito
MODO_PROG_USER_COD, // Modo Programacion: el usuario esta ingresando los 5 codigos (20 digitos)
MODO_PROG_STORE_COD // Modo Programacion: se almacenan los codigos nuevos (reemplazan los anteriores)
} MODOS;
MODOS state = MODO_ESPERA;
/* ==================== CODIGOS DE ACCESO ==================== */
#define LONGITUD_CODIGO 4
#define NUM_CODIGOS 5
static int codigos_validos[NUM_CODIGOS][LONGITUD_CODIGO]; // codigos activos (en RAM)
static int codigo_ingresado[LONGITUD_CODIGO];
static int digitos_ingresados = 0;
static int codigos_temp[NUM_CODIGOS][LONGITUD_CODIGO]; // buffer mientras se programa
static int codigo_actual = 0; // indice de codigo 0..4 que se esta capturando
static int digito_actual = 0; // indice de digito dentro del codigo 0..3
QueueHandle_t handlerQueue;
volatile int64_t last_press_time[NUM_BOTONES] = {0};
/* ==================== ISR ==================== */
static void IRAM_ATTR gpio_interrupt_handler(void *args) {
int idx = (int)args; // indice dentro de botones[]
int64_t ahora = esp_timer_get_time() / 1000;
if (ahora - last_press_time[idx] > DEBOUNCE_MS) {
last_press_time[idx] = ahora;
int pin = botones[idx];
xQueueSendFromISR(handlerQueue, &pin, NULL);
}
}
/* ==================== INICIALIZACION DE GPIO ==================== */
void init_gpio() {
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 0);
gpio_reset_pin(BUZZER_PIN);
gpio_set_direction(BUZZER_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(BUZZER_PIN, 0);
gpio_reset_pin(DOOR_PIN);
gpio_set_direction(DOOR_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(DOOR_PIN, 0);
for (int i = 0; i < NUM_BOTONES; i++) {
gpio_reset_pin(botones[i]);
gpio_set_direction(botones[i], GPIO_MODE_INPUT);
gpio_pulldown_en(botones[i]);
gpio_pullup_dis(botones[i]);
gpio_set_intr_type(botones[i], GPIO_INTR_POSEDGE);
gpio_isr_handler_add(botones[i], gpio_interrupt_handler, (void *)i);
}
}
/* Traduce el pin fisico al digito que representa (1-4). 0 si no es un digito (ej. boton de modo) */
int obtener_numeros(int pinNumber) {
switch (pinNumber) {
case 18: return 1;
case 19: return 2;
case 21: return 3;
case 22: return 4;
default: return 0;
}
}
/* ==================== UTILIDADES ==================== */
void sonar_buzzer(int ms) {
gpio_set_level(BUZZER_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(ms));
gpio_set_level(BUZZER_PIN, 0);
}
void resetear_intento_acceso() {
digitos_ingresados = 0;
}
bool codigo_es_valido(int intento[LONGITUD_CODIGO]) {
for (int i = 0; i < NUM_CODIGOS; i++) {
bool coincide = true;
for (int j = 0; j < LONGITUD_CODIGO; j++) {
if (codigos_validos[i][j] != intento[j]) {
coincide = false;
break;
}
}
if (coincide) return true;
}
return false;
}
void mostrar_prompt_acceso() {
printf("\n=== MODO ACCESO ===\n");
printf("Ingrese un codigo (4 digitos):\n");
}
void iniciar_modo_programacion() {
printf("\n=== MODO PROGRAMACION ===\n");
printf("Ingrese 5 codigos de 4 digitos cada uno (20 digitos en total).\n");
printf("Los codigos anteriores seran reemplazados.\n");
gpio_set_level(LED_PIN, 1); // LED encendido durante TODO el Modo Programacion
codigo_actual = 0;
digito_actual = 0;
state = MODO_PROG_ESPERA;
}
void finalizar_modo_programacion() {
memcpy(codigos_validos, codigos_temp, sizeof(codigos_validos));
printf("\nCodigos almacenados correctamente.\n");
gpio_set_level(LED_PIN, 0);
state = MODO_ESPERA;
mostrar_prompt_acceso();
}
/* ==================== TAREA PRINCIPAL ==================== */
void LED_Control_Task(void *params) {
int pinNumber;
mostrar_prompt_acceso();
while (true) {
if (xQueueReceive(handlerQueue, &pinNumber, portMAX_DELAY)) {
int valor = obtener_numeros(pinNumber);
switch (state) {
/* ---------- MODO ACCESO ---------- */
case MODO_ESPERA:
if (pinNumber == PIN_MODO) {
iniciar_modo_programacion();
} else if (valor != 0) {
codigo_ingresado[0] = valor;
digitos_ingresados = 1;
state = MOD_ACCESO_ING_COD;
printf("Digito %d/%d ingresado\n", digitos_ingresados, LONGITUD_CODIGO);
}
break;
case MOD_ACCESO_ING_COD:
if (valor != 0) {
codigo_ingresado[digitos_ingresados] = valor;
digitos_ingresados++;
printf("Digito %d/%d ingresado\n", digitos_ingresados, LONGITUD_CODIGO);
if (digitos_ingresados == LONGITUD_CODIGO) {
state = codigo_es_valido(codigo_ingresado) ? MOD_ACCESO_IN : MOD_ACCESO_NEG;
}
}
// Si se presiona el boton de modo a la mitad del ingreso, se ignora en esta version.
break;
/* ---------- MODO PROGRAMACION ---------- */
case MODO_PROG_ESPERA:
case MODO_PROG_USER_COD:
if (valor != 0) {
codigos_temp[codigo_actual][digito_actual] = valor;
digito_actual++;
printf("Codigo %d - digito %d/%d\n", codigo_actual + 1, digito_actual, LONGITUD_CODIGO);
state = MODO_PROG_USER_COD;
if (digito_actual == LONGITUD_CODIGO) {
digito_actual = 0;
codigo_actual++;
printf("Codigo %d capturado.\n", codigo_actual);
if (codigo_actual == NUM_CODIGOS) {
state = MODO_PROG_STORE_COD;
}
}
}
break;
default:
break;
}
/* ---------- Acciones asociadas a estados terminales ---------- */
if (state == MOD_ACCESO_IN) {
printf("ACCESO AUTORIZADO\n");
gpio_set_level(LED_PIN, 1);
gpio_set_level(DOOR_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(2000));
gpio_set_level(LED_PIN, 0);
gpio_set_level(DOOR_PIN, 0);
resetear_intento_acceso();
state = MODO_ESPERA;
mostrar_prompt_acceso();
} else if (state == MOD_ACCESO_NEG) {
printf("ACCESO DENEGADO\n");
sonar_buzzer(500);
resetear_intento_acceso();
state = MODO_ESPERA;
mostrar_prompt_acceso();
} else if (state == MODO_PROG_STORE_COD) {
finalizar_modo_programacion();
}
}
}
}
/* ==================== MAIN ==================== */
void app_main() {
handlerQueue = xQueueCreate(10, sizeof(int));
gpio_install_isr_service(0);
init_gpio();
xTaskCreate(LED_Control_Task, "LED_Control_Task", 2048, NULL, 1, NULL);
}