Fichador de trabajo

Trabajo realizado por el Grupo 1 del campus de Móstoles, formado por Sergio Carrascosa Sánchez, Francisco Daniel Frías Quero, Jorge García-Mauriño Mollá y Jose Manuel Soler Siles.

1.- INTRODUCCIÓN AL PROYECTO Y OBJETIVOS PERSEGUIDOS

En este proyecto se nos planteó la posibilidad de desarrollar un proyecto en Arduino de forma totalmente libre, sin pautas ni guión alguno. Nuestra preferencia fue siempre desarrollar algo funcional, que tuviese utilidad en el mundo real y por lo tanto algo que los consumidores y otras empresas usarían. 

Dada la experiencia laboral de algunos de los miembros de nuestro grupo, se planteó la idea desde el inicio de realizar un fichador. Un fichador es un dispositivo colocado en la entrada del puesto de trabajo que registra la jornada laboral de los trabajadores de la empresa. Al ver que era algo nuevo, que ningún grupo de años anteriores había hecho y su gran utilidad en el mundo real, que era lo que buscábamos, nos decantamos por el mismo. Tenía la innovación y la utilidad que queríamos.

El objetivo principal de este proyecto era por tanto el desarrollo de un fichador funcional que registrase la entrada y salida de empleados en Arduino. Otro objetivos fueron la familiarización con el IDE de Arduino, aprendizaje más avanzado sobre el equipo hardware de Arduino (circuitería, componentes electrónicos … etc).

1.1.- REQUISITOS INICIALES

Los requisitos iniciales del proyecto fueron:

  • Pantalla táctil con una interfaz simple y entendible para cualquier trabajador de la empresa.
  • Funcionalidad de fichar para entrar y salir de la empresa
  • Funcionalidad de registrar a un nuevo empleado (sólo realizable por el administrador).
  • En caso de éxito o error en las funcionalidades previamente mencionadas, presencia de efecto sonoro y lumínico para acompañar al mensaje impreso por pantalla.
  • Implementación de un sensor de huellas en el fichador de arduino.
  • Carcasa y aspecto físico de fichador.

2.- CASOS DE USO

El fichador arduino tiene 3 funcionalidades principalmente, llevadas a cabo por distintos actores:

  • Acciones realizadas por los trabajadores
    • Fichar: acción que registra la entrada de un trabajador al puesto de trabajo.
    • Desfichar: acción que registra la salida de un trabajador del puesto de trabajo.
  • Acción realizada por el administrador:
    • Alta empleado: acción que guarda un nuevo empleado en el fichero y le asigna una nueva clave única.

3.- ESTRUCTURA, CONSTRUCCIÓN E IMPLEMENTACIÓN

Dada la naturaleza de nuestro proyecto, el pivote fundamental del mismo sería el código que nos permitiese realizar  las distintas funciones requeridas del fichador. Debido a esto, el énfasis inicial del proyecto se enfocó en el desarrollo del código del fichador. El proceso de desarrollo del proyecto fue: código, circuito y montaje físico (apariencia).

No fue necesario el montaje del circuito en un primer momento ya que la pantalla táctil nos permitía conectarla directamente a la placa Arduino Uno R3 y probar así el código que íbamos desarrollando. Las funcionalidades de los LEDs y el buzzer se implementarían una vez terminada toda la parte software del proyecto.

3.1.- CÓDIGO ARDUINO

En primer lugar, el código comienza con la definición de todas las librerías necesarias para su ejecución mediante las directivas #include y la declaración de diversas macros a través de #define.

Posteriormente, se definen también todas las variables que necesitarán ser de carácter global, como son:

  • Relacionadas con la pantalla táctil:
    • Adafruit_TFTLCD tft: La variable de la propia pantalla
    • TouchScreen ts: La variable para gestionar el aspecto táctil de la pantalla
    • Una serie de botones que se imprimirán posteriormente en la pantalla
  • Relacionadas con el código:
    • int touchXY[2]: Array de dos posiciones en donde se guardan las coordenadas de la última pulsación realizada por el usuario en la pantalla táctil
    • int vista: Almacena un número con la vista que queremos mostrar
    • String clave: Clave del usuario actual que esté utilizando el fichador
    • int longString: Longitud del String clave

Una vez dentro ya del setup(), se llama a la función resetTouch() (cuyo funcionamiento es detallado con posterioridad), inicializamos el monitor serie y establecemos los pines de los LEDs y el buzzer como de salida. También se llevan a cabo otras configuraciones básicas de la pantalla como un reseteo, inicialización mediante su identificador el cuál es proporcionado por el fabricante, se establece la rotación de la misma y coloreamos de negro el fondo. Asimismo, a la variable vista se le otorga el valor 0, la pantalla inicial del fichador.

Con respecto al loop(), aquí nos encontramos con un código que simplemente se encarga de, a través de una gran estructura if-else, comprobar qué número está almacenado en la variable vista para así llamar a su método correspondiente.

