Caja de seguridad con autenticaciones biométricas y código pin

Caja de seguridad de apertura automática. Dispone de autenticación facial, de huella y de código pin. Permite proteger de una manera mucho más segura lo que se quiera guardar dentro.

La madera de contrachapado junto con los bordes metálicos, permiten hacer de esta caja un espacio seguro en el que guardar tus artilugios de mayor valor. Con sus autenticaciones biométricas, además de la clave, podrás asegurarte de que estos artilugios sólo lleguen a las manos de las personas en las que más confías.

Índice

  1. Introducción
  2. Funcionamiento
  3. Material usado y costes
  4. Diseño
  5. Implementación
  6. Construcción
  7. Problemas y soluciones encontradas
  8. Posibles mejoras
  9. Demostración
  10. Autores

Introducción

Para llevar a cabo este proyecto, se comenzó planteando un problema que queríamos solucionar. Las cajas fuertes que vemos normalmente sólo disponen de un código pin. Esto las hace vulnerables a robo, puesto que, al final, únicamente un código pin como seguridad puede no ser suficiente.

Esto nos llevó a desarrollar la idea de crear una caja fuerte que tuviera más opciones de seguridad, mediante el uso de sistemas extras además del pin, como las autenticaciones biométricas.

Inicialmente, se ideó únicamente hacerlo con una autenticación de huella, pero, al considerarlo insuficiente, decidimos añadir el reconocimiento facial al proyecto, lo que, posteriormente, sería lo que más dificultad traería a su desarrollo.

Funcionamiento

El funcionamiento del proyecto, realmente, es sencillo. Al final, se trata de una puerta que, en el caso de que todas las autenticaciones necesarias para cada caso se lleven a cabo correctamente, se dispondrá de distintas funciones, como puede ser la de desbloquear la puerta de la caja fuerte, entrar en el «modo administrador», insertar una nueva huella permitida, etc.

Se diferencian varios casos de uso:

  • Si un usuario no permitido intenta abrir la caja insertando su huella, el LCD mostrará que se trata de un usuario no identificado, y la puerta no se desbloqueará, mostrándose eso mismo en la pantalla del LCD de nuevo.
  • Si un usuario permitido, sin ser usuario administrador, intenta abrir la caja insertando su huella, el LCD mostrará que se trata de un usuario identificado, y la puerta se desbloqueará, mostrándose el mensaje correspondiente también en pantalla.
  • Si un usuario administrador intenta abrir la caja con su huella, ocurrirá lo mismo que con un usuario permitido, pero, además, este usuario, una vez identificado por el reconocimiento facial, y habiendo metido el código correspondiente para su correspondiente acceso, podrá insertar más huellas por medio del teclado hexadecimal. En el teclado, la ‘#’ tiene la función de «Continuar», y el ‘*’ la de «Cancelar», las cuales serán necesarias para usar el «modo administrador».
  • Como caso de uso incorrecto, debe mencionarse que cualquier usuario que trate de acceder a funciones que no corresponden con su rol, al necesitar acceder a las mismas por medio de las autenticaciones biométricas, simplemente no será capaz de conseguirlo.

Material usado y costes

Por parte de los materiales, detallamos en la tabla a continuación los finalmente utilizados, además del coste de los mismos.

MaterialesCostes
ESP32 USB C ESP32 CAM Placa de Desarrollo, WLAN/Bluetooth, ESP32 DC 5V Placa de Desarrollo de Doble núcleo con módulo de cámara 264011,99 €
Módulo USB a UART TTL (CP2102 DTR PIN ARCP2102P6)7 €
Adaptador USB C a entrada USB de Carga y Datos3 €
Sensor de huellas dactilares FPM10A10,02 €
Interruptor de fin de carrera3,21 €
Teclado hexadecimal 4×4Proporcionado en el kit
LCD con Bus I2CProporcionado en el kit
ServomotorProporcionado en el kit
ResistenciasProporcionado en el kit
CablesProporcionado en el kit
Placa Arduino UNOProporcionado en el kit
Tablón de madera 60×60 cm (2 unidades)0 €
Tablón de madera fina 60×60 cm (2 unidades)0 €
Manillar pequeño0 €
Tornillos (Aprox. 20 unidades)0 €
Piezas de metal0 €
Extras (para montaje, decoración, etc.)36,40 €
Coste total71,62

