banner

Ir para o Forum

ESP32 com RFID: Controle de Acesso


Que tal um método de identificação automática através de sinais de rádio que pode lhe trazer segurança e controlar o acesso de pessoas a um determinado local? Isso é possível através da identificação por radiofrequência ou RFID, do inglês Radio Frequency Identification. E é a montagem desse sistema que vou lhe ensinar hoje.
Esse sistema funciona com um cartão ou também pode ser um chaveiro que possui um chip em seu interior. Por meio da identificação dos dados deste cartão ou chip é que é possível fazer o controle de acesso, método bastante utilizado em registros de pontos de funcionários, transporte público, bibliotecas, entre outros.
Nosso objetivo, portanto, é criar um programa, no qual poderemos tanto fazer a leitura de um cartão (ou tag) RFID quanto gravar os dados nele. Utilizamos um WiFi NodeMCU-32S e um módulo RFID-RC522.
Importante destacar que podemos armazenar e recuperar dados nestes chips ou cartões remotamente através de dispositivos, já que estes chegam a ter 1k de memória.


Funcionamento

Um sistema RFID é composto basicamente de um transceptor com decodificador e uma antena e um transponder. E como funciona? Esses cartões têm uma bobina dentro deles. Quando você aproxima-os do leitor, eles emitem um sinal de radiofrequência através das antenas conectadas ao leitor. O tag energizado, que é o cartão, modula as informações gravadas em sua memória e envia esses dados ao leitor. Esse cartão, então, entra no campo de leitura e recebe energia do leitor para executar as operações. O leitor do RFID recebe as informações enviadas pelo tag, decodifica e envia os dados para aplicação do servidor.

Memória

Como mencionado, temos 1k de memória dentro deste tipo de chip. E, a memória EEPROM está organizada da seguinte maneira: são 16 setores de 4 blocos. Cada bloco contém 16 bytes. Lembrando que, dentro do código fonte, você só faz referência ao número do bloco.

Circuito

Nesta imagem nós temos um chaveiro que possui um chip de RFID e nós temos o tradicional cartão, além da nossa montagem. E como que funciona esse circuito? Bom, na internet, você vai encontrar o módulo RFID-RC522 sendo muito utilizado com o Arduino, mas o problema é que esse Arduino, seja, Mega, Nano, independente do modelo, ele não tem comunicação, como rede WiFi, Ethernet, entre outras. Portanto, nós usamos aqui o ESP32. Ele já conta com Bluetooth, RF, ou seja, fácil comunicação. Destaco aqui, então, que quase tudo que funciona com o Arduino também funciona com o ESP32.
Voltando ao circuito, quando, durante a análise de um cartão ou chip, acender o led verde, isso quer dizer que a identificação foi feita e o acesso é liberado. Quando acender o led vermelho quer dizer que o dado não foi autenticado.


WiFi NodeMCU-32S ESP-WROOM-32


RFID-RC522

Aqui temos imagens do cartão e do chaveiro, bem como a antena do RFID. Um detalhe importante é que a Interface dele é SPI.


Montagem

Na nossa montagem nós temos o ESP32 alimentado pela USB e também ligado na serial da IDE Arduino, dois leds para indicar se a leitura foi bem sucedida ou não, e o leitor de RFID, o RC522. Temos o chaveiro com chip e o cartão.
Colocando o chaveiro sobre o leitor aparece a opção de 0 para leitura de dados e, 1, para gravação destes dados. Fazemos um exemplo que mostra que, após a leitura do chip ou cartão, se acender o led verde é que o leitor reconheceu o número. Em caso de acender o led vermelho, quer dizer que ocorreu algum tipo de erro e não foi efetuada a autenticação.
No exemplo, ainda mostro como gravar dados no tag, o que será explicado mais abaixo.

Bibliotecas

Adicione a seguinte biblioteca “MFRC522”.
Basta acessar “Sketch >> Incluir Bibliotecas >> Gerenciar Bibliotecas...”

Código fonte

Nosso programa funcionará da seguinte maneira: após iniciar, o programa ficará esperando que um cartão ou tag seja identificado. Após isso, um menu surgirá para que o usuário escolha entre fazer uma leitura ou gravar algo. Em seguida a operação será realizada.

Setup

Nesta parte tratamos da inclusão das bibliotecas e definimos os tamanhos do buffer e dos dados do bloco. Criamos objetos e inicializamos os pinos, bem como a serial, a comunicação SPI, os leds e o serviço da antena. Já começo a incluir também mensagens no serial monitor.

