CONTROL DE ACCESOS RFID

Hola:

Desde hace ya un tiempo, estoy enfrascado en un proyecto de control de accesos por RFID. Utilizo un arduino Mega, una shield Ethernet SD, un modulo RFID-RC522, un reloj DS1307 y un display LCD con I2c.

El funcionamiento es el siguiente: Al acercar una tarjeta valida, ademas de prender un led verde, activa un rele y envia el numero de serie de la tarjeta junto con la fecha y hora y algunos datos mas (ip, num. de lector, etc.) a una tabla SQL alojada en un servidor de una pequeña intranet. En el caso de una tarjeta no valida, prende un led rojo y registra los datos en la tabla sin activar el rele. A su vez, todas las operaciones quedan registradas en la SD, generandose un fichero diario a modo de log. Tambien presenta la informacion consecuente en un display LCD 2x16.

La IP se le asigna por DHCP y a traves de comandos serie se le asigna el num. de lector y algun dato mas de configuracion que queda almacenado en la eeprom del arduino.

Tambien existe una tarjeta MASTER que pone el lector en modo PROGRAMA y con la que se dan de alta y de baja las tarjetas validas simplemente acercandolas al lector con lo que quedan almacenadas tambien en la eeprom.

Hasta aqui todo esta funcionando correctamente, pero ahora necesito añadir varios lectores mas al sistema y quisiera buscar un metodo para no tener que gestionar las tarjetas de forma individual, sino en todos los lectores a la vez.

Una posible solucion seria que la lista de tarjetas validas este almacenada en un fichero de texto en la SD y que al acercar la tarjeta al lector, la busque en dicho fichero en vez de en la eeprom como hace actualmente. Este fichero podria estar alojado en el servidor y que periodicamente, a traves de FTP, se telecargue en los diiferentes lectores, con lo cual, siempre tendrian todos la version mas actualizada.

Ahora vienen mis dudas:

1.- ¿Alguna otra solucion mas sencilla…?

1.- No se si es posible buscar una cadena con el numero de tarjeta (hex) dentro de un fichero de texto en la SD, ni siquiera cuanto tiempo tardaria en encontrarla entre por ejemplo, 100 tarjetas como maximo. No he encontrado nada parecido por el foro.

2.- Mientras tanto, estoy trabajando ahora en la parte de la telecarga a los lectores y adaptando el codigo siguiente que encontre en el foro:

/*
 */
#include <SdFat.h>
#include <SdFatUtil.h>
#include <SPI.h>
#include <Ethernet.h>

// comment out next line to write to SD from FTP server
//#define FTPWRITE

#define error(s) error_P(PSTR(s))

// MAC de la ethernet shield. This must be unique
byte mac[] = {
  0x90, 0xA2, 0xDA, 0x0D, 0x4E, 0xD7 }; 

// FTP Server name
IPAddress server ( 192, 168, 1, 2 ); // Server IP Red local

// Network settings
IPAddress ip( 192, 168, 1, 150 );   // Set the static IP address to use if the DHCP fails to assign
IPAddress gateway( 192, 168, 1, 255 );
IPAddress subnet( 255, 255, 255, 0 );

// SDCARD STUFF 
Sd2Card card;
SdVolume volume;
SdFile root;
SdFat sd;
SdBaseFile file;

EthernetClient client;
EthernetClient dclient;

char outBuf[128];
char outCount;

// change fileName to your file (8.3 format!)
char DataFile[13] = "test.txt";

const uint8_t chipSelect = 4;

ArduinoOutStream cout(Serial);

//...............errors..............
void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

void setup()
{
  Serial.begin(9600);

  pinMode(10,OUTPUT);
  digitalWrite(10,HIGH);

  if(sd.begin(4) == 0)
  {
    Serial.println("SD init fail");          
  }

  Ethernet.begin(mac, ip, gateway, gateway, subnet); 
  digitalWrite(10,HIGH);
  delay(2000);
  Serial.println("Ready. Press f");
}

void loop()
{
  byte inChar;

  inChar = Serial.read();

  if(inChar == 'f')
  {
    if(doFTP()) Serial.println("FTP OK");
    else Serial.println("FTP FAIL");
  }
}