Por parte de los materiales de 0 €, se trata de los materiales que han sido proporcionados de manera gratuita por parte de un familiar de una de las integrantes del grupo, permitiéndonos ahorrar considerablemente en su montaje.

Además, los materiales cuyo precio se define por “Proporcionado en el kit” se refiere a materiales que han sido proporcionados por los docentes en un kit entregado a principio de curso, o a materiales de los que los integrantes del grupo ya disponíamos.

Diseño

Para el diseño del circuito electrónico, este se ha creado con ayuda de la aplicación Tinkercad, con la cual se pudo hacer un diseño más cercano a la realidad, tal y como se muestra en la siguiente imagen.

Para explicar las conexiones realizadas en el circuito, lo dividiremos por componentes en los siguientes apartados para un mejor entendimiento.

Placa ESP32 con Cámara OV2640

Placa ESP32Placa Protoboard
GNDGND
+5V5V

Es necesario destacar que, debido a que el puerto de la ESP32 del que se dispone es de tipo USB C, ha sido necesario comprar dos adaptadores, un adaptador USB C a entrada USB, y un adaptador USB a TTL, consiguiendo así conectarla con la placa Arduino.

Sensor de Huella Dactilar

Puertos ArduinoPines Sensor de Huella Dactilar
2TXD
3RXD
5V+5V
GNDGND

Fin de carrera (Botón en el diseño con Tinkercad)

Puertos ArduinoPines Servomotor
4NO (Placa Protoboard)
5VNO (Placa Protoboard)
GNDCOM

Cabe destacar que para la implementación del botón se han usado dos patillas NO y COM. Para hacerlo, primero se ha limitado la transmisión de corriente mediante una resistencia de 10 kΩ, y después se ha conectado al circuito el botón mediante la patilla NO, la cual proporciona los datos al puerto Arduino 4 y la corriente al mismo tiempo, posteriormente cerrando el circuito desde la patilla COM del botón, la cual conecta al GND.

Servomotor

Puertos ArduinoPines Servomotor
5Data
5VVCC
GNDGND

Teclado Hexadecimal 4×4

Puertos ArduinoPines Teclado Hexadecimal 4×4
6Columna 4
7Columna 3
8Columna 2
9Columna 1
10Fila 4
11Fila 3
12Fila 2
13Fila 1

LCD con un Bus I2C

Puertos ArduinoPines LCD I2C
A4SDA
A5SCL
5VVCC
GNDGND

Implementación

Por parte de la implementación software, el proyecto se creó con el siguiente código:

  • Archivo para que se abra el servidor web, y para que los pines de la cámara se establezcan y conecten correctamente (código de ejemplo).
#include "esp_camera.h"
#include <WiFi.h>

//
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
//            Ensure ESP32 Wrover Module or other board with PSRAM is selected
//            Partial images will be transmitted if image exceeds buffer size
//
//            You must select partition scheme from the board menu that has at least 3MB APP space.
//            Face Recognition is DISABLED for ESP32 and ESP32-S2, because it takes up from 15
//            seconds to process single frame. Face Detection is ENABLED if PSRAM is enabled as well

// ===================
// Select camera model
// ===================
//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE  // Has PSRAM
//#define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM
//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
//#define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM
//#define CAMERA_MODEL_M5STACK_CAMS3_UNIT  // Has PSRAM
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
//#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
// ** Espressif Internal Boards **
//#define CAMERA_MODEL_ESP32_CAM_BOARD
//#define CAMERA_MODEL_ESP32S2_CAM_BOARD
//#define CAMERA_MODEL_ESP32S3_CAM_LCD
//#define CAMERA_MODEL_DFRobot_FireBeetle2_ESP32S3 // Has PSRAM
//#define CAMERA_MODEL_DFRobot_Romeo_ESP32S3 // Has PSRAM
#include "camera_pins.h"

// ===========================
// Enter your WiFi credentials
// ===========================
const char *ssid = "iPhone de Paula";
const char *password = "***********";

