Lector de tarjetas

Realizado por Lucas Rodríguez Bravo, Rodrigo Dueñas Herrero y Lucía Álvarez Blázquez

1-              Objetivos y planteamiento del proyecto

La primera idea que se tuvo a la hora de plantear el proyecto fue realizar un lector de tarjetas. Esta idea le pareció correcta al grupo y fue la elegida. Además, se decidió que fuera multifuncional.

El siguiente paso fue idear como iba a ser su funcionamiento. Se usó la idea de las tarjetas perforadas como inspiración y se ideó que las tarjetas que fuera a leer el lector tendrían huecos para dejar pasar la luz.

Después, se pensaron las posibles funciones que podría a llegar a haber. Se pensó rápidamente la de identificación y de sumas. Por último, se valoró que sonase un sonido por cada hueco de la tarjeta. Sin embargo, se cambió esta idea por hacer sonar una letra, que se obtendría por su orden en el alfabeto, en morse, ya que se pensó que sería más práctico.

Finalmente, se decidió que funcionase a través de la obtención de números en binario que se obtendrían por el paso o no de la luz.

2-              Funcionamiento

El lector es una máquina de estados, en la que cada estado es un modo. Estos modos hacen cada uno cosas diferentes, pero se basan en el mismo proceso:

  • En la protoboard existen LEDs blancos encendidos todo el tiempo. En frente de ellos hay fotorresistencias que reciben la luz.
  • Las tarjetas presentan huecos, cuya posición está alineado con las fotorresistencias y LEDs. Al meter la tarjeta en el lector, se separan las LDRs y los LEDs, de tal manera que una fotorresistencia solo podrá recibir luz si la tarjeta introducida tiene hueco en su posición.
  • Las LDRs están numeradas del 0 al 4. Cuando la fotorresistencia recibe luz se genera un 1 en su posición. Sino la recibe, se genera un 0. De esta manera se obtiene un número en binario, que luego se convierte a decimal.

Ilustración 1: Tarjeta de valor 26 (11010).

El dispositivo tiene dos botones. Uno se usa para cambiar de estado al siguiente y otro para realizar la acción correspondiente al modo.

Con el número obtenido se harán diferentes acciones en función del modo:

  1. MODO ID: Se imprime en la pantalla LCD el número en decimal.
  2. MODO SUMAR: El número de la tarjeta se suma al valor almacenado anteriormente. Este valor se reinicia al cambiar de modo.
  3. MODO MORSE: Hace sonar en morse la letra que se obtenga a partir del número. Por ejemplo, la 1 sería la A, la 2 la B, etc.

Ilustración 2: Diagrama de estados.

3-              Hardware

Para la realización de este proyecto se han usado los siguientes componentes de la caja del pack de Arduino:

  • UNO R3 x1
  • Modulo LCD1602 x1
  • Protoboard x1
  • Protoboard placa de Expansión x1
  • Resistencias 330 ohmios x14
  • LEDs blancos x5
  • Potenciómetro x1
  • Zumbador x1
  • Botón x2
  • Fotorresistencias x5
  • Cables x53

Con estos componentes se ha montado el prototipo de una manera similar al siguiente esquema:

Ilustración 3: Esquema Arduino tinkercad

Como se ve las fotorresistencias funcionan como entradas analógicas, estando conectada a los pines de entrada analógicos de A0 a A4. Además, están conectadas en el orden en que se ordena los bits en el número binario.

Los botones se conectan a los pines digitales 2 y 3.

El zumbador está conectado al pin digital 9, que funciona como pin de salida analógica, ya que es un PWM.

Por último, se realizan las conexiones correctas de la pantalla LCD, utilizando un potenciómetro para regular la entrada de electricidad en ella.

4-              Extras

