NFC?
NFC (Near Field Communication) é um padrão de comunicação via rádio a pequenas distâncias (até 10 cm). Não por acaso, este padrão é o mesmo usado na comunicação dos cartões e tags MIFARE.
O Shield
O coração do shield NFC (cuja documentação pode ser vista na loja e na wiki do fabricante) é o chip PN532 do NXP. É um circuito integrado bastante versátil (inclui um microcontrolador); o firmware suporta uma quantidade grande de recursos (o manual tem 200 páginas e não entra nos detalhes do comandos MIFARE).
Felizmente existe uma biblioteca para uso do PN532 (desenvolvida pela Adafruit com colaborações do SeeedStudio). Ela disponibiliza apenas o básico, mas é o suficiente para algumas experiências. Eu usei a versão no site da Elecfreaks, mas no site da Adafruit tem um versão mais nova.
A comunicação entre o PN532 e o Arduino é via SPI, os demais pinos permanecem disponíveis (a placa possui conectores para interligar um outro shield). A antena está na própria placa, o alcance é limitado a 1 cm.
Cartões e Tags
Para fazer testes precisamos de alguns cartões e tags. Eu comprei alguns no SeeedStudio, todos com 1K byte de memória:
- Cartões
- Tag em formato de chaveiro
- Tag adesivo (etiqueta para livros)
![]() |
| Minha coleção de tags MIFARE |
![]() |
| Tag MIFARE no formato de chaveiro |
![]() |
| A etiqueta adesiva permite ver a antena. |
O objetivo desta demonstração é exercitar a gravação e leitura de dados e o uso das chaves de segurança. O software requer a biblioteca PN532:
// Exemplo de leitura/escrita em cartão/tag MIFARE com 1K de memória
// usando um NFC Shield baseado no PN532 da NXP
// Código a partir de exemplos do Seeed Technology Inc (www.seeedstudio.com)
#include <PN532.h>
// Pinos usados para conexão
#define SCK 13
#define MOSI 11
#define SS 10
#define MISO 12
// Objeto para acesso ao PN532
PN532 nfc(SCK, MISO, MOSI, SS);
// Chave de fabrica
// Pode mudar conforme o fabricante do cartao/tag !
uint8_t chave_fab[]= {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
// Chaves para o nosso teste
uint8_t chave_A[]= {0x01,0x02,0x03,0x04,0x05,0x06};
uint8_t chave_B[]= {0x06,0x05,0x04,0x03,0x02,0x01};
// Blocos que vamos usar
// No MIFARE 1K os blocos são numerados de 0 a 63
// Cada setor tem 4 blocos, o último bloco do setor é de configuração
const uint8_t bloco_dados = 12; // protegeBloco assume que é o 1o bloco do setor
const uint8_t bloco_cfg = 15; // CUIDADO COM O QUE ESCREVER AQUI
// Iniciacao
void setup(void) {
Serial.begin(9600);
Serial.println("Demonstracao MIFARE");
Serial.println();
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("NFC shield nao encontrada");
while (1); // fica parado aqui
}
// Informacoes sobre o shield
Serial.print("Achou NFC Shield ");
Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. ");
Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.');
Serial.println((versiondata>>8) & 0xFF, DEC);
// configura a placa
nfc.SAMConfig();
}
// Laco principal
void loop(void) {
uint32_t id;
char cmd [3];
uint8_t bloco[16];
uint8_t *pChave;
// procura um cartao ou tag MIFERE
id = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A);
if (id == 0) {
// nao achou
delay (500); // aguarda meio segundo
return; // tenta de novo
}
Serial.println();
Serial.print("Achou cartao #");
Serial.println(id);
Serial.print("Cmd: ");
leCmd (cmd);
switch (cmd[0]) {
case 'P': // Protege
if (!protegeBloco (id)) {
Serial.println ("Erro ao proteger o bloco!");
} else {
Serial.println ("Bloco protegido.");
}
break;
case 'D': // Desprotege
if (!cfgPadrao (id))
{
Serial.println ("Erro ao retornar a config de fabrica!");
} else {
Serial.println ("Retornado a config de fabrica.");
}
break;
case 'L': // Lê
pChave = chave_fab;
if (cmd[1] == 'A')
pChave = chave_A;
else if (cmd[1] == 'B')
pChave = chave_B;
if (!nfc.authenticateBlock(1, id, bloco_dados,
(cmd[1] == 'B')? KEY_B : KEY_A, pChave))
{
Serial.println ("Autenticacao falhou!");
break;
}
if (!nfc.readMemoryBlock(1, bloco_dados, bloco))
{
Serial.println ("Erro na leitura!");
break;
}
dump (bloco);
break;
case 'E': // Escreve
pChave = chave_fab;
if (cmd[1] == 'A')
pChave = chave_A;
else if (cmd[1] == 'B')
pChave = chave_B;
if (!nfc.authenticateBlock(1, id, bloco_dados,
(cmd[1] == 'B')? KEY_B : KEY_A, pChave))
{
Serial.println ("Autenticacao falhou!");
break;
}
for (int i = 0; i < 16; i++)
bloco[i] = i;
if (!nfc.writeMemoryBlock(1, bloco_dados, bloco))
{
Serial.println ("Erro na escrita!");
break;
}
dump (bloco);
break;
}
esperaEnter();
}
// Configura as chaves A e B
// Escrita com chave B
// Leitura com A ou B
int protegeBloco (uint32_t id) {
uint8_t bloco[16];
// Monta a configuração
memcpy (bloco, chave_A, 6);
bloco[6] = 0x78;
bloco[7] = 0x77;
bloco[8] = 0x88;
bloco[9] = 0;
memcpy (bloco+10, chave_B, 6);
dump (bloco);
// Autentica no bloco de configuracao
if (!nfc.authenticateBlock(1, id, bloco_cfg, KEY_A, chave_fab)) {
Serial.println ("Autenticacao com chave da fabrica na cfg falhou!");
return false;
}
// Grava o bloco de configuracao
if (!nfc.writeMemoryBlock (1, bloco_cfg, bloco)) {
Serial.println ("Erro ao gravar cfg!");
return false;
}
return true;
}
// Volta a configuracao de fabrica
int cfgPadrao (uint32_t id) {
uint8_t bloco[16];
// Monta a configuração
memcpy (bloco, chave_fab, 6);
bloco[6] = 0xFF;
bloco[7] = 0x07;
bloco[8] = 0x80;
bloco[9] = 0;
memcpy (bloco+10, chave_fab, 6);
// Autentica no bloco de configuracao
if (!nfc.authenticateBlock(1, id, bloco_cfg, KEY_B, chave_B)) {
Serial.println ("Autenticacao com chave B na cfg falhou!");
return false;
}
// Grava o bloco de configuracao
if (!nfc.writeMemoryBlock (1, bloco_cfg, bloco)) {
Serial.println ("Erro ao gravar cfg!");
return false;
}
return true;
}
// Le um comando finaliado por ENTER
void leCmd (char *cmd)
{
int i = 0;
int c;
while (true) {
c = Serial.read();
if (c == -1)
continue;
if (c == 0x0d)
break;
if ((c >= 'a') && (c <= 'z')) {
c -= 0x20;
}
if (i < 4)
cmd[i++] = c;
}
cmd[i] = 0;
Serial.println (cmd);
}
// Aguarda receber um ENTER
void esperaEnter() {
Serial.print ("Digite ENTER...");
while (Serial.read() != 0x0d)
;
Serial.println ();
}
// Lista o conteudo de um bloco
void dump (uint8_t *pBloco)
{
for (uint8_t i=0; i<16; i++)
{
Serial.print(*pBloco++, HEX);
Serial.print(" ");
}
Serial.println();
}
Atenção que para usar a biblioteca da Elecfreaks com as versões mais recentes do Arduino é necessário trocar as linhas #include <WProgram.h> por #include <Arduino.h>.A chave de fábrica utilizada na demonstração é a dos cartões e tags que eu tenho. Cartões e tags de outro fabricante podem ter uma chave diferente.
O transcript abaixo mostra a seguinte sequência de operações:
- O setor de dados é lido com a chave de fábrica.
- São reprogramadas as chaves e as condições de acesso.
- Não é mais possível a leitura do setor de dados com a chave de fábrica.
- A leitura do setor de dados pode ser feita com as chaves A e B.
- A escrita no setor de dados só pode ser feita com a chave B.
- São restauradas as chaves e condições de acesso de fábrica.
Demonstracao MIFARE Achou NFC Shield 32 Firmware ver. 1.6 Achou cartao #706831521 Cmd: L 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: P 1 2 3 4 5 6 78 77 88 0 6 5 4 3 2 1 Bloco protegido. Digite ENTER... Achou cartao #706831521 Cmd: L Autenticacao falhou! Digite ENTER... Achou cartao #706831521 Cmd: LA 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: LB 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: EA Erro na escrita! Digite ENTER... Achou cartao #706831521 Cmd: EB 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: D Retornado a config de fabrica. Digite ENTER... Achou cartao #706831521 Cmd: L 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER...ATENÇÃO: ao reprogramar as configurações de segurança você corre o risco de perder o acesso ao setor. Se você for fazer experiências, se prepare para ficar com um cartão ou tag "capenga"!




Nenhum comentário:
Postar um comentário