A continuación viene la implementación de cada una de las 11 vistas que tenemos en el código. Cada método es bastante similar entre sí, empezando siempre con un resetTouch() en el caso de que en la vista se espere alguna pulsación en la pantalla por parte del usuario. Luego se pasa a la impresión de todos los elementos de la vista (color de fondo, colocación y tamaño del texto…) para acabar finalmente con la funcionalidad que se espera que se realice en la propia vista: algunas esperan una pulsación en la pantalla, otras simplemente realizan un delay() con el objetivo de que el usuario lea lo que hay en pantalla…

Algunos aspectos reseñables de los métodos de las vistas son:

  • vista0(): El objetivo inicial era que esta vista mostrara en directo tanto la hora como el día. No obstante, al no haber podido cumplir con este objetivo, simplemente las imprimimos a través de las macros __DATE__ y __TIME__, almacenando ambas los datos correspondientes en el momento de la compilación.
  • vista3(): Esta es una de las vistas que requiere de la interacción con el script de Python que controla los ficheros. Más concretamente, una vez que el usuario ha introducido su clave mediante el teclado, el código escribe a través del monitor serie al script el String (“pyc “ + clave), indicando con las tres primeras letras que es algo que debe ser leído por el script Python (de ahí el “py”) y que es una clave (por eso la letra “c”), seguido todo ello por la propia clave. Posteriormente, en función de la respuesta proporcionada por el script de Python, transitaremos a una vista u otra.
  • vista4(): Al llegar a esta vista, la autentificación con la clave ha sido exitosa, por lo que a parte de imprimir texto por pantalla, también se encarga de hacer sonar el buzzer y de encender el LED azul.
  • vista5(): A esta vista se llega cuando la autentificación con la clave ha sido exitosa, pero dicha clave ya se encontraba en el fichero de entradas, por lo que supone una salida de la empresa. Es por ello que, antes de todo, la vista tiene un bucle en el que espera a que el script Python le envíe a través del monitor serie los datos con el tiempo que ha pasado el trabajador dentro de la empresa en el día. También hará sonar al buzzer y encenderá el LED azul
  • vista6(): La vista 6 tiene un funcionamiento exactamente igual al de la vista 3, pero el String escrito (“pya “ + clave), difiere puesto que la clave enviada al script Python es una clave de administrador (de ahí la “a”)
  • vista8(): Si se llega a esta vista es porque el administrador está tratando de crear un nuevo usuario para que pueda acceder al sistema con su propia clave. El código enviará ahora al script Python el String (“pyi “ + clave), siendo clave el identificador (de ahí la “i”) de 8 dígitos que el administrador le otorgó en la vista anterior y que dará paso a que el sistema le devuelva su propia clave generada automáticamente
  • vista9(): En esta vista se comunica que el registro del nuevo usuario ha sido un éxito y se muestra su nueva clave para identificarse en el fichador de la empresa. Es por esto por lo que antes de todo, se comienza con un bucle en el que el código espera que el script Python le comunique a través del monitor serie la clave que debe imprimir por pantalla
  • vista10(): Comunica un error de cualquier tipo, haciendo sonar también al buzzer y encendiendo el LED rojo

Por último, tenemos la implementación de otros cuatro métodos. getTouch() es un método cuya función es entrar en un bucle hasta que el usuario realice una pulsación en la pantalla. Para ello, comprobamos que la presión realizada por el usuario en la propia pantalla sea superior a MINPRESSURE, que en este caso es 1, y asignamos las coordenadas de la pulsación al array touchXY

resetTouch() es el encargado de resetear cada vez que es llamado el array touchXY. Cada vez que se resetea, ponemos tanto el valor de la X como el de la Y a -1, para distinguir del caso en el que el usuario pulsara en las coordenadas 0,0. 

printHomeButton(), como su nombre indica, es llamado cada vez que queremos imprimir el botón que da la posibilidad de volver al inicio en el caso de que el usuario se haya equivocado.

printKeyBoard() se ocupa de la impresión del teclado, con sus correspondientes números del 0 al 9 además del botón de eliminar. Primeramente realiza la impresión de todos estos botones, para luego pasar a un bucle while en el que se permanece mientras la longitud del String que el usuario está introduciendo sea menor a la longitud buscada. Dentro del while, llamamos en cada iteración a getTouch() y luego procedemos a distinguir en qué botón específico ha pulsado el usuario a través del análisis de las coordenadas. Si es un botón del 0-9, se concatena al String introducido, mientras que si pulsa en el botón de eliminar, la clave introducida hasta ahora se borra y se resetea la vista. 

3.2.- CÓDIGO PYTHON

El siguiente problema que se nos planteaba era dónde almacenar toda la información de usuario, claves y registros de salidas y entradas, por lo que decidimos guardar toda esa información en ficheros de texto que se puedan consultar fácilmente.