Además de los componentes electrónicos utilizados, se han necesitado hacer algunos componentes extra para complementar el funcionamiento del hardware. A continuación, se describen y se muestran imágenes de estos componentes:

  • Tarjetas perforadas: hechas a partir de cartulina y plastificadas posteriormente. Utilizadas para identificar los diferentes números binarios. Los huecos en las tarjetas están medidos para que dejen pasar la luz entre un LED y su correspondiente fotorresistencia. Para ahorrar material utilizamos las tarjetas por las dos caras, exceptuando la de 0, 31, 8 y 24 que no lo permiten ya que son simétricas. También, tienen el número decimal que representan en la esquina superior derecha.

Ilustración 4: Tarjetas perforadas

  • Carcasa: Carcasa modelada en sketchup e impresa en 3D. Utilizada para meter las tarjetas y separar los LEDs unos de otros para que focalicen a la fotorresistencia correspondiente.

Ilustración 5: Modelo 3D de carcasa                      Ilustración 6: Carcasa impresa en 3D

Ya se contaba los materiales para estos componentes, por lo que no hubo que gastarse dinero adicional.

5-              Desarrollo

  • Lo primero que se hizo fue hacer el circuito básico del lector, es decir, poner las LEDs y las fotorresistencias colocadas en oposición y conectarlas al Arduino. Se desarrollo un pequeño programa simplemente para comprobar si las LDRs recibían bien la luz de los LEDs y obteníamos el número en binario, que en este caso era 11111. Además, se realizó la función getBinary(), que transformaba el binario en decimal. Inmediatamente se vio que iba a ser necesario un elemento para separar la luz de los LEDs y por el que se pudiese introducir las tarjetas. Así, se hizo una versión muy simple de este cartón para hacerse una idea de cómo sería.

Ilustración 7: Carcasa de cartón

  • Después, se añadió el botón de cambio de estado. Así mismo, se programó el funcionamiento de los cambios de estado y se declararon las funciones de los modos. Seguidamente, se introdujo el siguiente botón, que al pulsarlo las funciones harían su función.

Ilustración 8: Protoboard con los 2 botones

  • Se comenzaron a realizar las funciones. Para las dos primeras (identificación y sumar) se mostraba el resultado impreso en el monitor virtual.
  • Para el modo morse se añadió un zumbador que generaría el sonido. A nivel software, se implementó su función y se añadieron las funciones point() y line(), que son las encargadas de gestionar la emisión de sonido.

Ilustración 9: zumbador

  • En paralelo a esto, se realizo el diseño de el lector de tarjetas en 3D para más tarde imprimirlo y se fueron haciendo las tarjetas necesarias para obtenerlos todos números del 0 al 31.
  • A continuación, se incorporó la carcasa del lector de tarjetas, de tal manera que al introducir una tarjeta separase las LDRs de los LEDs. Tras varias pruebas, ya que las tarjetas no dejaban pasar la luz de manera correcta y hubo que cambiar el tamaño de los huecos varias veces, se obtuvo el resultado adecuado.

Ilustración 10: Carcasa del lector

  • El último paso fue añadir la pantalla LCD, para lo cual usamos una placa de expansión de protoboard ya que se había acabado el hueco en la principal. Además, se realizó la programación de la LCD.

Ilustración 11: Pantalla LCD

6-              Software

A continuación, se explicará el código utilizado para el desarrollo del lector, desde la declaración de variables hasta las funciones que implementan cada modo.

  • Declaración de variables:
    • Lo primero es declarar el LCD. Se asignan los pines digitales necesarios y se asigna el LCD a sus pines.
    • Seguidamente, se realiza la declaración de constantes. Por un lado, se asignan los pines a los botones (2 y 3) y el pin al zumbador (9). Se iguala BINARY_SIZE, que será el tamaño del número binario, pointPause y linePause, que serán la cantidad de tiempo que suena el zumbador. La línea suena el triple que el punto. Por último, se establece la frecuencia a la que sonará el zumbador y la barrera de intensidad que tendrán que superar las fotorresistencias para conseguir un 1.
    • Por último, se declaran las variables del programa. Primero, se inicializan las variables responsables de gestionar el estado de los botones, siendo las variables que tengan la palabra extra aquellas destinadas a gestionar el botón de acción.Después se crean los arrays encargados de la entrada normalizada, normalizedSensor, y del número binario, binaryNumber. Luego se crea la variable mode en la que se guarda el modo actual y las variables modeOutput, que se encargan de guardar las salidas de cada modo.

