Building a basic RFID card reader

Background

Requirements

ADAFRUIT PN532 BREAKOUT BOARD

Adafruit RGB LCD Shield Kit

#include <Wire.h> #include <SPI.h> #include <Adafruit_PN532.h> #include <Adafruit_RGBLCDShield.h> #include <utility/Adafruit_MCP23017.h> // If using the breakout with SPI, define the pins for SPI communication. #define PN532_SCK (2) #define PN532_MOSI (3) #define PN532_SS (4) #define PN532_MISO (5) // If using the breakout or shield with I2C, define just the pins connected // to the IRQ and reset lines. Use the values below (2, 3) for the shield! #define PN532_IRQ (2) #define PN532_RESET (3) // Not connected by default on the NFC Shield // Use this line for a breakout with a software SPI connection (recommended): Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS); Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield(); #if defined(ARDUINO_ARCH_SAMD) // for Zero, output on USB Serial console, remove line below if using programming port to program the Zero! // also change #define in Adafruit_PN532.cpp library file #define Serial SerialUSB #endif

Modified Library

/**************************************************************************/ /*! Sets the MxRtyPassiveActivation byte of the RFConfiguration register @param sakValue Holds the value of the SAK attribute @returns 1 if everything executed properly, 0 for an error */ /**************************************************************************/ bool Adafruit_PN532::readCardSAK(uint8_t cardbaudrate, uint8_t * sakValue, uint16_t timeout) { pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) pn532_packetbuffer[2] = cardbaudrate; if (!sendCommandCheckAck(pn532_packetbuffer, 3, timeout)) { #ifdef PN532DEBUG PN532DEBUGPRINT.println(F("No card(s) read")); #endif return 0x0; // no cards read } // wait for a card to enter the field (only possible with I2C) if (!_usingSPI) { #ifdef PN532DEBUG PN532DEBUGPRINT.println(F("Waiting for IRQ (indicates card presence)")); #endif if (!waitready(timeout)) { #ifdef PN532DEBUG PN532DEBUGPRINT.println(F("IRQ Timeout")); #endif return 0x0; } } // read data packet readdata(pn532_packetbuffer, 20); /* ISO14443A card response should be in the following format: byte Description ------------- ------------------------------------------ b0..6 Frame header and preamble b7 Tags Found b8 Tag Number (only one used in this example) b9..10 SENS_RES b11 SEL_RES b12 NFCID Length b13..NFCIDLen NFCID */ if (pn532_packetbuffer[7] != 1) return 0; *sakValue = pn532_packetbuffer[11]; return 1; }
#ifdef MIFAREDEBUG PN532DEBUGPRINT.print(F("ATQA: 0x")); PN532DEBUGPRINT.println(sens_res, HEX); PN532DEBUGPRINT.print(F("SAK: 0x")); PN532DEBUGPRINT.println(pn532_packetbuffer[11]); #endif

LCD Screen Functions

#define WHITE 0x7 int selector = 0; String toDisplay[3] = {"1.Get UID", "2.Card Info", "3.Clone Card"}; void startScreen() { lcd.begin(16, 2); lcd.setBacklight(WHITE); }
void start() { // Got ok data, print it out! lcd.clear(); lcd.setCursor(0, 0); lcd.print(toDisplay[0]); lcd.setCursor(15, 0); lcd.print("<"); lcd.setCursor(0, 1); lcd.print(toDisplay[1]); }
void printFirstLine(String s1) { lcd.setCursor(0,0); lcd.print(s1); } void printSecondLine(String s1) { lcd.setCursor(0,1); lcd.print(s1); } void printDataDEC(uint32_t data) { lcd.print(data, DEC); } void printDataHEX(uint32_t data) { lcd.print(data, HEX); } void printUID(uint8_t num) { lcd.print(num, HEX); } void reset() { lcd.clear(); lcd.setCursor(0,0); } void printLines(String s1, String s2) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(s1); lcd.setCursor(0,1); lcd.print(s2); }unsigned long lastDebounceTime = 0; unsigned long debounceDelay = 50; void menu() { uint8_t buttons = lcd.readButtons(); if (buttons) { reset(); if (buttons & BUTTON_UP) { if ((millis() - lastDebounceTime) > debounceDelay) { switch(selector) { case 0: printLines(toDisplay[selector], toDisplay[selector+1]); lcd.setCursor(15, selector); lcd.print("<"); break; case 1: selector--; printLines(toDisplay[selector], toDisplay[selector+1]); lcd.setCursor(15, selector); lcd.print("<"); break; case 2: selector--; printLines(toDisplay[selector], toDisplay[selector+1]); lcd.setCursor(15, selector-1); lcd.print("<"); break; } } } if (buttons & BUTTON_DOWN) { if ((millis() - lastDebounceTime) > debounceDelay) { switch(selector) { case 0: selector++; printLines(toDisplay[selector-1], toDisplay[selector]); lcd.setCursor(15, selector); lcd.print("<"); break; case 1: selector++; printLines(toDisplay[selector-1], toDisplay[selector]); lcd.setCursor(15, selector-1); lcd.print("<"); break; case 2: printLines(toDisplay[selector-1], toDisplay[selector]); lcd.setCursor(15, selector-1); lcd.print("<"); break; } } } if (buttons & BUTTON_SELECT) { if (selector == 0) { getMifareUID(); } else if (selector == 1) { readCard(); } else { clone(); } } lastDebounceTime = millis(); } }

De-Bouncing

RFID Funtions

void startReader() { nfc.begin(); } void getBoardFirmware(uint32_t versiondata) { versiondata = nfc.getFirmwareVersion(); } void configToRead() { // configure board to read RFID tags nfc.SAMConfig(); }
//Returns the UID of the card being read void getMifareUID() { uint8_t success; uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) // Wait for an ISO14443A type cards (Mifare, etc.). When one is found // 'uid' will be populated with the UID, and uidLength will indicate // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) printLines("Waiting for an", "ISO14443A Card..."); success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); if (success) { // Display some basic information about the card printLines("Found ISO14443A card", "UID: "); for (int i = 0; i < uidLength; i++) { printUID(uid[i]); } } }// Finds the SAK value of the card and then determines what type // of RFID the card is void readCard() { uint8_t sakValue; uint8_t success; printLines("Waiting for an", "ISO14443A Card..."); success = nfc.readCardSAK(PN532_MIFARE_ISO14443A, &sakValue); if (success) { reset(); switch (sakValue) { case 0: printLines("Mifare Ultralight", "Detected"); break; case 8: printLines("Mifare Classic", "1K Detected"); break; case 18: printLines("Mifare Classic", "4K Detected"); break; case 24: printLines("Mifare DESFire", "Detected"); break; default: printLines("Could not read", "card"); break; } } }

RFID Main

void setup() { Serial.begin(115200); startReader(); startScreen(); uint32_t versiondata = nfc.getFirmwareVersion(); if (! versiondata) { reset(); printFirstLine("Couldn't find"); printSecondLine("board"); while (1); // halt } printFirstLine("Found chip PN5"); printDataHEX((versiondata >> 24) & 0xFF); printSecondLine("Firmware ver. "); printDataDEC((versiondata >> 16) & 0xFF); delay(1500); start(); configToRead(); }
void loop() { menu(); }

--

--

Leading with strategy, design and architecture, we connect cloud, data, and cyber to engineer and deliver large-scale, complex transformations.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
6point6

Leading with strategy, design and architecture, we connect cloud, data, and cyber to engineer and deliver large-scale, complex transformations.