SdFile fh;
byte doFTP()
{
  Serial.println("SD opened");

  if (client.connect(server,21)) {
    Serial.println("Command connected");
  } 
  else {
    fh.close();
    Serial.println("Command connection failed");
    return 0;
  }

  if(!eRcv()) return 0;
  client.write("USER arduftp\r\n");

  if(!eRcv()) return 0;
  client.write("PASS arduftp\r\n");

  if(!eRcv()) return 0;
  client.write("SYST\r\n");

  if(!eRcv()) return 0;
  client.write("PASV\r\n");

  if(!eRcv()) return 0;
  char *tStr = strtok(outBuf,"(,");
  int array_pasv[6];
  for ( int i = 0; i < 6; i++) {
    tStr = strtok(NULL,"(,");
    array_pasv[i] = atoi(tStr);
    if(tStr == NULL)
    {
      Serial.println("Bad PASV Answer");    
    }
  }

  unsigned int hiPort,loPort;

  hiPort = array_pasv[4] << 8;
  loPort = array_pasv[5] & 255;

  Serial.print("Data port: ");
  hiPort = hiPort | loPort;
  Serial.println(hiPort);

  if (dclient.connect(server,hiPort)) {
    Serial.println("Data connected");
  } 
  else {
    Serial.println("Data connection failed");
    client.stop();
    fh.close();
    return 0;
  }

  client.write("RETR ");
  client.println(DataFile);

  if(!eRcv())
  {
    dclient.stop();
    return 0;
  }

  while(dclient.connected())
  {
    while(dclient.available())
    {
      char c = dclient.read();
      fh.write(c);      
      Serial.write(c); 
    }
  }

  dclient.stop();
  Serial.println("Data disconnected");

  if(!eRcv()) return 0;
  client.write("QUIT\r\n");

  if(!eRcv()) return 0;
  client.stop();
  Serial.println("Command disconnected");

  fh.close();
  Serial.println("SD closed");
  return 1;
}


byte eRcv()
{
  byte respCode;
  byte thisByte;

  while(!client.available()) delay(1);

  respCode = client.peek();
  outCount = 0;

  while(client.available())
  {  
    thisByte = client.read();    
    Serial.write(thisByte);

    if(outCount < 127)
    {
      outBuf[outCount] = thisByte;
      outCount++;      
      outBuf[outCount] = 0;
    }
  }

  if(respCode >= '4')
  {
    efail();
    return 0;  
  }

  return 1;
}

void efail()
{
  byte thisByte = 0;
  client.write("QUIT\r\n");

  while(!client.available()) delay(1);

  while(client.available())
  {  
    thisByte = client.read();    
    Serial.write(thisByte);
  }

  client.stop();
  Serial.println("Command disconnected");
  fh.close();
  Serial.println("SD closed");
}

ocurre lo siguiente:

ReaReady. Press f
SD opened
Command connected
220 ProFTPD 1.3.4a Server (Debian) [::ffff:192.168.1.2]
331 Password required for arduftp
230 User arduftp logged in
215 UNIX Type: L8
227 Entering Passive Mode (192,168,1,2,185,200).
Data port: 47560
Data connected
150 Opening ASCII mode data connection for test.txt
Data disconnected
226 Transfer complete
221 Goodbye.
Command disconnected
SD closed
FTP OK

pero no descarga nada en la SD y no encuentro el problema. Alguien me puede echar una mano.

Gracias…

¿Y no sería más sencillo que, ya que tienes implementado un servidor MySQL, cargar los códigos de las tarjetas y verificarlas en ese servidor? Te quitas de enmedio las SD y el tener que andar replicando el fichero de códigos de tarjetas por todos los arduinos, amén de que te será mucho más sencillo gestionar las altas y bajas ahí.

Esa fue mi primera idea, pero de esa forma perderia la funcionalidad en caso de fallo en la comunicacion, y eso no me interesa. Necesito que puedan trabajar en modo local, al menos con la ultima version del fichero. Por eso replico tambien la tabla logger en la SD.

Hola Carl0701,