Ilustración 12: Declaración e inicialización del LCD.

Ilustración 13: Declaración de constantes.

Ilustración 14: Declaración de variables.

  • Setup: En esta función se inicia el LCD, se ponen los pines de los botones en modo entrada, del zumbador en modo salida, y comienza la comunicación serial para el monitor virtual.

Ilustración 15: Función setup()

  • getBinary: Esta función recibe un array de 1s y 0s y lo traduce a un número decimal sumando cada número del array multiplicado por 2 elevado al índice del número en el array.

Ilustración 16: Función getBinary().

  • modeId: Al inicio de la función se comprueba si el botón está pulsado. Si es el caso, se realiza el cuerpo de la función.

Una vez detectada la pulsación del botón, se guarda en la variable modeOutput el resultado de la función getBinary(), que ha sido guardado en la variable binaryToInt.

Ilustración 17: Función modeId().

  • modeAdd: Al inicio de la función se comprueba si el botón está pulsado. Si es el caso, se realiza el cuerpo de la función.

Esta función es similar a modeId pero en vez de actualizar el modeOutput cambiándolo directamente por binaryToInt, se va sumando esta variable al resultado, para así poder sumar distintos números.

Ilustración 18: Función modeAdd().

  • modeMorse: Al inicio de la función se comprueba si el botón está pulsado. Si es el caso, se realiza el cuerpo de la función.

Obtenemos el número de tarjeta a través de getBinary() y lo guardamos en la variable aux. Según el número obtenido, se guarda la letra correspondiente en modeOutput2 y se muestra en el LCD.

Inmediatamente después, se ejecutan las funciones point() y line(). Estas dos funciones son las encargadas de hacer que se emita un punto o una línea de morse. Se utiliza tone() para hacer sonar al zumbador y se le deja sonar el tiempo asignado a pointPause o a linePause.

Por último, se retorna a la función y se termina su ejecución.

Ilustración 19: Función modeMorse().

Ilustración 20: Función point().

Ilustración 21: Función line().

  • Loop: El loop principal del programa consiste en, primero leer todos los valores de los sensores y botones, además de poner en el array del número binario los valores sacados de las fotorresistencias y guardarlo como decimal en binaryToInt usando getBinary(). A continuación, se detecta si se ha pulsado el botón de cambio de modo para cambiarlo en el caso de que se haya pulsado y se ejecuta la función correspondiente al modo actual usando un switch y la variable mode. Por último, se escribe en la pantalla lcd el modo actual y el resultado de este.

Ilustración 22: Primera parte función loop().

Ilustración 23: Segunda parte función loop().

7-              Problemas y soluciones

La mayoría de los problemas que hemos encontrado han sido problemas de ajuste, que se han solucionado a base de prueba y error.

  •  Al introducir la carcasa en el sistema hubo que modificar varias veces el treshold hasta encontrar la solución óptima, que fue un valor bajo ya que este dejaba pasaba menos luz que la esperada.
  • A la hora de cortar los huecos en las tarjetas hubo que a ver varias pruebas ya que estas dejaban pasar poco la luz, por lo que se cambia el diseño de las tarjetas de huecos redondos a huecos rectangulares que dejaban entrar toda la luz.

Hubo un problema en el programa que nos costó encontrar, y era que a la hora de pasar de un float a un int en getBinary(), esto se casteaba mal, dando resultados menores de los que deberían ser. Se fue poco a poco depurando hasta encontrar el fallo. La solución fue simplemente hacer todas las variables float.

