Viernes, 24 de Octubre de 2025

Creando una máquina de fichar casera

9 de abril de 2023
m
Máquina de fichar con RFID

Seguro que odias la máquina de fichar. Ya sabes, ese aparato que te encuentras a la entrada de tu trabajo y cuenta hasta el último segundo que pasas allí.

Pues en este artículo te cuento cómo construir tu propia máquina de tortura de fichar. Y lo haremos utilizando el mismo sistema que puedes encontrar en aplicaciones como las susodichas máquinas de fichar, las máquinas de autopréstamo de las bibliotecas o los chips que registran los tiempos en las carreras: la tecnología RFID.

¿Qué es RFID?

RFID significa identificación por radiofrecuencia (Radio Frequency IDentification, en inglés). Es decir, este sistema hace uso de ondas electromagnéticas en el espectro de la radiofrecuencia para identificar algo. ¿Y qué es ese algo? Las llamadas etiquetas (o tags, en inglés) RFID.

Estas etiquetas RFID están compuestas por un chip que almacena cierta información (un código, por ejemplo) y una antena capaz de captar las ondas electromagnéticas de una frecuencia determinada y usar la energía de esa onda para responder con la información almacenada.

De esta manera, tan sólo necesitaremos un lector que emita ondas de la frecuencia deseada y pueda captar la respuesta de los tags de los que deseemos extraer su información.

Dicho de una manera muy simple, una etiqueta RFID ejerce una función parecida a la de un código de barras, pero con dos ventajas notables:

  • No hace falta visión directa entre la etiqueta RFID y el lector.
  • Los chips RFID son muy resistentes.

¿Os imagináis que en una carrera todos los corredores tuvieran que hacer cola amistosa y pacientemente para registrar su tiempo? ¿O que se nos olvidase la tarjeta de fichar en el bolsillo y la lavadora la destruyese? Pues eso.

No obstante, esta tecnología también tiene sus limitaciones.

La primera de ellas es la distancia. Al tratarse de ondas de baja energía, el radio de acción se limita a unos pocos metros. Esto puede ser solucionado con el uso de las llamadas etiquetas RFID activas, Estos tags, aparte de los componentes mencionados, tienen su propia fuente de alimentación.

El otro gran inconveniente haría referencia a la seguridad. Debemos extremar la precaución con el hecho de que puedan leer nuestra información con según qué fines. Por eso el sistema que diseñaremos tiene únicamente fines educativos. No te recomiendo en ningún caso que lo implantes en una aplicación real.

El lector MIFARE MFRC522

El elemento clave en el montaje que te propongo hoy es el lector de etiquetas RFID, que en realidad se trata de un emisor-receptor de radiofrecuencia.

Los hay de todas las tipologías, características, precios… En este artículo utilizaremos uno de los más asequibles: el MIFARE MFRC522. Podemos hacernos con uno de estos dispositivos por menos de 2 euros pero, vuelvo a insistir, esto ya nos da idea de que no es un componente adecuado para montar una aplicación profesional.

No obstante, para nuestros proyectos domésticos es más que suficiente. Es tremendamente sencillo conectarlo a un microcontrolador tipo Arduino y utilizarlo a nuestro antojo, ya que incorpora comunicación por bus SPI, bus I2C y UART.

Para conseguirlo tan sólo debemos hacer el conexionado correcto y, como casi siempre, utilizar las librerías oportunas.

En nuestro caso optaremos por usar el bus SPI, así que importaremos la librería SPI.h y cablearemos MFRC522 tal como requiere esta librería. Si tenemos en cuenta su pinout:

Pinout del chip MIFARE MFRC522 usado en nuestra máquina de fichar

El conexionado será:

  • SDA al pin 10 de Arduino. Esto hace referencia a la «Selección de Esclavo (Slave Select)». El bus SPI tiene arquitectura maestro-esclavo. Hay que conectar el maestro con todos sus esclavos, ya sea uno a uno o en cascada.
  • SCK al pin 13 de Arduino. Es la señal de reloj. El bus SPI es síncrono.
  • MOSI al pin 11 de Arduino.
  • MISO al pin12 de Arduino. La comunicación por SPI es full duplex. Hay dos líneas de comunicación que funcionan simultáneamente: MOSI (Master-out, Slave-in)y MISO (Master-in, Slave-out)
  • RST al pin 9 de Arduino.
  • Vcc a la salida de 3.3 V de Arduino.
  • GND a la red de GND del montaje. Como he dicho en otros artículos, es fundamental siempre conectar los GND de todos los elementos de nuestro proyecto entre sí, de modos que el nivel de GND sea el mismo para todos.