void startCameraServer();
void setupLedFlash(int pin);

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_UXGA;
  config.pixel_format = PIXFORMAT_JPEG;  // for streaming
  //config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
  config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 12;
  config.fb_count = 1;

  // if PSRAM IC present, init with UXGA resolution and higher JPEG quality
  //                      for larger pre-allocated frame buffer.
  if (config.pixel_format == PIXFORMAT_JPEG) {
    if (psramFound()) {
      config.jpeg_quality = 10;
      config.fb_count = 2;
      config.grab_mode = CAMERA_GRAB_LATEST;
    } else {
      // Limit the frame size when PSRAM is not available
      config.frame_size = FRAMESIZE_SVGA;
      config.fb_location = CAMERA_FB_IN_DRAM;
    }
  } else {
    // Best option for face detection/recognition
    config.frame_size = FRAMESIZE_240X240;
#if CONFIG_IDF_TARGET_ESP32S3
    config.fb_count = 2;
#endif
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t *s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);        // flip it back
    s->set_brightness(s, 1);   // up the brightness just a bit
    s->set_saturation(s, -2);  // lower the saturation
  }
  // drop down frame size for higher initial frame rate
  if (config.pixel_format == PIXFORMAT_JPEG) {
    s->set_framesize(s, FRAMESIZE_QVGA);
  }

#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif

#if defined(CAMERA_MODEL_AI_THINKER)
  s->set_vflip(s, 1);
#endif

// Setup LED FLash if LED pin is defined in camera_pins.h
#if defined(LED_GPIO_NUM)
  setupLedFlash(LED_GPIO_NUM);
#endif

  WiFi.begin(ssid, password);
  WiFi.setSleep(false);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  startCameraServer();

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");
}

void loop() {
  // Do nothing. Everything is done in another task by the web server
  delay(10000);
}
  • Archivo de Python para el reconocimiento facial y para la señal Arduino.
import face_recognition
import numpy as np
import cv2
import requests
import serial
import os

# Configuración
ESP32_CAM_URL = "http://172.20.10.3/capture"  # Sustituye con la IP de tu ESP32-CAM
CARPETA_rostros_autorizados = "c:/Users/peb17/Desktop/rostros_autorizados"  # Carpeta con imágenes conocidas
ARDUINO_PORT = "COM5"  # Sustituye con el puerto donde está conectado tu Arduino
BAUDRATE = 9600


# Función para cargar los rostros autorizados desde la carpeta
def cargar_rostros_autorizados(carpeta):
    if not os.path.exists(carpeta):
        print(f"Error: Directory '{carpeta}' does not exist.")
        exit()
    rostros_autorizados = []
    nombres = []
    for archivo in os.listdir(carpeta):
        # Solo consideramos archivos de imagen (puedes ampliar esta lista si es necesario)
        if archivo.lower().endswith(('.png', '.jpg', '.jpeg')):
            ruta_imagen = os.path.join(carpeta, archivo)
            imagen = face_recognition.load_image_file(ruta_imagen)
            encoding = face_recognition.face_encodings(imagen)
            if encoding:
                rostros_autorizados.append(encoding[0])  # Añadir el encoding del rostro
                nombres.append(os.path.splitext(archivo)[0])  # El nombre será el nombre del archivo (sin extensión)
            else:
                print(f"Advertencia: No se pudo extraer el rostro de {archivo}")
    print(f"Rostros autorizados cargados: {len(rostros_autorizados)}")
    return rostros_autorizados, nombres


# Obtener imagen de la ESP32-CAM
def capturar_imagen(url):
    try:
        response = requests.get(url, stream=True)
        if response.status_code == 200:
            bytes_img = bytearray(response.content)
            np_array = np.frombuffer(bytes_img, dtype=np.uint8)
            imagen = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
            return imagen
    except Exception as e:
        print(f"Error al capturar imagen: {e}")
    return None

# Configuración inicial
rostros_autorizados_conocidas, nombres_conocidos = cargar_rostros_autorizados(CARPETA_rostros_autorizados)
arduino = serial.Serial(ARDUINO_PORT, BAUDRATE)
print("Sistema listo. Esperando imágenes...")