Por ello hemos programado un script en Python que se encarga de manejar y hacer comprobaciones sobre los ficheros del sistema. Para llevar a cabo la comunicación entre el script y el programa de Arduino, donde se encuentra todo lo explicado hasta el momento, se emplea el monitor serie.

Para iniciar esa comunicación, creamos una nueva variable serialArduino. La instanciamos con los parámetros del puerto serie que estemos utilizando en Arduino.

El script Python estará constantemente leyendo y decodificando la entrada del monitor serie en un bucle infinito hasta encontrar algún input que comience con py, lo cual significa que le hemos enviado algún mensaje para que lo procese.

Dependiendo del caso, ejecutaremos unas funciones u otras. A esas funciones les pasaremos como argumento la entrada leída y decodificada, pero sin el prefijo que hemos utilizado para identificar el caso.

Si el mensaje comenzaba con pya, significa que le hemos pasado una clave de administrador (ejecutaremos comprobarAdmin), si comienza con pyc es una clave de empleado (ejecutaremos ficharUsuario y encontrarUsuario), y si empieza con pyi la entrada es un nuevo identificador a registrar (ejecutaremos guardarUsuario).

comprobarAdmin(clave): abriremos el fichero admin.txt y leeremos su primera línea. Si la clave es igual que la primera línea del fichero significa que es correcta, y escribimos true codificado en el monitor serie para que lo lea el Arduino. Si no era igual, escribimos false.

encontrarUsuario(clave): Este método recorre el archivo usuariosClaves.txt linea a linea, leyendo y comparando la parte de las claves de cada usuario almacenado con la que introduce el empleado mediante la pantalla. De esta forma, si la clave leída es igual a la introducida, obtenemos el identificador correspondiente a esa clave y lo devolvemos.

En caso de no estar asociada la clave introducida a ningún usuario, el método devuelve el objeto none.

ficharUsuario(identificador): En primer lugar, se crea la carpeta del día correspondiente en caso de no existir, así como los ficheros de entrada.txt y salida.txt vacíos.

Este programa se usa junto al anterior, pues el identificador pasado es el valor de retorno de la ejecución del método encontrarUsuario. Por este motivo, si el identificador es igual a none, hacemos que el método no se ejecute retornando un 0, ya que significa que la clave que metió el usuario no está en el fichero usuariosClaves.txt. A continuación, almacenamos la hora actual para registrarla en el programa, y comprobamos cuántas veces aparece el identificador dado en los ficheros de entrada y salida. Esto lo hacemos para saber si lo que corresponde hacer con el usuario es fichar o desfichar: 

  • Si las veces que se ha entrado y salido son iguales, corresponde una entrada. 
  • Si se ha entrado una vez más que salido, corresponde una salida.

En función de esto abrimos el fichero correspondiente y registramos la entrada/salida del usuario con la hora a la que se ha realizado. En caso de ser una salida, además mostramos la duración de la última jornada laboral realizada, simplemente restando la hora de la salida actual con la última hora de entrada que se registró para ese identificador. 

guardarUsuario(identificador): Abriremos los ficheros usuariosClaves.txt y contadorClave.txt. El primero contiene el listado de las claves junto con sus identificadores, mientras que el segundo la información de la última clave de usuario asignada.

Nos guardamos en una variable la última clave asignada y le sumamos uno, para que en caso de que se registre con éxito el usuario podamos asignársela y guardarla de nuevo en el fichero. Ahora comprobaremos si el identificador que nos ha escrito el usuario ya existía previamente en el listado de usuarios y claves. Si ya existía enviamos false al monitor serie. Pero si no existía, guardamos el identificador con su nueva clave en el fichero  usuariosClaves.txt, la información de esa nueva clave en contadorClave.txt y comunicamos por el monitor serie que todo ha salido bien y la clave asignada (con un segundo y medio de diferencia para que el código Arduino pueda recibirlo adecuadamente).

3.3.- ESTRUCTURA FICHEROS

Organización de los ficheros y carpetas

El programa se ejecuta en la carpeta raíz del proyecto, donde encontramos los siguientes archivos: 

  • admin.txt: Se trata del fichero donde se almacena el identificador del administrador que está habilitado para registrar nuevos empleados mediante el fichador. 
  • contadorClave.txt: Es el fichero en el que almacenamos la última clave asignada para, a la hora de añadir un nuevo empleado, asignarle una clave nueva incrementada. 
  • usuariosClaves.txt: Es el fichero en el que se encuentra la lista de todos los empleados de la empresa. El formato en el que hemos decidido guardarlo es el siguiente:
  • A cada empleado le corresponde una línea del archivo.
  • De cada empleado se almacena el identificador (8 dígitos correspondiendo con los dígitos del DNI) y, separado por un espacio, la clave numérica generada automáticamente de 4 dígitos. 
  • Ejemplo: 12345678 1003
  • Carpetas del día : Las carpetas del día tienen por nombre la fecha del día del que se trata y en ellas se almacenan dos ficheros que son: 
  • entrada.txt: Es el fichero donde se guardan las entradas de los usuarios. Se registra de cada empleado el identificador y, separado por un guión, la hora en la que se ha realizado la entrada. 