Hecho esto, también debemos emplear la librería MFRC522.h.

Objetivo del proyecto

En internet podéis encontrar multitud de ejemplos sencillos para hacer funcionar un MIFARE MFRC522 y extraer el código de un tag RFID, sea este en forma de etiqueta, tarjeta, llavero etc.

En este proyecto vamos a ir un paso más allá y vamos crear una aplicación completa que simula una máquina de fichar. Para ello, además de configurar el bus SPI y el lector, vamos a hacer que con cada lectura se active un LED y se produzca un sonido y haremos que el código leído pase por comunicación serie a nuestro ordenador, en el que desarrollaremos una aplicación de registro de fichajes.

Una vez registrada la información, podemos hacer con ella lo que queramos. En esta ocasión nos conformaremos con habilitar la posibilidad de guardar un pequeño archivo con el listado de marcajes generado. Pero podríamos guardar la información en una base de datos, en una hoja de Excel (no os lo recomiendo, soy enemigo acérrimo de de gestionar empresas con millones de hojas Excel) o en el sistema que prefiramos.

Componentes de la máquina de fichar

Para la construcción de nuestra máquina de fichar hemos empleado estos elementos:

Dispuestos según el siguiente esquema:

Esquema de conexionado de la máquina de fichar

Código Arduino

Al código base para leer una tarjeta RFID MIFARE, con las librerías indicadas y los pines necesarios definidos, le vamos a añadir los pines correspondientes al LED y al zumbador e iniciaremos la comunicación serie que nos permitirá verificar visual y sonoramente que se ha leído la tarjeta correctamente y transmitir el código leído a la aplicación de control.

#include <SPI.h>
#include <MFRC522.h>
const int RST_PIN = 9;        // Pin 9 para el reset del RC522
const int SS_PIN = 10;
int speakerPin=2;
int ledPin=3;
 
MFRC522 rfid(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key; 
// Init array that will store new NUID 
byte nuidPICC[3];
void setup() 
{ 
  Serial.begin(9600);
  SPI.begin();          // Init SPI bus
  rfid.PCD_Init();      // Init MFRC522 
  pinMode(speakerPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
  for (byte i = 0; i < 6; i++) 
    {  key.keyByte[i] = 0xFF;
    }
  //Serial.println(F("This code scan the MIFARE Classsic NUID."));
  //Serial.print(F("Using the following key:"));
  //printHex(key.keyByte, MFRC522::MF_KEY_SIZE);
}
// ------------------------------------------------------------------
void printHex(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}
void printDec(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], DEC);
  }
}
//------------------------------------------------------------------
void loop() {
  // Buscamos tarjetas
  if ( ! rfid.PICC_IsNewCardPresent())
    return;
  // SI la encuentra la leemos
  if ( ! rfid.PICC_ReadCardSerial())
    return;
  //Serial.print(F("PICC type: "));
  MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
  //Serial.println(rfid.PICC_GetTypeName(piccType));
  // Check is the PICC of Classic MIFARE type
  if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&  
    piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
    piccType != MFRC522::PICC_TYPE_MIFARE_4K) 
      {
        //Serial.println(F("Your tag is not of type MIFARE Classic."));
        return;
      }
  if (rfid.uid.uidByte[0] != nuidPICC[0] || 
    rfid.uid.uidByte[1] != nuidPICC[1] || 
    rfid.uid.uidByte[2] != nuidPICC[2] || 
    rfid.uid.uidByte[3] != nuidPICC[3] ) 
    {
        //Serial.println(F("A new card has been detected."));
    
        //Serial.println(F("The NUID tag is:"));
        //Serial.print(F("In hex: "));
        printHex(rfid.uid.uidByte, rfid.uid.size);
        Serial.println();
        digitalWrite(ledPin, HIGH);
        playTone();
        //Serial.print(F("In dec: "));
        //printDec(rfid.uid.uidByte, rfid.uid.size);
        //Serial.println();
     
       rfid.PICC_HaltA();
       rfid.PCD_StopCrypto1();
    }
    delay(600);
    digitalWrite(ledPin, LOW);
}
void playTone()
{
  for(long i=0; i<500*1000L; i+= 1136*2)
  {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(1136);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(1136);
  }
}