while True:
    # Capturar imagen de la ESP32-CAM
    imagen = capturar_imagen(ESP32_CAM_URL)
    if imagen is not None:
        # Mostrar la imagen para depuración
        cv2.imshow("Imagen Capturada", imagen)
        cv2.waitKey(1)  # Espera por 1 milisegundo para mostrar la imagen

        # Convertir la imagen a RGB
        rgb_imagen = cv2.cvtColor(imagen, cv2.COLOR_BGR2RGB)

        # Detectar rostros en la imagen capturada
        ubicaciones_rostros = face_recognition.face_locations(rgb_imagen)
        print(f"Ubicaciones de rostros detectados: {ubicaciones_rostros}")
        encodings_rostros = face_recognition.face_encodings(rgb_imagen, ubicaciones_rostros)

        if not ubicaciones_rostros:
            print("No se detectaron rostros.")
            # No se hace nada si no hay rostros
            continue

        cara_reconocida = False
        for encoding in encodings_rostros:
            # Comparar con los rostros autorizados conocidos
            resultados = face_recognition.compare_faces(rostros_autorizados_conocidas, encoding, tolerance=0.5)
            if True in resultados:
                indice = resultados.index(True)
                nombre = nombres_conocidos[indice]
                print(f"Cara reconocida: {nombre}")
                arduino.write(b'2')  # Enviar señal de "Cara conocida" al Arduino
                cara_reconocida = True
                break
        
        if not cara_reconocida:
            print("Cara no  reconocida")
            arduino.write(b'1')  # Enviar señal de "No reconocida" al Arduino

    else:
        print("No se pudo capturar imagen")
  • Archivo Arduino para la configuración del comportamiento del resto de componentes.
//LIBRERIAS
#include <Adafruit_Fingerprint.h>
#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

//SENSOR DE HUELLA
SoftwareSerial mySerial(2, 3);
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
int id_finding;
int id;
int temp;
char resp;

//SERVOMOTOR Y FINAL DE CARRERA
Servo servo;
int angulo;
const int boton = 4;
bool abierto = false;
unsigned long lastButtonTime = 0;               // Tiempo del último cambio de estado del botón
unsigned long lastFingerprintTime = 0;          // Tiempo de la última operación de huella
const unsigned long debounceDelay = 300;        // Retardo para debounce del botón
const unsigned long fingerprintTimeout = 5000;  // Tiempo máximo para registrar huella
//PANTALLA LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
String mensajeActual = "";

//KEYPAD
const byte rows = 4;
const byte columns = 4;
char keys[rows][columns] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};
const byte rowPins[rows] = { 13, 12, 11, 10 };
const byte columnPins[columns] = { 9, 8, 7, 6 };
Keypad keypad = Keypad(makeKeymap(keys), rowPins, columnPins, rows, columns);
String codigo_ingresado = "";
String codigo_correcto = "1234";

//CAMARA
bool admin = false;
//FUNCIONES ADICIONALES
void leerHuella();
void validarCodigo();
void verificarEstadoPuerta();
int getFingerprintIDez();
uint8_t getFingerprintEnroll(int id_enroll);


void setup() {
  lcd.init();
  lcd.backlight();
  finger.begin(57600);
  Serial.begin(9600);
  if (!finger.verifyPassword()) {
    lcd.print("Sensor no encontrado");
    while (1)
      ;
  } else {
    lcd.print("SEGURIDAD");
    lcd.setCursor(0, 1);
    lcd.print("LISTA");
  }
  delay(1500);
  lcd.clear();
  servo.attach(5);
  servo.write(0);
  pinMode(boton, INPUT_PULLUP);  // Configura el botón como entrada con resistencia pull-up
}


void loop() {
  verificarEstadoPuerta();  // Verificar botón y estado de la puerta
  verificarAdmin();
  if (!abierto) {  // Si la puerta está cerrada
    leerHuella();
  } else {  // Si la puerta está abierta
    validarCodigo();
  }
}

void verificarAdmin() {

  char receivedChar = Serial.read();
  if (receivedChar == '2') {
    admin = true;
    lcd.setCursor(15, 1);
    lcd.print("*");
  } else if (receivedChar == '1') {
    admin = false;
    lcd.setCursor(15, 1);
    lcd.print(" ");
  }
  if (admin) {
    lcd.setCursor(15, 1);
    lcd.print("*");
  } else {
    lcd.setCursor(15, 1);
    lcd.print(" ");
  }
}
void verificarEstadoPuerta() {
  static unsigned long lastDebounceTime = 0;  // Último tiempo de cambio de estado
  static bool lastButtonState = HIGH;         // Último estado conocido del botón
  bool reading = digitalRead(boton);          // Leer el estado actual del botón

  // Si el estado del botón ha cambiado
  if (reading != lastButtonState) {
    lastDebounceTime = millis();  // Reiniciar el temporizador
  }

  // Si el estado ha estado estable por más de debounceDelay
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != abierto) {
      abierto = reading;  // Actualizar el estado de la puerta
      if (abierto) {
        abrirPuerta();
      } else {
        cerrarPuerta();
      }
    }
  }

  lastButtonState = reading;  // Guardar el estado actual
}
void leerHuella() {
  id_finding = getFingerprintIDez();
  if (id_finding == 0) {
    lcd.setCursor(0, 1);
    lcd.print("No identificado");
    delay(1000);
    lcd.clear();
    lcd.print("Puerta cerrada");
  } else if (id_finding > 0) {
    lcd.clear();
    lcd.print("Usuario");
    lcd.setCursor(0, 1);
    lcd.print("identificado");
    delay(1000);
    abrirPuerta();
  }
}