Ejemplo: 12345678-09:31:22 

  • salida.txt: Es el fichero en el que guardamos las salidas de los usuarios. El formato es el mismo que el del archivo entrada.txt.

3.4.- CIRCUITO

El montaje del circuito no fue complejo y se realizó una vez que todo el software del fichador estaba realizado. Los principales componentes utilizados son: 

  • Placa Arduino Uno R3
  • Protoboard
  • Dos resistencias de 5KΩ
  • Dos diodos LED
  • Buzzer

La principal característica es que la pantalla táctil ocupa la mayoría de los puertos de la placa Arduino Uno R3, estos son: las entradas analógicas del A0 al A4, las salidas digitales de la D0 a la D10 y los puertos de GRND, 3.3V y 5V. A pesar de ello no hemos necesitado multiplexores para operar con más componentes.

El circuito se compone de 4 subcircuitos:

  • El subcircuito de la pantalla
  • Dos subcircuitos que encienden las LEDS: sus respectivas salidas digitales están situadas en los puertos 11 y 12. Su funcionamiento es el siguiente: cuando la placa Uno lo requiera, mandará una señal a través de sus puertos que activarán las luces LED y dicha señal se mandará a tierra, cerrando así el circuito.
  • Subcircuito del buzzer: la salida digital se sitúa en el puerto 13 y funciona igual que el caso de las LEDS.

3.5.- MONTAJE FÍSICO

Para la apariencia del fichador comenzamos comprando y pintando la caja de plateado para darle una apariencia metálica. Acto seguido, realizamos los cortes necesarios para las LEDs, pantalla táctil y para la entrada del cable USB que conecta la placa con el ordenador.

Una vez realizado esto, el último paso fue meter la circuitería dentro de la caja, colocar las luces LEDS, la pantalla y enchufar el cable USB y con eso tendríamos el proyecto finalizado.

4.- PROBLEMAS ENCONTRADOS Y SOLUCIONES

El primer problema con el que nos encontramos fue el hecho de no poder implementar uno de los requisitos iniciales, el lector de huellas, que queríamos para el proyecto ya que disponíamos de un mes para realizarlo, y el lector de huellas más disponible en el mercado tenía un tiempo de entrega estimado de tres semanas. Esto nos dejaba un margen muy pequeño con el que trabajar, apenas una semana para implementarlo, quedando así el lector fuera del proyecto final.

Otro problema que surgió, aunque menos importante, fue que en la vista inicial del fichador, que se encarga de dar la bienvenida y mostrar la fecha y hora, queríamos que tanto la fecha como la hora se mostrarán a tiempo real. Al comprender que la tarea iba a demorar demasiado la realización de otras funciones, decidimos prescindir de ella, mostrando únicamente la fecha y hora del momento de la compilación del programa, como se explicó anteriormente.

Con todo, el principal problema que tuvimos fue a la hora de trabajar con los ficheros. Originalmente programamos prácticamente toda la funcionalidad del sistema de carpetas y archivos en C, para poder integrarlo en el mismo código que las funcionalidades de la pantalla. Sin embargo, al juntarlo todo empezaron a surgir gran cantidad de errores relacionados con la importación de paquetes y funciones no soportadas por el compilador. Tras muchas horas intentando solucionarlo y buscando en Internet, nos dimos cuenta de que no era posible guardar los archivos en el ordenador tal y como nosotros queríamos en un principio. Al final, dimos con la solución que hemos acabado implementando en el proyecto final, un pequeño script en Python que se comunicará con el programa Arduino a través del monitor serie y se encargará de toda la gestión de los ficheros necesarios para que el fichador funcione.

5.- PRESUPUESTO

ElementoCoste
Kit Arduino proporcionado por URJC0€
Pantalla táctil ELEGOO TFT15€
Cables extra Macho-Hembra3€
Caja contenedor del circuito3€
Presupuesto total21€

6.- Anexo de código

6.1 Código Arduino

#include <Arduino.h>
#include <time.h>
#include <TouchScreen.h>
#include <Adafruit_GFX.h>
#include <Adafruit_TFTLCD.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <Fonts/FreeSansBold18pt7b.h>
 
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4
#define _OPEN_SYS_ITOA_EXT
#define LONGID 8
#define LONGCLAVE 4
 
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin
 
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
 
#define MINPRESSURE 1
#define MAXPRESSURE 1000
 
#define AZUL 13
#define ROJO 12
#define BUZZER 11
 