8-              Reparto de tareas.

Prácticamente el trabajo de los miembros del grupo ha sido el mismo en todo momento, los únicos casos en los que se diferencian eran en los que un miembro era bueno haciendo algo, o en el caso contrario, tenía la imposibilidad de participar.

Aun así, en la siguiente tabla se muestra que miembros del grupo han participado en que tareas. Las casillas con X son aquellas en las que el miembro ha participado.

 DiseñoHardwareSoftwareCarcasaTarjetasPresentación
LucasXXXX  
LucíaXX  XX
RodrigoXXX   

Vídeo

Código

#include <LiquidCrystal.h>

const int rs = 12, en = 11, d4 = 7, d5 = 6, d6 = 5, d7 = 4;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


const int BINARY_SIZE = 3;
const int buttonPin = 2; 
const int buttonExtraPin = 3;
const int buzzerPin = 9;
const float pointPause = 500;
const float linePause = pointPause * 3;
const float binaryThreshold =0.7;
const unsigned int TONE = 127;
int buttonState = LOW;
int buttonExtraState = LOW;
bool buttonPressed = false;
bool buttonExtraPressed = false;
float binaryNumber[BINARY_SIZE];
float binaryToInt =0;
float normalizedSensor[BINARY_SIZE];
int mode = 0;
float modeOutput[2] = {0,0};
char modeOutput2 = ' ';

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  pinMode(buzzerPin, OUTPUT);
  // Print a message to the LCD.
  pinMode(buttonPin, INPUT);
  pinMode(buttonExtraPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  
  
  // print the number of seconds since reset:
  
  buttonState = digitalRead(buttonPin);
  buttonExtraState = digitalRead(buttonExtraPin);
  int sensorValue[BINARY_SIZE];
  sensorValue[0]= analogRead(A0);
  sensorValue[1]= analogRead(A1);
  sensorValue[2]= analogRead(A2);
  sensorValue[3]= analogRead(A3);
  sensorValue[4]= analogRead(A4);
  for(int i=0 ; i< BINARY_SIZE; i++){
    normalizedSensor[i] = ((float)(sensorValue[i])/4)/255;
    if(normalizedSensor[i]>binaryThreshold){
      binaryNumber[i]=1;
    }
    else{
     binaryNumber[i]=0;
    }
  }
  binaryToInt = getBinary(binaryNumber);
  if (buttonState == HIGH) {
    if(buttonPressed==false){
      buttonPressed=true;
    }
  }
  else{
    if(buttonPressed){
      buttonPressed=false;
      mode++;
      if(mode>=3){
        mode=0;
      }   
    }
  }
  lcd.setCursor(0, 0);
  lcd.print("modo: ");
  switch(mode){
    case 0:
    lcd.print("ID   ");
      modeId();
      break;
    case 1:
    lcd.print("Suma ");
      modeAdd();
      break;
    case 2:
    lcd.print("Morse");
      modeOutput[1]=0;
      modeMorse();
      break;
  }
  lcd.setCursor(0, 1);
  lcd.print("                          ");
  lcd.setCursor(0, 1);
  lcd.print("Result: ");
  if(mode!=2){
    lcd.print(modeOutput[mode]);
  }
  else{
    lcd.print(modeOutput2);
  }
  
  Serial.print("mode: ");
  Serial.print(mode);
  Serial.println(" ");
  delay(1);
}


void modeId(){
  if (buttonExtraState == HIGH) {
    if(buttonExtraPressed==false){
      buttonExtraPressed=true;
    }
  }
  else{    
    if(buttonExtraPressed){
      buttonExtraPressed=false;
      modeOutput[0] = binaryToInt;
    }
  }
  Serial.print(" Output: ");
  Serial.print(modeOutput[0]);
  Serial.print(" ");
}