Código Java

La funcionalidad «real» se la daremos en este parte. Hemos creado una aplicación que simula una máquina de fichar.

Esta aplicación está hecha en código Java puro, sin hacer uso de ningún framework ni ninguna funcionalidad del IDE para generar interfaces gráficas. Lo podéis compilar tal cual aparece aquí (evidentemente, teniendo instalado el jdk en nuestro PC, y os funcionará.

El único «añadido» son las librerías para usar la comunicación serie. Sus nombres son RXTXcomm.jar, rxtxSerial.dll y rxtxParallel.dll. Podéis encontrarlas en algunos repositorios de Internet.

Para que funcionen tan sólo debemos hacer lo siguiente:

Copiar RXTXcomm.jar en el directorio <JAVA_HOME>\jre\lib\ext.

Copiar rxtxSerial.dll en el directorio <JAVA_HOME>\jre\bin y en el directorio windows/system32.

Copiar rxtxParallel.dll en el directorio <JAVA_HOME>\jre\bin y en el directorio windows/system32

siendo <JAVA_HOME> la ruta donde habéis instalado JDK (Java Development Kit).

Y ahora sí, el código. Es un poco caótico porque he mezclado la parte gráfica con las funcionalidades en un intento de hacer todo en un único archivo que podrías componer directamente en el bloc de notas, por ejemplo. Si sois puretas de la programación, os sangrarán los ojos.

package fichajes;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Date;
import java.text.*;
import java.awt.event.*;
import java.awt.*;
import javafx.embed.swing.JFXPanel;
import javafx.stage.FileChooser;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import com.sun.glass.ui.Platform;
import gnu.io.CommPortIdentifier; 
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent; 
import gnu.io.SerialPortEventListener; 
import java.io.IOException;
public class MaquinaFichar extends JFrame implements SerialPortEventListener
{
	int segundos;//manejar el valor del contador
	boolean frozen; //manejar el estado del contador
	Timer timer = new Timer();
	Date date = new Date();
	SimpleDateFormat ft=new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
	
	JFrame marco=new JFrame("Aplicación de control de fichajes");
	JLabel titulo=new JLabel();
	JLabel hora=new JLabel();
	JLabel onoff=new JLabel();
	JButton encender=new JButton();
	JLabel inicioregistro=new JLabel("Inicio de registro mostrado");
	JTextField tf_inicioregistro=new JTextField(20);
	JLabel finregistro=new JLabel("Fin de registro mostrado");
	JTextField tf_finregistro=new JTextField(20);
	JButton btn_guardar=new JButton();
	JButton btn_abrir=new JButton();
	int encendido=0;
	int conectado=1;
	JPanel pnl_encendido=new JPanel();
	JLabel lbl_puerto=new JLabel("Puerto de conexión:");
	String[] str_puerto={"COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","COM10","COM11","COM12","COM13","COM14","COM15","COM16","COM17","COM18","COM19","COM20","COM21","COM22","COM23","COM24","COM25","COM26"};
	JComboBox cmb_puerto=new JComboBox(str_puerto);
	JLabel lbl_baudrate=new JLabel("Baud Rate:");
	String[] str_baudrate={"4800","9600","19200","38400","57600","115200","234000","460800","921600","1382400"};
	JComboBox cmb_baudrate=new JComboBox(str_baudrate);
	JTextField txt_noticias=new JTextField(40);
	JPanel pnl_puerto=new JPanel();
	JPanel pnl_baudrate=new JPanel();
	final JFXPanel fxPanel = new JFXPanel();
	
	File selectedFile=null;
	File savedFile=null;
	String nombrearchivo=null;
	String infile=null;
	JTextArea texto=new JTextArea();
	JScrollPane scroll=new JScrollPane(texto);
	String personal[][]=new String[2][2];
	
	
	/** The output stream to the port */
	private OutputStream output = null;
	private BufferedReader input;
	SerialPort serialPort;
	private String PORT_NAME = "COM1";
	/** Milliseconds to block while waiting for port open */
	private static final int TIME_OUT = 2000;
	/** Default bits per second for COM port. */
	private static int DATA_RATE = 9600;
	
	MaquinaFichar()
	{	
		try
		{
			marco.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("fondo.png")))));
		}
		catch(IOException e)
	    {
	        e.printStackTrace();
	     
	    }
		personal[0][0]="25 9A 0F 11";
		personal[1][0]="81 EE 3C D9";
		personal[0][1]="out";
		personal[1][1]="out";
		timer.schedule(new MiTarea(), 0, 1000);
		marco.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		marco.setIconImage(Toolkit.getDefaultToolkit().createImage("maquinafichar.png"));
		marco.setResizable(false);
		GridBagLayout gridbag=new GridBagLayout();
		GridBagConstraints gbc=new GridBagConstraints();
		marco.setLayout(gridbag);
		gbc.insets=new Insets(10,10,10,10);
		gbc.gridx=0;
		gbc.gridy=0;
		gbc.gridwidth=6;
		gbc.gridheight=1;
		gbc.weightx=1;
		gbc.weighty=1;
		titulo.setFont(new Font("Arial", Font.PLAIN, 60));
		titulo.setText("REGISTRO DE FICHAJES");
		marco.add(titulo,gbc);
		gbc.gridx=0;
		gbc.gridy=1;
		gbc.gridwidth=4;
		gbc.gridheight=1;
		gbc.weightx=1;
		gbc.weighty=1;
		hora.setFont(new Font("Arial", Font.PLAIN, 48));
		hora.setText(ft.format(date));
		hora.setOpaque(true);
		hora.setBackground(Color.cyan);
		marco.add(hora,gbc);
		/*gbc.gridx=4;
		gbc.gridy=1;
		gbc.gridwidth=1;
		gbc.gridheight=1;
		gbc.weightx=1;
		gbc.weighty=1;
		gbc.anchor=GridBagConstraints.EAST;*/
		//encender.setPreferredSize(new Dimension(100, 100));
		encender.setIcon(new ImageIcon("redbutton.png"));
		encender.setName("encender");
		encender.setToolTipText("Encender/Apagar");
		encender.setBorderPainted(false); 
		encender.setContentAreaFilled(false); 
		encender.setFocusPainted(false); 
		encender.setCursor(new Cursor(Cursor.HAND_CURSOR));
		//encender.setOpaque(false);
		encender.addMouseListener(new Seleccion());
		pnl_encendido.add(encender,gbc);
		/*gbc.gridx=5;
		gbc.gridy=1;
		gbc.gridwidth=1;
		gbc.gridheight=1;
		gbc.weightx=1;
		gbc.weighty=1;
		gbc.anchor=GridBagConstraints.WEST;*/
		//onoff.setText("<html><font size=20 color=red>OFF</font></html>");
		//encender.setPreferredSize(new Dimension(200, 100));
		onoff.setText("OFF");
		onoff.setFont(new Font("Arial", Font.PLAIN, 100));
		onoff.setForeground(Color.red);
		pnl_encendido.add(onoff,gbc);
		gbc.gridx=4;
		gbc.gridy=1;
		gbc.gridwidth=2;
		gbc.gridheight=1;
		gbc.weightx=1;
		gbc.weighty=1;
		gbc.anchor=GridBagConstraints.WEST;
		pnl_encendido.setOpaque(false);
		marco.add(pnl_encendido,gbc);
		gbc.gridx=0;
		gbc.gridy=2;
		gbc.gridwidth=4;
		gbc.gridheight=5;
		
		texto.setEditable(false);
		scroll.setPreferredSize(new Dimension(600, 400));
		marco.add(scroll,gbc);
		gbc.insets=new Insets(0,0,0,0);
		gbc.gridx=4;
		gbc.gridy=2;
		gbc.gridwidth=2;
		gbc.gridheight=1;
		gbc.anchor=GridBagConstraints.SOUTHWEST;
		inicioregistro.setFont(new Font("Arial", Font.PLAIN, 32));
		marco.add(inicioregistro,gbc);
		gbc.gridx=4;
		gbc.gridy=3;
		gbc.gridwidth=2;
		gbc.gridheight=1;
		tf_inicioregistro.setEditable(false);
		tf_inicioregistro.setFont(new Font("Arial", Font.PLAIN, 32));
		tf_inicioregistro.setBackground(Color.white);
		//tf_inicioregistro.setText("hola");
		gbc.anchor=GridBagConstraints.NORTHWEST;
		marco.add(tf_inicioregistro,gbc);
		gbc.gridx=4;
		gbc.gridy=4;
		gbc.gridwidth=2;
		gbc.gridheight=1;
		finregistro.setFont(new Font("Arial", Font.PLAIN, 32));
		gbc.anchor=GridBagConstraints.SOUTHWEST;
		marco.add(finregistro,gbc);
		gbc.gridx=4;
		gbc.gridy=5;
		gbc.gridwidth=2;
		gbc.gridheight=1;
		tf_finregistro.setEditable(false);
		tf_finregistro.setFont(new Font("Arial", Font.PLAIN, 32));
		tf_finregistro.setBackground(Color.white);
		gbc.anchor=GridBagConstraints.NORTHWEST;
		//tf_inicioregistro.setText("hola");
		marco.add(tf_finregistro,gbc);
		gbc.gridx=4;
		gbc.gridy=6;
		gbc.gridwidth=1;
		gbc.gridheight=1;
		gbc.anchor=GridBagConstraints.CENTER;
		btn_guardar.setFont(new Font("Arial", Font.PLAIN, 18));
		btn_guardar.setText("Guardar");
		btn_guardar.setIcon(new ImageIcon("guardar.png"));
		btn_guardar.setName("guardar");
		btn_guardar.addMouseListener(new Seleccion2());
		btn_guardar.setToolTipText("No hay registro para guardar");
		btn_guardar.setEnabled(false);
		marco.add(btn_guardar,gbc);
		gbc.gridx=5;
		gbc.gridy=6;
		gbc.gridwidth=1;
		gbc.gridheight=1;
		btn_abrir.setFont(new Font("Arial", Font.PLAIN, 18));
		btn_abrir.setText("Abrir");
		btn_abrir.setIcon(new ImageIcon("abrir.png"));
		btn_abrir.setName("abrir");
		//encender.setOpaque(false);
		btn_abrir.addMouseListener(new Seleccion2());
		marco.add(btn_abrir,gbc);
		//gbc.insets=new Insets(5,5,5,5);
		gbc.gridx=0;
		gbc.gridy=7;
		gbc.gridwidth=4;
		gbc.gridheight=1;
		//gbc.fill=GridBagConstraints.HORIZONTAL;
		txt_noticias.setEditable(false);
		txt_noticias.setBackground(Color.white);
		marco.add(txt_noticias,gbc);
		gbc.gridx=4;
		gbc.gridy=7;
		gbc.gridwidth=1;
		gbc.gridheight=1;
		gbc.weightx=1;
		//gbc.fill=GridBagConstraints.NONE;
		pnl_puerto.add(lbl_puerto,gbc);
		cmb_puerto.addItemListener(new PORTlistener());
		pnl_puerto.add(cmb_puerto,gbc);
		pnl_puerto.setOpaque(false);
		marco.add(pnl_puerto,gbc);
		gbc.gridx=5;
		gbc.gridy=7;
		gbc.gridwidth=1;
		gbc.gridheight=1;
		gbc.weightx=1;
		gbc.fill=GridBagConstraints.NONE;
		pnl_baudrate.add(lbl_baudrate,gbc);
		cmb_baudrate.addItemListener(new BRlistener());
		pnl_baudrate.add(cmb_baudrate,gbc);
		pnl_baudrate.setOpaque(false);
		marco.add(pnl_baudrate,gbc);
		
		
		
		marco.pack();
		marco.setVisible(true);
		marco.setLocationRelativeTo(null);
	}
	class Seleccion extends MouseAdapter
	{
		public void mousePressed(MouseEvent e)
		{
			Component aux=e.getComponent();
		}
		public void mouseReleased(MouseEvent e)
		{
			Component aux=e.getComponent();
			{
				if(aux.getName().equals("encender")&&encendido==1)
				{
					encender.setIcon(new ImageIcon("redbutton.png"));
					onoff.setText("OFF");
					onoff.setForeground(Color.red);
					encendido=0;
					conectado=0;
					serialPort.removeEventListener();
					serialPort.close();
					txt_noticias.setText("Conexión interrumpida");
					tf_finregistro.setText(hora.getText());
					btn_abrir.setToolTipText("Abrir registro previo");
					btn_abrir.setEnabled(true);
					btn_guardar.setToolTipText("Guardar registro");
					btn_guardar.setEnabled(true);
				}
				else if(aux.getName().equals("encender")&&encendido==0)
				{				
					initialize();
					if(conectado==1)
					{
						encender.setIcon(new ImageIcon("greenbutton.png"));
						onoff.setText("ON");
						onoff.setForeground(Color.green);
						encendido=1;
						tf_inicioregistro.setText(hora.getText());
						tf_finregistro.setText("En ejecución...");
						btn_abrir.setToolTipText("Para abrir un registro debe apagar la lectura de tarjetas");
						btn_abrir.setEnabled(false);
						btn_guardar.setToolTipText("Para guardar un registro debe apagar la lectura de tarjetas");
						btn_guardar.setEnabled(false);
					}
														
				}
				
			}
		}
	}
	
	class Seleccion2 extends MouseAdapter
	{
		public void mousePressed(MouseEvent e)
		{
			Component aux=e.getComponent();
		}
		public void mouseReleased(MouseEvent e)
		{
			Component aux=e.getComponent();
			{
				if(aux.getName().equals("abrir"))
				{
					FileChooser fileChooser = new FileChooser();
					selectedFile = fileChooser.showOpenDialog(null);
					if (selectedFile != null) {
						txt_noticias.setText("Fichero Seleccionado: " + selectedFile.getName());
						nombrearchivo=selectedFile.getAbsolutePath();
						Leer(nombrearchivo);
						//System.out.println(nombrearchivo);
					}
					else {
						txt_noticias.setText("Cancelada selección de fichero");
					}
				}
				if(aux.getName().equals("guardar"))
				{
					FileChooser fileChooser = new FileChooser();
					savedFile = fileChooser.showSaveDialog(null);
					if (savedFile != null) 
					{
						infile=savedFile.getAbsolutePath();
						Crear(infile);
						txt_noticias.setText("Guardado como: " + savedFile.getName());
					}
					else {
						txt_noticias.setText("Cancelado guerdado de fichero");
					}
				}
				
			}
		}
	}
	
	class BRlistener implements ItemListener
	{
		@Override
		public void itemStateChanged(ItemEvent evt) 
		{
			// TODO Auto-generated method stub
			 JComboBox cb = (JComboBox) evt.getSource();
			 String str_DATA_RATE = (String) cb.getSelectedItem();
			 DATA_RATE=Integer.parseInt(str_DATA_RATE);
			 txt_noticias.setText("BaudRate cambiado a: "+str_DATA_RATE);
		}
		
	}
	
	class PORTlistener implements ItemListener
	{
		@Override
		public void itemStateChanged(ItemEvent evt) 
		{
			// TODO Auto-generated method stub
			 JComboBox cb = (JComboBox) evt.getSource();
			 String str_PORT_NAME = (String) cb.getSelectedItem();
			 PORT_NAME=str_PORT_NAME;
			 txt_noticias.setText("Puerto cambiado a: "+str_PORT_NAME);
		}
		
	}
	
	class MiTarea extends TimerTask 
	{
		public void run() 
		{
			Date date = new Date();
			SimpleDateFormat ft=new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
			hora.setText(ft.format(date));
		}// end run()
	}
	
	public void initialize() 
	{
		 
		CommPortIdentifier portId = null;
		Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
 
		// iterate through, looking for the port
		while (portEnum.hasMoreElements()) 
		{
			CommPortIdentifier currPortId = (CommPortIdentifier)
					portEnum.nextElement();
 
			if (PORT_NAME.equals(currPortId.getName())) 
			{
				portId = currPortId;
				
				break;
			}
		}
		if (portId == null) 
		{
			System.out.println("Could not find COM port.");
			txt_noticias.setText("Conexión fallida");
			encendido=0;
			conectado=0;
			return;
		}
		else
		{
			conectado=1;
		}
		try 
		{
			// open serial port, and use class name for the appName.
			serialPort = (SerialPort) portId.open(this.getClass().getName(),
					TIME_OUT);
			// set port parameters
			serialPort.setSerialPortParams(DATA_RATE,
					SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1,
					SerialPort.PARITY_NONE);
			// open the streams
			input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
			output = serialPort.getOutputStream();
			// add event listeners
			serialPort.addEventListener(this);
			serialPort.notifyOnDataAvailable(true);
			//lbl_conectar.setIcon(new ImageIcon("lightON.png"));
			txt_noticias.setText("Conexión establecida");
		} 
		catch (Exception e) 
		{
			System.err.println(e.toString());
		}
	}
	
	/**
	 * Handle an event on the serial port. Read the data and print it.
	 */
	public synchronized void serialEvent(SerialPortEvent oEvent) 
	{
		if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) 
		{
			try 
			{
				String inputLine=input.readLine();
				//if(inputLine.trim().equals("25 9A 0F 11"))
					//txt_noticias.setText("wololo");
				//texto.append("Entrada: " + inputLine + "; " + hora.getText() + "\n");
				for(int i=0;i<2;i++)
				{
					//texto.append(Integer.toString(i));
					//texto.append(inputLine);
					
					if(Objects.equals(personal[i][0],inputLine.trim()) && personal[i][1].equals("out"))
					{
						personal[i][1]="in";
						texto.append("Entrada: " + inputLine + "; " + hora.getText() + "\r\n");
					}
					else if(Objects.equals(personal[i][0],inputLine.trim()) && personal[i][1].equals("in"))
					{
						personal[i][1]="out";
						texto.append("Salida: " + inputLine + "; " + hora.getText() + "\r\n");
					}
						
											
				}					
			} catch (Exception e) {
				System.err.println(e.toString());
			}
		}
	}
	
	public void Leer(String archivosalida)
	{	
		try
		{
			FileReader fr=new FileReader(nombrearchivo);
			BufferedReader textReader=new BufferedReader(fr);
			
			int numberOfLines=readLines();
			String[] textData=new String[numberOfLines];
	
			int i;
			for(i=0; i<numberOfLines;i++)
			{
				textData[i]=textReader.readLine();
				System.out.println(textData[i]);
				//texto.append(textData[i]+"\r\n");
		
			}
			if(textData[0].indexOf(':')>-1&&textData[1].indexOf(':')>-1)
			{
				if(numberOfLines>1 && Objects.equals(textData[0].substring(0, textData[0].indexOf(':')),"Inicio de registro mostrado") && Objects.equals(textData[1].substring(0, textData[1].indexOf(':')),"Fin de registro mostrado") )
				{
					tf_inicioregistro.setText(textData[0].substring(textData[0].indexOf(":")+1));
					tf_finregistro.setText(textData[1].substring(textData[1].indexOf(":")+1));
					for(i=2; i<numberOfLines;i++)
					{
						texto.append(textData[i]+"\r\n");
					}
					txt_noticias.setText("Registro leído correctamente");
				}
				else
				{
					txt_noticias.setText("Formato de archivo incorrecto");
				}
			}
			else
			{
				txt_noticias.setText("Formato de archivo incorrecto");
			}
			
			textReader.close();
		}
		
		catch(IOException e)
		{
		}
	}
	
	int readLines() throws IOException
	{
		FileReader file_to_read=new FileReader(nombrearchivo);
		BufferedReader bf=new BufferedReader(file_to_read);
		String aLine;
		int numberOfLines=0;
		while((aLine=bf.readLine())!=null)
		{
			numberOfLines++;
		}
		bf.close();
		return numberOfLines;
	}
	
	public void Crear(String archivoentrada)
	{
		try
		{
			Writer output = null;
  			File file = new File(archivoentrada);
  			output = new BufferedWriter(new FileWriter(file,true));
  			output.append("Inicio de registro mostrado: " + tf_inicioregistro.getText() + "\r\n");
  			output.append("Fin de registro mostrado: " + tf_finregistro.getText() + "\r\n");
  			output.append(texto.getText());
  			output.close();		
		}
		catch(IOException e)
		{
		}
	}
	
	public static void main(String[] args)
	{
		MaquinaFichar app=new MaquinaFichar();
	}
	
}

