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