PN532 NFC library for Seeed Studio's NFC Shield

Fork of PN532 by Yihui Xiong

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MifareUltralight.cpp Source File

MifareUltralight.cpp

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