#include <mfrc522.h> //biblioteca responsável pela comunicação com o módulo RFID-RC522
#include <SPI.h> //biblioteca para comunicação do barramento SPI

#define SS_PIN    21
#define RST_PIN   22

#define SIZE_BUFFER     18
#define MAX_SIZE_BLOCK  16

#define pinVerde     12
#define pinVermelho  32

//esse objeto 'chave' é utilizado para autenticação
MFRC522::MIFARE_Key key;
//código de status de retorno da autenticação
MFRC522::StatusCode status;

// Definicoes pino modulo RC522
MFRC522 mfrc522(SS_PIN, RST_PIN); 

void setup() {
  // Inicia a serial
  Serial.begin(9600);
  SPI.begin(); // Init SPI bus

  pinMode(pinVerde, OUTPUT);
  pinMode(pinVermelho, OUTPUT);
  
  // Inicia MFRC522
  mfrc522.PCD_Init(); 
  // Mensagens iniciais no serial monitor
  Serial.println("Aproxime o seu cartao do leitor...");
  Serial.println();

}

Loop

No Loop nós aguardamos a aproximação do cartão e selecionamos o mesmo. No menu, ofertamos as opções de ler ou gravar dados. Instruímos nesta parte quando o dispositivo deve deixar o estado ACTIVE para o estado de PARADA. Temos que utilizar tal método para possibilitar novas leituras.

void loop() 
{
   // Aguarda a aproximacao do cartao
  if ( ! mfrc522.PICC_IsNewCardPresent()) 
  {
    return;
  }
  // Seleciona um dos cartoes
  if ( ! mfrc522.PICC_ReadCardSerial()) 
  {
    return;
  }

  //chama o menu e recupera a opção desejada
  int opcao = menu();
  
  if(opcao == 0) 
    leituraDados();
  else if(opcao == 1) 
    gravarDados();
  else {
    Serial.println(F("Opção Incorreta!"));
    return;
  }
  // instrui o PICC quando no estado ACTIVE a ir para um estado de "parada"
  mfrc522.PICC_HaltA(); 
  // "stop" a encriptação do PCD, deve ser chamado após a comunicação com autenticação, caso contrário novas comunicações não poderão ser iniciadas
  mfrc522.PCD_StopCrypto1();  
}

Leitura

Nesta parte tratamos a leitura dos dados do cartão/tag. Temos que preparar todas as chaves, tratar do tamanho do buffer e fazer a autenticação do bloco que vamos operar. Por fim, definimos a impressão dos dados lidos.

//faz a leitura dos dados do cartão/tag
void leituraDados()
{
  //imprime os detalhes tecnicos do cartão/tag
  mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); 

  //Prepara a chave - todas as chaves estão configuradas para FFFFFFFFFFFFh (Padrão de fábrica).
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

  //buffer para colocar os dados ligos
  byte buffer[SIZE_BUFFER] = {0};

  //bloco que faremos a operação
  byte bloco = 1;
  byte tamanho = SIZE_BUFFER;


  //faz a autenticação do bloco que vamos operar
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloco, &key, &(mfrc522.uid)); //line 834 of MFRC522.cpp file
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Authentication failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    digitalWrite(pinVermelho, HIGH);
    delay(1000);
    digitalWrite(pinVermelho, LOW);
    return;
  }

  //faz a leitura dos dados do bloco
  status = mfrc522.MIFARE_Read(bloco, buffer, &tamanho);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Reading failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    digitalWrite(pinVermelho, HIGH);
    delay(1000);
    digitalWrite(pinVermelho, LOW);
    return;
  }
  else{
      digitalWrite(pinVerde, HIGH);
      delay(1000);
      digitalWrite(pinVerde, LOW);
  }

  Serial.print(F("\nDados bloco ["));
  Serial.print(bloco);Serial.print(F("]: "));

  //imprime os dados lidos
  for (uint8_t i = 0; i < MAX_SIZE_BLOCK; i++)
  {
      Serial.write(buffer[i]);
  }
  Serial.println(" ");
}

Gravação