void validarCodigo() {
  lcd.setCursor(0, 1);
  char key = keypad.getKey();
  if (key) {
    if (key == '#') {  // Confirmar código
      if (codigo_ingresado == codigo_correcto) {
        if (admin) {
          lcd.clear();
          lcd.print("Codigo correcto");
          delay(1000);
          getFingerprintEnroll(finger.templateCount + 1);
        } else {
          lcd.clear();
          lcd.print("No modo admin");
          delay(1000);
        }
        lcd.clear();
        lcd.print("Puerta abierta");
      } else {
        lcd.clear();
        lcd.print("Codigo");
        lcd.setCursor(0,1);
        lcd.print("Incorrecto");
        delay(1000);
        codigo_ingresado = "";
        lcd.clear();
        lcd.print("Puerta abierta");
      }
      codigo_ingresado = "";  // Reinicia el código
    } else if (key == '*') {  // Cancelar ingreso
      lcd.clear();
      lcd.print("Ingreso cancelado");
      codigo_ingresado = "";
      delay(1000);
      lcd.clear();
      lcd.print("Puerta abierta");
    } else if (codigo_ingresado.length() < 4) {
      codigo_ingresado += key;  // Agregar dígito al código
      lcd.print(codigo_ingresado);
    }
  }
}

int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK) return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK) return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK) return 0;

  return finger.fingerID;
}
uint8_t getFingerprintEnroll(int id_enroll) {
  int p = -1;
  //Serial.print("Registrando ID: ");
  //Serial.println(id_enroll);
  lcd.clear();
  lcd.print("Coloque su dedo");
  temp = 0;
  while (temp < 5000) {
    temp++;
    //Serial.println(temp);
    p = finger.getImage();
    switch (p) {
      case FINGERPRINT_OK:
        lcd.clear();
        lcd.print("Huella leida");
        break;
      default:
        //Serial.println("Unknown error 1");
        break;
    }
    if (p == FINGERPRINT_OK) {
      break;  //termina el while
    }
  }
  if (temp >= 5000) {
    lcd.clear();
    lcd.print("Termino el tiempo");
    return 0;
  }
  // OK success!

  p = finger.image2Tz(1);
  switch (p) {
    case FINGERPRINT_OK:
      lcd.clear();
      lcd.print("Imagen convertida");
      break;
    default:
      lcd.clear();
      lcd.print("Error conviertiendo imagen");
      return p;
  }
  lcd.clear();
  lcd.print("Retire el dedo");
  delay(2000);
  lcd.clear();
  lcd.print("Coloque el dedo");
  lcd.setCursor(0, 1);
  lcd.print("nuevamente");
  delay(500);
  temp = 0;
  while (temp < 5000) {
    temp++;
    //Serial.println(temp);
    p = finger.getImage();
    if (p == FINGERPRINT_OK) {
      lcd.clear();
      lcd.print("Huella leida");
      delay(500);
      break;
    }
  }
  if (temp == 5000) {
    lcd.clear();
    lcd.print("Termino el tiempo");
    delay(500);
    return 0;
  }
  // OK success!
  p = finger.image2Tz(2);
  switch (p) {
    case FINGERPRINT_OK:
      lcd.clear();
      lcd.print("Imagen convertida");
      delay(1000);
      break;
    default:
      lcd.clear();
      lcd.print("Error convirtiendo imagen");
      delay(1000);
      return p;
  }
  // OK converted!
  lcd.print("Creando huella: ");
  Serial.println(id_enroll);
  p = finger.createModel();
  if (p == FINGERPRINT_OK) {
    lcd.clear();
    lcd.print("Modelo correcto!");
    delay(1000);
  } else {
    lcd.clear();
    lcd.print("Error creando modelo");
    delay(1000);
    return p;
  }

  p = finger.storeModel(id_enroll);
  if (p == FINGERPRINT_OK) {
    lcd.clear();
    lcd.print("Guardado!");
    delay(1000);
  } else {
    lcd.clear();
    lcd.print("Error guardando");
    delay(1000);
    return p;
  }
  return 1;
}
void abrirPuerta() {
  if (angulo != 180) {
    servo.attach(5);   // Conectar el servo
    servo.write(180);  // Mover a la posición
    delay(500);        // Esperar para asegurar la posición
    servo.detach();    // Desconectar el servo
    angulo = 180;
  }
  mostrarMensaje("Puerta abierta");
}