/* Vistas de la aplicación
0 --> Salvapantallas (hora y fecha)
1 --> Elección opción (botones)
2 --> Introducir código (texto temporal)
3 --> Introducir código (teclado)
4 --> Éxito código entrada (bienvenido + hora)
5 --> Éxito código salida (adiós + info duración)
6 --> Registrar administrador (introducir su clave)
7 --> Introducir identificador registrar (texto)
8 --> Introducir identificador registrar (teclado)
9 --> Mostrar clave automática
10 --> Error (texto) + vuelta a 1
*/
 
//Declaración de variables globales - pantalla
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
Adafruit_GFX_Button botonRojo;
Adafruit_GFX_Button botonAzul;
Adafruit_GFX_Button botonInicio;
Adafruit_GFX_Button boton0;
Adafruit_GFX_Button boton1;
Adafruit_GFX_Button boton2;
Adafruit_GFX_Button boton3;
Adafruit_GFX_Button boton4;
Adafruit_GFX_Button boton5;
Adafruit_GFX_Button boton6;
Adafruit_GFX_Button boton7;
Adafruit_GFX_Button boton8;
Adafruit_GFX_Button boton9;
Adafruit_GFX_Button botonCancelar;
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
 
//Declaración de variables globales - código
int touchXY[2];
int vista;
String clave;
int longString;
 
void setup() {
  
   //Llamamos a resetTouch al inicio de todos los métodos
   resetTouch();
 
   //Inicializamos el monitor serie
   Serial.begin(9600);
 
   //Establecemos como salida los pines de los LEDs y el buzzer
   pinMode(AZUL, OUTPUT);
   pinMode(ROJO, OUTPUT);
   pinMode(BUZZER, OUTPUT);
 
   //Establecemos parámetros básicos de la pantalla
   tft.reset();
   uint16_t identifier = 0x9341;
   tft.begin(identifier);
   tft.setRotation(1);
   tft.fillScreen(BLACK);
 
   //Establecemos la fuente
   tft.setFont(&FreeSansBold18pt7b);
 
   //Inicializamos la vista a la pantalla inicial
   vista = 0;
}
 
void loop(){
   if (vista == 0) {
       vista0();
   }
   else if (vista == 1) {
       vista1();
   }
   else if (vista == 2) {
       vista2();
   }
   else if (vista == 3) {
       vista3();
   }
   else if (vista == 4) {
       vista4();
   }
   else if (vista == 5) {
       vista5();
   }
   else if (vista == 6) {
       vista6();
   }
   else if (vista == 7) {
       vista7();
   }
   else if (vista == 8) {
       vista8();
   }
   else if (vista == 9) {
       vista9();
   }
   else if (vista == 10) {
       vista10();
   }
}
 
void vista0 (){
 
   resetTouch();
 
   //Cada vez que se llega a la vista 0, el salvapantalla, el string de la clave o ident se reinicia
   clave = "";
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
   tft.setCursor(50, 70);
   tft.setTextSize(1);
   tft.setFont(&FreeSansBold18pt7b);
   tft.setTextColor(WHITE);
   tft.println("Bienvenidos");
   tft.setCursor(55, 120);
   tft.println(__DATE__); 
   tft.setCursor(85, 170); 
   tft.println(__TIME__);
  
   //Esperar a un toque y avanzar
   getTouch();
   vista = 1;
}
 
void vista1() {
 
   resetTouch();
  
   tft.fillScreen(BLACK);
 
   //Límites boton azul
   //845x750   545x750
   //845x250   545x250
 
   tft.setFont();
 
   botonAzul.initButton(&tft, 80, 125, 115, 145, BLUE, BLUE, WHITE, "FICHAR", 2);
   botonAzul.drawButton();
 
   //Límites botón rojo
   //445x750   145x750
   //445x250   145x250
 
   botonRojo.initButton(&tft, 235, 125, 115, 145, RED, RED, WHITE, "REGISTRAR", 2);
   botonRojo.drawButton();
 
   tft.setFont(&FreeSansBold18pt7b);
 
   //Implementamos funcionalidad de la vista
   while(vista == 1) {
       getTouch();
      
       //Detectamos si la pulsación está dentro del rango del botón azul
       if((touchXY[0] < 845) && (touchXY[0] > 545) && (touchXY[1] < 750) && (touchXY[1] > 250)){
           vista = 2;
       }
       //Detectamos si la pulsación está dentro del rango del botón rojo
       else if ((touchXY[0] < 445) && (touchXY[0] > 145) && (touchXY[1] < 750) && (touchXY[1] > 250)){
           vista = 6;
       }
   }
}
 
void vista2() {
 
   //Impresión de la pantalla
   tft.fillScreen(BLACK);
   tft.setCursor(25,110);
   tft.setTextSize(1);
   tft.setTextColor(WHITE);
   tft.println("Introduzca clave");
   tft.setCursor(50,150);
   tft.println("de empleado");
 
   delay(2000);
 
   //Establecemos la vista 3
   vista = 3;
}
 