Para gravar dados no cartão/tag temos que seguir alguns passos. A partir do momento que escolhermos a opção de gravação, temos 30 segundos para fazer a entrada de dados via serial. Inserirmos os dados a serem gravados com o caractere “#” e preparamos a chave. Será preciso zerar o buffer e fazer a gravação no bloco 1, já que no bloco 0 temos gravado o número do cartão, que já vem de fábrica. Portanto, não mexemos no bloco 0.
Tratamos na sequência do tamanho de dados e inserimos um comando para autenticação e habilitação de uma comunicação segura. Também colocamos mensagens de erro iguais a da parte da leitura para exibição em caso de dado não autenticado. Gravamos os dados no bloco devido.
//faz a gravação dos dados no cartão/tag
void gravarDados()
{
  //imprime os detalhes tecnicos do cartão/tag
  mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); 
  // aguarda 30 segundos para entrada de dados via Serial
  Serial.setTimeout(30000L) ;     
  Serial.println(F("Insira os dados a serem gravados com o caractere '#' ao final\n[máximo de 16 caracteres]:"));

  //Prepara a chave - todas as chaves estão configuradas para FFFFFFFFFFFFh (Padrão de fábrica).
  for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

  //buffer para armazenamento dos dados que iremos gravar
  byte buffer[MAX_SIZE_BLOCK] = "";
  byte bloco; //bloco que desejamos realizar a operação
  byte tamanhoDados; //tamanho dos dados que vamos operar (em bytes)

  //recupera no buffer os dados que o usuário inserir pela serial
  //serão todos os dados anteriores ao caractere '#'
  tamanhoDados = Serial.readBytesUntil('#', (char*)buffer, MAX_SIZE_BLOCK);
  //espaços que sobrarem do buffer são preenchidos com espaço em branco
  for(byte i=tamanhoDados; i < MAX_SIZE_BLOCK; i++)
  {
    buffer[i] = ' ';
  }
 
  bloco = 1; //bloco definido para operação
  String str = (char*)buffer; //transforma os dados em string para imprimir
  Serial.println(str);

  //Authenticate é um comando para autenticação para habilitar uma comuinicação segura
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A,
                                    bloco, &key, &(mfrc522.uid));

  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    digitalWrite(pinVermelho, HIGH);
    delay(1000);
    digitalWrite(pinVermelho, LOW);
    return;
  }
  //else Serial.println(F("PCD_Authenticate() success: "));
 
  //Grava no bloco
  status = mfrc522.MIFARE_Write(bloco, buffer, MAX_SIZE_BLOCK);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Write() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    digitalWrite(pinVermelho, HIGH);
    delay(1000);
    digitalWrite(pinVermelho, LOW);
    return;
  }
  else{
    Serial.println(F("MIFARE_Write() success: "));
    digitalWrite(pinVerde, HIGH);
    delay(1000);
    digitalWrite(pinVerde, LOW);
  }
 
}

Menu

Aqui programamos o Menu. O monitor expõe todas as opções e aguarda o envio de dados. Quando uma opção é selecionada, ele retira 48 do valor lido, que é o 0 da tabela Ascii. Essa tabela é antiga e não utilizada no PC, mas no Arduino e em microcontroladores vocês vão precisar lidar com ela. Se você não conhece, dá uma pesquisada na internet para saber do que se trata.

//menu para escolha da operação
int menu()
{
  Serial.println(F("\nEscolha uma opção:"));
  Serial.println(F("0 - Leitura de Dados"));
  Serial.println(F("1 - Gravação de Dados\n"));

  //fica aguardando enquanto o usuário nao enviar algum dado
  while(!Serial.available()){};

  //recupera a opção escolhida
  int op = (int)Serial.read();
  //remove os proximos dados (como o 'enter ou \n' por exemplo) que vão por acidente
  while(Serial.available()) {
    if(Serial.read() == '\n') break; 
    Serial.read();
  }
  return (op-48);//do valor lido, subtraimos o 48 que é o ZERO da tabela ascii
}



Faça download dos arquivos:







6 comentários:

  1. Olá! No meu ESP não roda e dá esta mensagem na IDE do Arduino: AVISO: a biblioteca MFRC522 alega rodar em arquitetura(s) [avr e pode ser incompatível com sua placa atual, que roda em arquitetura(s) STM32F1.

    ResponderExcluir
    Respostas
    1. Essa é apenas uma mensagem de Warning!
      Para desabilitá-la, edite o arquivo "library.properties" alterando a linha:

      architectures=avr,STM32F1,teensy,esp8266

      para

      architectures=*

      Excluir
  2. Alguém tentou em vez de gravar os dados digitados gravar uma string? estou obtendo dificuldade em fazer a conversão, poderiam me ajudar por favor?

    ResponderExcluir
    Respostas
    1. Olá, seguindo o código que temos no exemplo temos que transformar a String em um array de char, para isso utilize os seguintes comandos:

      String str = "teste";
      byte buff[MAX_SIZE_BLOCK];
      str.toCharArray((char*)buff, str.length()+1);

      a partir daí o buff já contém o conteúdo da String.

      Excluir
    2. Fiz tudo certo mais não deu certo !

      Excluir

Tecnologia do Blogger.