void cerrarPuerta() {
  if (angulo != 125) {
    servo.attach(5);   // Conectar el servo
    servo.write(125);  // Mover a la posición
    delay(500);        // Esperar para asegurar la posición
    servo.detach();    // Desconectar el servo
    angulo = 125;
    admin = 0;
  }
  mostrarMensaje("Puerta cerrada");
}

void mostrarMensaje(String mensaje) {
  if (mensaje != mensajeActual) {  // Solo actualiza si el mensaje es diferente
    lcd.clear();
    lcd.print(mensaje);
    mensajeActual = mensaje;  // Guarda el mensaje actual
  }
}

Construcción

Por parte de la construcción física del diseño, se llevó a cabo en los siguientes pasos:

  1. Se planteó un boceto inicial, porque se necesitaba una caja bastante grande, que cumpliese con la función principal, y a su vez, contuviese los componentes necesarios.
  2. Se contactó con el familiar de una integrante que estaba bastante emocionado por el proyecto, y se ofreció a montar la base de la caja con varios tablones de madera y tornillos para fijar las dos estructuras y la puerta.
  3. Se diseñó la manera en la que dispondríamos los componentes que necesitaban acceso al exterior de la caja, y se hicieron los agujeros necesarios para sus conexiones.
  4. Se diseñaron las estructuras para el LCD y para la cámara que las sujetarían en cierto ángulo y protegerían y esconderían los componentes.
  5. Se cortaron las piezas necesarias para estas estructuras de un cartón bastante resistente, y se pegaron con silicona caliente. Los refuerzos que se necesitaron se hicieron con cinta americana.
  6. Para la caja grande, se hicieron varias placas de este mismo material para crear un «falso fondo», con el que, además de disimular las imperfecciones y mejorar la estética del interior de la caja, se pudo esconder el fin de carrera, así como sus cables y los del servomotor.
  7. Se pintó toda la estructura de negro mate, el pomo y el interior de la caja (el suelo y los falsos fondos) se pintaron de plata, y finalmente se añadieron tiras de goma EVA plateada brillante para darle nuestro toque personal.

Cabe destacar que algunos pasos intermedios fueron el lijar, añadir masilla para corregir imperfecciones de la estructura, o ir perfeccionando la pintura en algunas zonas para una mejor presentación.

Problemas y soluciones encontradas

Nuestro problema principal fue conseguir que funcionase la red neuronal, debido a que estábamos usando una placa Nano BLE 33 Sense Lite con cámara OV7675, que era únicamente compatible con Edge Impulse. Se llegó a conseguir que se identificasen personas, pero, al final, lo que se quería era que se identificase si era una persona admitida o no.

Después de darle muchas vueltas a este problema, que, aun cambiando la visión del objetivo a la búsqueda de «validación» y no «reconocimiento» como estábamos haciendo, sin conseguir demasiados avances, decidimos intentar reprogramarla por medio de Python, pensando que este método era compatible con nuestra placa.

Al final, nos dimos cuenta de que habíamos conseguido implementar el código necesario, pero no el objetivo, puesto que el reconocimiento facial seguía sin funcionar. De esta forma, llegamos a la conclusión de que lo mejor sería intentarlo de nuevo con una nueva placa ESP32 con una cámara OV2640, porque se ajustaba mejor a lo que necesitábamos.