estoy muy interesado en este proyecto, ya que puse un post hace poco pidiendo ayuda sobre un proyecto con las mismas caracteristicas, estoy esperando que me llegue material de china para ponerme manos a la obra, pero mientras tanto me gustaría que me especificases el material que estás usando y como has estructurado el proyecto a parte de lo que ya has mencionado. He realizado pequeños proyectos de arduino pero nunca de esta complejidad, estoy un poco perdido.

Seguiré este post e intentaré ayudar en lo que sea posible.

PD: Por que no conectas todas las placas a una EPROM externa? Asi todas consultarían los codigos de las tarjetas de una misma fuente.

Bueno, el material utilizado es el descrito en el primer post, ademas de una proto shield que alberga los leds y el piezoelectrico y tambien una pequeña placa para el rele que actua sobre una cerradura electrica.

Tome como base el proyecto de Instructables http://www.instructables.com/id/Arduino-RC522-RFID-Door-Unlock/?lang=es al que fui añadiendo funcionalidades poco a poco durante varios meses de trabajo.

Al final quedo un conjunto bastante fiable que funciona a la perfeccion.

Ahora necesito dotar de lectores varias estancias distanciadas entre si y es por lo que he decidido montar la intranet.

No puedo utilizar una eeprom externa por la distancia entre los diferentes lectores. Ademas, utilizando un servidor, puedo monitorizar en tiempo real los accesos e incluso activar los reles individualmente y a distancia en caso necesario.

Ahora necesito encontrar la manera de buscar el numero de serie de la tarjeta leida en un fichero csv alojado en la SD. La parte de la telecarga a los diferentes equipos ya la tengo operativa.

Por favor usen los tags. Veo enlaces sin tags ( Carl0701 )

¿Qué tipo de dato representaría una tarjeta? Lo digo, porque posiblemente lo puedas almacenar como un unsigned long, con lo que las comparaciones se simplificarían bastante.

Yo esto lo ice en modo local de la siguiente manera:

En la se dentro de una carpeta cada vez que des de alta una targeta creas un fichero con el numero se serie, y dentro vacío o puedes guardar información que sea, el funcionamiento se simplifica por que ya hay una función para buscar por nombre de fichero si no existe le denegas el acceso

Por otro lado recordaros que estamos trabajando con ordenadores en miniatura puedes hacer el funcionamiento en local y cada día se sincronice con el servidor o dos veces por día o cada 5 minutos si te da la gana, o cada x veces que se entra o se sale...

Noter:

Recupero el num. de serie de la tarjeta en la string “tag_id”.

String tag_id = "";

// Look for new cards (in case you wonder what PICC means: proximity integrated circuit card)
  if ( ! mfrc522.PICC_IsNewCardPresent()) {//if PICC_IsNewCardPresent returns 1, a new card has been found and we continue
    return 0;//if it did not find a new card is returns a '0' and we return to the start of the loop
  }

  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) {//if PICC_ReadCardSerial returns 1, the "uid" struct (see MFRC522.h lines 238-45)) contains the ID of the read card.
    return 0;//if it returns a '0' something went wrong and we return to the start of the loop
  }

  // There are Mifare PICCs which have 4 byte or 7 byte UID care if you use 7 byte PICC
  for (int i = 0; i < mfrc522.uid.size; i++) {  // for size of uid.size write uid.uidByte to readCard
    readCard[i] = mfrc522.uid.uidByte[i];
  }

tag_id = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    String uid_a = String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
    String uid_b = String(mfrc522.uid.uidByte[i], HEX);
    tag_id = tag_id + uid_a + uid_b;
  }

GO_zalo, gracias por el interes. Debe haber otra forma de hacer esto, buscando por ejemplo una cadena de texto dentro de un fichero CSV, pero no se como hacerlo.

Si, pero dime que inconveniente tiene mi sistema?

Piensa que tu hablas de una base de datos y el fat32 es justamente eso y ya esta implementado.

Con mi sistema solo tienes que abrir un archivo, con el tuyo abrprño e ir leyendo si hay 100 usuarios mira pero al hay 1k o 10k pues imagínate lo que puedes tardar

No se trata de inconveniente, tu idea es perfectamente valida y sencilla de aplicar, pero imagina el siguiente supuesto:

