Uma das aplicações mais comuns de Rfid no dia-a-dia é o controle de passagens (bilhetagem). Neste post vamos ver como isto pode ser feito com os cartões MIFARE e simular esta operação usando o Módulo Leitor RfId Mfrc522 ligado a um Arduino.
Material Necessário
Para a parte prática você precisará:
- Kit Módulo Leitor Rfid Mfrc522 Mifare
- Arduino Uno
- Cabos macho-fêmea para ligação do módulo ao Arduino
IMPORTANTE: Como vamos escrever no cartão e reconfigurá-lo, use somente um cartão vazio e que você possa descartar caso algo ocorra errado.
Conhecendo um pouco o cartão RFID Mifare
MIFARE é a marca da NXP (que já foi uma divisão da Philips) para uma série de integrados desenvolvidos para uso em cartões de proximidade ou smartcards sem contato.
O Mifare Classic (no qual vamos nos focar) é basicamente um dispositivo de memória no qual podem ser feitas leituras e escritas sem contato a curta distância, condicionadas a recursos de segurança. A memória disponível é dividida em setores, numerados a partir de zero. Cada setor pode ser protegido de uma forma independente dos demais. O setor, por sua vez, é dividido em blocos de 16 bytes. O último bloco de cada setor é usado para configurar a proteção do setor. O primeiro bloco do primeiro setor é o “bloco do fabricante” e (em teoria) não pode ser alterado. Ele contem a identificação única do cartão ou tag. O cartão e o tag que vem no kit possuem 1K de memória, o que corresponde a 16 setores cada um com 4 blocos.
O acesso a cada setor é controlado por uma configuração e duas chaves de 48 bits (chamadas A e B), armazenadas no último bloco do setor. A forma como a configuração é armazenada neste bloco é um pouco confusa. Temos três bits para cada bloco e, para maior segurança, estes bits são gravados duas vezes (o valor normal e o invertido):
A interpretação dos três bits é diferente entre os blocos de dados e o bloco de configuração:
Antes de qualquer operação de leitura ou escrita, é necessário fazer uma autenticação, usando uma das chaves. Uma pegadinha é que uma chave que possa ser lida não pode ser usada para autenticação. A chave A nunca pode ser lida, mas a leitura da chave B pode ser permitida (três primeiras linhas da primeira tabela acima), neste caso a chave B passa a ser um dado ao invés de chave. Em todo acesso o cartão verifica se o bloco de configuração é valido, se não for todo o setor é bloqueado de forma definitiva (uma forma fácil de perder um setor).
Os cartões/tags novos vem com o acesso configurado para permitir ler e escrever nos blocos de dados com a chave A ou B. Porém, o bloco de configuração só pode ser lido ou escrito usando a chave A e a chave B pode ser lida. Portanto, apenas a chave A pode ser usada para autenticação; tipicamente vem de fábrica como FFFFFFFFFFFF.
Usando um cartão RFID Mifare para um sistema de passagens
Na aplicação de bilhetagem (ou de “carteira eletrônica”), o cartão (ou tag) é carregado com uma certa quantidade de “créditos” que vão sendo consumidos em troca de serviços ou bens. Olhando novamente a tabela de controle de blocos de dados, repare que existem dois usos possíveis: dados e valor. Você poderia implementar a bilhetagem usando o modo dados, mas o modo valor existe exatamente para que as operações de carga e consumo sejam feitas de forma mais segura.
No uso como dados o cartão não se preocupa com o que você escreve nos blocos, para uso como valor o conteúdo do bloco precisa estar em um formato específico:
Na figura acima value é o valor (saldo); para maior segurança ele é repetido três vezes no bloco (uma delas negado). O valor é um número com sinal de quatro bytes (no formato complemento de 2); ou seja de -2.147.483.648 a 2.147.483.647. adr é o endereço de um outro bloco do setor para “gerenciamento de backup” (segundo o datasheet, não encontrei explicações ou exemplo de uso).
Uma vez configurado um bloco para uso como valor, o seu acesso fica condicionado a possuir o formato correto e existem quatro operações básicas que podem ser feitas com ele:
- incremento, onde um “crédito” é somado ao valor; o resultado fica em um registrador temporário interno ao cartão
- decremento, onde um “débito” é subtraído do valor; o resultado fica em um registrador temporário interno ao cartão
- restauração, onde o valor atual é copiado para o registrador temporário
- transferência, onde o valor no registrador temporário é gravado no bloco
Portanto um carga é feita combinando um incremento com transferência e um consumo é feito combinando um decremento com transferência.
Olhando novamente a tabela do controle de acesso, percebemos que no modo “001” qualquer uma das chaves pode ser usada para fazer um consumo, mas não há como fazer uma carga (isto é útil para cartões pré-carregados e descartáveis). No modo “110” qualquer chave pode ser usada para fazer um consumo mas apenas a B pode fazer uma carga (os pontos onde o cartão é usado conhecem apenas a chave A; a chave B fica somente nos poucos pontos de recarga).
Montagem do Sistema de Passagem com leitor RFID e Arduino
A conexão do módulo Rfid ao Arduino deve ser feita conforme a figura abaixo:
Código para o Sistema de Passagem com leitor RFID e Arduino
Para o nosso programa exemplo vamos usar a biblioteca MFRC522 que pode ser instalada diretamente da IDE:
Neste exemplo vamos usar o Serial Monitor da IDE para interagir com o programa. Para isto certifique-se que ele está configurado como abaixo:
O programa permite as seguintes operações, cada uma disparada por uma letra:
- (I)niciar o cartão: reconfigura o bloco x do setor y para o modo dados (usando a chave de fábrica ou a nossa chave B), grava um valor inicial de 20 e o reconfigura para o modo valor com as nossas chaves A e B
- (R)ecarga: acrescenta 20 “créditos” ao saldo no cartão
- (C)onsumo: tenta fazer uma “compra” com o custo de 3 “créditos”
- (S)aldo: apresenta o saldo no cartão
- (F)abrica: volta o setor para a configuração de fábrica
Abaixo o código:
#include <SPI.h> #include <MFRC522.h> // Acesso ao módulo Rfid const byte RST_PIN = 9; // pino de reset const byte SS_PIN = 10; // pino de seleção MFRC522 mfrc522(SS_PIN, RST_PIN); // Chaves typedef enum { FABRICA, NOSSA, OUTRA } CHAVE; MFRC522::MIFARE_Key chaveFabrica; MFRC522::MIFARE_Key chaveA; MFRC522::MIFARE_Key chaveB; CHAVE chave; // Vamos usar o setor #4, que contem os blocos 16 a 19 const byte SETOR = 4; const byte BOLOCO_VAL = 17; const byte BLOCO_CFG = 19; // Valores de recarga e consumo const int32_t VALOR_RECARGA = 10; const int32_t VALOR_CONSUMO = 3; // Iniciação void setup() { // Inicia a porta serial Serial.begin (115200); // Inicia o acesso ao módulo SPI.begin(); mfrc522.PCD_Init(); // Inicia as chaves for (byte i = 0; i < 6; i++) { chaveFabrica.keyByte[i] = 0xFF; } chaveA.keyByte[0] = 'D'; chaveA.keyByte[1] = 'Q'; chaveA.keyByte[2] = 'S'; chaveA.keyByte[3] = 'o'; chaveA.keyByte[4] = 'f'; chaveA.keyByte[5] = 't'; chaveB.keyByte[0] = '1'; chaveB.keyByte[1] = '2'; chaveB.keyByte[2] = '3'; chaveB.keyByte[3] = '4'; chaveB.keyByte[4] = '5'; chaveB.keyByte[5] = '6'; } // Laço principal void loop() { // Solicita uma opção Serial.print ("(I)nicia, (R)ecarrega, (C)onsome, (S)aldo ou (F)abrica?"); int c; while (Serial.read() != -1) { delay(10); } do { c = Serial.read(); c = toupper(c); } while (strchr("IRCSF", c) == NULL); char cmd[2] = ""; cmd[0] = (char) c; Serial.println (cmd); // Pede para aproximar um cartão if (aguardaCartao()) { // Trata a opção if (c == 'I') { inicia(); } else if (c == 'R') { recarrega(); } else if (c == 'C') { consome(); } else if (c == 'S') { saldo(); } else if (c == 'F') { fabrica(); } mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } Serial.println(); } // Inicia o cartão para uso void inicia() { MFRC522::StatusCode status; // Se autentica com a chave B status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, BLOCO_CFG, (chave == FABRICA)? &chaveFabrica : &chaveB, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na autenticacao!"); return; } // Grava o setor de valor com o formato correto if (!iniciaValor(BOLOCO_VAL)) { return; } // Reconfigura o setor para usar nossas chaves e as condições de acesso desejadas // 1o bloco 000 configuração padrão, pode acessar com chaves A ou B // 2o bloco 110 valor, leitura/decremento com chave A ou B, escrita/incremento só com B // 3o bloco 000 configuração padrão, pode acessar com chaves A ou B // 4o bloco 011 escrita só com B, chave A só lê bits de acesso byte cfgBuffer[16]; memset (cfgBuffer, 0, 16); memcpy (cfgBuffer, &chaveA, 6); memcpy (cfgBuffer+10, &chaveB, 6); mfrc522.MIFARE_SetAccessBits(&cfgBuffer[6], 0, 6, 0, 3); status = mfrc522.MIFARE_Write(BLOCO_CFG, cfgBuffer, 16); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro ao configurar!"); } Serial.println ("Cartao iniciado"); } // Recarrega o cartão, acrescentando VALOR_RECARGA "créditos" void recarrega() { MFRC522::StatusCode status; if (chave != NOSSA) { Serial.println ("Cartao nao iniciado!"); return; } // Se autentica com a chave B status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, BLOCO_CFG, &chaveB, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na autenticacao!"); return; } status = mfrc522.MIFARE_Increment(BOLOCO_VAL, VALOR_RECARGA); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro ao recarregar!"); return; } status = mfrc522.MIFARE_Transfer(BOLOCO_VAL); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na salvar novo saldo!"); return; } // Mostra o novo saldo saldo(); } // Consome VALOR_CONSUMO "créditos" void consome() { MFRC522::StatusCode status; if (chave != NOSSA) { Serial.println ("Cartao nao iniciado!"); return; } // Se autentica com a chave A status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCO_CFG, &chaveA, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na autenticacao!"); return; } // Vê se tem saldo suficente int32_t valor; status = mfrc522.MIFARE_GetValue(BOLOCO_VAL, &valor); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na leitura!"); return; } if (valor < VALOR_CONSUMO) { Serial.println ("Saldo insuficiente!"); return; } // Atualiza o saldo status = mfrc522.MIFARE_Decrement(BOLOCO_VAL, VALOR_CONSUMO); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro ao debitar!"); return; } status = mfrc522.MIFARE_Transfer(BOLOCO_VAL); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na salvar novo saldo!"); return; } // Mostra o novo saldo saldo(); } // Informa o saldo void saldo() { MFRC522::StatusCode status; if (chave != NOSSA) { Serial.println ("Cartao nao iniciado!"); return; } // Se autentica com a chave A status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCO_CFG, &chaveA, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na autenticacao!"); return; } // Le o Valor int32_t valor; status = mfrc522.MIFARE_GetValue(BOLOCO_VAL, &valor); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na leitura!"); return; } Serial.print ("Saldo: "); Serial.println (valor); } // Recoloca o cartão na condição de fábrica void fabrica() { MFRC522::StatusCode status; // Se autentica com a chave B status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, BLOCO_CFG, (chave == FABRICA)? &chaveFabrica : &chaveB, &(mfrc522.uid)); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro na autenticacao!"); return; } // Reconfigura o setor para a configuração padrão byte cfgBuffer[16]; memset (cfgBuffer, 0, 16); memcpy (cfgBuffer, &chaveFabrica, 6); memcpy (cfgBuffer+10, &chaveFabrica, 6); mfrc522.MIFARE_SetAccessBits(&cfgBuffer[6], 0, 0, 0, 1); status = mfrc522.MIFARE_Write(BLOCO_CFG, cfgBuffer, 16); if (status != MFRC522::STATUS_OK) { Serial.println ("Erro ao configurar!"); } Serial.println ("Cartao de volta a condicao de fabrica"); } // Aguarda apresentar um cartão ou digitar algo bool aguardaCartao() { Serial.println ("Aproxime um cartao (digite qualquer tecla para desistir)"); while (true) { if (Serial.read() != -1) { return false; } if (!mfrc522.PICC_IsNewCardPresent()) continue; if (mfrc522.PICC_ReadCardSerial()) break; } Serial.print ("ID: "); dump_buffer (mfrc522.uid.uidByte, mfrc522.uid.size); Serial.println(); Serial.print(F("Tipo: ")); MFRC522::PICC_Type tipo = mfrc522.PICC_GetType(mfrc522.uid.sak); Serial.println(mfrc522.PICC_GetTypeName(tipo)); // Verifica se tipo aceito if ( (tipo == MFRC522::PICC_TYPE_MIFARE_MINI) || (tipo == MFRC522::PICC_TYPE_MIFARE_1K) || (tipo == MFRC522::PICC_TYPE_MIFARE_4K) ) { chave = testaChave(); return chave != OUTRA; } Serial.println ("Cartao incompativel!"); return false; } // Verifica qual a chave usada no nosso setor CHAVE testaChave() { MFRC522::StatusCode status; status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCO_CFG, &chaveFabrica, &(mfrc522.uid)); if (status == MFRC522::STATUS_OK) { Serial.println ("Usa chave da fábrica"); return FABRICA; } // Reinicia a comunicação, para poder testar outra chave mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); mfrc522.PICC_IsNewCardPresent(); mfrc522.PICC_ReadCardSerial(); status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCO_CFG, &chaveA, &(mfrc522.uid)); if (status == MFRC522::STATUS_OK) { Serial.println ("Usa a nossa chave"); return NOSSA; } Serial.println ("Chave desconhecida!"); return OUTRA; } // Inicia um bloco no formato apropriado para valor bool iniciaValor(byte bloco) { MFRC522::StatusCode status; byte valueBlock[] = { 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, bloco, ~bloco, bloco, ~bloco }; status = mfrc522.MIFARE_Write(bloco, valueBlock, 16); if (status != MFRC522::STATUS_OK) { Serial.print("Erro ao gravar bloco!"); return false; } return true; } // Mostra o conteudo de um buffer em hexa void dump_buffer(byte *buffer, byte tam) { for (byte i = 0; i < tam; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } }
Resultado do Sistema de Passagem com leitor RFID e Arduino
A imagem abaixo mostra um exemplo de funcionamento.
Você pode experimentar com outros valores de recarga e consumo alterando as linhas que definem as constantes VALOR_RECARGA e VALOR_CONSUMO. Você pode também mudar as chaves em setup(), porém tome o cuidado de retornar o cartão à condição de fábrica antes de iniciá-lo com uma nova chave.
Sugestões para Projetos
Neste post vimos o básico de como usar o cartão com bilhetagem, disparando as operações através do monitor serial. Algumas ideias de projeto usando estes conhecimentos:
- Conectar um display e botões ao Arduino para operar sem conexão a um micro.
- Após um consumo bem sucedido, acionar um servo motor para liberar uma recompensa
- Montar uma mini “vending machine“, onde um produto é selecionado e, após registrar o consumo, um motor de passo ou servo é usado para entregar o produto.
Gostou do post Sistema de passagens com Módulo Leitor Rfid e Arduino? Ajude-nos a melhorar o blog comentando abaixo sobre este tutorial.
Ola Daniel
Gostaria de saber se existe uma antena de Longo Alcance Rfid para indicar para usar com Arduíno para projetos de controle de movimentação de patrimônio?? Vi esse Leitor De Cartão Rfid Impermeável De Longo Alcance R16-7db P em https://produto.mercadolivre.com.br/MLB-3745712489-leitor-de-carto-rfid-impermeavel-de-longo-alcance-r16-7db-p-_JM#polycard_client=bookmarks será que daria certo.
Não tenho experiência com isso, mas existem alguns leitores Mifare que falam em até 10cm de alcance. Este leitor que você menciona é para 125kHz, não funciona com cartões Mifare.
Boa noite,
É possível integrar vários RFID com o arduino, por exemplo com ligação em série. Se sim, terias como elaborar um artigo sobre este tema?
Você pode ligar vários leitores sim. No artigo eu falei mais sobre o cartão, mas o leitor utilize comunicação SPI. Os sinais SCK, MISO e MOSI podem ser ligados a vários leitores (em paralelo), porém cada leitor precisa de duas saídas digitais (não compartilhadas) no Arduino para os sinais SS e RST.
Muito bom este sistema, pretendo utilizar com as “crianças”. Fazem a atividade, recebem créditos, depois no final, usam para “comprar” alguns brindes. Show.
Olá, Boa Tarde, Me chamo Antonio Marcos , sou discente do curso de agrocomputação Fatec/Senai MT , gostária de ver a possibilidade de me ajudar estou com o projeto de acesso usando um micro controlador esp32 e um kiit modulo rfid mfrc 522e não encontrei nada neste sentido só que um projeto é normal por usb e outro por bluetooth deixo meu e-mail para contato [email protected]
sem mais elevo minhas estimas e considerações
Parabéns Daniel, gostei muito do Projeto, vou usar na Escola Pública para ajudá-los.