void vista3() {
 
   //Como la vista va a analizar las pulsaciones, reiniciamos las variables
   resetTouch();
 
   tft.fillScreen(BLACK);
  
   //Establecemos la longitud del string a detectar por el teclado y llamamos a su método
   longString = 4;
   printKeyBoard(0);
 
   //Si se ha recogido exitosamente la clave, pasamos a la vista 4
   if(clave != ""){
       Serial.println("pyc " + clave);
       String inputRecibido = "";
       while (!(inputRecibido.equals("trueE") || inputRecibido.equals("trueS") || inputRecibido.equals("false"))) {
           inputRecibido = Serial.readStringUntil('\n');
       }
 
       if (inputRecibido.equals("trueE")) {
           vista = 4;
       }
       else if (inputRecibido.equals("trueS")) {
           vista = 5;
       }
       else if (inputRecibido.equals("false")) {
           vista = 10;
       }
   }
}
 
void vista4() {
 
   digitalWrite(AZUL, HIGH);
 
   tone(BUZZER, 3000, 720);
   tone(BUZZER, 4000, 480);
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
   tft.setCursor(90,80);
   tft.setTextColor(WHITE);
   tft.setTextSize(1);
   tft.println("Entrada");
   tft.setCursor(73,130);
   tft.println("registrada");
   tft.setCursor(80,180);
   tft.println("con exito");
 
   delay(2000);
 
   digitalWrite(AZUL, LOW);
 
   vista = 0;
}
 
void vista5() {
   String inputRecibido = "";
   while (!((inputRecibido.length() == 8) || (inputRecibido.length() == 7))) {
       inputRecibido = Serial.readStringUntil('\n');
   }
 
   digitalWrite(AZUL, HIGH);
 
   tone(BUZZER, 3000, 720);
   tone(BUZZER, 4000, 480);
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
   tft.setCursor(20,80);
   tft.setTextColor(WHITE);
   tft.setTextSize(1);
   tft.println("Salida registrada");
   tft.setCursor(10, 130);
   tft.println("Duracion jornada:");
   tft.setCursor(95, 180);
   tft.println(inputRecibido);
 
   delay(2000);
 
   digitalWrite(AZUL, LOW);
 
   vista = 0;
  
}
 
void vista6() {
  
   //Como la vista va a analizar las pulsaciones, reiniciamos las variables
   resetTouch();
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
 
   //Establecemos la longitud del string a detectar por el teclado y llamamos a su método
   longString = 4;
   printKeyBoard(0);
 
   if(clave != ""){
       Serial.println("pya " + clave);
       String inputRecibido = "";
       while (!(inputRecibido.equals("true") || inputRecibido.equals("false"))) {
           inputRecibido = Serial.readStringUntil('\n');
       }
 
       if (inputRecibido.equals("true")) {
           vista = 7;
       }
       else if (inputRecibido.equals("false")) {
           vista = 10;
       }
   }
}
 
void vista7() {
  
   //Impresión de la vista
   tft.fillScreen(BLACK);
   tft.setCursor(70,80);
   tft.setTextSize(1);
   tft.setTextColor(WHITE);
   tft.println("Introduzca");
   tft.setCursor(55,130);
   tft.println("identificador");
   tft.setCursor(70,180);
   tft.println("a registrar");
 
   delay(2000);
 
   //Actualizamos a la vista 8
   vista = 8;
}
 
void vista8() {
 
   //Como la vista va a analizar las pulsaciones, reiniciamos las variables
   resetTouch();
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
 
   //Establecemos la longitud del string a detectar por el teclado y llamamos a su método
   clave = "";
   longString = 8;
   printKeyBoard(1);
 
   if(clave != ""){
       Serial.println("pyi " + clave);
       String inputRecibido = "";
       while (!(inputRecibido.equals("true") || inputRecibido.equals("false"))) {
           inputRecibido = Serial.readStringUntil('\n');
       }
 
       if (inputRecibido.equals("true")) {
           vista = 9;
       }
       else if (inputRecibido.equals("false")) {
           vista = 10;
       }
   }
}
 
void vista9() {
   String inputRecibido = "";
   while (!(inputRecibido.length() == 4)) {
       inputRecibido = Serial.readStringUntil('\n');
   }
 
   digitalWrite(AZUL, HIGH);
 
   tone(BUZZER, 3000, 720);
   tone(BUZZER, 4000, 480);
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
   tft.setCursor(75,80);
   tft.setTextColor(GREEN);
   tft.setTextSize(1);
   tft.println("EXITO AL");
   tft.setCursor(55, 130);
   tft.print("REGISTRAR");
   tft.setCursor(50, 180);
   tft.print("CLAVE: ");
   tft.println(inputRecibido);
 
   delay(2000);
 
   digitalWrite(AZUL, LOW);
 
   //Se vuelve al salvapantallas
   vista = 0;
}
 