Tengo 20 lectores conectados a la red. En la SD de cada uno de ellos hay un total de 50 ficheros, uno por cada tarjeta valida y necesito dar de baja una tarjeta (en todos ellos a la vez, se entiende).

Con mi sistema seria tan sencillo como editar el fichero CSV en el servidor, borrar la linea de dicha tarjeta y esperar que a la hora programada los lectores se conecten por FTP al servidor y se bajen el fichero actualizado.

Con tu sistema no sabria como hacer para borrar remotamente el fichero en los 20 lectores a la vez.

No se si me he explicado bien...

Hola. Veo que las tarjetas pueden ser con id de cuatro o de siete bytes. ¿De cuáles son las tuyas?. Lo digo, porque si son cuatro bytes puedes tratar cada id como un unsigned long, con lo que podrías meterlos en un fichero binario y las comparaciones son tan sencillas como "if (a==b)".

Si, además de las tarjetas, tienes pensado meter más datos (nombre, apellidos... )pudiera resultar de utilidad la librería que estoy desarrollando. Aún está en beta, pero creo que ya va funcionando. Lo único es que trabajará con su propio tipo de archivo creado por la propia librería (no vas a poder editarlo a mano). La ventaja es que mantiene los registros ordenados según una función de comparación (que debe definir el usuario), con lo que las búsquedas entre muchos registros a través del índice deberían ser bastante ágiles (esperemos :grin: ). Si crees que pudiera servirte, pásate por el hilo y la intentamos adaptar a tus necesidades. No obstante, está pensada más para tipos de datos más complejos (en tu caso sólo registras un dato, que además podría ser el propio índice).

Gracias Noter. Las tarjetas con las que trabajo son de 4 bytes y en una primera version y para empezar a funcionar habia pensado en un solo dato pero mas adelante podria añadir, ademas de nombre y apellidos, una fecha de caducidad a partir de la cual, la tarjeta dejaria de funcionar sin necesidad de eliminarla del fichero, o incluso un rango de horas (h_inicio y h_final) en las que la tarjeta actuaría sobre el rele de apertura, pero todo eso vendra despues...

Echare un vistazo al hilo y ya te cuento...

Saludos

Bueno, para empezar a andar he cogido la solución dada en el post Comparar un variable con un código guardado en la tarjeta SD y lo he implementado en mi codigo. De momento, todo funciona perfectamente y los tiempos hasta ahora son razonables. Ahora toca hacer pruebas aumentando el numero de tarjetas validas en el fichero veremos como se comporta.

Buenos dias muchachos, estoy realizando un proyecto similar al que mencionan aqui.
Se me presento la dificultad de que al intentar almacenar el tag id de la tarjeta en la base de datos mysql el registro siempre esta nulo. Sospecho de alguna incompatibilidad en el tipo de datos entre el string que usa arduino y el varchar que puse en la base de datos.
Alguna idea? Les dejo el codigo para algun consejo.
Gracias

String content= "";
        byte letter;
        for (byte i = 0; i < mfrc522.uid.size; i++) 
        {
           Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
           Serial.print(mfrc522.uid.uidByte[i], HEX);
           content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
           content.concat(String(mfrc522.uid.uidByte[i], HEX)); 
        }
        Serial.println();
        Serial.print("Message : ");
        content.toUpperCase();
        Serial.println("Conectando...");
          if (client.connect(server, 80)>0) 
            {  // Conexion con el servidor
                client.print("GET /afgym/data.php?valor="); // Enviamos los datos por GET
                client.print(content);
                client.println(" HTTP/1.0");
                client.println("User-Agent: Arduino 1.0");
                client.println();
                Serial.println("Conectado");
        } else {
                Serial.println("Fallo en la conexion");
        }
        client.stop();
        client.flush();

Evita el que tu petición get contenga espacios, ya que el servidor no los acepta muy bien. Me refiero a la línea content.concat.

Gracias noter! Le quite los espacios y ya puedo guardar en la BD!

Ya está, ya oficializaron la reapertura de un hilo que llevaba inactivo alrededor de un año. Francisco no vió o ignoró según su conveniencia el aviso en rojo que le pega el sistema avisando de que este hilo llevaba inactivo más de 120 días. Cierro el hilo, si Francisco necesita más ayuda que abra otro hilo.