Para crear un selector de guardado/apertura de archivos con un aspecto moderno, hemos creado la siguiente clase:

package fichajes;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.paint.Color;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
import javafx.geometry.Insets;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import java.io.File;
import java.util.List;
public class SelectFileChooserExample
        extends Application {
	private Text actionStatus;
	private Stage savedStage;
	private static final String titleTxt = "JavaFX File Chooser Example 1";
	/*public static void main(String [] args) {
		Application.launch(args);
	}*/
	@Override
	public void start(Stage primaryStage) {
	
		primaryStage.setTitle(titleTxt);	
		// Window label
		Label label = new Label("Select File Choosers");
		label.setTextFill(Color.DARKBLUE);
		label.setFont(Font.font("Calibri", FontWeight.BOLD, 36));
		HBox labelHb = new HBox();
		labelHb.setAlignment(Pos.CENTER);
		labelHb.getChildren().add(label);
		// Buttons
		Button btn1 = new Button("Choose a file...");
		btn1.setOnAction(new SingleFcButtonListener());
		HBox buttonHb1 = new HBox(10);
		buttonHb1.setAlignment(Pos.CENTER);
		buttonHb1.getChildren().addAll(btn1);
		Button btn2 = new Button("Choose multiple PDF files...");
		btn2.setOnAction(new MultipleFcButtonListener());
		HBox buttonHb2 = new HBox(10);
		buttonHb2.setAlignment(Pos.CENTER);
		buttonHb2.getChildren().addAll(btn2);
		// Status message text
		actionStatus = new Text();
		actionStatus.setFont(Font.font("Calibri", FontWeight.NORMAL, 20));
		actionStatus.setFill(Color.FIREBRICK);
		// Vbox
		VBox vbox = new VBox(30);
		vbox.setPadding(new Insets(25, 25, 25, 25));;
		vbox.getChildren().addAll(labelHb, buttonHb1, buttonHb2, actionStatus);
		// Scene
		Scene scene = new Scene(vbox, 500, 300); // w x h
		primaryStage.setScene(scene);
		primaryStage.show();
		savedStage = primaryStage;
	}
	private class SingleFcButtonListener implements EventHandler<ActionEvent> {
		@Override
		public void handle(ActionEvent e) {
			showSingleFileChooser();
		}
	}
	private void showSingleFileChooser() {
	
		FileChooser fileChooser = new FileChooser();
		File selectedFile = fileChooser.showOpenDialog(null);
		if (selectedFile != null) {
			actionStatus.setText("File selected: " + selectedFile.getName());
		}
		else {
			actionStatus.setText("File selection cancelled.");
		}
	}
	private class MultipleFcButtonListener implements EventHandler<ActionEvent> {
		@Override
		public void handle(ActionEvent e) {
			showMultipleFileChooser();
		}
	}
	private void showMultipleFileChooser() {
		FileChooser fileChooser = new FileChooser();
		fileChooser.setTitle("Select PDF files");
		fileChooser.setInitialDirectory(new File("X:\\testdir\\two"));
		fileChooser.getExtensionFilters().addAll(
			new ExtensionFilter("PDF Files", "*.pdf"));
		List<File> selectedFiles = fileChooser.showOpenMultipleDialog(savedStage);
		if (selectedFiles != null) {
			actionStatus.setText("PDF Files selected [" + selectedFiles.size() + "]: " +
					selectedFiles.get(0).getName() + "..");
		}
		else {
			actionStatus.setText("PDF file selection cancelled.");
		}
	}
}

Prueba de funcionamiento de la máquina de fichar

Veamos la máquina de fichar en acción.

Como veis, esta tecnología tiene un sinfín de aplicaciones. El límite lo pone tu imaginación.

Publicaciones relacionadas

Controla una Roomba a tu voluntad

Controla una Roomba a tu voluntad

En este artículo te muestro una característica poco conocida de los robots de limpieza Roomba de iRobot: la interfaz abierta de Roomba (o ROI, Roomba Open Interface, por sus siglas en inglés). Esta interfaz es un puerto de conexión serie que permite a Roomba intercambiar mensajes con otros dispositivos como microcontroladores, ordenadores o smartphones. El protocolo de información es público y gracias a él podemos añadir a Roomba cuantas funcionalidades se nos ocurran.

0 comentarios

Enviar un comentario

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