void vista10() {
 
   digitalWrite(ROJO, HIGH);
 
   tone(BUZZER, 1000, 800);
   tone(BUZZER, 100, 720);
 
   //Impresión de la vista
   tft.fillScreen(BLACK);
   tft.setCursor(45,100);
   tft.setTextColor(RED);
   tft.setTextSize(1);
   tft.println("HA SURGIDO");
   tft.setCursor(60, 150);
   tft.println("UN ERROR");
 
   delay(2000);
 
   digitalWrite(ROJO, LOW);
 
   //Se vuelve al salvapantallas
   vista = 0;
}
 
void getTouch(){
   while(1){
       TSPoint p = ts.getPoint();
       pinMode(XM, OUTPUT);
       pinMode(YP, OUTPUT);
       if (p.z > MINPRESSURE) {
           touchXY[0] = p.y;
           touchXY[1] = p.x;
           break;
       }
   }
}
 
void resetTouch() {
   touchXY[0] = -1;
   touchXY[1] = -1;
}
 
void printHomeButton() {
   botonInicio.initButton(&tft, 305, 15, 20, 20, RED, RED, WHITE, "X", 2);
   botonInicio.drawButton();
}
 
void printKeyBoard(int op){
    
   /*Alturas --> fila 1 550 y 370
               fila 2 330 y 160*/
 
   /*  0 --> 834 y 725
       1 --> 700 y 580
       2 --> 543 y 425
       3 --> 400 y 285
       4 --> 250 y 140
       ///////////////
   */
   //Botón eliminar -->    260x780     130x780
   //                      260x610     130x610
 
   //Botón home --> si touchXY[0] < 130
   //              si touchXY[1] > 840
 
   //Reestablecemos la fuente
   tft.setFont();
 
   //Impresión de los numéricos del teclado, el botón eliminar y el botón de volver a inicio
   printHomeButton();
 
   boton0.initButton(&tft, 50,135, 50,50, WHITE, BLACK, WHITE, "0", 2);
   boton0.drawButton(true);
 
   boton1.initButton(&tft, 105,135, 50,50, WHITE, BLACK, WHITE, "1", 2);
   boton1.drawButton(true);
 
   boton2.initButton(&tft, 160,135, 50,50, WHITE, BLACK, WHITE, "2", 2);
   boton2.drawButton(true);
 
   boton3.initButton(&tft, 215,135, 50,50, WHITE, BLACK, WHITE, "3", 2);
   boton3.drawButton(true);
  
   boton4.initButton(&tft, 270,135, 50,50, WHITE, BLACK, WHITE, "4", 2);
   boton4.drawButton(true);
  
   boton5.initButton(&tft, 50,195, 50,50, WHITE, BLACK, WHITE, "5", 2);
   boton5.drawButton(true);
  
   boton6.initButton(&tft, 105,195, 50,50, WHITE, BLACK, WHITE, "6", 2);
   boton6.drawButton(true);
 
   boton7.initButton(&tft, 160,195, 50,50, WHITE, BLACK, WHITE, "7", 2);
   boton7.drawButton(true);
 
   boton8.initButton(&tft, 215,195, 50,50, WHITE, BLACK, WHITE, "8", 2);
   boton8.drawButton(true);
  
   boton9.initButton(&tft, 270,195, 50,50, WHITE, BLACK, WHITE, "9", 2);
   boton9.drawButton(true);
 
   botonCancelar.initButton(&tft, 270, 70, 50,50, RED, WHITE, RED, "<-", 2);
   botonCancelar.drawButton(true);
 
   tft.setCursor(40,55);
   tft.setTextSize(4);
 
   tft.setTextColor(WHITE);
   while(clave.length() < longString){
       getTouch();
       if(touchXY[1] < 780 && touchXY[1] > 610 && touchXY[0] < 260 && touchXY[0] > 130){
           clave = "";
           break;
       }
       else if(touchXY[1] < 550 && touchXY[1] > 370){ //fila 1
           if(touchXY[0] < 834 && touchXY[0] > 725){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("0");
               }
               clave+="0"; 
           }
           else if(touchXY[0] < 700 && touchXY[0] > 580){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("1");
               }
               clave+="1";
           }
           else if(touchXY[0] < 543 && touchXY[0] > 425){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("2");
               }
               clave+="2";
           }
           else if(touchXY[0] < 400 && touchXY[0] > 285){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("3");
               }
               clave+="3";
           }
           else if(touchXY[0] < 250 && touchXY[0] > 140){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("4");
               }
               clave+="4";
           }
       }
       else if(touchXY[1] < 330 && touchXY[1] > 160){
           if(touchXY[0] < 834 && touchXY[0] > 725){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("5");
               }
               clave+="5";
           }
           else if(touchXY[0] < 700 && touchXY[0] > 580){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("6");
               }
               clave+="6";
           }
           else if(touchXY[0] < 543 && touchXY[0] > 425){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("7");
               }
               clave+="7";
           }
           else if(touchXY[0] < 400 && touchXY[0] > 285){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("8");
               }
               clave+="8";
           }
           else if(touchXY[0] < 250 && touchXY[0] > 140){
               if (op == 0) {
                   tft.print("*");
               }
               else if (op == 1) {
                   tft.print("9");
               }
               clave+="9";
           }
       }
       else if (touchXY[0] < 130 && touchXY[1] > 840) { //botonHome
           vista = 1;
           clave = "";
           break;
       }
       delay(300);
   }
 
   tft.setFont(&FreeSansBold18pt7b);
 
}

