PN532 NFC shield of Adafruit based on PN532 of Seeed.

Fork of PN532 by Seeed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MifareUltralight.cpp Source File

MifareUltralight.cpp

00001 #include "MifareUltralight.h"
00002 
00003 #include <string.h>
00004 
00005 #include "PN532_debug.h"
00006 
00007 #define ULTRALIGHT_PAGE_SIZE 4
00008 #define ULTRALIGHT_READ_SIZE 4 // we should be able to read 16 uint8_ts at a time
00009 
00010 #define ULTRALIGHT_DATA_START_PAGE 4
00011 #define ULTRALIGHT_MESSAGE_LENGTH_INDEX 1
00012 #define ULTRALIGHT_DATA_START_INDEX 2
00013 #define ULTRALIGHT_MAX_PAGE 63
00014 
00015 #define NFC_FORUM_TAG_TYPE_2 ("NFC Forum Type 2")
00016 
00017 MifareUltralight::MifareUltralight(PN532& nfcShield)
00018 {
00019     nfc = &nfcShield;
00020     ndefStartIndex = 0;
00021     messageLength = 0;
00022 }
00023 
00024 MifareUltralight::~MifareUltralight()
00025 {
00026 }
00027 
00028 NfcTag MifareUltralight::read(uint8_t * uid, unsigned int uidLength)
00029 {
00030     if (isUnformatted())
00031     {
00032         DMSG("WARNING: Tag is not formatted.");
00033         return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2);
00034     }
00035 
00036     readCapabilityContainer(); // meta info for tag
00037     findNdefMessage();
00038     calculateBufferSize();
00039 
00040     if (messageLength == 0) { // data is 0x44 0x03 0x00 0xFE
00041         NdefMessage message = NdefMessage();
00042         message.addEmptyRecord();
00043         return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, message);
00044     }
00045 
00046     bool success;
00047     uint8_t page;
00048     uint8_t index = 0;
00049     uint8_t buffer[bufferSize];
00050     for (page = ULTRALIGHT_DATA_START_PAGE; page < ULTRALIGHT_MAX_PAGE; page++)
00051     {
00052         // read the data
00053         success = nfc->mifareultralight_ReadPage(page, &buffer[index]);
00054         if (success)
00055         {
00056             #ifdef MIFARE_ULTRALIGHT_DEBUG
00057             DMSG("Page ");Serial.print(page);DMSG(" ");
00058             nfc->PrintHexChar(&buffer[index], ULTRALIGHT_PAGE_SIZE);
00059             #endif
00060         }
00061         else
00062         {
00063             DMSG("Read failed ");DMSG_INT(page);
00064             // TODO error handling
00065             messageLength = 0;
00066             break;
00067         }
00068 
00069         if (index >= (messageLength + ndefStartIndex))
00070         {
00071             break;
00072         }
00073 
00074         index += ULTRALIGHT_PAGE_SIZE;
00075     }
00076 
00077     NdefMessage ndefMessage = NdefMessage(&buffer[ndefStartIndex], messageLength);
00078     return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, ndefMessage);
00079 
00080 }
00081 
00082 bool MifareUltralight::write(NdefMessage& m, uint8_t * uid, unsigned int uidLength)
00083 {
00084 
00085      if (isUnformatted())
00086     {
00087         DMSG("WARNING: Tag is not formatted.\r\n");
00088         return false;
00089     }
00090     readCapabilityContainer(); // meta info for tag
00091 
00092     messageLength  = m.getEncodedSize();
00093     ndefStartIndex = messageLength < 0xFF ? 2 : 4;
00094     calculateBufferSize();
00095 
00096     if(bufferSize>tagCapacity) {
00097        /* #ifdef MIFARE_ULTRALIGHT_DEBUG
00098         Serial.print(F("Encoded Message length exceeded tag Capacity "));Serial.println(tagCapacity);
00099         #endif*/
00100         return false;
00101     }
00102 
00103     uint8_t encoded[bufferSize];
00104     uint8_t *  src = encoded;
00105     unsigned int position = 0;
00106     uint8_t page = ULTRALIGHT_DATA_START_PAGE;
00107 
00108     // Set message size. With ultralight should always be less than 0xFF but who knows?
00109 
00110     encoded[0] = 0x3;
00111     if (messageLength < 0xFF)
00112     {
00113         encoded[1] = messageLength;
00114     }
00115     else
00116     {
00117         encoded[1] = 0xFF;
00118         encoded[2] = ((messageLength >> 8) & 0xFF);
00119         encoded[3] = (messageLength & 0xFF);
00120     }
00121 
00122     m.encode(encoded+ndefStartIndex);
00123     // this is always at least 1 byte copy because of terminator.
00124     memset(encoded+ndefStartIndex+messageLength,0,bufferSize-ndefStartIndex-messageLength);
00125     encoded[ndefStartIndex+messageLength] = 0xFE; // terminator
00126 
00127     while (position < bufferSize){ //bufferSize is always times pagesize so no "last chunk" check
00128         // write page
00129         if (!nfc->mifareultralight_WritePage(page, src))
00130             return false;
00131         page++;
00132         src+=ULTRALIGHT_PAGE_SIZE;
00133         position+=ULTRALIGHT_PAGE_SIZE;
00134     }
00135     return true;
00136 }
00137 bool MifareUltralight::isUnformatted()
00138 {
00139     uint8_t page = 4;
00140     uint8_t data[ULTRALIGHT_READ_SIZE];
00141     bool success = nfc->mifareultralight_ReadPage (page, data);
00142     if (success)
00143     {
00144         return (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF);
00145     }
00146     else
00147     {
00148         DMSG("Error. Failed read page ");DMSG_INT(page);
00149         return false;
00150     }
00151 }
00152 
00153 // page 3 has tag capabilities
00154 void MifareUltralight::readCapabilityContainer()
00155 {
00156     uint8_t data[ULTRALIGHT_PAGE_SIZE];
00157     int success = nfc->mifareultralight_ReadPage (3, data);
00158     if (success)
00159     {
00160         // See AN1303 - different rules for Mifare Family uint8_t2 = (additional data + 48)/8
00161         tagCapacity = data[2] * 8;
00162         #ifdef MIFARE_ULTRALIGHT_DEBUG
00163         DMSG("Tag capacity "));Serial.print(tagCapacity);DMSG(F(" uint8_ts");
00164         #endif
00165 
00166         // TODO future versions should get lock information
00167     }
00168 }
00169 
00170 // read enough of the message to find the ndef message length
00171 void MifareUltralight::findNdefMessage()
00172 {
00173     int page;
00174     uint8_t data[12]; // 3 pages
00175     uint8_t* data_ptr = &data[0];
00176 
00177     // the nxp read command reads 4 pages, unfortunately adafruit give me one page at a time
00178     bool success = true;
00179     for (page = 4; page < 6; page++)
00180     {
00181         success = success && nfc->mifareultralight_ReadPage(page, data_ptr);
00182         #ifdef MIFARE_ULTRALIGHT_DEBUG
00183         DMSG("Page "));Serial.print(page);Serial.print(F(" - ");
00184         nfc->PrintHexChar(data_ptr, 4);
00185         #endif
00186         data_ptr += ULTRALIGHT_PAGE_SIZE;
00187     }
00188 
00189     if (success)
00190     {
00191         if (data[0] == 0x03)
00192         {
00193             messageLength = data[1];
00194             ndefStartIndex = 2;
00195         }
00196         else if (data[5] == 0x3) // page 5 uint8_t 1
00197         {
00198             // TODO should really read the lock control TLV to ensure uint8_t[5] is correct
00199             messageLength = data[6];
00200             ndefStartIndex = 7;
00201         }
00202     }
00203 
00204     #ifdef MIFARE_ULTRALIGHT_DEBUG
00205     DMSG("messageLength ");DMSG(messageLength);
00206     DMSG("ndefStartIndex ");DMSG(ndefStartIndex);
00207     #endif
00208 }
00209 
00210 // buffer is larger than the message, need to handle some data before and after
00211 // message and need to ensure we read full pages
00212 void MifareUltralight::calculateBufferSize()
00213 {
00214     // TLV terminator 0xFE is 1 uint8_t
00215     bufferSize = messageLength + ndefStartIndex + 1;
00216 
00217     if (bufferSize % ULTRALIGHT_READ_SIZE != 0)
00218     {
00219         // buffer must be an increment of page size
00220         bufferSize = ((bufferSize / ULTRALIGHT_READ_SIZE) + 1) * ULTRALIGHT_READ_SIZE;
00221     }
00222 }