Esta fue una solución viable, pero su conexión a la placa Arduino fue, posteriormente, otra de las cosas que más inconvenientes provocaron. Esto fue debido a que, a falta de un adaptador USB C a pines Arduino, tuvimos que comprar primero un adaptador USB C a entrada USB A, para, posteriormente, conectarlo ya sí con el módulo USB a UART TTL, el cual fue bastante complicado de encontrar.

Posteriormente, llegaron los problemas de software, en los que nos encontramos problemas como que la cámara encendía pero no funcionaba, o problemas con librerías que no se detectaban. Esto era debido a que no éramos capaces de meter el programa de Python en la placa ESP32 con cámara OV2640, pero optamos por subir la imagen que toma a un servidor web de donde el programa Python toma el programa y cumple su función.

Después vino el problema de cómo conectar la cámara con el resto del programa, lo que se solucionó fácilmente, debido a que la cámara ESP32 tiene un microprocesador al cual se le ha añadido el programa que abre el servidor Wi-Fi, para que a través de ese servidor Wi-Fi esta sea capaz de conectarse con nuestro programa en Python, necesitando sólo voltaje y tierra. Así, Python ejecuta todo el programa y si detecta una señal determinada, se envía la cara a la placa Arduino desde el puerto serie, y sino se envía otra. Cabe destacar que otro importante problema que tuvimos que enfrentar fue el haber empezado este proyecto teniendo nulos conocimientos en este lenguaje de programación, pero con ayuda de varios códigos en webs y ayuda en tutoriales, se consiguió implementar el código necesario.

Por otro lado, el sensor de huella también dio algún que otro problema. Ya de por sí, el conector del que este sensor disponía no era compatible con los componentes electrónicos que teníamos nosotros (puesto que necesitábamos hacer uso de los pines de la placa Arduino, y estaban los seis cables unidos entre sí para una entrada específica que no teníamos), por lo que tuvimos que optar por cortar uno de los extremos de los cables y pelarlos uno a uno, para posteriormente soldarlos a nuestros cables hembra. De esta manera, conseguimos conectar el sensor a la placa Arduino exitosamente.

Una vez estaba el sensor conectado, comenzaron los problemas relacionados con la detección y comparación de huella, principalmente porque pensamos que no tenía memoria, lo que hizo que comprásemos una SD y su lector correspondiente, pero no conseguíamos almacenar ninguna huella. Esto se solucionó rápidamente, al darnos cuenta de que el sensor realmente sí disponía de memoria, por lo que con ayuda de códigos de ejemplo y búsqueda de información que se fue recopilando, se consiguieron las funciones y mensajes correctos para sacar el partido que se quería al sensor.

Por último, nos encontramos con el problema de los pines. Nos dimos cuenta de que, al querer usar tantos componentes, nos quedábamos sin pines para poder hacerlo, necesitando apañarnos con los pines RX y TX para la cámara. Sin embargo la solución fue más sencilla de lo que pensábamos, puesto que la cámara dependía de un módulo ESP32 con su propio código, que lo único que necesitaba era transmitir en red y no comunicarse con la Arduino. Es por esto que se decidió no conectar los pines del adaptador TTL con los pines 0 y 1 del Arduino, que además son reservados para la comunicación en serie de la placa, y que esta solo dependiera de la conexión. Y funcionó.

Posibles mejoras

Consideramos que este proyecto podría mejorarse en los siguientes aspectos:

  • Incluir el código en Python en la ESP32 para no depender de un ordenador para la lectura de imágenes.
  • Incluir un módulo Wi-Fi o Bluetooth en la placa Arduino para recibir las señales del programa en Python sin necesidad de un cable USB.
  • Añadir un LED que indique el estado de la puerta (verde abierto, rojo cerrado, por ejemplo).
  • Implementar un sonido para notificar que se abre la puerta.
  • Mejora de los materiales para el exterior.
  • Mejorar el sistema de cerrojo.

Demostración

Autores

Este proyecto ha sido realizado por:

  • Adrián Álvarez Alonso
  • Yamila Calopino Romero
  • Paula Escuderos Benítez
  • Lucía Rodríguez Fernández

También te podría gustar...

2 Respuestas

  1. Juan Manuel dice:

    Enhorabuena por el éxito del proyecto conseguido. En los proyectos informáticos, a pesar de los muchos esfuerzos que se realizan, no siempre se consiguen estos éxitos, de hecho, un porcentaje muy elevado no lo son. Felicidades por la persistencia y y el buen trabajo realizado.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *