Generic library for working with PN532-like chips
Fork of PN532 by
MifareUltralight.cpp@11:5b8afec8bee6, 2015-02-04 (annotated)
- Committer:
- r4z0r7o3
- Date:
- Wed Feb 04 19:26:03 2015 +0000
- Revision:
- 11:5b8afec8bee6
- Parent:
- 10:f959b305a571
Fix missing space in tag print()
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
yihui | 3:4189a10038e6 | 1 | #include "MifareUltralight.h" |
yihui | 3:4189a10038e6 | 2 | |
yihui | 3:4189a10038e6 | 3 | #include "PN532_debug.h" |
yihui | 3:4189a10038e6 | 4 | |
yihui | 3:4189a10038e6 | 5 | #define ULTRALIGHT_PAGE_SIZE 4 |
yihui | 3:4189a10038e6 | 6 | #define ULTRALIGHT_READ_SIZE 4 // we should be able to read 16 uint8_ts at a time |
yihui | 3:4189a10038e6 | 7 | |
yihui | 3:4189a10038e6 | 8 | #define ULTRALIGHT_DATA_START_PAGE 4 |
yihui | 3:4189a10038e6 | 9 | #define ULTRALIGHT_MESSAGE_LENGTH_INDEX 1 |
yihui | 3:4189a10038e6 | 10 | #define ULTRALIGHT_DATA_START_INDEX 2 |
yihui | 3:4189a10038e6 | 11 | #define ULTRALIGHT_MAX_PAGE 63 |
yihui | 3:4189a10038e6 | 12 | |
yihui | 3:4189a10038e6 | 13 | #define NFC_FORUM_TAG_TYPE_2 ("NFC Forum Type 2") |
yihui | 3:4189a10038e6 | 14 | |
yihui | 3:4189a10038e6 | 15 | MifareUltralight::MifareUltralight(PN532& nfcShield) |
yihui | 3:4189a10038e6 | 16 | { |
yihui | 3:4189a10038e6 | 17 | nfc = &nfcShield; |
yihui | 3:4189a10038e6 | 18 | ndefStartIndex = 0; |
yihui | 3:4189a10038e6 | 19 | messageLength = 0; |
yihui | 3:4189a10038e6 | 20 | } |
yihui | 3:4189a10038e6 | 21 | |
yihui | 3:4189a10038e6 | 22 | MifareUltralight::~MifareUltralight() |
yihui | 3:4189a10038e6 | 23 | { |
yihui | 3:4189a10038e6 | 24 | } |
yihui | 3:4189a10038e6 | 25 | |
yihui | 3:4189a10038e6 | 26 | NfcTag MifareUltralight::read(uint8_t * uid, unsigned int uidLength) |
yihui | 3:4189a10038e6 | 27 | { |
yihui | 3:4189a10038e6 | 28 | if (isUnformatted()) |
yihui | 3:4189a10038e6 | 29 | { |
r4z0r7o3 | 10:f959b305a571 | 30 | DMSG("WARNING: Tag is not formatted.\n"); |
yihui | 3:4189a10038e6 | 31 | return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); |
yihui | 3:4189a10038e6 | 32 | } |
yihui | 3:4189a10038e6 | 33 | |
yihui | 3:4189a10038e6 | 34 | readCapabilityContainer(); // meta info for tag |
yihui | 3:4189a10038e6 | 35 | findNdefMessage(); |
yihui | 3:4189a10038e6 | 36 | calculateBufferSize(); |
yihui | 3:4189a10038e6 | 37 | |
yihui | 3:4189a10038e6 | 38 | if (messageLength == 0) { // data is 0x44 0x03 0x00 0xFE |
yihui | 3:4189a10038e6 | 39 | NdefMessage message = NdefMessage(); |
yihui | 3:4189a10038e6 | 40 | message.addEmptyRecord(); |
yihui | 3:4189a10038e6 | 41 | return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, message); |
yihui | 3:4189a10038e6 | 42 | } |
yihui | 3:4189a10038e6 | 43 | |
yihui | 3:4189a10038e6 | 44 | bool success; |
yihui | 3:4189a10038e6 | 45 | uint8_t page; |
yihui | 3:4189a10038e6 | 46 | uint8_t index = 0; |
yihui | 3:4189a10038e6 | 47 | uint8_t buffer[bufferSize]; |
yihui | 3:4189a10038e6 | 48 | for (page = ULTRALIGHT_DATA_START_PAGE; page < ULTRALIGHT_MAX_PAGE; page++) |
yihui | 3:4189a10038e6 | 49 | { |
yihui | 3:4189a10038e6 | 50 | // read the data |
yihui | 3:4189a10038e6 | 51 | success = nfc->mifareultralight_ReadPage(page, &buffer[index]); |
yihui | 3:4189a10038e6 | 52 | if (success) |
yihui | 3:4189a10038e6 | 53 | { |
yihui | 3:4189a10038e6 | 54 | #ifdef MIFARE_ULTRALIGHT_DEBUG |
yihui | 3:4189a10038e6 | 55 | DMSG("Page ");Serial.print(page);DMSG(" "); |
yihui | 3:4189a10038e6 | 56 | nfc->PrintHexChar(&buffer[index], ULTRALIGHT_PAGE_SIZE); |
r4z0r7o3 | 10:f959b305a571 | 57 | DMSG("\n"); |
yihui | 3:4189a10038e6 | 58 | #endif |
yihui | 3:4189a10038e6 | 59 | } |
yihui | 3:4189a10038e6 | 60 | else |
yihui | 3:4189a10038e6 | 61 | { |
r4z0r7o3 | 10:f959b305a571 | 62 | DMSG("Read failed ");DMSG_INT(page);DMSG("\n"); |
yihui | 3:4189a10038e6 | 63 | // TODO error handling |
yihui | 3:4189a10038e6 | 64 | messageLength = 0; |
yihui | 3:4189a10038e6 | 65 | break; |
yihui | 3:4189a10038e6 | 66 | } |
yihui | 3:4189a10038e6 | 67 | |
yihui | 3:4189a10038e6 | 68 | if (index >= (messageLength + ndefStartIndex)) |
yihui | 3:4189a10038e6 | 69 | { |
yihui | 3:4189a10038e6 | 70 | break; |
yihui | 3:4189a10038e6 | 71 | } |
yihui | 3:4189a10038e6 | 72 | |
yihui | 3:4189a10038e6 | 73 | index += ULTRALIGHT_PAGE_SIZE; |
yihui | 3:4189a10038e6 | 74 | } |
yihui | 3:4189a10038e6 | 75 | |
yihui | 3:4189a10038e6 | 76 | NdefMessage ndefMessage = NdefMessage(&buffer[ndefStartIndex], messageLength); |
yihui | 3:4189a10038e6 | 77 | return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, ndefMessage); |
yihui | 3:4189a10038e6 | 78 | |
yihui | 3:4189a10038e6 | 79 | } |
yihui | 3:4189a10038e6 | 80 | |
icefeet | 5:51f820fbd18a | 81 | bool MifareUltralight::write(NdefMessage& m, uint8_t * uid, unsigned int uidLength) |
icefeet | 5:51f820fbd18a | 82 | { |
icefeet | 5:51f820fbd18a | 83 | |
icefeet | 5:51f820fbd18a | 84 | if (isUnformatted()) |
icefeet | 5:51f820fbd18a | 85 | { |
icefeet | 5:51f820fbd18a | 86 | printf("WARNING: Tag is not formatted.\r\n"); |
icefeet | 5:51f820fbd18a | 87 | return false; |
icefeet | 5:51f820fbd18a | 88 | } |
icefeet | 5:51f820fbd18a | 89 | readCapabilityContainer(); // meta info for tag |
icefeet | 5:51f820fbd18a | 90 | |
icefeet | 5:51f820fbd18a | 91 | messageLength = m.getEncodedSize(); |
icefeet | 5:51f820fbd18a | 92 | ndefStartIndex = messageLength < 0xFF ? 2 : 4; |
icefeet | 5:51f820fbd18a | 93 | calculateBufferSize(); |
icefeet | 5:51f820fbd18a | 94 | |
icefeet | 5:51f820fbd18a | 95 | if(bufferSize>tagCapacity) { |
icefeet | 5:51f820fbd18a | 96 | /* #ifdef MIFARE_ULTRALIGHT_DEBUG |
icefeet | 5:51f820fbd18a | 97 | Serial.print(F("Encoded Message length exceeded tag Capacity "));Serial.println(tagCapacity); |
icefeet | 5:51f820fbd18a | 98 | #endif*/ |
icefeet | 5:51f820fbd18a | 99 | return false; |
icefeet | 5:51f820fbd18a | 100 | } |
icefeet | 5:51f820fbd18a | 101 | |
icefeet | 5:51f820fbd18a | 102 | uint8_t encoded[bufferSize]; |
icefeet | 5:51f820fbd18a | 103 | uint8_t * src = encoded; |
icefeet | 5:51f820fbd18a | 104 | unsigned int position = 0; |
icefeet | 5:51f820fbd18a | 105 | uint8_t page = ULTRALIGHT_DATA_START_PAGE; |
icefeet | 5:51f820fbd18a | 106 | |
icefeet | 5:51f820fbd18a | 107 | // Set message size. With ultralight should always be less than 0xFF but who knows? |
icefeet | 5:51f820fbd18a | 108 | |
icefeet | 5:51f820fbd18a | 109 | encoded[0] = 0x3; |
icefeet | 5:51f820fbd18a | 110 | if (messageLength < 0xFF) |
icefeet | 5:51f820fbd18a | 111 | { |
icefeet | 5:51f820fbd18a | 112 | encoded[1] = messageLength; |
icefeet | 5:51f820fbd18a | 113 | } |
icefeet | 5:51f820fbd18a | 114 | else |
icefeet | 5:51f820fbd18a | 115 | { |
icefeet | 5:51f820fbd18a | 116 | encoded[1] = 0xFF; |
icefeet | 5:51f820fbd18a | 117 | encoded[2] = ((messageLength >> 8) & 0xFF); |
icefeet | 5:51f820fbd18a | 118 | encoded[3] = (messageLength & 0xFF); |
icefeet | 5:51f820fbd18a | 119 | } |
icefeet | 5:51f820fbd18a | 120 | |
icefeet | 5:51f820fbd18a | 121 | m.encode(encoded+ndefStartIndex); |
icefeet | 5:51f820fbd18a | 122 | // this is always at least 1 byte copy because of terminator. |
icefeet | 5:51f820fbd18a | 123 | memset(encoded+ndefStartIndex+messageLength,0,bufferSize-ndefStartIndex-messageLength); |
icefeet | 5:51f820fbd18a | 124 | encoded[ndefStartIndex+messageLength] = 0xFE; // terminator |
icefeet | 5:51f820fbd18a | 125 | |
icefeet | 5:51f820fbd18a | 126 | while (position < bufferSize){ //bufferSize is always times pagesize so no "last chunk" check |
icefeet | 5:51f820fbd18a | 127 | // write page |
icefeet | 5:51f820fbd18a | 128 | if (!nfc->mifareultralight_WritePage(page, src)) |
icefeet | 5:51f820fbd18a | 129 | return false; |
icefeet | 5:51f820fbd18a | 130 | page++; |
icefeet | 5:51f820fbd18a | 131 | src+=ULTRALIGHT_PAGE_SIZE; |
icefeet | 5:51f820fbd18a | 132 | position+=ULTRALIGHT_PAGE_SIZE; |
icefeet | 5:51f820fbd18a | 133 | } |
icefeet | 5:51f820fbd18a | 134 | return true; |
icefeet | 5:51f820fbd18a | 135 | } |
yihui | 3:4189a10038e6 | 136 | bool MifareUltralight::isUnformatted() |
yihui | 3:4189a10038e6 | 137 | { |
yihui | 3:4189a10038e6 | 138 | uint8_t page = 4; |
yihui | 3:4189a10038e6 | 139 | uint8_t data[ULTRALIGHT_READ_SIZE]; |
yihui | 3:4189a10038e6 | 140 | bool success = nfc->mifareultralight_ReadPage (page, data); |
yihui | 3:4189a10038e6 | 141 | if (success) |
yihui | 3:4189a10038e6 | 142 | { |
yihui | 3:4189a10038e6 | 143 | return (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF); |
yihui | 3:4189a10038e6 | 144 | } |
yihui | 3:4189a10038e6 | 145 | else |
yihui | 3:4189a10038e6 | 146 | { |
r4z0r7o3 | 10:f959b305a571 | 147 | DMSG("Error. Failed read page ");DMSG_INT(page);DMSG("\n"); |
yihui | 3:4189a10038e6 | 148 | return false; |
yihui | 3:4189a10038e6 | 149 | } |
yihui | 3:4189a10038e6 | 150 | } |
yihui | 3:4189a10038e6 | 151 | |
yihui | 3:4189a10038e6 | 152 | // page 3 has tag capabilities |
yihui | 3:4189a10038e6 | 153 | void MifareUltralight::readCapabilityContainer() |
yihui | 3:4189a10038e6 | 154 | { |
yihui | 3:4189a10038e6 | 155 | uint8_t data[ULTRALIGHT_PAGE_SIZE]; |
yihui | 3:4189a10038e6 | 156 | int success = nfc->mifareultralight_ReadPage (3, data); |
yihui | 3:4189a10038e6 | 157 | if (success) |
yihui | 3:4189a10038e6 | 158 | { |
yihui | 3:4189a10038e6 | 159 | // See AN1303 - different rules for Mifare Family uint8_t2 = (additional data + 48)/8 |
yihui | 3:4189a10038e6 | 160 | tagCapacity = data[2] * 8; |
yihui | 3:4189a10038e6 | 161 | #ifdef MIFARE_ULTRALIGHT_DEBUG |
r4z0r7o3 | 10:f959b305a571 | 162 | DMSG("Tag capacity "));Serial.print(tagCapacity);DMSG(F(" uint8_ts\n"); |
yihui | 3:4189a10038e6 | 163 | #endif |
yihui | 3:4189a10038e6 | 164 | |
yihui | 3:4189a10038e6 | 165 | // TODO future versions should get lock information |
yihui | 3:4189a10038e6 | 166 | } |
yihui | 3:4189a10038e6 | 167 | } |
yihui | 3:4189a10038e6 | 168 | |
yihui | 3:4189a10038e6 | 169 | // read enough of the message to find the ndef message length |
yihui | 3:4189a10038e6 | 170 | void MifareUltralight::findNdefMessage() |
yihui | 3:4189a10038e6 | 171 | { |
yihui | 3:4189a10038e6 | 172 | int page; |
yihui | 3:4189a10038e6 | 173 | uint8_t data[12]; // 3 pages |
yihui | 3:4189a10038e6 | 174 | uint8_t* data_ptr = &data[0]; |
yihui | 3:4189a10038e6 | 175 | |
yihui | 3:4189a10038e6 | 176 | // the nxp read command reads 4 pages, unfortunately adafruit give me one page at a time |
yihui | 3:4189a10038e6 | 177 | bool success = true; |
yihui | 3:4189a10038e6 | 178 | for (page = 4; page < 6; page++) |
yihui | 3:4189a10038e6 | 179 | { |
yihui | 3:4189a10038e6 | 180 | success = success && nfc->mifareultralight_ReadPage(page, data_ptr); |
yihui | 3:4189a10038e6 | 181 | #ifdef MIFARE_ULTRALIGHT_DEBUG |
yihui | 3:4189a10038e6 | 182 | DMSG("Page "));Serial.print(page);Serial.print(F(" - "); |
yihui | 3:4189a10038e6 | 183 | nfc->PrintHexChar(data_ptr, 4); |
r4z0r7o3 | 10:f959b305a571 | 184 | DMSG("\n"); |
yihui | 3:4189a10038e6 | 185 | #endif |
yihui | 3:4189a10038e6 | 186 | data_ptr += ULTRALIGHT_PAGE_SIZE; |
yihui | 3:4189a10038e6 | 187 | } |
yihui | 3:4189a10038e6 | 188 | |
yihui | 3:4189a10038e6 | 189 | if (success) |
yihui | 3:4189a10038e6 | 190 | { |
yihui | 3:4189a10038e6 | 191 | if (data[0] == 0x03) |
yihui | 3:4189a10038e6 | 192 | { |
yihui | 3:4189a10038e6 | 193 | messageLength = data[1]; |
yihui | 3:4189a10038e6 | 194 | ndefStartIndex = 2; |
yihui | 3:4189a10038e6 | 195 | } |
yihui | 3:4189a10038e6 | 196 | else if (data[5] == 0x3) // page 5 uint8_t 1 |
yihui | 3:4189a10038e6 | 197 | { |
yihui | 3:4189a10038e6 | 198 | // TODO should really read the lock control TLV to ensure uint8_t[5] is correct |
yihui | 3:4189a10038e6 | 199 | messageLength = data[6]; |
yihui | 3:4189a10038e6 | 200 | ndefStartIndex = 7; |
yihui | 3:4189a10038e6 | 201 | } |
yihui | 3:4189a10038e6 | 202 | } |
yihui | 3:4189a10038e6 | 203 | |
yihui | 3:4189a10038e6 | 204 | #ifdef MIFARE_ULTRALIGHT_DEBUG |
yihui | 3:4189a10038e6 | 205 | DMSG("messageLength ");DMSG(messageLength); |
r4z0r7o3 | 10:f959b305a571 | 206 | DMSG(" ndefStartIndex ");DMSG(ndefStartIndex);DMSG("\n"); |
yihui | 3:4189a10038e6 | 207 | #endif |
yihui | 3:4189a10038e6 | 208 | } |
yihui | 3:4189a10038e6 | 209 | |
yihui | 3:4189a10038e6 | 210 | // buffer is larger than the message, need to handle some data before and after |
yihui | 3:4189a10038e6 | 211 | // message and need to ensure we read full pages |
yihui | 3:4189a10038e6 | 212 | void MifareUltralight::calculateBufferSize() |
yihui | 3:4189a10038e6 | 213 | { |
yihui | 3:4189a10038e6 | 214 | // TLV terminator 0xFE is 1 uint8_t |
yihui | 3:4189a10038e6 | 215 | bufferSize = messageLength + ndefStartIndex + 1; |
yihui | 3:4189a10038e6 | 216 | |
yihui | 3:4189a10038e6 | 217 | if (bufferSize % ULTRALIGHT_READ_SIZE != 0) |
yihui | 3:4189a10038e6 | 218 | { |
yihui | 3:4189a10038e6 | 219 | // buffer must be an increment of page size |
yihui | 3:4189a10038e6 | 220 | bufferSize = ((bufferSize / ULTRALIGHT_READ_SIZE) + 1) * ULTRALIGHT_READ_SIZE; |
yihui | 3:4189a10038e6 | 221 | } |
yihui | 3:4189a10038e6 | 222 | } |