diff --git a/Due.h b/Due.h index c7f9f0b..a97886a 100644 --- a/Due.h +++ b/Due.h @@ -4,22 +4,22 @@ #ifndef Due_h #define Due_h -#if defined(__SAM3X8E__) - #define PROGMEM - #define pgm_read_byte(x) (*((char *)x)) -// #define pgm_read_word(x) (*((short *)(x & 0xfffffffe))) - #define pgm_read_word(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) - #define pgm_read_byte_near(x) (*((char *)x)) - #define pgm_read_byte_far(x) (*((char *)x)) -// #define pgm_read_word_near(x) (*((short *)(x & 0xfffffffe)) -// #define pgm_read_word_far(x) (*((short *)(x & 0xfffffffe))) - #define pgm_read_word_near(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) - #define pgm_read_word_far(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x)))) - #define PSTR(x) x - #if defined F - #undef F - #endif - #define F(X) (X) -#endif +//#if defined(__SAM3X8E__) +// #define PROGMEM +// #define pgm_read_byte(x) (*((char *)x)) +//// #define pgm_read_word(x) (*((short *)(x & 0xfffffffe))) +// #define pgm_read_word(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) +// #define pgm_read_byte_near(x) (*((char *)x)) +// #define pgm_read_byte_far(x) (*((char *)x)) +//// #define pgm_read_word_near(x) (*((short *)(x & 0xfffffffe)) +//// #define pgm_read_word_far(x) (*((short *)(x & 0xfffffffe))) +// #define pgm_read_word_near(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) +// #define pgm_read_word_far(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x)))) +// #define PSTR(x) x +// #if defined F +// #undef F +// #endif +// #define F(X) (X) +//#endif -#endif \ No newline at end of file +#endif diff --git a/MifareClassic.cpp b/MifareClassic.cpp index f591c93..1b745dc 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -4,11 +4,11 @@ #define LONG_TLV_SIZE 4 #define SHORT_TLV_SIZE 2 -#define MIFARE_CLASSIC ("Mifare Classic") - -MifareClassic::MifareClassic(PN532& nfcShield) +MifareClassic::MifareClassic(PN532& nfcShield, uint8_t *staticBuf, unsigned int staticBufSize) { - _nfcShield = &nfcShield; + _nfcShield = &nfcShield; + _staticBufSize = staticBufSize; + _staticBuf = staticBuf; } MifareClassic::~MifareClassic() @@ -31,26 +31,39 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) if (success) { if (!decodeTlv(data, messageLength, messageStartIndex)) { - return NfcTag(uid, uidLength, "ERROR"); // TODO should the error message go in NfcTag? + return NfcTag(uid, uidLength, NfcTag::UNKNOWN); // TODO should the error message go in NfcTag? } } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Error. Failed read block "));Serial.println(currentBlock); - return NfcTag(uid, uidLength, MIFARE_CLASSIC); +#endif + return NfcTag(uid, uidLength, NfcTag::MIFARE_CLASSIC); } } else { +#ifdef NDEF_USE_SERIAL Serial.println(F("Tag is not NDEF formatted.")); +#endif // TODO set tag.isFormatted = false - return NfcTag(uid, uidLength, MIFARE_CLASSIC); + return NfcTag(uid, uidLength, NfcTag::MIFARE_CLASSIC); } // this should be nested in the message length loop int index = 0; int bufferSize = getBufferSize(messageLength); - uint8_t buffer[bufferSize]; + // use shared static buffer + if ( _staticBufSize < bufferSize ) + { +#ifdef NDEF_USE_SERIAL + Serial.print(F("Error. Static buffer to small. is: "));Serial.print(_staticBufSize); + Serial.print(F("required: "));Serial.print(bufferSize); +#endif + return NfcTag(uid, uidLength, NfcTag::MIFARE_CLASSIC); + } + uint8_t *buffer = _staticBuf; #ifdef MIFARE_CLASSIC_DEBUG Serial.print(F("Message Length "));Serial.println(messageLength); @@ -66,7 +79,9 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); if (!success) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Error. Block Authentication failed for "));Serial.println(currentBlock); +#endif // TODO error handling } } @@ -82,7 +97,9 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Read failed "));Serial.println(currentBlock); +#endif // TODO handle errors here } @@ -99,7 +116,7 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) } } - return NfcTag(uid, uidLength, MIFARE_CLASSIC, &buffer[messageStartIndex], messageLength); + return NfcTag(uid, uidLength, NfcTag::MIFARE_CLASSIC, &buffer[messageStartIndex], messageLength); } int MifareClassic::getBufferSize(int messageLength) @@ -144,7 +161,9 @@ int MifareClassic::getNdefStartIndex(byte *data) } else { +#ifdef NDEF_USE_SERIAL Serial.print("Unknown TLV ");Serial.println(data[i], HEX); +#endif return -2; } } @@ -166,7 +185,9 @@ bool MifareClassic::decodeTlv(byte *data, int &messageLength, int &messageStartI if (i < 0 || data[i] != 0x3) { +#ifdef NDEF_USE_SERIAL Serial.println(F("Error. Can't decode message length.")); +#endif return false; } else @@ -198,13 +219,17 @@ boolean MifareClassic::formatNDEF(byte * uid, unsigned int uidLength) boolean success = _nfcShield->mifareclassic_AuthenticateBlock (uid, uidLength, 0, 0, keya); if (!success) { +#ifdef NDEF_USE_SERIAL Serial.println(F("Unable to authenticate block 0 to enable card formatting!")); +#endif return false; } success = _nfcShield->mifareclassic_FormatNDEF(); if (!success) { +#ifdef NDEF_USE_SERIAL Serial.println(F("Unable to format the card for NDEF")); +#endif } else { @@ -216,31 +241,43 @@ boolean MifareClassic::formatNDEF(byte * uid, unsigned int uidLength) { if (!(_nfcShield->mifareclassic_WriteDataBlock (i, emptyNdefMesg))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write block "));Serial.println(i); +#endif } } else { if (!(_nfcShield->mifareclassic_WriteDataBlock (i, sectorbuffer0))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write block "));Serial.println(i); +#endif } } if (!(_nfcShield->mifareclassic_WriteDataBlock (i+1, sectorbuffer0))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write block "));Serial.println(i+1); +#endif } if (!(_nfcShield->mifareclassic_WriteDataBlock (i+2, sectorbuffer0))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write block "));Serial.println(i+2); +#endif } if (!(_nfcShield->mifareclassic_WriteDataBlock (i+3, sectorbuffer4))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write block "));Serial.println(i+3); +#endif } } else { unsigned int iii=uidLength; +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to authenticate block "));Serial.println(i); +#endif _nfcShield->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, (uint8_t*)&iii); } } @@ -276,7 +313,9 @@ boolean MifareClassic::formatMifare(byte * uid, unsigned int uidLength) success = _nfcShield->mifareclassic_AuthenticateBlock (uid, uidLength, BLOCK_NUMBER_OF_SECTOR_TRAILER(idx), 1, (uint8_t *)KEY_DEFAULT_KEYAB); if (!success) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Authentication failed for sector ")); Serial.println(idx); +#endif return false; } @@ -286,7 +325,9 @@ boolean MifareClassic::formatMifare(byte * uid, unsigned int uidLength) memset(blockBuffer, 0, sizeof(blockBuffer)); if (!(_nfcShield->mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 2, blockBuffer))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write to sector ")); Serial.println(idx); +#endif } } else @@ -295,11 +336,15 @@ boolean MifareClassic::formatMifare(byte * uid, unsigned int uidLength) // this block has not to be overwritten for block 0. It contains Tag id and other unique data. if (!(_nfcShield->mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 3, blockBuffer))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write to sector ")); Serial.println(idx); +#endif } if (!(_nfcShield->mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 2, blockBuffer))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write to sector ")); Serial.println(idx); +#endif } } @@ -307,7 +352,9 @@ boolean MifareClassic::formatMifare(byte * uid, unsigned int uidLength) if (!(_nfcShield->mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 1, blockBuffer))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write to sector ")); Serial.println(idx); +#endif } // Step 3: Reset both keys to 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF @@ -319,7 +366,9 @@ boolean MifareClassic::formatMifare(byte * uid, unsigned int uidLength) // Step 4: Write the trailer block if (!(_nfcShield->mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)), blockBuffer))) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unable to write trailer block of sector ")); Serial.println(idx); +#endif } } return true; @@ -328,40 +377,60 @@ boolean MifareClassic::formatMifare(byte * uid, unsigned int uidLength) boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) { - uint8_t encoded[m.getEncodedSize()]; + int encodedSize = m.getEncodedSize(); + // use shared static buffer + if ( _staticBufSize < encodedSize *2 ) + { + #ifdef NDEF_USE_SERIAL + Serial.print(F("Error. Static buffer to small. is: "));Serial.print(_staticBufSize); + Serial.print(F("required: "));Serial.print(encodedSize *2); + #endif + return false; + } + uint8_t *encoded = _staticBuf; m.encode(encoded); - uint8_t buffer[getBufferSize(sizeof(encoded))]; - memset(buffer, 0, sizeof(buffer)); + int bufferSize = getBufferSize(encodedSize); + // use shared static buffer + if ( _staticBufSize < encodedSize + bufferSize ) + { + #ifdef NDEF_USE_SERIAL + Serial.print(F("Error. Static buffer to small. is: "));Serial.print(_staticBufSize); + Serial.print(F("required: "));Serial.print(encodedSize + bufferSize); + #endif + return false; + } + uint8_t *buffer = _staticBuf + encodedSize; + memset(buffer, 0, bufferSize); #ifdef MIFARE_CLASSIC_DEBUG - Serial.print(F("sizeof(encoded) "));Serial.println(sizeof(encoded)); - Serial.print(F("sizeof(buffer) "));Serial.println(sizeof(buffer)); + Serial.print(F("sizeof(encoded) "));Serial.println(encodedSize); + Serial.print(F("sizeof(buffer) "));Serial.println(bufferSize); #endif - if (sizeof(encoded) < 0xFF) + if (encodedSize < 0xFF) { buffer[0] = 0x3; - buffer[1] = sizeof(encoded); - memcpy(&buffer[2], encoded, sizeof(encoded)); - buffer[2+sizeof(encoded)] = 0xFE; // terminator + buffer[1] = encodedSize; + memcpy(&buffer[2], encoded, encodedSize); + buffer[2+encodedSize] = 0xFE; // terminator } else { buffer[0] = 0x3; buffer[1] = 0xFF; - buffer[2] = ((sizeof(encoded) >> 8) & 0xFF); - buffer[3] = (sizeof(encoded) & 0xFF); - memcpy(&buffer[4], encoded, sizeof(encoded)); - buffer[4+sizeof(encoded)] = 0xFE; // terminator + buffer[2] = ((encodedSize >> 8) & 0xFF); + buffer[3] = (encodedSize & 0xFF); + memcpy(&buffer[4], encoded, encodedSize); + buffer[4+encodedSize] = 0xFE; // terminator } // Write to tag - int index = 0; + unsigned int index = 0; int currentBlock = 4; uint8_t key[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; // this is Sector 1 - 15 key - while (index < sizeof(buffer)) + while (index < bufferSize) { if (_nfcShield->mifareclassic_IsFirstBlock(currentBlock)) @@ -369,7 +438,9 @@ boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) int success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); if (!success) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Error. Block Authentication failed for "));Serial.println(currentBlock); +#endif return false; } } @@ -384,7 +455,9 @@ boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Write failed "));Serial.println(currentBlock); +#endif return false; } index += BLOCK_SIZE; diff --git a/MifareClassic.h b/MifareClassic.h index 681959e..f2a3630 100644 --- a/MifareClassic.h +++ b/MifareClassic.h @@ -9,7 +9,7 @@ class MifareClassic { public: - MifareClassic(PN532& nfcShield); + MifareClassic(PN532& nfcShield, uint8_t *staticBuf, unsigned int staticBufSize); ~MifareClassic(); NfcTag read(byte *uid, unsigned int uidLength); boolean write(NdefMessage& ndefMessage, byte *uid, unsigned int uidLength); @@ -17,6 +17,8 @@ class MifareClassic boolean formatMifare(byte * uid, unsigned int uidLength); private: PN532* _nfcShield; + unsigned int _staticBufSize; + uint8_t *_staticBuf; int getBufferSize(int messageLength); int getNdefStartIndex(byte *data); bool decodeTlv(byte *data, int &messageLength, int &messageStartIndex); diff --git a/MifareUltralight.cpp b/MifareUltralight.cpp index 9cf941b..b34c51a 100644 --- a/MifareUltralight.cpp +++ b/MifareUltralight.cpp @@ -8,11 +8,12 @@ #define ULTRALIGHT_DATA_START_INDEX 2 #define ULTRALIGHT_MAX_PAGE 63 -#define NFC_FORUM_TAG_TYPE_2 ("NFC Forum Type 2") -MifareUltralight::MifareUltralight(PN532& nfcShield) +MifareUltralight::MifareUltralight(PN532& nfcShield, uint8_t *staticBuf, unsigned int staticBufSize) { nfc = &nfcShield; + _staticBufSize = staticBufSize; + _staticBuf = staticBuf; ndefStartIndex = 0; messageLength = 0; } @@ -25,8 +26,10 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) { if (isUnformatted()) { +#ifdef NDEF_USE_SERIAL Serial.println(F("WARNING: Tag is not formatted.")); - return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); +#endif + return NfcTag(uid, uidLength, NfcTag::TYPE_2); } readCapabilityContainer(); // meta info for tag @@ -36,13 +39,22 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) if (messageLength == 0) { // data is 0x44 0x03 0x00 0xFE NdefMessage message = NdefMessage(); message.addEmptyRecord(); - return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, message); + return NfcTag(uid, uidLength, NfcTag::TYPE_2, message); } boolean success; uint8_t page; uint8_t index = 0; - byte buffer[bufferSize]; + // use shared static buffer + if ( _staticBufSize < bufferSize ) + { + #ifdef NDEF_USE_SERIAL + Serial.print(F("Error. Static buffer to small. is: "));Serial.print(_staticBufSize); + Serial.print(F("required: "));Serial.print(bufferSize); + #endif + return NfcTag(uid, uidLength, NfcTag::UNKNOWN); + } + byte *buffer = _staticBuf; for (page = ULTRALIGHT_DATA_START_PAGE; page < ULTRALIGHT_MAX_PAGE; page++) { // read the data @@ -56,9 +68,10 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Read failed "));Serial.println(page); - // TODO error handling - messageLength = 0; +#endif + return NfcTag(uid, uidLength, NfcTag::TYPE_2); messageLength = 0; break; } @@ -71,7 +84,7 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) } NdefMessage ndefMessage = NdefMessage(&buffer[ndefStartIndex], messageLength); - return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, ndefMessage); + return NfcTag(uid, uidLength, NfcTag::TYPE_2, ndefMessage); } @@ -86,7 +99,9 @@ boolean MifareUltralight::isUnformatted() } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Error. Failed read page "));Serial.println(page); +#endif return false; } } @@ -166,7 +181,9 @@ boolean MifareUltralight::write(NdefMessage& m, byte * uid, unsigned int uidLeng { if (isUnformatted()) { +#ifdef NDEF_USE_SERIAL Serial.println(F("WARNING: Tag is not formatted.")); +#endif return false; } readCapabilityContainer(); // meta info for tag @@ -182,8 +199,17 @@ boolean MifareUltralight::write(NdefMessage& m, byte * uid, unsigned int uidLeng return false; } - uint8_t encoded[bufferSize]; - uint8_t * src = encoded; + // use shared static buffer + if ( _staticBufSize < bufferSize ) + { + #ifdef NDEF_USE_SERIAL + Serial.print(F("Error. Static buffer to small. is: "));Serial.print(_staticBufSize); + Serial.print(F("required: "));Serial.print(bufferSize); + #endif + return false; + } + uint8_t *encoded = _staticBuf; + uint8_t *src = encoded; unsigned int position = 0; uint8_t page = ULTRALIGHT_DATA_START_PAGE; diff --git a/MifareUltralight.h b/MifareUltralight.h index 2b98849..28121fa 100644 --- a/MifareUltralight.h +++ b/MifareUltralight.h @@ -8,13 +8,15 @@ class MifareUltralight { public: - MifareUltralight(PN532& nfcShield); + MifareUltralight(PN532& nfcShield, uint8_t *staticBuf, unsigned int staticBufSize); ~MifareUltralight(); NfcTag read(byte *uid, unsigned int uidLength); boolean write(NdefMessage& ndefMessage, byte *uid, unsigned int uidLength); boolean clean(); private: PN532* nfc; + unsigned int _staticBufSize; + uint8_t *_staticBuf; unsigned int tagCapacity; unsigned int messageLength; unsigned int bufferSize; diff --git a/Ndef.cpp b/Ndef.cpp index 4de4481..846bc89 100644 --- a/Ndef.cpp +++ b/Ndef.cpp @@ -1,9 +1,10 @@ #include "Ndef.h" +#ifdef NDEF_USE_SERIAL // Borrowed from Adafruit_NFCShield_I2C void PrintHex(const byte * data, const long numBytes) { - uint32_t szPos; + int32_t szPos; for (szPos=0; szPos < numBytes; szPos++) { Serial.print("0x"); @@ -22,7 +23,7 @@ void PrintHex(const byte * data, const long numBytes) // Borrowed from Adafruit_NFCShield_I2C void PrintHexChar(const byte * data, const long numBytes) { - uint32_t szPos; + int32_t szPos; for (szPos=0; szPos < numBytes; szPos++) { // Append leading 0 for small values @@ -55,3 +56,4 @@ void DumpHex(const byte * data, const long numBytes, const unsigned int blockSiz data += blockSize; } } +#endif diff --git a/Ndef.h b/Ndef.h index d5cba82..f5bed4f 100644 --- a/Ndef.h +++ b/Ndef.h @@ -7,12 +7,12 @@ #include -#ifndef NULL - #define NULL (void *)0 -#endif +//#define NDEF_DEBUG 1 +#define NDEF_USE_SERIAL +#ifdef NDEF_USE_SERIAL void PrintHex(const byte *data, const long numBytes); void PrintHexChar(const byte *data, const long numBytes); void DumpHex(const byte *data, const long numBytes, const int blockSize); - #endif +#endif \ No newline at end of file diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 5bede86..98881ea 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -5,7 +5,7 @@ NdefMessage::NdefMessage(void) _recordCount = 0; } -NdefMessage::NdefMessage(const byte * data, const int numBytes) +NdefMessage::NdefMessage(const byte * data, const uint16_t numBytes) { #ifdef NDEF_DEBUG Serial.print(F("Decoding "));Serial.print(numBytes);Serial.println(F(" bytes")); @@ -15,7 +15,7 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) _recordCount = 0; - int index = 0; + uint16_t index = 0; while (index <= numBytes) { @@ -23,20 +23,20 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) // decode tnf - first byte is tnf with bit flags // see the NFDEF spec for more info byte tnf_byte = data[index]; - bool mb = (tnf_byte & 0x80) != 0; - bool me = (tnf_byte & 0x40) != 0; - bool cf = (tnf_byte & 0x20) != 0; - bool sr = (tnf_byte & 0x10) != 0; - bool il = (tnf_byte & 0x8) != 0; + // bool mb = tnf_byte & 0x80; + bool me = tnf_byte & 0x40; + // bool cf = tnf_byte & 0x20; + bool sr = tnf_byte & 0x10; + bool il = tnf_byte & 0x8; byte tnf = (tnf_byte & 0x7); NdefRecord record = NdefRecord(); record.setTnf(tnf); index++; - int typeLength = data[index]; + uint8_t typeLength = data[index]; - int payloadLength = 0; + uint32_t payloadLength = 0; if (sr) { index++; @@ -45,13 +45,14 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) else { payloadLength = - ((0xFF & data[++index]) << 24) - | ((0xFF & data[++index]) << 16) - | ((0xFF & data[++index]) << 8) - | (0xFF & data[++index]); + (static_cast(data[index]) << 24) + | (static_cast(data[index+1]) << 16) + | (static_cast(data[index+2]) << 8) + | static_cast(data[index+3]); + index += 4; } - int idLength = 0; + uint8_t idLength = 0; if (il) { index++; @@ -82,7 +83,7 @@ NdefMessage::NdefMessage(const NdefMessage& rhs) { _recordCount = rhs._recordCount; - for (int i = 0; i < _recordCount; i++) + for (uint8_t i = 0; i < _recordCount; i++) { _records[i] = rhs._records[i]; } @@ -100,14 +101,14 @@ NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) { // delete existing records - for (int i = 0; i < _recordCount; i++) + for (uint8_t i = 0; i < _recordCount; i++) { // TODO Dave: is this the right way to delete existing records? _records[i] = NdefRecord(); } _recordCount = rhs._recordCount; - for (int i = 0; i < _recordCount; i++) + for (uint8_t i = 0; i < _recordCount; i++) { _records[i] = rhs._records[i]; } @@ -115,15 +116,15 @@ NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) return *this; } -unsigned int NdefMessage::getRecordCount() +uint8_t NdefMessage::getRecordCount() { return _recordCount; } -int NdefMessage::getEncodedSize() +uint16_t NdefMessage::getEncodedSize() { - int size = 0; - for (int i = 0; i < _recordCount; i++) + uint16_t size = 0; + for (uint8_t i = 0; i < _recordCount; i++) { size += _records[i].getEncodedSize(); } @@ -135,16 +136,59 @@ void NdefMessage::encode(uint8_t* data) { // assert sizeof(data) >= getEncodedSize() uint8_t* data_ptr = &data[0]; + uint16_t offset = getHeaderSize(); - for (int i = 0; i < _recordCount; i++) + for (uint8_t i = 0; i < _recordCount; i++) { _records[i].encode(data_ptr, i == 0, (i + 1) == _recordCount); - // TODO can NdefRecord.encode return the record size? - data_ptr += _records[i].getEncodedSize(); + + uint16_t encodedSize = _records[i].getEncodedSize(); + _offsets[i] = offset + encodedSize - _records[i].getPayloadLength(); + + offset += encodedSize; + data_ptr += encodedSize; + } + +} + + +uint16_t NdefMessage::getHeaderSize() { + return 2 + (getEncodedSize() > 254 ? 2 : 0); + // TLV is 0x03 + 1 byte length + // OR if size > 254, 0x03 + 3 byte length + // See getHeader() +} + +void NdefMessage::getHeader(byte* header) +{ + uint16_t payloadLength = getEncodedSize(); + bool lengthy = payloadLength > 254; + header[0] = 0x3; + if (lengthy) { + header[1] = 0xFF; + header[2] = payloadLength >> 8; + header[3] = payloadLength; + } else { + header[1] = payloadLength; } +} + +uint16_t NdefMessage::getPackagedSize() +{ + return getEncodedSize() + getHeaderSize() + 1; +} + +void NdefMessage::getPackaged(uint8_t *data) +{ + uint16_t size = getPackagedSize(); + + getHeader(data); + encode(&data[getHeaderSize()]); + data[size-1] = 0xFE; // Termination byte for TLV } + boolean NdefMessage::addRecord(NdefRecord& record) { @@ -156,96 +200,118 @@ boolean NdefMessage::addRecord(NdefRecord& record) } else { +#ifdef NDEF_USE_SERIAL Serial.println(F("WARNING: Too many records. Increase MAX_NDEF_RECORDS.")); +#endif return false; } } -void NdefMessage::addMimeMediaRecord(String mimeType, String payload) +void NdefMessage::addMimeMediaRecord(const char *mimeType, const char* payload) { - - byte payloadBytes[payload.length() + 1]; - payload.getBytes(payloadBytes, sizeof(payloadBytes)); - - addMimeMediaRecord(mimeType, payloadBytes, payload.length()); + addMimeMediaRecord(mimeType, reinterpret_cast(payload), strlen(payload)); } -void NdefMessage::addMimeMediaRecord(String mimeType, uint8_t* payload, int payloadLength) +void NdefMessage::addMimeMediaRecord(const char *mimeType, const byte* payload, uint16_t payloadLength) { - NdefRecord r = NdefRecord(); + NdefRecord r; r.setTnf(TNF_MIME_MEDIA); - byte type[mimeType.length() + 1]; - mimeType.getBytes(type, sizeof(type)); - r.setType(type, mimeType.length()); - + r.setType(reinterpret_cast(mimeType), strlen(mimeType)); r.setPayload(payload, payloadLength); + addRecord(r); + +} +void NdefMessage::addUnknownRecord(const byte *payload, int payloadLength) +{ + NdefRecord r; + r.setTnf(TNF_UNKNOWN); + + r.setType(payload, 0); + r.setPayload(payload, payloadLength); addRecord(r); + } -void NdefMessage::addTextRecord(String text) + +void NdefMessage::addTextRecord(const char *text) { - addTextRecord(text, "en"); + addTextRecord(text, ""); } -void NdefMessage::addTextRecord(String text, String encoding) +void NdefMessage::addTextRecord(const char *text, const char *encoding) { - NdefRecord r = NdefRecord(); + NdefRecord r; r.setTnf(TNF_WELL_KNOWN); uint8_t RTD_TEXT[1] = { 0x54 }; // TODO this should be a constant or preprocessor r.setType(RTD_TEXT, sizeof(RTD_TEXT)); - // X is a placeholder for encoding length - // TODO is it more efficient to build w/o string concatenation? - String payloadString = "X" + encoding + text; - - byte payload[payloadString.length() + 1]; - payloadString.getBytes(payload, sizeof(payload)); + // encoding length + const uint8_t prefixSize = 5; + byte prefix[prefixSize]; + byte encodingSize = strlen(encoding); + prefix[0] = encodingSize; + for (uint8_t i=0; encoding[i] && (i+1) < prefixSize; ++i) // limit encoding to max 4 bytes + prefix[i+1] = encoding[i]; - // replace X with the real encoding length - payload[0] = encoding.length(); - - r.setPayload(payload, payloadString.length()); + // set payload + r.setPayload(prefix, prefixSize, reinterpret_cast(text), strlen(text)); addRecord(r); } -void NdefMessage::addUriRecord(String uri) +void NdefMessage::addUriRecord(const char *uri) { - NdefRecord* r = new NdefRecord(); - r->setTnf(TNF_WELL_KNOWN); + NdefRecord r; + r.setTnf(TNF_WELL_KNOWN); uint8_t RTD_URI[1] = { 0x55 }; // TODO this should be a constant or preprocessor - r->setType(RTD_URI, sizeof(RTD_URI)); + r.setType(RTD_URI, sizeof(RTD_URI)); + + // encoding prefix + const uint8_t prefixSize = 1; + byte prefix[prefixSize] = {0}; + + // set payload + r.setPayload(prefix, prefixSize, reinterpret_cast(uri), strlen(uri)); + + addRecord(r); +} - // X is a placeholder for identifier code - String payloadString = "X" + uri; - byte payload[payloadString.length() + 1]; - payloadString.getBytes(payload, sizeof(payload)); +void NdefMessage::addExternalRecord(const char *type,const char* payload) +{ + addExternalRecord(type, reinterpret_cast(payload), strlen(payload)); +} - // add identifier code 0x0, meaning no prefix substitution - payload[0] = 0x0; +void NdefMessage::addExternalRecord(const char *type, const byte *payload, uint16_t payloadLength) +{ + NdefRecord r; + r.setTnf(TNF_EXTERNAL_TYPE); + + r.setType(reinterpret_cast(type), strlen(type)); + r.setPayload(payload, payloadLength); + addRecord(r); +} - r->setPayload(payload, payloadString.length()); - addRecord(*r); - delete(r); +void NdefMessage::addAndroidApplicationRecord(const char *packageName) +{ + addExternalRecord("android.com:pkg", packageName); } void NdefMessage::addEmptyRecord() { - NdefRecord* r = new NdefRecord(); - r->setTnf(TNF_EMPTY); - addRecord(*r); - delete(r); + NdefRecord r; + r.setTnf(TNF_EMPTY); + addRecord(r); } -NdefRecord NdefMessage::getRecord(int index) +NdefRecord NdefMessage::getRecord(uint8_t index) { - if (index > -1 && index < _recordCount) + if (index > -1 && index < static_cast(_recordCount)) { return _records[index]; } @@ -255,20 +321,29 @@ NdefRecord NdefMessage::getRecord(int index) } } -NdefRecord NdefMessage::operator[](int index) +uint16_t NdefMessage::getOffset(uint8_t index) +{ + if (index < _recordCount) { + return _offsets[index]; + } + return 0; +} + +NdefRecord NdefMessage::operator[](uint8_t index) { return getRecord(index); } +#ifdef NDEF_USE_SERIAL void NdefMessage::print() { Serial.print(F("\nNDEF Message "));Serial.print(_recordCount);Serial.print(F(" record")); _recordCount == 1 ? Serial.print(", ") : Serial.print("s, "); Serial.print(getEncodedSize());Serial.println(F(" bytes")); - int i; - for (i = 0; i < _recordCount; i++) + for (unsigned int i = 0; i < _recordCount; i++) { _records[i].print(); } } +#endif diff --git a/NdefMessage.h b/NdefMessage.h index 4a5b0d5..6c902c5 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -10,30 +10,53 @@ class NdefMessage { public: NdefMessage(void); - NdefMessage(const byte *data, const int numBytes); + NdefMessage(const byte *data, const uint16_t numBytes); NdefMessage(const NdefMessage& rhs); ~NdefMessage(); NdefMessage& operator=(const NdefMessage& rhs); - int getEncodedSize(); // need so we can pass array to encode + uint16_t getEncodedSize(); // need so we can pass array to encode void encode(byte *data); + uint16_t getHeaderSize(); + void getHeader(byte* header); + uint16_t getPackagedSize(); + void getPackaged(uint8_t *data); + boolean addRecord(NdefRecord& record); - void addMimeMediaRecord(String mimeType, String payload); - void addMimeMediaRecord(String mimeType, byte *payload, int payloadLength); - void addTextRecord(String text); - void addTextRecord(String text, String encoding); - void addUriRecord(String uri); + void addMimeMediaRecord(const char *mimeType, const char *payload); + void addMimeMediaRecord(const char *mimeType, const byte *payload, uint16_t payloadLength); + void addTextRecord(const char *text); + void addTextRecord(const char *text, const char *encoding); + void addUriRecord(const char *uri); + + void addExternalRecord(const char *type, const char *payload); + void addExternalRecord(const char *type, const byte *payload, uint16_t payloadLength); + + /** + * Creates an Android Application Record (AAR) http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#aar + * Use an AAR record to cause a P2P message pushed to your Android phone to launch your app even if it's not running. + * Note, Android version must be >= 4.0 and your app must have the package you pass to this method + * + * @param packageName example: "com.acme.myapp" + */ + void addAndroidApplicationRecord(const char *packageName); + + void addUnknownRecord(const byte *payload, int payloadLength); void addEmptyRecord(); - unsigned int getRecordCount(); - NdefRecord getRecord(int index); - NdefRecord operator[](int index); + uint8_t getRecordCount(); + NdefRecord getRecord(uint8_t index); + uint16_t getOffset(uint8_t index); + NdefRecord operator[](uint8_t index); +#ifdef NDEF_USE_SERIAL void print(); +#endif private: NdefRecord _records[MAX_NDEF_RECORDS]; + uint16_t _offsets[MAX_NDEF_RECORDS]; //Stores address offsets of payloads in packaged NDEF unsigned int _recordCount; }; -#endif \ No newline at end of file +#endif diff --git a/NdefRecord.cpp b/NdefRecord.cpp index e26fe26..ea181d4 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -113,34 +113,14 @@ NdefRecord& NdefRecord::operator=(const NdefRecord& rhs) return *this; } -// size of records in bytes -int NdefRecord::getEncodedSize() -{ - int size = 2; // tnf + typeLength - if (_payloadLength > 0xFF) - { - size += 4; - } - else - { - size += 1; - } - - if (_idLength) - { - size += 1; - } - - size += (_typeLength + _payloadLength + _idLength); - return size; +byte NdefRecord::getHeaderSize() { + return getEncodedSize() - _payloadLength; } -void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) -{ - // assert data > getEncodedSize() - uint8_t* data_ptr = &data[0]; +void NdefRecord::getHeader(byte *headerData, bool firstRecord, bool lastRecord) { + uint8_t* data_ptr = &headerData[0]; *data_ptr = getTnfByte(firstRecord, lastRecord); data_ptr += 1; @@ -166,7 +146,6 @@ void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) data_ptr += 1; } - //Serial.println(2); memcpy(data_ptr, _type, _typeLength); data_ptr += _typeLength; @@ -175,9 +154,40 @@ void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) memcpy(data_ptr, _id, _idLength); data_ptr += _idLength; } - +} + +// size of records in bytes +unsigned int NdefRecord::getEncodedSize() +{ + unsigned int size = 2; // tnf + typeLength + if (_payloadLength > 0xFF) + { + size += 4; + } + else + { + size += 1; + } + + if (_idLength) + { + size += 1; + } + + size += (_typeLength + _payloadLength + _idLength); + + return size; +} + +void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) +{ + // assert data > getEncodedSize() + + getHeader(data, firstRecord, lastRecord); + + uint8_t* data_ptr = &data[getHeaderSize()]; + memcpy(data_ptr, _payload, _payloadLength); - data_ptr += _payloadLength; } byte NdefRecord::getTnfByte(bool firstRecord, bool lastRecord) @@ -233,21 +243,12 @@ unsigned int NdefRecord::getIdLength() return _idLength; } -String NdefRecord::getType() +const byte *NdefRecord::getType() { - char type[_typeLength + 1]; - memcpy(type, _type, _typeLength); - type[_typeLength] = '\0'; // null terminate - return String(type); + return _type; } -// this assumes the caller created type correctly -void NdefRecord::getType(uint8_t* type) -{ - memcpy(type, _type, _typeLength); -} - -void NdefRecord::setType(const byte * type, const unsigned int numBytes) +void NdefRecord::setType(const byte * type, unsigned int numBytes) { if(_typeLength) { @@ -259,48 +260,53 @@ void NdefRecord::setType(const byte * type, const unsigned int numBytes) _typeLength = numBytes; } -// assumes the caller sized payload properly -void NdefRecord::getPayload(byte *payload) +const byte *NdefRecord::getPayload() { - memcpy(payload, _payload, _payloadLength); + return _payload; } -void NdefRecord::setPayload(const byte * payload, const int numBytes) +void NdefRecord::setPayload(const byte * payload, int numBytes) { - if (_payloadLength) - { - free(_payload); - } - - _payload = (byte*)malloc(numBytes); - memcpy(_payload, payload, numBytes); - _payloadLength = numBytes; + setPayload(NULL, 0, payload, numBytes); } -String NdefRecord::getId() +void NdefRecord::setPayload(const byte *prefix, int prefixSize, const byte *payload, int numBytes) { - char id[_idLength + 1]; - memcpy(id, _id, _idLength); - id[_idLength] = '\0'; // null terminate - return String(id); + int size = prefixSize + numBytes; + if (_payloadLength && _payloadLength < size) + { + free(_payload); + } + if (_payloadLength < size) + { + _payload = (byte*)malloc(size); + } + if ( prefixSize ) + memcpy(_payload, prefix, prefixSize); + if ( numBytes ) + memcpy(_payload + prefixSize, payload, numBytes); + _payloadLength = size; } -void NdefRecord::getId(byte *id) +const byte *NdefRecord::getId() { - memcpy(id, _id, _idLength); + return _id; } void NdefRecord::setId(const byte * id, const unsigned int numBytes) { - if (_idLength) + if (_idLength && _payloadLength < numBytes) { - free(_id); + free(_id); + } + if (_idLength < numBytes) + { + _id = (byte*)malloc(numBytes); } - - _id = (byte*)malloc(numBytes); memcpy(_id, id, numBytes); _idLength = numBytes; } +#ifdef NDEF_USE_SERIAL void NdefRecord::print() { @@ -350,3 +356,4 @@ void NdefRecord::print() Serial.print(F(" Record is "));Serial.print(getEncodedSize());Serial.println(" bytes"); } +#endif diff --git a/NdefRecord.h b/NdefRecord.h index a74d22e..72cc909 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -22,7 +22,9 @@ class NdefRecord ~NdefRecord(); NdefRecord& operator=(const NdefRecord& rhs); - int getEncodedSize(); + byte getHeaderSize(); + void getHeader(byte *headerData, bool firstRecord, bool lastRecord); + unsigned int getEncodedSize(); void encode(byte *data, bool firstRecord, bool lastRecord); unsigned int getTypeLength(); @@ -30,20 +32,19 @@ class NdefRecord unsigned int getIdLength(); byte getTnf(); - void getType(byte *type); - void getPayload(byte *payload); - void getId(byte *id); - - // convenience methods - String getType(); - String getId(); + const byte *getType(); + const byte *getId(); + const byte *getPayload(); void setTnf(byte tnf); - void setType(const byte *type, const unsigned int numBytes); - void setPayload(const byte *payload, const int numBytes); - void setId(const byte *id, const unsigned int numBytes); + void setType(const byte *type, unsigned int numBytes); + void setPayload(const byte *payload, int numBytes); + void setPayload(const byte *prefix, int prefixSize, const byte *payload, int numBytes); + void setId(const byte *id, unsigned int numBytes); +#ifdef NDEF_USE_SERIAL void print(); +#endif private: byte getTnfByte(bool firstRecord, bool lastRecord); byte _tnf; // 3 bit diff --git a/NfcAdapter.cpp b/NfcAdapter.cpp index 30efe3e..eaf9262 100644 --- a/NfcAdapter.cpp +++ b/NfcAdapter.cpp @@ -1,8 +1,10 @@ #include -NfcAdapter::NfcAdapter(PN532Interface &interface) +NfcAdapter::NfcAdapter(PN532Interface &interface, uint8_t *staticBuf, unsigned int staticBufSize) { shield = new PN532(interface); + _staticBufSize = staticBufSize; + _staticBuf = staticBuf; } NfcAdapter::~NfcAdapter(void) @@ -18,15 +20,19 @@ void NfcAdapter::begin(boolean verbose) if (! versiondata) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Didn't find PN53x board")); +#endif while (1); // halt } if (verbose) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Found chip PN5")); Serial.println((versiondata>>24) & 0xFF, HEX); Serial.print(F("Firmware ver. ")); Serial.print((versiondata>>16) & 0xFF, DEC); Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); +#endif } // configure board to read RFID tags shield->SAMConfig(); @@ -50,7 +56,6 @@ boolean NfcAdapter::tagPresent(unsigned long timeout) boolean NfcAdapter::erase() { - boolean success; NdefMessage message = NdefMessage(); message.addEmptyRecord(); return write(message); @@ -59,14 +64,18 @@ boolean NfcAdapter::erase() boolean NfcAdapter::format() { boolean success; +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC if (uidLength == 4) { - MifareClassic mifareClassic = MifareClassic(*shield); + MifareClassic mifareClassic = MifareClassic(*shield, _staticBuf, _staticBufSize); success = mifareClassic.formatNDEF(uid, uidLength); } else +#endif { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unsupported Tag.")); +#endif success = false; } return success; @@ -76,25 +85,32 @@ boolean NfcAdapter::clean() { uint8_t type = guessTagType(); - if (type == TAG_TYPE_MIFARE_CLASSIC) +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC + if (type == NfcTag::MIFARE_CLASSIC) { #ifdef NDEF_DEBUG Serial.println(F("Cleaning Mifare Classic")); #endif - MifareClassic mifareClassic = MifareClassic(*shield); + MifareClassic mifareClassic = MifareClassic(*shield, _staticBuf, _staticBufSize); return mifareClassic.formatMifare(uid, uidLength); } - else if (type == TAG_TYPE_2) + else +#endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA + if (type == NfcTag::TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Cleaning Mifare Ultralight")); #endif - MifareUltralight ultralight = MifareUltralight(*shield); + MifareUltralight ultralight = MifareUltralight(*shield, _staticBuf, _staticBufSize); return ultralight.clean(); } else +#endif { +#ifdef NDEF_USE_SERIAL Serial.print(F("No driver for card type "));Serial.println(type); +#endif return false; } @@ -105,30 +121,38 @@ NfcTag NfcAdapter::read() { uint8_t type = guessTagType(); - if (type == TAG_TYPE_MIFARE_CLASSIC) +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC + if (type == NfcTag::MIFARE_CLASSIC) { #ifdef NDEF_DEBUG Serial.println(F("Reading Mifare Classic")); #endif - MifareClassic mifareClassic = MifareClassic(*shield); + MifareClassic mifareClassic = MifareClassic(*shield, _staticBuf, _staticBufSize); return mifareClassic.read(uid, uidLength); } - else if (type == TAG_TYPE_2) + else +#endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA + if (type == NfcTag::TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Reading Mifare Ultralight")); #endif - MifareUltralight ultralight = MifareUltralight(*shield); + MifareUltralight ultralight = MifareUltralight(*shield, _staticBuf, _staticBufSize); return ultralight.read(uid, uidLength); } - else if (type == TAG_TYPE_UNKNOWN) + else +#endif + if (type == NfcTag::UNKNOWN) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); +#endif return NfcTag(uid, uidLength); } else { - Serial.print(F("No driver for card type "));Serial.println(type); + // Serial.print(F("No driver for card type "));Serial.println(type); // TODO should set type here return NfcTag(uid, uidLength); } @@ -140,30 +164,40 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) boolean success; uint8_t type = guessTagType(); - if (type == TAG_TYPE_MIFARE_CLASSIC) +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC + if (type == NfcTag::MIFARE_CLASSIC) { #ifdef NDEF_DEBUG Serial.println(F("Writing Mifare Classic")); #endif - MifareClassic mifareClassic = MifareClassic(*shield); + MifareClassic mifareClassic = MifareClassic(*shield, _staticBuf, _staticBufSize); success = mifareClassic.write(ndefMessage, uid, uidLength); } - else if (type == TAG_TYPE_2) + else +#endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA + if (type == NfcTag::TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Writing Mifare Ultralight")); #endif - MifareUltralight mifareUltralight = MifareUltralight(*shield); + MifareUltralight mifareUltralight = MifareUltralight(*shield, _staticBuf, _staticBufSize); success = mifareUltralight.write(ndefMessage, uid, uidLength); } - else if (type == TAG_TYPE_UNKNOWN) + else +#endif + if (type == NfcTag::UNKNOWN) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); +#endif success = false; } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("No driver for card type "));Serial.println(type); +#endif success = false; } @@ -185,10 +219,10 @@ unsigned int NfcAdapter::guessTagType() if (uidLength == 4) { - return TAG_TYPE_MIFARE_CLASSIC; + return NfcTag::MIFARE_CLASSIC; } else { - return TAG_TYPE_2; + return NfcTag::TYPE_2; } } diff --git a/NfcAdapter.h b/NfcAdapter.h index 1ef9e59..f3073eb 100644 --- a/NfcAdapter.h +++ b/NfcAdapter.h @@ -6,10 +6,20 @@ #include #include +// choose supported formats +#define NDEF_SUPPORT_MIFARE_CLASSIC +#define NDEF_SUPPORT_MIFARE_ULTRA + // Drivers -#include -#include +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC + #include +#endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA + #include +#endif + +// tag types #define TAG_TYPE_MIFARE_CLASSIC (0) #define TAG_TYPE_1 (1) #define TAG_TYPE_2 (2) @@ -22,7 +32,7 @@ class NfcAdapter { public: - NfcAdapter(PN532Interface &interface); + NfcAdapter(PN532Interface &interface, uint8_t *staticBuf, unsigned int staticBufSize); ~NfcAdapter(void); void begin(boolean verbose=true); @@ -39,6 +49,8 @@ class NfcAdapter { PN532* shield; byte uid[7]; // Buffer to store the returned UID unsigned int uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) + unsigned int _staticBufSize; + uint8_t *_staticBuf; unsigned int guessTagType(); }; diff --git a/NfcTag.cpp b/NfcTag.cpp index 10c004d..ade66fc 100644 --- a/NfcTag.cpp +++ b/NfcTag.cpp @@ -4,7 +4,7 @@ NfcTag::NfcTag() { _uid = 0; _uidLength = 0; - _tagType = "Unknown"; + _tagType = NfcTag::UNKNOWN; _ndefMessage = (NdefMessage*)NULL; } @@ -12,11 +12,11 @@ NfcTag::NfcTag(byte *uid, unsigned int uidLength) { _uid = uid; _uidLength = uidLength; - _tagType = "Unknown"; + _tagType = NfcTag::UNKNOWN; _ndefMessage = (NdefMessage*)NULL; } -NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType) +NfcTag::NfcTag(byte *uid, unsigned int uidLength, NfcTag::Type tagType) { _uid = uid; _uidLength = uidLength; @@ -24,7 +24,7 @@ NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType) _ndefMessage = (NdefMessage*)NULL; } -NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType, NdefMessage& ndefMessage) +NfcTag::NfcTag(byte *uid, unsigned int uidLength, NfcTag::Type tagType, NdefMessage& ndefMessage) { _uid = uid; _uidLength = uidLength; @@ -33,7 +33,7 @@ NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType, NdefMessage& } // I don't like this version, but it will use less memory -NfcTag::NfcTag(byte *uid, unsigned int uidLength, String tagType, const byte *ndefData, const int ndefDataLength) +NfcTag::NfcTag(byte *uid, unsigned int uidLength, NfcTag::Type tagType, const byte *ndefData, const int ndefDataLength) { _uid = uid; _uidLength = uidLength; @@ -70,28 +70,7 @@ void NfcTag::getUid(byte *uid, unsigned int uidLength) memcpy(uid, _uid, _uidLength < uidLength ? _uidLength : uidLength); } -String NfcTag::getUidString() -{ - String uidString = ""; - for (int i = 0; i < _uidLength; i++) - { - if (i > 0) - { - uidString += " "; - } - - if (_uid[i] < 0xF) - { - uidString += "0"; - } - - uidString += String((unsigned int)_uid[i], (unsigned char)HEX); - } - uidString.toUpperCase(); - return uidString; -} - -String NfcTag::getTagType() +NfcTag::Type NfcTag::getTagType() { return _tagType; } @@ -105,11 +84,12 @@ NdefMessage NfcTag::getNdefMessage() { return *_ndefMessage; } +#ifdef NDEF_USE_SERIAL void NfcTag::print() { Serial.print(F("NFC Tag - "));Serial.println(_tagType); - Serial.print(F("UID "));Serial.println(getUidString()); + //Serial.print(F("UID "));Serial.println(getUidString()); // TODO: fix when dust has settled if (_ndefMessage == NULL) { Serial.println(F("\nNo NDEF Message")); @@ -119,3 +99,4 @@ void NfcTag::print() _ndefMessage->print(); } } +#endif diff --git a/NfcTag.h b/NfcTag.h index 39fa6cd..a7e556d 100644 --- a/NfcTag.h +++ b/NfcTag.h @@ -8,24 +8,27 @@ class NfcTag { public: + enum Type { MIFARE_CLASSIC = 0, TYPE_1, TYPE_2, TYPE_3, TYPE_4, UNKNOWN = 99 }; + NfcTag(); NfcTag(byte *uid, unsigned int uidLength); - NfcTag(byte *uid, unsigned int uidLength, String tagType); - NfcTag(byte *uid, unsigned int uidLength, String tagType, NdefMessage& ndefMessage); - NfcTag(byte *uid, unsigned int uidLength, String tagType, const byte *ndefData, const int ndefDataLength); + NfcTag(byte *uid, unsigned int uidLength, Type tagType); + NfcTag(byte *uid, unsigned int uidLength, Type tagType, NdefMessage& ndefMessage); + NfcTag(byte *uid, unsigned int uidLength, Type tagType, const byte *ndefData, const int ndefDataLength); ~NfcTag(void); NfcTag& operator=(const NfcTag& rhs); uint8_t getUidLength(); void getUid(byte *uid, unsigned int uidLength); - String getUidString(); - String getTagType(); + Type getTagType(); boolean hasNdefMessage(); NdefMessage getNdefMessage(); +#ifdef NDEF_USE_SERIAL void print(); +#endif private: byte *_uid; unsigned int _uidLength; - String _tagType; // Mifare Classic, NFC Forum Type {1,2,3,4}, Unknown + Type _tagType; // Mifare Classic, NFC Forum Type {1,2,3,4}, Unknown NdefMessage* _ndefMessage; // TODO capacity // TODO isFormatted diff --git a/new.cpp b/new.cpp new file mode 100644 index 0000000..a0b5597 --- /dev/null +++ b/new.cpp @@ -0,0 +1,41 @@ +/* + Copyright (c) 2014 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "new.h" + +void *operator new(size_t size) { + return malloc(size); +} + +void *operator new[](size_t size) { + return malloc(size); +} + +void operator delete(void * ptr) { + free(ptr); +} + +void operator delete[](void * ptr) { + free(ptr); +} +//http://www.avrfreaks.net/forum/avr-c-micro-how?name=PNphpBB2&file=viewtopic&t=59453 +int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);}; +void __cxa_guard_release (__guard *g) {*(char *)g = 1;}; +void __cxa_guard_abort (__guard *) {}; +void __cxa_pure_virtual(void) {}; diff --git a/new.h b/new.h new file mode 100644 index 0000000..403459a --- /dev/null +++ b/new.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2014 Arduino. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef NEW_H +#define NEW_H + +#include + +void * operator new(size_t size); +void * operator new[](size_t size); +void operator delete(void * ptr); +void operator delete[](void * ptr); + +//http://www.avrfreaks.net/forum/avr-c-micro-how?name=PNphpBB2&file=viewtopic&t=59453 +__extension__ typedef int __guard __attribute__((mode (__DI__))); + +extern "C" int __cxa_guard_acquire(__guard *); +extern "C" void __cxa_guard_release (__guard *); +extern "C" void __cxa_guard_abort (__guard *); +extern "C" void __cxa_pure_virtual(void); +#endif + diff --git a/ntag.cpp b/ntag.cpp new file mode 100644 index 0000000..d8e0f7c --- /dev/null +++ b/ntag.cpp @@ -0,0 +1,683 @@ +#include "ntag.h" +#ifdef ARDUINO_STM_NUCLEU_F103RB +#include "HardWire.h" +HardWire HWire(1, I2C_REMAP);// | I2C_BUS_RESET); // I2c1 +#else +#include "Wire.h" +#define HWire Wire +#endif +#include + +#include + + +Ntag::Ntag(DEVICE_TYPE dt, byte fd_pin, byte vout_pin, byte i2c_address): + _dt(dt), + _fd_pin(fd_pin), + _vout_pin(vout_pin), + _i2c_address(i2c_address), + _rfBusyStartTime(0), + _triggered(false) +{ + _debouncer = Bounce(); +} + +bool Ntag::begin(){ + bool bResult=true; + HWire.begin(); +#ifndef ARDUINO_SAM_DUE + HWire.beginTransmission(_i2c_address); + bResult=HWire.endTransmission()==0; +#else + //Arduino Due always sends at least 2 bytes for every I²C operation. This upsets the NTAG. + return true; +#endif + if(_vout_pin!=0){ + pinMode(_vout_pin, INPUT); + } + pinMode(_fd_pin, INPUT); + _debouncer.attach(_fd_pin); + _debouncer.interval(5); // interval in ms + return bResult; +} + +bool Ntag::isReaderPresent() +{ + if(_vout_pin==0) + { + return false; + } + return digitalRead(_vout_pin)==HIGH; +} + +void Ntag::detectI2cDevices(){ + for(byte i=0;i<0x80;i++){ + + HWire.beginTransmission(i); + if(HWire.endTransmission()==0) + { + Serial.print(F("Found I2C device at 0x")); + Serial.println(i,HEX); + delay(100); + +/* This is broken and will hang the I2C bus + if (i != _i2c_address) { + byte setAddress = _i2c_address; + _i2c_address = i; + + byte config[NTAG_BLOCK_SIZE] = {0x0}; + if (readBlock(CONFIG, 0, config, NTAG_BLOCK_SIZE) && config[0] == NXP_MFR_ID) { + Serial.print(F("Found NXP device (mfr code 0x04). Reassigning address to ")); + Serial.println(setAddress, HEX); + delay(100); + config[0] = setAddress << 1; + //if (writeBlock(CONFIG, 0, config)) { Serial.println(" ...success."); } + } else { + Serial.println("Does not respond as NTAG."); + delay(100); + //Wire.begin(); + } + //HWire.endTransmission(); + _i2c_address = setAddress; + } +*/ + } + Serial.print(i); Serial.print(" "); delay(100); + } +} + +byte Ntag::getUidLength() +{ + return UID_LENGTH; +} + +bool Ntag::getUid(byte *uid, unsigned int uidLength) +{ + byte data[UID_LENGTH]; + if(!readBlock(CONFIG, 0,data,UID_LENGTH)) + { + return false; + } + if(data[0]!=4) + { + return false; + } + memcpy(uid, data, UID_LENGTH < uidLength ? UID_LENGTH : uidLength); + return true; +} + + +bool Ntag::setFd_ReaderHandshake(){ + //return writeRegister(NC_REG, 0x3C,0x18); + return writeRegister(NC_REG, 0x3C,0x28); + //0x28: FD_OFF=10b, FD_ON=10b : FD constant low + //Start of read by reader always clears the FD-pin. + //At the end of the read by reader, the FD-pin becomes high (most of the times) + //0x18: FD pulse high (13.9ms wide) at the beginning of the read sequence, no effect on write sequence. + //0x14: FD_OFF=01b, FD_ON=01b : FD constant high + //0x24: FD constant high +} + +bool Ntag::isRfBusy(){ + byte regVal; + const byte RF_LOCKED=5; + _debouncer.update(); + //Reading this register clears the FD-pin. + //When continuously polling this register while RF reading or writing is ongoing, high will be returned for 2ms, followed + //by low for 9ms, then high again for 2ms then low again for 9ms and so on. + //To get a nice clean high or low instead of spikes, a software retriggerable monostable that triggers on rfBusy will be used. + if(!readRegister(NS_REG, regVal)) + { + Serial.println("Can't read register."); + } + if(bitRead(regVal,RF_LOCKED) || _debouncer.rose()) + { + //retrigger monostable + _rfBusyStartTime=millis(); + _triggered=true; + return true; + } + if(_triggered && millis()<_rfBusyStartTime+30) + { + //a zero has been read, but monostable hasn't run out yet + return true; + } + return false; +} + +//Mirror SRAM to EEPROM +//Remark that the SRAM mirroring is only valid for the RF-interface. +//For the I²C-interface, you still have to use blocks 0xF8 and higher to access SRAM area (see datasheet Table 6) +bool Ntag::setSramMirrorRf(bool bEnable, byte mirrorBaseBlockNr){ + _mirrorBaseBlockNr = bEnable ? mirrorBaseBlockNr : 0; + if(!writeRegister(SRAM_MIRROR_BLOCK,0xFF,mirrorBaseBlockNr)){ + return false; + } + //disable pass-through mode (because it's not compatible with SRAM⁻mirroring: datasheet §11.2). + //enable/disable SRAM memory mirror + return writeRegister(NC_REG, 0x42, bEnable ? 0x02 : 0x00); +} + +bool Ntag::readConfigBlock(byte *data) { + return readBlock(CONFIG, 0, data, NTAG_BLOCK_SIZE); +} + +bool Ntag::readConfigBytes() { + byte data[16] = {0}; + bool dump = false; + bool ok = true; + + // Read and check block 56, dynamic lock bytes and access control + if (!readBlock(REGISTER, 56, data, NTAG_BLOCK_SIZE)) { + Serial.println("Unable to read registers, may be bricked..."); + ok = false; + } + for (uint8_t i = 8; i < 15; i++) { + if (data[i] != 0) { + Serial.println("Non-zero lock byte!"); + dump = true; + ok = false; + } + } + if (data[15] != 0xFF) { + Serial.print("Password protect range changed from default to: "); + Serial.println(data[15]); + ok = false; + } + if (dump) { + Serial.println("Dynamic lock bytes (block 56):"); + printHex(&data[8], 4); + Serial.println("RFU & AUTH0"); + printHex(&data[12], 4); + } + + // Read and check block 57, password (reads as 0) and other locking bits + dump = false; + if (!readBlock(REGISTER, 57, data, NTAG_BLOCK_SIZE)) { + Serial.println("Unable to read registers in block 57, may be bricked..."); + ok = false; + } + for (uint8_t i = 0; i < 16; i++) { + if (data[i] != 0) { + Serial.println("Non-zero byte in configuration register block 57!"); + dump = true; + ok = false; + break; + } + } + if (dump) { + Serial.print("57:0-3 "); + printHex(data, 4); + Serial.print("ACCESS (NFC access restrict): "); + Serial.println(data[0],2); + Serial.print("57:4-7 "); + printHex(&data[4], 4); + Serial.print("57:8-11 "); + printHex(&data[8], 4); + Serial.print("57:12-15 "); + printHex(&data[12], 4); + Serial.print("PT_I2C (I2C access restrict): "); + Serial.println(data[12], 2); + } + + // Read and check block 58, (startup) configuration registers + readBlock(REGISTER, 58, data, NTAG_BLOCK_SIZE); + uint8_t config_ref[7] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00}; + dump = false; + for (uint8_t i = 0; i < 7; i++) { + if (data[i] != config_ref[i]) { + dump = true; + ok = false; + Serial.println("Bad values in config (startup) registers at 0x3A (58): "); + break; + } + } + if (dump) { + printHex(data, 7); + Serial.println("expected:"); + printHex(config_ref, 7); + } + + // Read and check block 254, session registers + readBlock(REGISTER, 254, data, NTAG_BLOCK_SIZE); + dump = false; + for (uint8_t i = 0; i < 7; i++) { + if (data[i] != config_ref[i]) { + dump = true; + ok = false; + Serial.println("Bad values in session registers at 0xFE (254): "); + break; + } + } + if (dump) { + printHex(data, 7); + Serial.println("expected:"); + printHex(config_ref, 7); + } + + return ok; +} + + +bool Ntag::resetConfigBytes() { + bool ok = true; + + byte data[16] = {0}; + // block 56, last 2 pages (8 bytes) should be all 0 except AUTH0 which should be 0xFF + data[15] = 0xFF; + // This hard-coded hack is block 56, byte 8 + if (!write(REGISTER, 0x388, &data[8], 8)) { + Serial.println("Colud not reset dynamic lock bytes and password protected region!"); + ok = false; + } + + + // block 57 should be all 0's + data[15] = 0x00; + if (!writeBlock(REGISTER, 57, data)) { + Serial.println("Could not reset configuration register block 57!"); + ok = false; + } + + // Block 58, configuration (startup) register should be as follows + byte config[7] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00}; + if (!write(REGISTER, 58*16, config, 7)) { + Serial.println("Could not set startup config registers!"); + ok = false; + } + + config[6] = 0x01; + if (!write(REGISTER, 254*16, config, 7)) { + Serial.println("Could not set session config registers!"); + ok = false; + } + + if (ok) { Serial.println("\nConfiguration registers reset successful.\n"); } + + return ok; +} + + +bool Ntag::setContainerClass() { + byte ccdata[4] = NTAG_CC_NDEF_FULL; + return setContainerClass(ccdata); +} + +// TODO augment to write lock bits as well, with #defined intelligent defaults to pick from +bool Ntag::setContainerClass(byte* ccdata) +{ + byte config[NTAG_BLOCK_SIZE]; + read(CONFIG, 0, config, NTAG_BLOCK_SIZE); // Read existing configuration block + config[0] = DEFAULT_I2C_ADDRESS << 1; // Byte 0 always reads as 0x04, but must be written + // as I2C address in top 7 bits + memcpy(&config[12], ccdata, 4); // Container class is last 4 of 16 byte config block + write(CONFIG, 0, config, NTAG_BLOCK_SIZE); +} + +bool Ntag::writeNdef(uint16_t address, NdefMessage &message, bool sprint=true){ + // Determine whether address is SRAM; if not assume EEPROM (user). + // If assumption is wrong, the write operations will (and should) fail. + BLOCK_TYPE bt = SRAM; + if(address/NTAG_BLOCK_SIZE < 0xF8 || address/NTAG_BLOCK_SIZE > 0xFB){ bt = USERMEM; } // See isAddressValid + + uint16_t size = message.getPackagedSize(); + byte data[size]; + message.getPackaged(data); + +#ifdef NDEF_USE_SERIAL + Serial.println("success.\n Now writing..."); + Serial.print("Free memory just before write: "); + Serial.println(freeMemory()); + Serial.flush(); +#endif + + return write(bt, address, data, size); + /* + // Get & write the NDEF header, incrementing address + uint8_t ndefHeaderSize = message.getHeaderSize(); + byte ndefHeader[ndefHeaderSize]; + message.getHeader(ndefHeader); + if (sprint) { + Serial.print("Header at "); + Serial.print(address); + Serial.print(", data: "); + printHex(ndefHeader, ndefHeaderSize); + } + if (!write(bt, address, ndefHeader, ndefHeaderSize)) { + if (sprint) { + Serial.print("Write failed to address "); + Serial.print(address); + Serial.print(", block type "); + Serial.println(bt, HEX); + } + return false; + } + address += ndefHeaderSize; + /* + // Iterate over the records, writing each + // if no payload, then Serial.print the starting address, size, ending address + for (uint8_t i = 0; i < message.getRecordCount(); i++) { + NdefRecord rec = message.getRecord(i); + if (rec.hasPayload()) { + uint8_t encodedSize = rec.getEncodedSize(); + byte encoded[encodedSize]; + rec.encode(encoded, i == 0, i == message.getRecordCount()-1); + if (!write(bt, address, encoded, encodedSize)) { return false; } + if (sprint) { + Serial.print(F("Wrote record ")); + Serial.print(i); + Serial.print(F(" starting at address ")); + Serial.println(address); + rec.print(); + } + address += encodedSize; + } else { + uint8_t headerSize = rec.getHeaderSize(); + byte header[headerSize]; + rec.getHeader(header, i == 0, i == message.getRecordCount()-1); + if (!write(bt, address, header, headerSize)) { + Serial.print("Write failed to address "); + Serial.println(address); + return false; } + if (sprint) { + Serial.print(F("Wrote record ")); + Serial.print(i); + Serial.print(F(" starting at address ")); + Serial.print(address); + Serial.println(F(" (HEADER ONLY)")); + Serial.print(F("Payload starts at address ")); + Serial.println(address + headerSize); + rec.print(); + } + address += rec.getEncodedSize(); + } + + } + + // Write the 0xFE termination byte + byte term[1] = {0xFE}; + if (!write(bt, address, term, 1)) { return false; } + if (sprint){ + Serial.print("Finished writing NDEF. Termination byte at address "); + Serial.println(address); + } + */ + //return true; +} + +bool Ntag::zeroEeprom() +{ + byte blockNr = 1; + byte data[NTAG_BLOCK_SIZE] = {0}; + while ( blockNr < 0x38 && writeBlock(USERMEM, blockNr, data)) { // Figure out why this is and code appropriately + blockNr++; + } + // TODO zero first 8 bytes of block 38... + if (!isAddressValid(USERMEM, blockNr)) { return true; } // If we made it through all user mem + return false; +} + + +bool Ntag::readSram(uint16_t address, byte *pdata, uint16_t length) +{ + return read(SRAM, address+SRAM_BASE_ADDR, pdata, length); +} + +bool Ntag::writeSram(uint16_t address, byte *pdata, uint16_t length) +{ + return write(SRAM, address+SRAM_BASE_ADDR, pdata, length); +} + +bool Ntag::readEeprom(uint16_t address, byte *pdata, uint16_t length) +{ + return read(USERMEM, address+EEPROM_BASE_ADDR, pdata, length); +} + +bool Ntag::writeEeprom(uint16_t address, byte *pdata, uint16_t length) +{ + return write(USERMEM, address+EEPROM_BASE_ADDR, pdata, length); +} + +void Ntag::releaseI2c() +{ + //reset I2C_LOCKED bit + writeRegister(NS_REG,0x40,0); +} + +bool Ntag::write(BLOCK_TYPE bt, uint16_t address, byte* pdata, uint16_t length) +{ + byte readbuffer[NTAG_BLOCK_SIZE]; + uint16_t writeLength; + byte* wptr=pdata; + byte blockNr=address/NTAG_BLOCK_SIZE; + + if(address % NTAG_BLOCK_SIZE !=0) + { + //start address doesn't point to start of block, so the bytes in this block that precede the address range must + //be read. + if(!readBlock(bt, blockNr, readbuffer, NTAG_BLOCK_SIZE)) + { + return false; + } + writeLength=min(NTAG_BLOCK_SIZE - (address % NTAG_BLOCK_SIZE), length); + memcpy(readbuffer + (address % NTAG_BLOCK_SIZE), pdata, writeLength); + if(!writeBlock(bt, blockNr, readbuffer)) + { + return false; + } + wptr+=writeLength; + blockNr++; + } + while(wptr < pdata+length) + { + writeLength=(pdata+length-wptr > NTAG_BLOCK_SIZE ? NTAG_BLOCK_SIZE : pdata+length-wptr); + if(writeLength!=NTAG_BLOCK_SIZE){ + if(!readBlock(bt, blockNr, readbuffer, NTAG_BLOCK_SIZE)) + { + return false; + } + memcpy(readbuffer, wptr, writeLength); + } + if(!writeBlock(bt, blockNr, writeLength==NTAG_BLOCK_SIZE ? wptr : readbuffer)) + { + return false; + } + wptr+=writeLength; + blockNr++; + } + if (bt == USERMEM) { _lastMemBlockWritten = --blockNr; } + return true; +} + +bool Ntag::read(BLOCK_TYPE bt, uint16_t address, byte* pdata, uint16_t length) +{ + byte readbuffer[NTAG_BLOCK_SIZE]; + uint16_t readLength; + byte* wptr=pdata; + + readLength=min(NTAG_BLOCK_SIZE, (address % NTAG_BLOCK_SIZE) + length); + if(!readBlock(bt, address/NTAG_BLOCK_SIZE, readbuffer, readLength)) + { + return false; + } + readLength-=address % NTAG_BLOCK_SIZE; + memcpy(wptr,readbuffer + (address % NTAG_BLOCK_SIZE), readLength); + wptr+=readLength; + for(byte i=(address/NTAG_BLOCK_SIZE)+1;wptr NTAG_BLOCK_SIZE ? NTAG_BLOCK_SIZE : pdata+length-wptr); + if(!readBlock(bt, i, wptr, readLength)) + { + return false; + } + wptr+=readLength; + } + return true; +} + +bool Ntag::readBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data, byte data_size) +{ + if(data_size>NTAG_BLOCK_SIZE || !writeBlockAddress(bt, memBlockAddress)){ + return false; + } + if(!end_transmission()){ + return false; + } + HWire.beginTransmission(_i2c_address); + if(HWire.requestFrom(_i2c_address,data_size)!=data_size){ + return false; + } + byte i=0; + while(HWire.available()) + { + p_data[i++] = HWire.read(); + } + return i==data_size; +} + +bool Ntag::setLastNdefBlock() +{ + //When SRAM mirroring is used, the LAST_NDEF_BLOCK must point to USERMEM, not to SRAM + return writeRegister(LAST_NDEF_BLOCK, 0xFF, isAddressValid(SRAM, _lastMemBlockWritten) ? + _lastMemBlockWritten - (SRAM_BASE_ADDR>>4) + _mirrorBaseBlockNr : _lastMemBlockWritten); +} + +bool Ntag::writeBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data) +{ + if(!writeBlockAddress(bt, memBlockAddress)){ + return false; + } + for (int i=0; i6 || !writeBlockAddress(REGISTER, STARTUP_REG_ADDR)){ + return false; + } + if(HWire.write(regAddr)!=1){ + bRetVal=false; + } + if(!end_transmission()){ + return false; + } + HWire.beginTransmission(_i2c_address); + if(HWire.requestFrom(_i2c_address,(byte)1)!=1){ + return false; + } + value=HWire.read(); + return bRetVal; +} + + +bool Ntag::writeRegister(REGISTER_NR regAddr, byte mask, byte regdat) +{ + bool bRetVal=false; + if(regAddr>7 || !writeBlockAddress(REGISTER, STARTUP_REG_ADDR)){ // Note that 0xFE is session registers, volatile if power cycles! + return false; + } + if (HWire.write(regAddr)==1 && + HWire.write(mask)==1 && + HWire.write(regdat)==1){ + bRetVal=true; + } + return end_transmission() && bRetVal; +} + +bool Ntag::writeBlockAddress(BLOCK_TYPE dt, byte addr) +{ + if(!isAddressValid(dt, addr)){ +#ifdef NDEF_USE_SERIAL + Serial.print("NTAG: Invalid block : 0x"); + Serial.print(addr, HEX); + Serial.print(" for type "); + Serial.println(dt); + Serial.flush(); +#endif + return false; + } + HWire.beginTransmission(_i2c_address); + return HWire.write(addr)==1; +} + +bool Ntag::end_transmission(void) +{ + return HWire.endTransmission()==0; + //I2C_LOCKED must be either reset to 0b at the end of the I2C sequence or wait until the end of the watch dog timer. +} + +bool Ntag::isAddressValid(BLOCK_TYPE type, byte blocknr){ + switch(type){ + case CONFIG: + if(blocknr!=0){ + return false; + } + break; + case USERMEM: + switch (_dt) { + case NTAG_I2C_1K: + if(blocknr < 1 || blocknr > 0x38){ + return false; + } + break; + case NTAG_I2C_2K: + if(blocknr < 1 || blocknr > 0x78){ // Not consistent with observed failure at 0x3B + return false; + } + break; + default: + return false; + } + break; + case SRAM: + if(blocknr < 0xF8 || blocknr > 0xFB){ + return false; + } + break; + case REGISTER: + if(blocknr == SESSION_REG_ADDR || blocknr == STARTUP_REG_ADDR + || (blocknr >= 56 && blocknr <= 58) + || blocknr == 254) { + return true; + } + break; + default: + return false; + } + return true; +} + + +void Ntag::printHex(byte* data, uint8_t len) { + for(int i=0;i +#include + +#define NDEF_USE_SERIAL + +#define NTAG_CC_NDEF_FULL {0xE1, 0x10, 0x6D, 0x00} // Container class to use all of sector 0 for NDEF + +class Ntag +{ +public: + typedef enum{ + NTAG_I2C_1K, + NTAG_I2C_2K + }DEVICE_TYPE; + typedef enum{ + NC_REG, + LAST_NDEF_BLOCK, + SRAM_MIRROR_BLOCK, + WDT_LS, + WDT_MS, + I2C_CLOCK_STR, + NS_REG + }REGISTER_NR; + static const uint16_t NTAG_BLOCK_SIZE=16; + static const byte NXP_MFR_ID=0x04; + Ntag(DEVICE_TYPE dt, byte fd_pin, byte vout_pin, byte i2c_address = DEFAULT_I2C_ADDRESS); + void detectI2cDevices();//Comes in handy when you accidentally changed the I²C address of the NTAG. + bool begin(); + bool getUid(byte *uid, unsigned int uidLength); + byte getUidLength(); + bool isRfBusy(); + bool isReaderPresent(); + + // TODO: There is a choice of writing to configuration or session registers for the SRAM mirror setting. + // only startup (configuration) currently works, and is hard-coded default by consequence of + // writeRegister() implementation. + // Session config is lost on power-on reset. + bool setSramMirrorRf(bool bEnable, byte mirrorBaseBlockNr); + bool setFd_ReaderHandshake(); + bool readConfigBlock(byte *data); + bool readConfigBytes(); // Return false if unable to read *or* non-default values + bool resetConfigBytes(); // To factory defaults + bool setContainerClass(); + bool setContainerClass(byte* ccdata); + bool writeNdef(uint16_t address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 16) + bool zeroEeprom(); + bool readEeprom(uint16_t address, byte* pdata, uint16_t length);//starts at address 0 + bool writeEeprom(uint16_t address, byte* pdata, uint16_t length);//starts at address 0 + bool readSram(uint16_t address, byte* pdata, uint16_t length);//starts at address 0 + bool writeSram(uint16_t address, byte* pdata, uint16_t length);//starts at address 0 + bool readRegister(REGISTER_NR regAddr, byte &value); + bool writeRegister(REGISTER_NR regAddr, byte mask, byte regdat); + bool setLastNdefBlock(); // The block whose memory read governs FD pin toggle rules + void releaseI2c(); +private: + typedef enum{ + CONFIG=0x1,//BLOCK0 (putting this in a separate block type, because errors here can "brick" the device.) + USERMEM=0x2,//EEPROM + REGISTER=0x4,//Settings registers + SRAM=0x8 + }BLOCK_TYPE; + static const byte UID_LENGTH=7; + static const byte DEFAULT_I2C_ADDRESS=0x55; + static const uint16_t EEPROM_BASE_ADDR=0x1<<4; + static const uint16_t SRAM_BASE_ADDR=0xF8<<4; + static const uint16_t STARTUP_REG_ADDR=0x3A; + static const uint16_t SESSION_REG_ADDR=0xFE; + bool write(BLOCK_TYPE bt, uint16_t address, byte* pdata, uint16_t length); + bool read(BLOCK_TYPE bt, uint16_t address, byte* pdata, uint16_t length); + bool readBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data, byte data_size); + bool writeBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data); + bool writeBlockAddress(BLOCK_TYPE dt, byte addr); + bool end_transmission(void); + bool isAddressValid(BLOCK_TYPE dt, byte blocknr); + bool setLastNdefBlock(byte memBlockAddress); + void printHex(byte* data, uint8_t len); + byte _i2c_address; + DEVICE_TYPE _dt; + byte _fd_pin; + byte _vout_pin; + byte _lastMemBlockWritten; + byte _mirrorBaseBlockNr; + Bounce _debouncer; + unsigned long _rfBusyStartTime; + bool _triggered; +}; + +#endif // NTAG_H diff --git a/tests/NdefMemoryTest/NdefMemoryTest.ino b/tests/NdefMemoryTest/NdefMemoryTest.ino index b20c3ae..e5671dd 100644 --- a/tests/NdefMemoryTest/NdefMemoryTest.ino +++ b/tests/NdefMemoryTest/NdefMemoryTest.ino @@ -2,7 +2,8 @@ #include #include #include - #include +#include +#include void leakCheck(void (*callback)()) { @@ -30,7 +31,9 @@ void record() void emptyRecord() { NdefRecord* r = new NdefRecord(); +#ifdef NDEF_USE_SERIAL r->print(); +#endif delete r; } @@ -42,17 +45,19 @@ void textRecord() r->setType(type, sizeof(type)); uint8_t payload[] = { 0x1A, 0x1B, 0x1C }; r->setPayload(payload, sizeof(payload)); +#ifdef NDEF_USE_SERIAL r->print(); +#endif delete r; } void recordMallocZero() { NdefRecord r = NdefRecord(); - String type = r.getType(); - String id = r.getId(); - byte payload[r.getPayloadLength()]; - r.getPayload(payload); + String type = (const char *) r.getType(); + String id = (const char *) r.getId(); + const byte *payload = r.getPayload(); //[r.getPayloadLength()]; + //payload = r.getPayload(); } // this is OK @@ -66,7 +71,9 @@ void emptyMessage() void printEmptyMessage() { NdefMessage* m = new NdefMessage(); +#ifdef NDEF_USE_SERIAL m->print(); +#endif delete m; } @@ -74,14 +81,18 @@ void printEmptyMessage() void printEmptyMessageNoNew() { NdefMessage m = NdefMessage(); +#ifdef NDEF_USE_SERIAL m.print(); +#endif } void messageWithTextRecord() { NdefMessage m = NdefMessage(); m.addTextRecord("foo"); +#ifdef NDEF_USE_SERIAL m.print(); +#endif } void messageWithEmptyRecord() @@ -89,7 +100,9 @@ void messageWithEmptyRecord() NdefMessage m = NdefMessage(); NdefRecord r = NdefRecord(); m.addRecord(r); +#ifdef NDEF_USE_SERIAL m.print(); +#endif } void messageWithoutHelper() @@ -102,7 +115,9 @@ void messageWithoutHelper() uint8_t payload[] = { 0x02, 0x65, 0x6E, 0x66, 0x6F, 0x6F }; r.setPayload(payload, sizeof(payload)); m.addRecord(r); +#ifdef NDEF_USE_SERIAL m.print(); +#endif } void messageWithId() @@ -117,28 +132,36 @@ void messageWithId() uint8_t id[] = { 0x0, 0x0, 0x0 }; r.setId(id, sizeof(id)); m.addRecord(r); +#ifdef NDEF_USE_SERIAL m.print(); +#endif } void message80() { NdefMessage message = NdefMessage(); message.addTextRecord("This record is 80 characters.X01234567890123456789012345678901234567890123456789"); +#ifdef NDEF_USE_SERIAL //message.print(); +#endif } void message100() { NdefMessage message = NdefMessage(); message.addTextRecord("This record is 100 characters.0123456789012345678901234567890123456789012345678901234567890123456789"); +#ifdef NDEF_USE_SERIAL //message.print(); +#endif } void message120() { NdefMessage message = NdefMessage(); message.addTextRecord("This record is 120 characters.012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); +#ifdef NDEF_USE_SERIAL //message.print(); +#endif } void setup() { @@ -171,12 +194,19 @@ test(recordAccessorLeaks) test(messageLeaks) { + Serial.println(F("emptyMessage")); assertNoLeak(&emptyMessage); + Serial.println(F("printEmptyMessage")); assertNoLeak(&printEmptyMessage); + Serial.println(F("printEmptyMessageNoNew")); assertNoLeak(&printEmptyMessageNoNew); + Serial.println(F("messageWithTextRecord")); assertNoLeak(&messageWithTextRecord); + Serial.println(F("messageWithEmptyRecord")); assertNoLeak(&messageWithEmptyRecord); + Serial.println(F("messageWithoutHelper")); assertNoLeak(&messageWithoutHelper); + Serial.println(F("messageWithId")); assertNoLeak(&messageWithId); } diff --git a/tests/NdefMessageTest/NdefMessageTest.ino b/tests/NdefMessageTest/NdefMessageTest.ino index 6ca1fcf..8d95cd8 100644 --- a/tests/NdefMessageTest/NdefMessageTest.ino +++ b/tests/NdefMessageTest/NdefMessageTest.ino @@ -3,6 +3,7 @@ #include #include #include +#include // Custom Assertion void assertNoLeak(void (*callback)()) @@ -16,6 +17,10 @@ void assertNoLeak(void (*callback)()) void assertBytesEqual(const uint8_t* expected, const uint8_t* actual, int size) { for (int i = 0; i < size; i++) { // Serial.print("> ");Serial.print(expected[i]);Serial.print(" ");Serial.println(actual[i]); + if (expected[i] != actual[i]) { + Serial.print("\nassertBytesEqual() failing at index "); + Serial.println(i); + } assertEqual(expected[i], actual[i]); } } @@ -60,10 +65,10 @@ test(assign) assertEqual(r1.getPayloadLength(), r2.getPayloadLength()); assertEqual(r1.getIdLength(), r2.getIdLength()); - byte p1[r1.getPayloadLength()]; - byte p2[r2.getPayloadLength()]; - r1.getPayload(p1); - r2.getPayload(p2); + //byte p1[r1.getPayloadLength()]; + //byte p2[r2.getPayloadLength()]; + const byte *p1 = r1.getPayload(); + const byte *p2 = r2.getPayload(); int size = r1.getPayloadLength(); assertBytesEqual(p1, p2, size); @@ -99,10 +104,10 @@ test(assign2) // TODO check type - byte p1[r1.getPayloadLength()]; - byte p2[r2.getPayloadLength()]; - r1.getPayload(p1); - r2.getPayload(p2); + //byte p1[r1.getPayloadLength()]; + //byte p2[r2.getPayloadLength()]; + const byte * p1 = r1.getPayload(); + const byte * p2 = r2.getPayload(); int size = r1.getPayloadLength(); assertBytesEqual(p1, p2, size); @@ -120,7 +125,11 @@ test(assign3) { NdefMessage* m1 = new NdefMessage(); - m1->addTextRecord("We the People of the United States, in Order to form a more perfect Union..."); + const char text[77] = "We the People of the United States, in Order to form a more perfect Union..."; + Serial.print("strlen() of 77-char c-string: "); + Serial.println(strlen(text)); + + m1->addTextRecord(text); NdefMessage* m2 = new NdefMessage(); @@ -132,16 +141,20 @@ test(assign3) assertEqual(TNF_WELL_KNOWN, r.getTnf()); assertEqual(1, r.getTypeLength()); - assertEqual(79, r.getPayloadLength()); + assertEqual(81, r.getPayloadLength()); // 76 chars (excluding \0) of payload + 5-byte prefix assertEqual(0, r.getIdLength()); ::String s = "We the People of the United States, in Order to form a more perfect Union..."; + Serial.print("length() of String: "); + Serial.println(s.length()); + byte payload[s.length() + 1]; - s.getBytes(payload, sizeof(payload)); - - byte p[r.getPayloadLength()]; - r.getPayload(p); - assertBytesEqual(payload, p+3, s.length()); + s.getBytes(payload, sizeof(payload)); // This should copy 77 characters, so include the \0 + PrintHex(payload, 10); + //byte p[r.getPayloadLength()]; + const byte *p = r.getPayload(); + PrintHex(p, 10); + assertBytesEqual(payload, p+5, s.length()); // Offset must be 81 - 76 = 5 delete m2; } @@ -218,6 +231,155 @@ test(doublePayload) assertEqual(0, (start-end)); } + +test(message_packaging_size) +{ + NdefMessage m; + m.addTextRecord("012345678901234567890123456789012345678901234567890123456789"); // 60-char string (excluding \0) + m.addTextRecord("012345678901234567890123456789012345678901234567890123456789"); + m.addTextRecord("012345678901234567890123456789012345678901234567890123456789"); + + NdefRecord r = m.getRecord(0); + uint8_t rSize = r.getEncodedSize(); + Serial.print("Encoded record uses "); + Serial.print(rSize); + Serial.println(" bytes."); + + // Confirms expected headers when total message payload length < 254 bytes + assertEqual(rSize*3, m.getEncodedSize()); + assertEqual(rSize*3 + 3, m.getPackagedSize()); + + m.addTextRecord("012345678901234567890123456789012345678901234567890123456789"); + + // Now 4*60 = 240 bytes, plus record headers, puts us over 254 bytes + // -> 3-byte TLV length specification + 0x03 (start) + 0xFE (end) = 5 bytes + assertEqual(rSize*4, m.getEncodedSize()); + assertEqual(rSize*4 + 5, m.getPackagedSize()); +} + + +test(message_packaged_content) +{ + NdefMessage m; + NdefRecord r; + byte payload[3] = {0xAA, 0xBB, 0xCC}; + + r.setTnf(TNF_UNKNOWN); + r.setPayload(payload, 3); + m.addRecord(r); + + uint8_t len = m.getPackagedSize(); + uint8_t p[len]; + m.getPackaged(p); + + PrintHex(p, len); + assertEqual(0x03, p[0]); // Start of message tag + assertEqual(0xFE, p[len-1]); // End of message terminator + assertEqual(0xCC, p[len-2]); // End of record contents +} + + +// Opportunities for screw-ups in size type width and header logic exist once records are >254 bytes +test(big_record_handling) +{ + NdefRecord r; + uint16_t len = 300; + byte payload[len]; + randomSeed(0xB8); + for (uint16_t i = 0; i < len; i++) { + payload[i] = random(len); + } + + r.setPayload(payload, len); + r.setTnf(TNF_UNKNOWN); + + assertEqual(len, r.getPayloadLength()); + + Serial.print("Record with payload size "); + Serial.print(len); + Serial.print(" bytes has encoded size "); + uint16_t e_len = r.getEncodedSize(); + Serial.println(e_len); + assertTrue(e_len > len); + + NdefMessage m; + m.addRecord(r); + assertEqual(r.getEncodedSize(), m.getEncodedSize()); + assertEqual(r.getEncodedSize() + 5, m.getPackagedSize()); + Serial.print("NDEF package size: "); + Serial.println(m.getPackagedSize()); + + NdefRecord r2; + uint16_t len2 = 64; + byte payload2[len2]; + payload2[0] = 0x12; + r2.setPayload(payload2, len2); + r2.setTnf(TNF_UNKNOWN); + + m.addRecord(r2); + assertEqual(r.getEncodedSize() + r2.getEncodedSize() + 5, m.getPackagedSize()); + + Serial.print("Memory before packaging: "); + Serial.println(freeMemory()); + byte package[m.getPackagedSize()]; + m.getPackaged(package); + Serial.print("Memory after packaging: "); + Serial.println(freeMemory()); + + bool found = false; + for (uint16_t i = 0; i < m.getPackagedSize(); i++) { + if (package[i] == 0x12) { found = true; break; } + } + assertTrue(found); + + //Confirm that the termination byte is in the right spot + assertEqual(0xFE, package[m.getPackagedSize()-1]); + + //Confirm that the entire payload was copied into place (sounds paranoid, right?) + uint16_t offset = m.getHeaderSize() + r.getEncodedSize() - len; + for (uint16_t i = 0; i < len; i++) { + if (payload[i] != package[i+offset]) { + Serial.print("Package inconsistency at payload index "); + Serial.print(i); + Serial.println("; data:"); + PrintHex(&payload[i], 8); + Serial.print("Package data at index "); + Serial.println(i+offset); + PrintHex(&package[i+offset], 8); + } + assertEqual(payload[i], package[i+offset]); + } +} + + +test(payload_offset_calculation) { + NdefRecord r; + uint16_t len = 300; + byte payload[len]; + payload[0] = 0xAA; + r.setPayload(payload, len); + r.setTnf(TNF_UNKNOWN); + + + NdefRecord r2(r); + payload[0] = 0xBB; + r2.setPayload(payload, len); + + NdefMessage m; + m.addRecord(r); + m.addRecord(r2); + + uint16_t pSize = m.getPackagedSize(); + byte package[pSize]; + m.getPackaged(package); + + PrintHex(&package[m.getOffset(0)-3], 8); + + assertEqual(0xAA, package[m.getOffset(0)]); + assertEqual(0xBB, package[m.getOffset(1)]); +} + + test(aaa_printFreeMemoryAtStart) // warning: relies on fact tests are run in alphabetical order { Serial.println(F("---------------------")); @@ -233,6 +395,8 @@ test(zzz_printFreeMemoryAtEnd) // warning: relies on fact tests are run in alp Serial.println(F("=====================")); } + + void loop() { Test::run(); } \ No newline at end of file diff --git a/tests/NdefUnitTest/NdefUnitTest.ino b/tests/NdefUnitTest/NdefUnitTest.ino index d1f8c93..8904345 100644 --- a/tests/NdefUnitTest/NdefUnitTest.ino +++ b/tests/NdefUnitTest/NdefUnitTest.ino @@ -2,6 +2,7 @@ #include #include #include +#include void assertBytesEqual(const uint8_t* expected, const uint8_t* actual, uint8_t size) { for (int i = 0; i < size; i++) { @@ -10,7 +11,7 @@ void assertBytesEqual(const uint8_t* expected, const uint8_t* actual, uint8_t si } void setup() { - Serial.begin(9600); + Serial.begin(115200); } test(accessors) { @@ -33,18 +34,18 @@ test(accessors) { assertEqual(sizeof(id), record.getIdLength()); assertEqual(6, record.getIdLength()); - uint8_t typeCheck[record.getTypeLength()]; - record.getType(typeCheck); + //uint8_t typeCheck[record.getTypeLength()]; + const byte *typeCheck = record.getType(/*typeCheck*/); assertEqual(0x54, typeCheck[0]); assertBytesEqual(recordType, typeCheck, sizeof(recordType)); - uint8_t payloadCheck[record.getPayloadLength()]; - record.getPayload(&payloadCheck[0]); + //uint8_t payloadCheck[record.getPayloadLength()]; + const byte * payloadCheck = record.getPayload(/*&payloadCheck[0]*/); assertBytesEqual(payload, payloadCheck, sizeof(payload)); - uint8_t idCheck[record.getIdLength()]; - record.getId(&idCheck[0]); + //uint8_t idCheck[record.getIdLength()]; + const byte * idCheck = record.getId(/*&idCheck[0]*/); assertBytesEqual(id, idCheck, sizeof(id)); } @@ -68,15 +69,15 @@ test(newaccessors) { assertEqual(sizeof(id), record.getIdLength()); assertEqual(6, record.getIdLength()); - ::String typeCheck = record.getType(); + ::String typeCheck = (const char *)record.getType(); assertTrue(typeCheck.equals("T")); - byte payloadCheck[record.getPayloadLength()]; - record.getPayload(payloadCheck); + //byte payloadCheck[record.getPayloadLength()]; + const byte *payloadCheck = record.getPayload(/*payloadCheck*/); assertBytesEqual(payload, payloadCheck, sizeof(payload)); - byte idCheck[record.getIdLength()]; - record.getId(idCheck); + //byte idCheck[record.getIdLength()]; + const byte *idCheck = record.getId(/*idCheck*/); assertBytesEqual(id, idCheck, sizeof(id)); } @@ -101,15 +102,15 @@ test(assignment) assertEqual(sizeof(payload), record2.getPayloadLength()); assertEqual(sizeof(id), record2.getIdLength()); - ::String typeCheck = record.getType(); + ::String typeCheck = (const char *)record.getType(); assertTrue(typeCheck.equals("T")); - byte payload2[record2.getPayloadLength()]; - record2.getPayload(payload2); + //byte payload2[record2.getPayloadLength()]; + const byte *payload2 = record2.getPayload(/*payload2*/); assertBytesEqual(payload, payload2, sizeof(payload)); - byte id2[record.getIdLength()]; - record2.getId(id2); + //byte id2[record.getIdLength()]; + const byte *id2 = record2.getId(/*id2*/); assertBytesEqual(id, id2, sizeof(id)); } @@ -119,15 +120,15 @@ test(getEmptyPayload) assertEqual(TNF_EMPTY, r.getTnf()); assertEqual(0, r.getPayloadLength()); - byte payload[r.getPayloadLength()]; - r.getPayload(payload); + //byte payload[r.getPayloadLength()]; + const byte *payload = r.getPayload(/*payload*/); - byte id[r.getIdLength()]; - r.getId(id); + //byte id[r.getIdLength()]; + const byte *id = r.getId(/*id*/); byte empty[0]; - assertBytesEqual(empty, payload, sizeof(payload)); - assertBytesEqual(empty, id, sizeof(id)); + assertBytesEqual(empty, payload, r.getPayloadLength()); + assertBytesEqual(empty, id, r.getIdLength()); } test(encoding_without_record_id) { @@ -168,6 +169,34 @@ test(encoding_with_record_id) { assertBytesEqual(encodedBytes, expectedBytes, sizeof(encodedBytes)); } +// Tests ability to request the record header size and contents +test(record_header) { + // This follows the same setup as the record id test + NdefRecord record = NdefRecord(); + record.setTnf(TNF_WELL_KNOWN); + uint8_t recordType[] = { 0x54 }; // "T" Text Record + assertEqual(0x54, recordType[0]); + record.setType(recordType, sizeof(recordType)); + // 2 + "en" + "Unit Test" + uint8_t payload[] = { 0x02, 0x65, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74 }; + record.setPayload(payload, sizeof(payload)); + // testid + uint8_t id[] = { 0x74, 0x65, 0x73, 0x74, 0x69, 0x64}; + record.setId(id, sizeof(id)); + + uint8_t encodedBytes[record.getEncodedSize()]; + record.encode(encodedBytes, true, true); + uint8_t expectedBytes[] = { 217, 1, 12, 6, 84, 116, 101, 115, 116, 105, 100, 2, 101, 110, 85, 110, 105, 116, 32, 84, 101, 115, 116 }; + + // A little circular but does test the new logic + assertEqual(sizeof(payload), record.getEncodedSize() - record.getHeaderSize()); + + byte header[record.getHeaderSize()]; + record.getHeader(header, true, true); + assertBytesEqual(header, expectedBytes, record.getHeaderSize()); +} + + void loop() { Test::run(); -} +} \ No newline at end of file diff --git a/tests/NfcTagTest/NfcTagTest.ino b/tests/NfcTagTest/NfcTagTest.ino index 066096f..047bdbd 100644 --- a/tests/NfcTagTest/NfcTagTest.ino +++ b/tests/NfcTagTest/NfcTagTest.ino @@ -2,6 +2,7 @@ #include #include #include +#include void setup() { Serial.begin(9600); @@ -14,16 +15,16 @@ test(getUid) byte uidFromTag[sizeof(uid)]; NfcTag tag = NfcTag(uid, sizeof(uid)); - + assertEqual(sizeof(uid), tag.getUidLength()); - + tag.getUid(uidFromTag, sizeof(uidFromTag)); - + // make sure the 2 uids are the same for (int i = 0; i < sizeof(uid); i++) { assertEqual(uid[i], uidFromTag[i]); } - + // check contents, to ensure the original uid wasn't overwritten assertEqual(0x00, uid[0]); assertEqual(0xFF, uid[1]); diff --git a/tests/NtagHardwareTest/NtagHardwareTest.ino b/tests/NtagHardwareTest/NtagHardwareTest.ino new file mode 100644 index 0000000..9759575 --- /dev/null +++ b/tests/NtagHardwareTest/NtagHardwareTest.ino @@ -0,0 +1,162 @@ +#define HARDI2C +#include +#include +#include +#include +#include +#include +#include + + + +// In addition to I2C, these must be wired to Arduino for +// the features that use them to work. +#define FD_PIN (4) +#define VOUT_PIN (2) +#define VCC_PIN (3) // Assumes NTAG Vcc supplied using GPIO + +// Single global ntag instance +Ntag ntag(Ntag::NTAG_I2C_2K, FD_PIN, VOUT_PIN); + + +// Custom Assertion +void assertNoLeak(void (*callback)()) +{ + int start = freeMemory(); + (*callback)(); + int end = freeMemory(); + assertEqual(0, (start - end)); +} + +void assertBytesEqual(const uint8_t* expected, const uint8_t* actual, int size) { + for (int i = 0; i < size; i++) { + // Serial.print("> ");Serial.print(expected[i]);Serial.print(" ");Serial.println(actual[i]); + if (expected[i] != actual[i]) { + Serial.print("\nassertBytesEqual() failing at index "); + Serial.println(i); + } + assertEqual(expected[i], actual[i]); + } +} + +void setup() { + Serial.begin(9600); + pinMode(VCC_PIN, OUTPUT); + digitalWrite(VCC_PIN, HIGH); + if(!ntag.begin()){ + Serial.println("Can't find ntag. Expect tests to fail."); + } +} + +////////////// Begin Tests ////////////////////////////////////////////////////////////////////////// + +test(basic_config) { + + byte config[ntag.NTAG_BLOCK_SIZE]; + assertTrue(ntag.readConfigBlock(config)); + + // Memory address 0 (first byte of block 0) always reads as manufacturer ID + assertEqual(0x04 /*ntag.NXP_MFR_ID*/, config[0]); + + ntag.setContainerClass(); + assertTrue(ntag.readConfigBlock(config)); + + // Confirm we've successfully written an intelligent default Container Class data set + byte ccCheck[4] = NTAG_CC_NDEF_FULL; + assertBytesEqual(&config[12], ccCheck, 4); +} + + +// From test created by LieBtrau +test(eeprom_read_write) { + byte eepromdata[2*16]; + byte readeeprom[16]; + + for(byte i=0;i<2*16;i++){ + eepromdata[i]=0x80 | i; + } + + Serial.println("Writing block 1"); + assertTrue(ntag.writeEeprom(0,eepromdata,16)); + + Serial.println("Writing block 2"); + assertTrue(ntag.writeEeprom(16,eepromdata+16,16)); + + Serial.println("\nReading memory block 1"); + assertTrue(ntag.readEeprom(0,readeeprom,16)); + PrintHex(readeeprom,16); + assertBytesEqual(eepromdata, readeeprom, 16); + + Serial.println("Reading memory block 2"); + assertTrue(ntag.readEeprom(16,readeeprom,16)); + PrintHex(readeeprom,16); + assertBytesEqual(&eepromdata[16], readeeprom, 16); + + Serial.println("Reading bytes 10 to 20: partly block 1, partly block 2"); + assertTrue(ntag.readEeprom(10,readeeprom,10)); + PrintHex(readeeprom,10); + assertBytesEqual(&eepromdata[10], readeeprom, 10); + + + Serial.println("Writing byte 15 to 20: partly block 1, partly block 2"); + for(byte i=0;i<6;i++){ + eepromdata[i]=0x70 | i; + } + assertTrue(ntag.writeEeprom(15,eepromdata,6)); + + Serial.println("\nReading memory block 1"); + assertTrue(ntag.readEeprom(0,readeeprom,16)); + PrintHex(readeeprom,16); + + + Serial.println("Reading memory block 2"); + assertTrue(ntag.readEeprom(16,readeeprom,16)); + PrintHex(readeeprom,16); + assertBytesEqual(&eepromdata[1], readeeprom, 5); +} + + +test(write_big_ndef) { + NdefRecord r; + uint16_t len = 512; + byte payload[len]; + for (uint16_t i = 0x0; i < len; payload[i++] = i); + + r.setPayload(payload, len); + + assertEqual(len, r.getPayloadLength()); + + NdefMessage m; + m.addRecord(r); + assertEqual(r.getEncodedSize(), m.getEncodedSize()); + assertEqual(r.getEncodedSize() + 5, m.getPackagedSize()); + + ntag.zeroEeprom(); + uint16_t p_len = m.getPackagedSize(); + Serial.print("Packaged length in bytes: "); + Serial.println(p_len); + + byte data[p_len]; + m.getPackaged(data); + //ntag.writeEeprom(0, data, m.getPackagedSize()); + Serial.println("End of package: "); + PrintHex(&data[p_len-8], 16); + assertEqual(0xFE, data[p_len-1]); + assertEqual(0x03, data[0]); + + ntag.writeNdef(16, m, true); + Serial.println("Completed message write."); + byte readback[16]; + ntag.readEeprom(m.getPackagedSize()-1, readback, 16); + Serial.print("Confirming termination byte at page 0x"); Serial.println((m.getPackagedSize()-1)/4 + 4, HEX); Serial.flush(); + PrintHex(readback, 16); + assertEqual(0xFE, readback[0]); // Confirm termination byte in right place + + ntag.readEeprom(0, readback, 16); + assertEqual(0x03, readback[0]); // Confirm TLV start byte +} + + +void loop() { + Test::run(); +} \ No newline at end of file