void modeAdd(){
  if (buttonExtraState == HIGH) {
    if(buttonExtraPressed==false){
      buttonExtraPressed=true;
    }
  }
  else{
    if(buttonExtraPressed){
      buttonExtraPressed=false;
      modeOutput[1] += binaryToInt;
    }
  }
  Serial.print(" Output: ");
  Serial.print(modeOutput[1]);
  Serial.print(" ");
}

float getBinary(float binary[]) {
  float number=0;
  for(int i=0;i<BINARY_SIZE;i++){
    /*Serial.print(binary[i] * pow(2,i));
    Serial.print(" + ");*/
    number+= binary[i] * pow(2,i);   
  }
  Serial.print(" = ");
  Serial.print(number);
  return number;
}

void point(){
  tone(buzzerPin, TONE);
  delay(pointPause);
  noTone(buzzerPin);
  delay(pointPause);
}

void line(){
  tone(buzzerPin, TONE);
  delay(linePause);
  noTone(buzzerPin);
  delay(pointPause);
}


void modeMorse(){
  if (buttonExtraState == HIGH) {
    if(buttonExtraPressed==false){
      buttonExtraPressed=true;
    }
  }
  else{
    if(buttonExtraPressed){
      buttonExtraPressed=false;
      int aux = binaryToInt;
      if(binaryToInt>3){
        aux++;
      }
      switch(aux){
        case 1://A *-
        point(); line();
        modeOutput2 = 'A';
        break;
        case 2://B -***
        line(); point(); point(); point();
        modeOutput2 = 'B';
        break;
        case 3://C -*-*
        line(); point(); line(); point();
        modeOutput2 = 'C';
        break;
        case 4://D -**
        line(); point(); point();
        modeOutput2 = 'D';
        break;
        case 5://E *
        point();
        modeOutput2 = 'E';
        break;
        case 6://F **-*
        point(); point(); line(); point();
		modeOutput2 = 'F';
        break;
        case 7://G --*
        line(); line(); point();
        modeOutput2 = 'G';
        break;
        case 8://H ****
        point(); point(); point(); point();
        modeOutput2 = 'H';
        break;
        case 9://I **
        point(); point();
        modeOutput2 = 'I';
        break;
        case 10://J *---
        point(); line(); line(); line();
        modeOutput2 = 'J';
        break;
        case 11://K -*-
        line(); point(); line(); 
        modeOutput2 = 'K';
        break;
        case 12://L *-**
        point(); line(); point(); point();
        modeOutput2 = 'L';
        break;
        case 13://M -- 
        line(); line();
        modeOutput2 = 'M';
        break;
        case 14://N -*
        line(); point();
        modeOutput2 = 'N';
        break;
        case 15://O ---
        line(); line(); line();
        modeOutput2 = 'O';
        break;
        case 16://P *--*
        point(); line(); line(); point();
        modeOutput2 = 'P';
        break;
        case 17://Q --*-
        line(); line(); point(); line();
        modeOutput2 = 'Q';
        break;
        case 18://R *-*
        point(); line(); point();
        modeOutput2 = 'R';
        break;
        case 19://S ***
        point(); point(); point();
        modeOutput2 = 'S';
        break;
        case 20://T -
        line();
        modeOutput2 = 'T';
        break;
        case 21://U **-
        point(); point(); line();
        modeOutput2 = 'U';
        break;
        case 22://V ***-
        point(); point(); point(); line();
        modeOutput2 = 'V';
        break;
        case 23://W *--
        point(); line(); line();
        modeOutput2 = 'W';
        break;
        case 24://X -**-
        line(); point(); point(); line();
        modeOutput2 = 'X';
        break;
        case 25://Y -*--
        line(); point(); line(); line();
        modeOutput2 = 'Y';
        break;
        case 26://Z --**
        line(); line(); point(); point();
        modeOutput2 = 'Z';
        break;
      }
  	}
  }
  Serial.print(" Output: ");
  Serial.print(modeOutput2);
  Serial.print(" ");
}

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 *