6.2 Código Python

import os
import errno
from datetime import date
from datetime import datetime
import readline
import serial
import time
 
nuevaClave = ""
 
# FICHAR USUARIO
 
def encontrarUsuario (clave):
 
   with open ("usuariosClaves.txt", 'r') as us:
       encontrado = False
       for linea in us:
           if (str(linea[9:13]) == (str(clave))):
               encontrado = True
               return(linea[:8])
      
       if (encontrado == False):
           serialArduino.write("false".encode())
           return None
 
def ficharUsuario (identificador):
  
   fecha = str(date.today())
 
   try:
       os.mkdir(fecha)
   except OSError as e:
       if e.errno != errno.EEXIST:
           raise
 
   os.chdir(fecha)
 
   fileE = open('entrada.txt', 'a+')
   fileS = open('salida.txt', 'a+')
      
   if (identificador == None):
       os.chdir("..")
       return 0
      
  
  
   esSalida = False
   hora = str(datetime.now().time())[:8]
   vecesEntrada = 0
   vecesSalida = 0
   horaEntrada = ""
   with open('entrada.txt', 'r') as f:
       for linea in f:
           if (str(linea[:8]) == (str(identificador))):
               vecesEntrada += 1
               horaEntrada = linea[9:17]
  
   with open('salida.txt', 'r') as f:
       for linea in f:
           if (str(linea[:8]) == (str(identificador))):
               vecesSalida += 1
  
   print("Entrada: " + str(horaEntrada))
  
   if ((vecesEntrada == 0) or (vecesEntrada == vecesSalida)):
        with open('entrada.txt', 'a+') as f2:
            f2.write(str(identificador) + '-' + hora + '\n')
            serialArduino.write("trueE".encode())
           
   else:
       with open('salida.txt', 'a+') as f2:
            f2.write(str(identificador) + '-' + hora + '\n')
            h1 = datetime.strptime(hora, "%H:%M:%S")
            h2 = datetime.strptime(horaEntrada, "%H:%M:%S")
            duracionJornada = h1 - h2
            serialArduino.write("trueS".encode())
            strDuracionJornada = str(duracionJornada)
            print("strDuracionJornada: " + strDuracionJornada)
            time.sleep(1.5)
            serialArduino.write(strDuracionJornada.encode())
           
   os.chdir("..")
              
def guardarUsuario (identificador):
   file = open("usuariosClaves.txt", "r+")
   file2 = open("contadorClave.txt")
   ultimaClave = file2.readline()
   nuevaClave = int(ultimaClave) + 1
   file2.close()
   if(file.read().count(identificador) > 0):
       serialArduino.write("false".encode())
   else:
       file.write(identificador + " " + str(nuevaClave)+"\n")
       file2 = open("contadorClave.txt", "w")
       file2.write(str(nuevaClave))
       print("La nueva clave es " + str(nuevaClave))
       serialArduino.write("true".encode())
       time.sleep(1.5)
       serialArduino.write(str(nuevaClave).encode())
   file.close()
   file2.close()
  
def comprobarAdmin (clave):
   file = open("admin.txt")
   if(clave == (file.readline())):
       serialArduino.write("true".encode())
   else:
       serialArduino.write("false".encode())
   file.close()
 
serialArduino = serial.Serial("/dev/ttyACM0", 9600)
time.sleep(1)
 
while True:
   linea = ""
   while(not linea.startswith("py")):
       linea=serialArduino.readline().decode('ascii')
  
   print(linea)
   clave = linea[4:len(linea) - 2]
   print()
   print(clave)
   print()
   print("Vamos a comprobar el tipo de clave")
  
   if (linea.startswith("pya")):
       print("- Es una clave de administrador")
       comprobarAdmin(clave)
   elif (linea.startswith("pyc")):
       print("- Es una clave normal de trabajador")
       ficharUsuario(encontrarUsuario(clave))
   elif (linea.startswith("pyi")):
       print("- Estamos enviando un identificador")
       guardarUsuario(clave)

También te podría gustar...

Deja una respuesta

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