From 80fc99926bd07aa65b013e02271d4fa53258e811 Mon Sep 17 00:00:00 2001 From: Christopher Creutzig Date: Tue, 10 Mar 2015 13:27:44 +0100 Subject: [PATCH 01/35] compiles with -Wall -Werror -std=c++11 --- MifareClassic.cpp | 2 +- Ndef.cpp | 4 ++-- Ndef.h | 2 -- NdefMessage.cpp | 38 +++++++++++++++++++------------------- NfcAdapter.cpp | 6 +++--- NfcTag.cpp | 2 +- 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/MifareClassic.cpp b/MifareClassic.cpp index f591c93..7cbed0f 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -357,7 +357,7 @@ boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) } // 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 diff --git a/Ndef.cpp b/Ndef.cpp index 4de4481..0827fd8 100644 --- a/Ndef.cpp +++ b/Ndef.cpp @@ -3,7 +3,7 @@ // 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 +22,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 diff --git a/Ndef.h b/Ndef.h index 97e6524..fb5f296 100644 --- a/Ndef.h +++ b/Ndef.h @@ -7,8 +7,6 @@ #include -#define NULL (void *)0 - 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); diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 5bede86..3b69d52 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -23,11 +23,11 @@ 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(); @@ -36,7 +36,7 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) index++; int typeLength = data[index]; - int payloadLength = 0; + uint32_t payloadLength = 0; if (sr) { index++; @@ -44,11 +44,12 @@ 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]); + payloadLength = + (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; @@ -82,7 +83,7 @@ NdefMessage::NdefMessage(const NdefMessage& rhs) { _recordCount = rhs._recordCount; - for (int i = 0; i < _recordCount; i++) + for (unsigned int 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 (unsigned int 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 (unsigned int i = 0; i < _recordCount; i++) { _records[i] = rhs._records[i]; } @@ -123,7 +124,7 @@ unsigned int NdefMessage::getRecordCount() int NdefMessage::getEncodedSize() { int size = 0; - for (int i = 0; i < _recordCount; i++) + for (unsigned int i = 0; i < _recordCount; i++) { size += _records[i].getEncodedSize(); } @@ -136,7 +137,7 @@ void NdefMessage::encode(uint8_t* data) // assert sizeof(data) >= getEncodedSize() uint8_t* data_ptr = &data[0]; - for (int i = 0; i < _recordCount; i++) + for (unsigned int i = 0; i < _recordCount; i++) { _records[i].encode(data_ptr, i == 0, (i + 1) == _recordCount); // TODO can NdefRecord.encode return the record size? @@ -245,7 +246,7 @@ void NdefMessage::addEmptyRecord() NdefRecord NdefMessage::getRecord(int index) { - if (index > -1 && index < _recordCount) + if (index > -1 && index < static_cast(_recordCount)) { return _records[index]; } @@ -266,8 +267,7 @@ void NdefMessage::print() _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(); } diff --git a/NfcAdapter.cpp b/NfcAdapter.cpp index 30efe3e..50ac1b8 100644 --- a/NfcAdapter.cpp +++ b/NfcAdapter.cpp @@ -50,7 +50,6 @@ boolean NfcAdapter::tagPresent(unsigned long timeout) boolean NfcAdapter::erase() { - boolean success; NdefMessage message = NdefMessage(); message.addEmptyRecord(); return write(message); @@ -113,7 +112,8 @@ NfcTag NfcAdapter::read() MifareClassic mifareClassic = MifareClassic(*shield); return mifareClassic.read(uid, uidLength); } - else if (type == TAG_TYPE_2) + else + if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Reading Mifare Ultralight")); @@ -128,7 +128,7 @@ NfcTag NfcAdapter::read() } 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); } diff --git a/NfcTag.cpp b/NfcTag.cpp index 10c004d..8b3ec1f 100644 --- a/NfcTag.cpp +++ b/NfcTag.cpp @@ -73,7 +73,7 @@ void NfcTag::getUid(byte *uid, unsigned int uidLength) String NfcTag::getUidString() { String uidString = ""; - for (int i = 0; i < _uidLength; i++) + for (unsigned int i = 0; i < _uidLength; i++) { if (i > 0) { From 6a4e2839c8989a4d926ddd569349f6b507e4e31f Mon Sep 17 00:00:00 2001 From: Christopher Creutzig Date: Tue, 10 Mar 2015 13:37:40 +0100 Subject: [PATCH 02/35] fix indenting --- NdefMessage.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 3b69d52..b8d2f4c 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -44,12 +44,12 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) } else { - payloadLength = - (static_cast(data[index]) << 24) - | (static_cast(data[index+1]) << 16) - | (static_cast(data[index+2]) << 8) - | static_cast(data[index+3]); - index += 4; + payloadLength = + (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; From 3d31069da3d091c413d66b70e85c3ca808d4dfff Mon Sep 17 00:00:00 2001 From: Christopher Creutzig Date: Tue, 10 Mar 2015 14:52:10 +0100 Subject: [PATCH 03/35] make use of Serial optional --- MifareClassic.cpp | 44 +++++++++++++++++++++++++ Ndef.cpp | 2 ++ Ndef.h | 2 ++ NdefMessage.cpp | 4 +++ NdefMessage.h | 2 ++ NdefRecord.cpp | 4 ++- NdefRecord.h | 2 ++ NfcAdapter.cpp | 14 ++++++++ NfcTag.cpp | 2 ++ NfcTag.h | 2 ++ tests/NdefMemoryTest/NdefMemoryTest.ino | 24 +++++++++++++- tests/NfcTagTest/NfcTagTest.ino | 8 ++--- 12 files changed, 104 insertions(+), 6 deletions(-) diff --git a/MifareClassic.cpp b/MifareClassic.cpp index 7cbed0f..16b6afe 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -36,13 +36,17 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Error. Failed read block "));Serial.println(currentBlock); +#endif return NfcTag(uid, uidLength, 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); } @@ -66,7 +70,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 +88,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 } @@ -144,7 +152,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 +176,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 +210,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 +232,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 +304,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 +316,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 +327,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 +343,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 +357,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; @@ -369,7 +409,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 +426,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/Ndef.cpp b/Ndef.cpp index 0827fd8..846bc89 100644 --- a/Ndef.cpp +++ b/Ndef.cpp @@ -1,5 +1,6 @@ #include "Ndef.h" +#ifdef NDEF_USE_SERIAL // Borrowed from Adafruit_NFCShield_I2C void PrintHex(const byte * data, const long numBytes) { @@ -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 fb5f296..77595f9 100644 --- a/Ndef.h +++ b/Ndef.h @@ -7,8 +7,10 @@ #include +#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 b8d2f4c..7b79e66 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -157,7 +157,9 @@ boolean NdefMessage::addRecord(NdefRecord& record) } else { +#ifdef NDEF_USE_SERIAL Serial.println(F("WARNING: Too many records. Increase MAX_NDEF_RECORDS.")); +#endif return false; } } @@ -261,6 +263,7 @@ NdefRecord NdefMessage::operator[](int index) return getRecord(index); } +#ifdef NDEF_USE_SERIAL void NdefMessage::print() { Serial.print(F("\nNDEF Message "));Serial.print(_recordCount);Serial.print(F(" record")); @@ -272,3 +275,4 @@ void NdefMessage::print() _records[i].print(); } } +#endif diff --git a/NdefMessage.h b/NdefMessage.h index 4a5b0d5..bc42c9f 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -30,7 +30,9 @@ class NdefMessage NdefRecord getRecord(int index); NdefRecord operator[](int index); +#ifdef NDEF_USE_SERIAL void print(); +#endif private: NdefRecord _records[MAX_NDEF_RECORDS]; unsigned int _recordCount; diff --git a/NdefRecord.cpp b/NdefRecord.cpp index 5312e49..a7153aa 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -301,6 +301,7 @@ void NdefRecord::setId(const byte * id, const unsigned int numBytes) memcpy(_id, id, numBytes); _idLength = numBytes; } +#ifdef NDEF_USE_SERIAL void NdefRecord::print() { @@ -349,4 +350,5 @@ void NdefRecord::print() } Serial.print(F(" Record is "));Serial.print(getEncodedSize());Serial.println(" bytes"); -} \ No newline at end of file +} +#endif diff --git a/NdefRecord.h b/NdefRecord.h index a74d22e..bbdd8a2 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -43,7 +43,9 @@ class NdefRecord void setPayload(const byte *payload, const int numBytes); void setId(const byte *id, const 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 50ac1b8..a4e4d7d 100644 --- a/NfcAdapter.cpp +++ b/NfcAdapter.cpp @@ -18,15 +18,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(); @@ -65,7 +69,9 @@ boolean NfcAdapter::format() } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Unsupported Tag.")); +#endif success = false; } return success; @@ -93,7 +99,9 @@ boolean NfcAdapter::clean() } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("No driver for card type "));Serial.println(type); +#endif return false; } @@ -123,7 +131,9 @@ NfcTag NfcAdapter::read() } else if (type == TAG_TYPE_UNKNOWN) { +#ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); +#endif return NfcTag(uid, uidLength); } else @@ -158,12 +168,16 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) } else if (type == TAG_TYPE_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; } diff --git a/NfcTag.cpp b/NfcTag.cpp index 8b3ec1f..c0f4d3e 100644 --- a/NfcTag.cpp +++ b/NfcTag.cpp @@ -105,6 +105,7 @@ NdefMessage NfcTag::getNdefMessage() { return *_ndefMessage; } +#ifdef NDEF_USE_SERIAL void NfcTag::print() { @@ -119,3 +120,4 @@ void NfcTag::print() _ndefMessage->print(); } } +#endif diff --git a/NfcTag.h b/NfcTag.h index 39fa6cd..e3a9a9c 100644 --- a/NfcTag.h +++ b/NfcTag.h @@ -21,7 +21,9 @@ class NfcTag String getTagType(); boolean hasNdefMessage(); NdefMessage getNdefMessage(); +#ifdef NDEF_USE_SERIAL void print(); +#endif private: byte *_uid; unsigned int _uidLength; diff --git a/tests/NdefMemoryTest/NdefMemoryTest.ino b/tests/NdefMemoryTest/NdefMemoryTest.ino index b20c3ae..4d004ad 100644 --- a/tests/NdefMemoryTest/NdefMemoryTest.ino +++ b/tests/NdefMemoryTest/NdefMemoryTest.ino @@ -2,7 +2,7 @@ #include #include #include - #include +#include void leakCheck(void (*callback)()) { @@ -30,7 +30,9 @@ void record() void emptyRecord() { NdefRecord* r = new NdefRecord(); +#ifdef NDEF_USE_SERIAL r->print(); +#endif delete r; } @@ -42,7 +44,9 @@ 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; } @@ -66,7 +70,9 @@ void emptyMessage() void printEmptyMessage() { NdefMessage* m = new NdefMessage(); +#ifdef NDEF_USE_SERIAL m->print(); +#endif delete m; } @@ -74,14 +80,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 +99,9 @@ void messageWithEmptyRecord() NdefMessage m = NdefMessage(); NdefRecord r = NdefRecord(); m.addRecord(r); +#ifdef NDEF_USE_SERIAL m.print(); +#endif } void messageWithoutHelper() @@ -102,7 +114,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 +131,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() { diff --git a/tests/NfcTagTest/NfcTagTest.ino b/tests/NfcTagTest/NfcTagTest.ino index 066096f..0820bfd 100644 --- a/tests/NfcTagTest/NfcTagTest.ino +++ b/tests/NfcTagTest/NfcTagTest.ino @@ -14,16 +14,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]); From f231e10ca38c873e6317fe4c42e43efa921deb56 Mon Sep 17 00:00:00 2001 From: Christopher Creutzig Date: Tue, 10 Mar 2015 22:50:53 +0100 Subject: [PATCH 04/35] disable Mifare Classic, to save space --- MifareClassic.cpp | 4 +++- MifareClassic.h | 5 ++++- NfcAdapter.cpp | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/MifareClassic.cpp b/MifareClassic.cpp index 16b6afe..4a0149d 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -1,3 +1,4 @@ +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC #include "MifareClassic.h" #define BLOCK_SIZE 16 @@ -446,4 +447,5 @@ boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) } return true; -} \ No newline at end of file +} +#endif diff --git a/MifareClassic.h b/MifareClassic.h index 681959e..6605dfe 100644 --- a/MifareClassic.h +++ b/MifareClassic.h @@ -1,6 +1,8 @@ #ifndef MifareClassic_h #define MifareClassic_h +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC + #include #include #include @@ -22,4 +24,5 @@ class MifareClassic bool decodeTlv(byte *data, int &messageLength, int &messageStartIndex); }; -#endif \ No newline at end of file +#endif +#endif diff --git a/NfcAdapter.cpp b/NfcAdapter.cpp index a4e4d7d..9099bde 100644 --- a/NfcAdapter.cpp +++ b/NfcAdapter.cpp @@ -62,12 +62,14 @@ boolean NfcAdapter::erase() boolean NfcAdapter::format() { boolean success; +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC if (uidLength == 4) { MifareClassic mifareClassic = MifareClassic(*shield); success = mifareClassic.formatNDEF(uid, uidLength); } else +#endif { #ifdef NDEF_USE_SERIAL Serial.print(F("Unsupported Tag.")); @@ -81,6 +83,7 @@ boolean NfcAdapter::clean() { uint8_t type = guessTagType(); +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC if (type == TAG_TYPE_MIFARE_CLASSIC) { #ifdef NDEF_DEBUG @@ -89,7 +92,9 @@ boolean NfcAdapter::clean() MifareClassic mifareClassic = MifareClassic(*shield); return mifareClassic.formatMifare(uid, uidLength); } - else if (type == TAG_TYPE_2) + else +#endif + if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Cleaning Mifare Ultralight")); @@ -112,6 +117,7 @@ NfcTag NfcAdapter::read() { uint8_t type = guessTagType(); +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC if (type == TAG_TYPE_MIFARE_CLASSIC) { #ifdef NDEF_DEBUG @@ -121,6 +127,7 @@ NfcTag NfcAdapter::read() return mifareClassic.read(uid, uidLength); } else +#endif if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG @@ -150,6 +157,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) boolean success; uint8_t type = guessTagType(); +#ifdef NDEF_SUPPORT_MIFARE_CLASSIC if (type == TAG_TYPE_MIFARE_CLASSIC) { #ifdef NDEF_DEBUG @@ -158,7 +166,9 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) MifareClassic mifareClassic = MifareClassic(*shield); success = mifareClassic.write(ndefMessage, uid, uidLength); } - else if (type == TAG_TYPE_2) + else +#endif + if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Writing Mifare Ultralight")); From 169912777dea32963e4e04a137101891ded34302 Mon Sep 17 00:00:00 2001 From: Christopher Creutzig Date: Thu, 12 Mar 2015 18:34:57 +0100 Subject: [PATCH 05/35] further limit Serial --- MifareUltralight.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MifareUltralight.cpp b/MifareUltralight.cpp index 9cf941b..c139b7d 100644 --- a/MifareUltralight.cpp +++ b/MifareUltralight.cpp @@ -25,7 +25,9 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) { if (isUnformatted()) { +#ifdef NDEF_USE_SERIAL Serial.println(F("WARNING: Tag is not formatted.")); +#endif return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); } @@ -56,7 +58,9 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Read failed "));Serial.println(page); +#endif // TODO error handling messageLength = 0; break; @@ -86,7 +90,9 @@ boolean MifareUltralight::isUnformatted() } else { +#ifdef NDEF_USE_SERIAL Serial.print(F("Error. Failed read page "));Serial.println(page); +#endif return false; } } @@ -166,7 +172,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 From 3b471e049b45f4572637e7a61b0421701468e6ee Mon Sep 17 00:00:00 2001 From: Andrew Blair Date: Tue, 15 Sep 2015 23:38:54 -0700 Subject: [PATCH 06/35] Added method to create an Android Application Record (AAR) for launching your android app even when it's not running. --- NdefMessage.cpp | 14 ++++++++++++++ NdefMessage.h | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 5bede86..ff0e09c 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -235,6 +235,20 @@ void NdefMessage::addUriRecord(String uri) delete(r); } +void NdefMessage::addAndroidApplicationRecord(char *packageName) +{ + NdefRecord* r = new NdefRecord(); + r->setTnf(TNF_EXTERNAL_TYPE); + + char *RTD_AAR = "android.com:pkg"; // TODO this should be a constant or preprocessor + r->setType((uint8_t *)RTD_AAR, strlen(RTD_AAR)); + + r->setPayload((uint8_t *)packageName, strlen(packageName)); + + addRecord(*r); + delete(r); +} + void NdefMessage::addEmptyRecord() { NdefRecord* r = new NdefRecord(); diff --git a/NdefMessage.h b/NdefMessage.h index 4a5b0d5..fe1ba2f 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -24,6 +24,16 @@ class NdefMessage void addTextRecord(String text); void addTextRecord(String text, String encoding); void addUriRecord(String uri); + + /** + * 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(char *packageName); + void addEmptyRecord(); unsigned int getRecordCount(); From b80bb78bc7aa26aa8253365cb8a06a67624b4062 Mon Sep 17 00:00:00 2001 From: Christoph Tack Date: Mon, 5 Oct 2015 19:42:40 +0200 Subject: [PATCH 07/35] Copying from Arduino library and adding some other functions, see link in source for more info. These two files will override the new and delete operators to use C-functions. This will drastically decrease code size. Signed-off-by: Christoph Tack --- new.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ new.h | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 new.cpp create mode 100644 new.h 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 + From a6907c5be236b46fa490f444196e1ebbce3f105f Mon Sep 17 00:00:00 2001 From: Christoph Tack Date: Sat, 10 Oct 2015 21:59:45 +0200 Subject: [PATCH 08/35] Removing definition of NULL Signed-off-by: Christoph Tack --- Ndef.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ndef.h b/Ndef.h index 97e6524..d55be64 100644 --- a/Ndef.h +++ b/Ndef.h @@ -7,10 +7,10 @@ #include -#define NULL (void *)0 +//#define NULL (void *)0 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 \ No newline at end of file +#endif From 89dea94e797bcf37683f8658eeef7ac1a9fbecab Mon Sep 17 00:00:00 2001 From: Christoph Tack Date: Tue, 22 Dec 2015 21:03:21 +0100 Subject: [PATCH 09/35] Adding functionality to add unknown records Signed-off-by: Christoph Tack --- NdefMessage.cpp | 11 +++++++++++ NdefMessage.h | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 5bede86..fbc5862 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -184,6 +184,17 @@ void NdefMessage::addMimeMediaRecord(String mimeType, uint8_t* payload, int payl addRecord(r); } +void NdefMessage::addUnknownRecord(byte *payload, int payloadLength) +{ + NdefRecord r = NdefRecord(); + r.setTnf(TNF_UNKNOWN); + + r.setType(payload, 0); + r.setPayload(payload, payloadLength); + addRecord(r); +} + + void NdefMessage::addTextRecord(String text) { addTextRecord(text, "en"); diff --git a/NdefMessage.h b/NdefMessage.h index 4a5b0d5..1f4df65 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -24,6 +24,7 @@ class NdefMessage void addTextRecord(String text); void addTextRecord(String text, String encoding); void addUriRecord(String uri); + void addUnknownRecord(byte *payload, int payloadLength); void addEmptyRecord(); unsigned int getRecordCount(); @@ -36,4 +37,4 @@ class NdefMessage unsigned int _recordCount; }; -#endif \ No newline at end of file +#endif From 6a24d575c3b34279480e41aa955b1a87f682648d Mon Sep 17 00:00:00 2001 From: Christoph Tack Date: Fri, 15 Jan 2016 22:13:30 +0100 Subject: [PATCH 10/35] - Fixing stuff for Arduino Due and Arduino 1.6.6 --- Due.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) 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 From 2813a46c829eaa4d2a2fcdfeaf1723a0a024193a Mon Sep 17 00:00:00 2001 From: Christoph Tack Date: Fri, 15 Jan 2016 22:13:59 +0100 Subject: [PATCH 11/35] Adding option to show debug info Signed-off-by: Christoph Tack --- Ndef.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Ndef.h b/Ndef.h index d55be64..d334bad 100644 --- a/Ndef.h +++ b/Ndef.h @@ -7,6 +7,7 @@ #include +//#define NDEF_DEBUG 1 //#define NULL (void *)0 void PrintHex(const byte *data, const long numBytes); From 700e15955c05161343850fe47c22a3e0a70bd8e2 Mon Sep 17 00:00:00 2001 From: Christoph Tack Date: Fri, 15 Jan 2016 22:14:33 +0100 Subject: [PATCH 12/35] Return empty message when read failed Signed-off-by: Christoph Tack --- MifareUltralight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MifareUltralight.cpp b/MifareUltralight.cpp index 9cf941b..cf54d5c 100644 --- a/MifareUltralight.cpp +++ b/MifareUltralight.cpp @@ -57,7 +57,7 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) else { Serial.print(F("Read failed "));Serial.println(page); - // TODO error handling + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); messageLength = 0; break; } From 68e986245de4f3c808fd804d580d6edd60049021 Mon Sep 17 00:00:00 2001 From: Stephan Veigl Date: Sat, 28 Jan 2017 11:31:29 +0100 Subject: [PATCH 13/35] fix #ifdef #endif --- Ndef.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Ndef.h b/Ndef.h index de60507..43bd437 100644 --- a/Ndef.h +++ b/Ndef.h @@ -14,3 +14,4 @@ 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 From c5083e0e2b78ff48ea78b218a9371b7a7e03e1e2 Mon Sep 17 00:00:00 2001 From: Stephan Veigl Date: Sat, 28 Jan 2017 13:38:59 +0100 Subject: [PATCH 14/35] make Mifare Classic or Mifare Ultra selectable --- MifareClassic.cpp | 4 +--- MifareClassic.h | 5 +---- NfcAdapter.cpp | 12 ++++++++++-- NfcAdapter.h | 14 ++++++++++++-- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/MifareClassic.cpp b/MifareClassic.cpp index 4a0149d..16b6afe 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -1,4 +1,3 @@ -#ifdef NDEF_SUPPORT_MIFARE_CLASSIC #include "MifareClassic.h" #define BLOCK_SIZE 16 @@ -447,5 +446,4 @@ boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) } return true; -} -#endif +} \ No newline at end of file diff --git a/MifareClassic.h b/MifareClassic.h index 6605dfe..681959e 100644 --- a/MifareClassic.h +++ b/MifareClassic.h @@ -1,8 +1,6 @@ #ifndef MifareClassic_h #define MifareClassic_h -#ifdef NDEF_SUPPORT_MIFARE_CLASSIC - #include #include #include @@ -24,5 +22,4 @@ class MifareClassic bool decodeTlv(byte *data, int &messageLength, int &messageStartIndex); }; -#endif -#endif +#endif \ No newline at end of file diff --git a/NfcAdapter.cpp b/NfcAdapter.cpp index 9099bde..6339d38 100644 --- a/NfcAdapter.cpp +++ b/NfcAdapter.cpp @@ -94,6 +94,7 @@ boolean NfcAdapter::clean() } else #endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG @@ -103,6 +104,7 @@ boolean NfcAdapter::clean() return ultralight.clean(); } else +#endif { #ifdef NDEF_USE_SERIAL Serial.print(F("No driver for card type "));Serial.println(type); @@ -128,6 +130,7 @@ NfcTag NfcAdapter::read() } else #endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG @@ -136,7 +139,9 @@ NfcTag NfcAdapter::read() MifareUltralight ultralight = MifareUltralight(*shield); return ultralight.read(uid, uidLength); } - else if (type == TAG_TYPE_UNKNOWN) + else +#endif + if (type == TAG_TYPE_UNKNOWN) { #ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); @@ -168,6 +173,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) } else #endif +#ifdef NDEF_SUPPORT_MIFARE_ULTRA if (type == TAG_TYPE_2) { #ifdef NDEF_DEBUG @@ -176,7 +182,9 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) MifareUltralight mifareUltralight = MifareUltralight(*shield); success = mifareUltralight.write(ndefMessage, uid, uidLength); } - else if (type == TAG_TYPE_UNKNOWN) + else +#endif + if (type == TAG_TYPE_UNKNOWN) { #ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); diff --git a/NfcAdapter.h b/NfcAdapter.h index 1ef9e59..fa05665 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) From a100c7d2dbfe44df7c980a24795d101dffed321e Mon Sep 17 00:00:00 2001 From: Stephan Veigl Date: Sat, 28 Jan 2017 14:13:33 +0100 Subject: [PATCH 15/35] use ENUM for tag type instead of C++ String -> reduce memory size by 374 bytes --- MifareClassic.cpp | 10 ++++------ MifareUltralight.cpp | 9 ++++----- NfcAdapter.cpp | 20 ++++++++++---------- NfcAdapter.h | 2 +- NfcTag.cpp | 33 ++++++--------------------------- NfcTag.h | 13 +++++++------ 6 files changed, 32 insertions(+), 55 deletions(-) diff --git a/MifareClassic.cpp b/MifareClassic.cpp index 16b6afe..7123536 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -4,8 +4,6 @@ #define LONG_TLV_SIZE 4 #define SHORT_TLV_SIZE 2 -#define MIFARE_CLASSIC ("Mifare Classic") - MifareClassic::MifareClassic(PN532& nfcShield) { _nfcShield = &nfcShield; @@ -31,7 +29,7 @@ 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 @@ -39,7 +37,7 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) #ifdef NDEF_USE_SERIAL Serial.print(F("Error. Failed read block "));Serial.println(currentBlock); #endif - return NfcTag(uid, uidLength, MIFARE_CLASSIC); + return NfcTag(uid, uidLength, NfcTag::MIFARE_CLASSIC); } } else @@ -48,7 +46,7 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) 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 @@ -107,7 +105,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) diff --git a/MifareUltralight.cpp b/MifareUltralight.cpp index c396c6c..57c59f4 100644 --- a/MifareUltralight.cpp +++ b/MifareUltralight.cpp @@ -8,7 +8,6 @@ #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) { @@ -28,7 +27,7 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) #ifdef NDEF_USE_SERIAL Serial.println(F("WARNING: Tag is not formatted.")); #endif - return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); + return NfcTag(uid, uidLength, NfcTag::TYPE_2); } readCapabilityContainer(); // meta info for tag @@ -38,7 +37,7 @@ 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; @@ -61,7 +60,7 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) #ifdef NDEF_USE_SERIAL Serial.print(F("Read failed "));Serial.println(page); #endif - return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); messageLength = 0; + return NfcTag(uid, uidLength, NfcTag::TYPE_2); messageLength = 0; break; } @@ -74,7 +73,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); } diff --git a/NfcAdapter.cpp b/NfcAdapter.cpp index 6339d38..51b86e2 100644 --- a/NfcAdapter.cpp +++ b/NfcAdapter.cpp @@ -84,7 +84,7 @@ boolean NfcAdapter::clean() uint8_t type = guessTagType(); #ifdef NDEF_SUPPORT_MIFARE_CLASSIC - if (type == TAG_TYPE_MIFARE_CLASSIC) + if (type == NfcTag::MIFARE_CLASSIC) { #ifdef NDEF_DEBUG Serial.println(F("Cleaning Mifare Classic")); @@ -95,7 +95,7 @@ boolean NfcAdapter::clean() else #endif #ifdef NDEF_SUPPORT_MIFARE_ULTRA - if (type == TAG_TYPE_2) + if (type == NfcTag::TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Cleaning Mifare Ultralight")); @@ -120,7 +120,7 @@ NfcTag NfcAdapter::read() uint8_t type = guessTagType(); #ifdef NDEF_SUPPORT_MIFARE_CLASSIC - if (type == TAG_TYPE_MIFARE_CLASSIC) + if (type == NfcTag::MIFARE_CLASSIC) { #ifdef NDEF_DEBUG Serial.println(F("Reading Mifare Classic")); @@ -131,7 +131,7 @@ NfcTag NfcAdapter::read() else #endif #ifdef NDEF_SUPPORT_MIFARE_ULTRA - if (type == TAG_TYPE_2) + if (type == NfcTag::TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Reading Mifare Ultralight")); @@ -141,7 +141,7 @@ NfcTag NfcAdapter::read() } else #endif - if (type == TAG_TYPE_UNKNOWN) + if (type == NfcTag::UNKNOWN) { #ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); @@ -163,7 +163,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) uint8_t type = guessTagType(); #ifdef NDEF_SUPPORT_MIFARE_CLASSIC - if (type == TAG_TYPE_MIFARE_CLASSIC) + if (type == NfcTag::MIFARE_CLASSIC) { #ifdef NDEF_DEBUG Serial.println(F("Writing Mifare Classic")); @@ -174,7 +174,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) else #endif #ifdef NDEF_SUPPORT_MIFARE_ULTRA - if (type == TAG_TYPE_2) + if (type == NfcTag::TYPE_2) { #ifdef NDEF_DEBUG Serial.println(F("Writing Mifare Ultralight")); @@ -184,7 +184,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) } else #endif - if (type == TAG_TYPE_UNKNOWN) + if (type == NfcTag::UNKNOWN) { #ifdef NDEF_USE_SERIAL Serial.print(F("Can not determine tag type")); @@ -217,10 +217,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 fa05665..8fba4aa 100644 --- a/NfcAdapter.h +++ b/NfcAdapter.h @@ -7,7 +7,7 @@ #include // choose supported formats -//#define NDEF_SUPPORT_MIFARE_CLASSIC +#define NDEF_SUPPORT_MIFARE_CLASSIC #define NDEF_SUPPORT_MIFARE_ULTRA // Drivers diff --git a/NfcTag.cpp b/NfcTag.cpp index c0f4d3e..bfe2eef 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 (unsigned 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; } diff --git a/NfcTag.h b/NfcTag.h index e3a9a9c..a7e556d 100644 --- a/NfcTag.h +++ b/NfcTag.h @@ -8,17 +8,18 @@ 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 @@ -27,7 +28,7 @@ class NfcTag 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 From 191f260a7981eadcdd74dd531f5163c63e51fe07 Mon Sep 17 00:00:00 2001 From: Stephan Veigl Date: Sat, 28 Jan 2017 15:09:46 +0100 Subject: [PATCH 16/35] switch to global buffer --- MifareClassic.cpp | 65 ++++++++++++++++++++++++++++++++------------ MifareClassic.h | 4 ++- MifareUltralight.cpp | 28 ++++++++++++++++--- MifareUltralight.h | 4 ++- NfcAdapter.cpp | 18 ++++++------ NfcAdapter.h | 4 ++- 6 files changed, 91 insertions(+), 32 deletions(-) diff --git a/MifareClassic.cpp b/MifareClassic.cpp index 7123536..1b745dc 100644 --- a/MifareClassic.cpp +++ b/MifareClassic.cpp @@ -4,9 +4,11 @@ #define LONG_TLV_SIZE 4 #define SHORT_TLV_SIZE 2 -MifareClassic::MifareClassic(PN532& nfcShield) +MifareClassic::MifareClassic(PN532& nfcShield, uint8_t *staticBuf, unsigned int staticBufSize) { - _nfcShield = &nfcShield; + _nfcShield = &nfcShield; + _staticBufSize = staticBufSize; + _staticBuf = staticBuf; } MifareClassic::~MifareClassic() @@ -52,7 +54,16 @@ NfcTag MifareClassic::read(byte *uid, unsigned int uidLength) // 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); @@ -366,32 +377,52 @@ 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 @@ -399,7 +430,7 @@ boolean MifareClassic::write(NdefMessage& m, byte * uid, unsigned int uidLength) 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)) 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 57c59f4..b34c51a 100644 --- a/MifareUltralight.cpp +++ b/MifareUltralight.cpp @@ -9,9 +9,11 @@ #define ULTRALIGHT_MAX_PAGE 63 -MifareUltralight::MifareUltralight(PN532& nfcShield) +MifareUltralight::MifareUltralight(PN532& nfcShield, uint8_t *staticBuf, unsigned int staticBufSize) { nfc = &nfcShield; + _staticBufSize = staticBufSize; + _staticBuf = staticBuf; ndefStartIndex = 0; messageLength = 0; } @@ -43,7 +45,16 @@ NfcTag MifareUltralight::read(byte * uid, unsigned int uidLength) 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 @@ -188,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/NfcAdapter.cpp b/NfcAdapter.cpp index 51b86e2..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) @@ -65,7 +67,7 @@ boolean NfcAdapter::format() #ifdef NDEF_SUPPORT_MIFARE_CLASSIC if (uidLength == 4) { - MifareClassic mifareClassic = MifareClassic(*shield); + MifareClassic mifareClassic = MifareClassic(*shield, _staticBuf, _staticBufSize); success = mifareClassic.formatNDEF(uid, uidLength); } else @@ -89,7 +91,7 @@ boolean NfcAdapter::clean() #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 @@ -100,7 +102,7 @@ boolean NfcAdapter::clean() #ifdef NDEF_DEBUG Serial.println(F("Cleaning Mifare Ultralight")); #endif - MifareUltralight ultralight = MifareUltralight(*shield); + MifareUltralight ultralight = MifareUltralight(*shield, _staticBuf, _staticBufSize); return ultralight.clean(); } else @@ -125,7 +127,7 @@ NfcTag NfcAdapter::read() #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 @@ -136,7 +138,7 @@ NfcTag NfcAdapter::read() #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 @@ -168,7 +170,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) #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 @@ -179,7 +181,7 @@ boolean NfcAdapter::write(NdefMessage& ndefMessage) #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 diff --git a/NfcAdapter.h b/NfcAdapter.h index 8fba4aa..f3073eb 100644 --- a/NfcAdapter.h +++ b/NfcAdapter.h @@ -32,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); @@ -49,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(); }; From 2e8680ff06a0ebe4568da993b1c5f7223744a76c Mon Sep 17 00:00:00 2001 From: Stephan Veigl Date: Sat, 28 Jan 2017 17:52:03 +0100 Subject: [PATCH 17/35] replace C++ String with char* --- NdefMessage.cpp | 92 +++++++++++++++++++++---------------------------- NdefMessage.h | 10 +++--- NdefRecord.cpp | 65 ++++++++++++++++------------------ NdefRecord.h | 17 ++++----- 4 files changed, 81 insertions(+), 103 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index fe90476..b9ff1d6 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -164,111 +164,97 @@ boolean NdefMessage::addRecord(NdefRecord& record) } } -void NdefMessage::addMimeMediaRecord(String mimeType, String payload) +void NdefMessage::addMimeMediaRecord(char *mimeType, 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(char *mimeType, byte* payload, int 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(byte *payload, int payloadLength) { - NdefRecord r = NdefRecord(); + NdefRecord r; r.setTnf(TNF_UNKNOWN); r.setType(payload, 0); r.setPayload(payload, payloadLength); addRecord(r); + } -void NdefMessage::addTextRecord(String text) +void NdefMessage::addTextRecord(char *text) { - addTextRecord(text, "en"); + addTextRecord(text, ""); } -void NdefMessage::addTextRecord(String text, String encoding) +void NdefMessage::addTextRecord(char *text, 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(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)); - - // X is a placeholder for identifier code - String payloadString = "X" + uri; - - byte payload[payloadString.length() + 1]; - payloadString.getBytes(payload, sizeof(payload)); + r.setType(RTD_URI, sizeof(RTD_URI)); - // add identifier code 0x0, meaning no prefix substitution - payload[0] = 0x0; + // encoding prefix + const uint8_t prefixSize = 1; + byte prefix[prefixSize] = {0}; - r->setPayload(payload, payloadString.length()); + // set payload + r.setPayload(prefix, prefixSize, reinterpret_cast(uri), strlen(uri)); - addRecord(*r); - delete(r); + addRecord(r); } void NdefMessage::addAndroidApplicationRecord(char *packageName) { - NdefRecord* r = new NdefRecord(); - r->setTnf(TNF_EXTERNAL_TYPE); + NdefRecord r; + r.setTnf(TNF_EXTERNAL_TYPE); char *RTD_AAR = "android.com:pkg"; // TODO this should be a constant or preprocessor - r->setType((uint8_t *)RTD_AAR, strlen(RTD_AAR)); + r.setType((uint8_t *)RTD_AAR, strlen(RTD_AAR)); - r->setPayload((uint8_t *)packageName, strlen(packageName)); + r.setPayload((uint8_t *)packageName, strlen(packageName)); - addRecord(*r); - delete(r); + addRecord(r); } 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) diff --git a/NdefMessage.h b/NdefMessage.h index efbd825..ecf7513 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -19,11 +19,11 @@ class NdefMessage void encode(byte *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(char *mimeType, char *payload); + void addMimeMediaRecord(char *mimeType, byte *payload, int payloadLength); + void addTextRecord(char *text); + void addTextRecord(char *text, char *encoding); + void addUriRecord(char *uri); /** * Creates an Android Application Record (AAR) http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#aar diff --git a/NdefRecord.cpp b/NdefRecord.cpp index 006c5b1..9682cce 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -233,21 +233,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,45 +250,49 @@ 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; } diff --git a/NdefRecord.h b/NdefRecord.h index bbdd8a2..63a7b9f 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -30,18 +30,15 @@ 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(); From 9e78272bccf85c224c3e167bd31959a2a4025da8 Mon Sep 17 00:00:00 2001 From: Stephan Veigl Date: Sun, 29 Jan 2017 00:44:05 +0100 Subject: [PATCH 18/35] addExternalRecord() --- NdefMessage.cpp | 43 ++++++++++++++++++++++++++----------------- NdefMessage.h | 17 ++++++++++------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index b9ff1d6..6d36620 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -164,23 +164,23 @@ boolean NdefMessage::addRecord(NdefRecord& record) } } -void NdefMessage::addMimeMediaRecord(char *mimeType, char* payload) +void NdefMessage::addMimeMediaRecord(const char *mimeType, const char* payload) { - addMimeMediaRecord(mimeType, reinterpret_cast(payload), strlen(payload)); + addMimeMediaRecord(mimeType, reinterpret_cast(payload), strlen(payload)); } -void NdefMessage::addMimeMediaRecord(char *mimeType, byte* payload, int payloadLength) +void NdefMessage::addMimeMediaRecord(const char *mimeType, const byte* payload, int payloadLength) { NdefRecord r; r.setTnf(TNF_MIME_MEDIA); - r.setType(reinterpret_cast(mimeType), strlen(mimeType)); + r.setType(reinterpret_cast(mimeType), strlen(mimeType)); r.setPayload(payload, payloadLength); addRecord(r); } -void NdefMessage::addUnknownRecord(byte *payload, int payloadLength) +void NdefMessage::addUnknownRecord(const byte *payload, int payloadLength) { NdefRecord r; r.setTnf(TNF_UNKNOWN); @@ -192,12 +192,12 @@ void NdefMessage::addUnknownRecord(byte *payload, int payloadLength) } -void NdefMessage::addTextRecord(char *text) +void NdefMessage::addTextRecord(const char *text) { addTextRecord(text, ""); } -void NdefMessage::addTextRecord(char *text, char *encoding) +void NdefMessage::addTextRecord(const char *text, const char *encoding) { NdefRecord r; r.setTnf(TNF_WELL_KNOWN); @@ -214,12 +214,12 @@ void NdefMessage::addTextRecord(char *text, char *encoding) prefix[i+1] = encoding[i]; // set payload - r.setPayload(prefix, prefixSize, reinterpret_cast(text), strlen(text)); + r.setPayload(prefix, prefixSize, reinterpret_cast(text), strlen(text)); addRecord(r); } -void NdefMessage::addUriRecord(char *uri) +void NdefMessage::addUriRecord(const char *uri) { NdefRecord r; r.setTnf(TNF_WELL_KNOWN); @@ -232,22 +232,31 @@ void NdefMessage::addUriRecord(char *uri) byte prefix[prefixSize] = {0}; // set payload - r.setPayload(prefix, prefixSize, reinterpret_cast(uri), strlen(uri)); + r.setPayload(prefix, prefixSize, reinterpret_cast(uri), strlen(uri)); addRecord(r); } -void NdefMessage::addAndroidApplicationRecord(char *packageName) + +void NdefMessage::addExternalRecord(const char *type,const char* payload) { - NdefRecord r; - r.setTnf(TNF_EXTERNAL_TYPE); + addExternalRecord(type, reinterpret_cast(payload), strlen(payload)); +} - char *RTD_AAR = "android.com:pkg"; // TODO this should be a constant or preprocessor - r.setType((uint8_t *)RTD_AAR, strlen(RTD_AAR)); +void NdefMessage::addExternalRecord(const char *type, const byte *payload, int payloadLength) +{ + NdefRecord r; + r.setTnf(TNF_EXTERNAL_TYPE); + + r.setType(reinterpret_cast(type), strlen(type)); + r.setPayload(payload, payloadLength); + addRecord(r); +} - r.setPayload((uint8_t *)packageName, strlen(packageName)); - addRecord(r); +void NdefMessage::addAndroidApplicationRecord(const char *packageName) +{ + addExternalRecord("android.com:pkg", packageName); } void NdefMessage::addEmptyRecord() diff --git a/NdefMessage.h b/NdefMessage.h index ecf7513..4291be2 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -19,11 +19,14 @@ class NdefMessage void encode(byte *data); boolean addRecord(NdefRecord& record); - void addMimeMediaRecord(char *mimeType, char *payload); - void addMimeMediaRecord(char *mimeType, byte *payload, int payloadLength); - void addTextRecord(char *text); - void addTextRecord(char *text, char *encoding); - void addUriRecord(char *uri); + void addMimeMediaRecord(const char *mimeType, const char *payload); + void addMimeMediaRecord(const char *mimeType, const byte *payload, int 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, int payloadLength); /** * Creates an Android Application Record (AAR) http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#aar @@ -32,9 +35,9 @@ class NdefMessage * * @param packageName example: "com.acme.myapp" */ - void addAndroidApplicationRecord(char *packageName); + void addAndroidApplicationRecord(const char *packageName); - void addUnknownRecord(byte *payload, int payloadLength); + void addUnknownRecord(const byte *payload, int payloadLength); void addEmptyRecord(); unsigned int getRecordCount(); From 0ef1bbfcc65bd65c3fb5b63e522bfdb9cb7b85d6 Mon Sep 17 00:00:00 2001 From: eecharlie Date: Tue, 4 Apr 2017 20:03:13 -0700 Subject: [PATCH 19/35] Update readme with note on fork purpose --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 14dba0d..486bb7b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ NFC Data Exchange Format (NDEF) is a common data format that operates across all This code works with the [Adafruit NFC Shield](https://www.adafruit.com/products/789), [Seeed Studio NFC Shield v2.0](http://www.seeedstudio.com/depot/nfc-shield-v20-p-1370.html) and the [Seeed Studio NFC Shield](http://www.seeedstudio.com/depot/nfc-shield-p-916.html?cPath=73). The library supports I2C for the Adafruit shield and SPI with the Seeed shields. The Adafruit Shield can also be modified to use SPI. It should also work with the [Adafruit NFC Breakout Board](https://www.adafruit.com/products/364). +### A note on this fork +The purpose of this fork is to implement some memory-saving features to avoid stack overflows on Arduinos. Specifically, to make it possible to go through the process of defining NDEF records and messages and getting the header data, but without dragging payload data along with it. This will allow payload data to be moved around separately, and in whatever chunk size you desire. + ### Supports - Reading from Mifare Classic Tags with 4 byte UIDs. - Writing to Mifare Classic Tags with 4 byte UIDs. From 17651e0c67326b30d9b4e829513af75315d422cf Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Tue, 4 Apr 2017 20:41:02 -0700 Subject: [PATCH 20/35] Import ntag class from LieBtrau/arduino-ntag. No edits; will not build. https://github.com/LieBtrau/arduino-ntag https://github.com/LieBtrau/arduino-ntag/commit/89396ced0c9c97130c9caba37f081de22ddfd2b3 --- ntag.cpp | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ntag.h | 71 +++++++++++ 2 files changed, 443 insertions(+) create mode 100644 ntag.cpp create mode 100644 ntag.h diff --git a/ntag.cpp b/ntag.cpp new file mode 100644 index 0000000..cb04b66 --- /dev/null +++ b/ntag.cpp @@ -0,0 +1,372 @@ +#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 + + +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("Found I²C device on : 0x"); + Serial.println(i,HEX); + } + } +} + +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::readSram(word address, byte *pdata, byte length) +{ + return read(SRAM, address+SRAM_BASE_ADDR, pdata, length); +} + +bool Ntag::writeSram(word address, byte *pdata, byte length) +{ + return write(SRAM, address+SRAM_BASE_ADDR, pdata, length); +} + +bool Ntag::readEeprom(word address, byte *pdata, byte length) +{ + return read(USERMEM, address+EEPROM_BASE_ADDR, pdata, length); +} + +bool Ntag::writeEeprom(word address, byte *pdata, byte 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, word address, byte* pdata, byte length) +{ + byte readbuffer[NTAG_BLOCK_SIZE]; + byte 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++; + } + _lastMemBlockWritten = --blockNr; + return true; +} + +bool Ntag::read(BLOCK_TYPE bt, word address, byte* pdata, byte length) +{ + byte readbuffer[NTAG_BLOCK_SIZE]; + byte 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, 0xFE)){ + 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, 0xFE)){ + 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)){ + 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){ + return false; + } + break; + default: + return false; + } + break; + case SRAM: + if(blocknr < 0xF8 || blocknr > 0xFB){ + return false; + } + break; + case REGISTER: + if(blocknr != 0xFE){ + return false; + } + break; + default: + return false; + } + return true; +} diff --git a/ntag.h b/ntag.h new file mode 100644 index 0000000..c5da029 --- /dev/null +++ b/ntag.h @@ -0,0 +1,71 @@ +#ifndef NTAG_H +#define NTAG_H + +#include "Arduino.h" +#include + +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; + 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(); + bool setSramMirrorRf(bool bEnable, byte mirrorBaseBlockNr); + bool setFd_ReaderHandshake(); + bool readEeprom(word address, byte* pdata, byte length);//starts at address 0 + bool writeEeprom(word address, byte* pdata, byte length);//starts at address 0 + bool readSram(word address, byte* pdata, byte length);//starts at address 0 + bool writeSram(word address, byte* pdata, byte length);//starts at address 0 + bool readRegister(REGISTER_NR regAddr, byte &value); + bool writeRegister(REGISTER_NR regAddr, byte mask, byte regdat); + bool setLastNdefBlock(); + 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 byte NTAG_BLOCK_SIZE=16; + static const word EEPROM_BASE_ADDR=0x1<<4; + static const word SRAM_BASE_ADDR=0xF8<<4; + bool write(BLOCK_TYPE bt, word address, byte* pdata, byte length); + bool read(BLOCK_TYPE bt, word address, byte* pdata, byte 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); + 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 From 842eee2480408ff6baf089ec4af470f2be739145 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Tue, 4 Apr 2017 21:55:59 -0700 Subject: [PATCH 21/35] Add no-payload option to NdefRecord. No payload flag lets you still examine/access header bytes. Create example showing this, but no unit tests. --- NdefRecord.cpp | 57 ++++++++++++++++--- NdefRecord.h | 9 ++- .../NdefWithoutPayload/NdefWithoutPayload.ino | 47 +++++++++++++++ 3 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 examples/NdefWithoutPayload/NdefWithoutPayload.ino diff --git a/NdefRecord.cpp b/NdefRecord.cpp index 9682cce..707e5da 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -10,6 +10,19 @@ NdefRecord::NdefRecord() _type = (byte *)NULL; _payload = (byte *)NULL; _id = (byte *)NULL; + _noPayload = false; +} + +NdefRecord::NdefRecord(const int payloadNumBytes) //: NdefRecord() (requires C++11) +{ + _tnf = 0; + _typeLength = 0; + _payloadLength = payloadNumBytes; + _idLength = 0; + _type = (byte *)NULL; + _payload = (byte *)NULL; + _id = (byte *)NULL; + _noPayload = true; } NdefRecord::NdefRecord(const NdefRecord& rhs) @@ -54,7 +67,7 @@ NdefRecord::~NdefRecord() free(_type); } - if (_payloadLength) + if (_payloadLength && !_noPayload) { free(_payload); } @@ -176,10 +189,27 @@ void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) data_ptr += _idLength; } - memcpy(data_ptr, _payload, _payloadLength); - data_ptr += _payloadLength; + if (!_noPayload) { + memcpy(data_ptr, _payload, _payloadLength); + data_ptr += _payloadLength; + } } +int NdefRecord::getHeaderSize() +{ + return (getEncodedSize() - _payloadLength); +} + +void NdefRecord::getHeader(byte *data, bool firstRecord, bool lastRecord) { + bool temp = _noPayload; + + _noPayload = false; + encode(data, firstRecord, lastRecord); + + _noPayload = temp; +} + + byte NdefRecord::getTnfByte(bool firstRecord, bool lastRecord) { int value = _tnf; @@ -276,6 +306,7 @@ void NdefRecord::setPayload(const byte *prefix, int prefixSize, const byte *payl if ( numBytes ) memcpy(_payload + prefixSize, payload, numBytes); _payloadLength = size; + _noPayload = false; } const byte *NdefRecord::getId() @@ -296,7 +327,16 @@ void NdefRecord::setId(const byte * id, const unsigned int numBytes) memcpy(_id, id, numBytes); _idLength = numBytes; } -#ifdef NDEF_USE_SERIAL + +//#ifdef NDEF_USE_SERIAL + +static void PrintHexChar(byte* data, byte size){ + for(int i=0;i + +#include + +#include +#include + +#include +#include + + + +void setup(){ + Serial.begin(115200); + Serial.println("start"); + + NdefMessage message = NdefMessage(); + + // Create an NDEF record with a dummy payload and examine header + NdefRecord rec = NdefRecord(10); + Serial.print("ID length: "); + Serial.println(rec.getIdLength()); + rec.print(); + + unsigned int header_len = rec.getHeaderSize(); + byte header[header_len]; + rec.getHeader(header, true, true); + Serial.println("\n Record header: "); + showBlockInHex(header, header_len); + +void loop() { + // put your main code here, to run repeatedly: + +} + +void showBlockInHex(byte* data, byte size){ + for(int i=0;i Date: Tue, 4 Apr 2017 22:54:06 -0700 Subject: [PATCH 22/35] Implement functions to get NDEF header from NdefMessage Not tested but demonstrated in NdefWithoutPayload example. --- NdefMessage.cpp | 18 +++++++++ NdefMessage.h | 6 ++- NdefRecord.cpp | 5 +++ NdefRecord.h | 1 + .../NdefWithoutPayload/NdefWithoutPayload.ino | 37 +++++++++++++++++-- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 6d36620..4562a6a 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -146,6 +146,24 @@ void NdefMessage::encode(uint8_t* data) } +unsigned int NdefMessage::getHeaderSize() { + return 3 + (getEncodedSize() > 254 ? 2 : 0); +} + +void NdefMessage::getHeader(byte* header) +{ + unsigned int 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; + } +} + boolean NdefMessage::addRecord(NdefRecord& record) { diff --git a/NdefMessage.h b/NdefMessage.h index 4291be2..a3ea942 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -4,7 +4,7 @@ #include #include -#define MAX_NDEF_RECORDS 4 +#define MAX_NDEF_RECORDS 8 class NdefMessage { @@ -16,7 +16,9 @@ class NdefMessage NdefMessage& operator=(const NdefMessage& rhs); int getEncodedSize(); // need so we can pass array to encode - void encode(byte *data); + void encode(byte *data); + unsigned int getHeaderSize(); + void getHeader(byte* header); boolean addRecord(NdefRecord& record); void addMimeMediaRecord(const char *mimeType, const char *payload); diff --git a/NdefRecord.cpp b/NdefRecord.cpp index 707e5da..4e5254c 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -263,6 +263,11 @@ unsigned int NdefRecord::getIdLength() return _idLength; } +bool NdefRecord::hasPayload() +{ + return !_noPayload; +} + const byte *NdefRecord::getType() { return _type; diff --git a/NdefRecord.h b/NdefRecord.h index b2f3bab..e7945d3 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -32,6 +32,7 @@ class NdefRecord unsigned int getTypeLength(); int getPayloadLength(); unsigned int getIdLength(); + bool hasPayload(); byte getTnf(); const byte *getType(); diff --git a/examples/NdefWithoutPayload/NdefWithoutPayload.ino b/examples/NdefWithoutPayload/NdefWithoutPayload.ino index 630efd2..1b361f0 100644 --- a/examples/NdefWithoutPayload/NdefWithoutPayload.ino +++ b/examples/NdefWithoutPayload/NdefWithoutPayload.ino @@ -14,7 +14,6 @@ #include - void setup(){ Serial.begin(115200); Serial.println("start"); @@ -22,7 +21,7 @@ void setup(){ NdefMessage message = NdefMessage(); // Create an NDEF record with a dummy payload and examine header - NdefRecord rec = NdefRecord(10); + NdefRecord rec = NdefRecord(80); Serial.print("ID length: "); Serial.println(rec.getIdLength()); rec.print(); @@ -30,9 +29,41 @@ void setup(){ unsigned int header_len = rec.getHeaderSize(); byte header[header_len]; rec.getHeader(header, true, true); - Serial.println("\n Record header: "); + Serial.println(F("\n Record header: ")); showBlockInHex(header, header_len); + Serial.print(F("\n Size of record in memory: ")); + Serial.println(sizeof(rec)); + + if (message.addRecord(rec)) { Serial.println(F("Added record.")); } + unsigned int msgHeader_len = message.getHeaderSize(); + byte msgHeader[msgHeader_len]; + message.getHeader(msgHeader); + Serial.println("\n Message header: "); + showBlockInHex(msgHeader, msgHeader_len); + + Serial.print("Record count: "); + Serial.println(message.getRecordCount()); + + Serial.print(F("\n Size of message in memory: ")); + Serial.println(sizeof(message)); + + Serial.println(F("\n (Adding more copies of record to message)")); + if (message.addRecord(rec)) { Serial.println(F("Added record.")); } + if (message.addRecord(rec)) { Serial.println(F("Added record.")); } + + Serial.print("Record count: "); + Serial.println(message.getRecordCount()); + msgHeader_len = message.getHeaderSize(); + byte msgHeader2[msgHeader_len]; + message.getHeader(msgHeader2); + Serial.println("\n Message header: "); + showBlockInHex(msgHeader2, msgHeader_len); + + Serial.print("\n Size of message in memory: "); + Serial.println(sizeof(message)); +} + void loop() { // put your main code here, to run repeatedly: From c71592c17b8e8f6a34108378118b80fb17cf3bc6 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Sun, 9 Apr 2017 15:48:48 -0700 Subject: [PATCH 23/35] Update unit tests. Some memory tests fail. I think the tests were broken (not up to date with refactored functions) when I forked the library, so I don't know whether these tests previously passed. --- NdefMessage.cpp | 3 +- NdefRecord.cpp | 29 ++++++++------- NdefRecord.h | 2 +- NfcTag.cpp | 2 +- tests/NdefMemoryTest/NdefMemoryTest.ino | 20 ++++++++-- tests/NdefMessageTest/NdefMessageTest.ino | 21 ++++++----- tests/NdefUnitTest/NdefUnitTest.ino | 45 ++++++++++++----------- tests/NfcTagTest/NfcTagTest.ino | 1 + 8 files changed, 71 insertions(+), 52 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 4562a6a..a7333f2 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -147,7 +147,7 @@ void NdefMessage::encode(uint8_t* data) } unsigned int NdefMessage::getHeaderSize() { - return 3 + (getEncodedSize() > 254 ? 2 : 0); + return 2 + (getEncodedSize() > 254 ? 2 : 0); } void NdefMessage::getHeader(byte* header) @@ -171,6 +171,7 @@ boolean NdefMessage::addRecord(NdefRecord& record) { _records[_recordCount] = record; _recordCount++; + Serial.println(F("Added record.")); return true; } else diff --git a/NdefRecord.cpp b/NdefRecord.cpp index 4e5254c..17dc3a3 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -10,11 +10,12 @@ NdefRecord::NdefRecord() _type = (byte *)NULL; _payload = (byte *)NULL; _id = (byte *)NULL; - _noPayload = false; + _hasPayload = false; } NdefRecord::NdefRecord(const int payloadNumBytes) //: NdefRecord() (requires C++11) { + Serial.println("Creating record without payload."); _tnf = 0; _typeLength = 0; _payloadLength = payloadNumBytes; @@ -22,7 +23,7 @@ NdefRecord::NdefRecord(const int payloadNumBytes) //: NdefRecord() (requires C++ _type = (byte *)NULL; _payload = (byte *)NULL; _id = (byte *)NULL; - _noPayload = true; + _hasPayload = false; } NdefRecord::NdefRecord(const NdefRecord& rhs) @@ -67,7 +68,7 @@ NdefRecord::~NdefRecord() free(_type); } - if (_payloadLength && !_noPayload) + if (_payloadLength && _hasPayload) { free(_payload); } @@ -152,7 +153,6 @@ int NdefRecord::getEncodedSize() void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) { // assert data > getEncodedSize() - uint8_t* data_ptr = &data[0]; *data_ptr = getTnfByte(firstRecord, lastRecord); @@ -188,11 +188,12 @@ void NdefRecord::encode(byte *data, bool firstRecord, bool lastRecord) memcpy(data_ptr, _id, _idLength); data_ptr += _idLength; } - - if (!_noPayload) { + + if (_hasPayload) { memcpy(data_ptr, _payload, _payloadLength); data_ptr += _payloadLength; } + } int NdefRecord::getHeaderSize() @@ -201,12 +202,12 @@ int NdefRecord::getHeaderSize() } void NdefRecord::getHeader(byte *data, bool firstRecord, bool lastRecord) { - bool temp = _noPayload; + bool temp = _hasPayload; - _noPayload = false; + _hasPayload = false; // Hack: encode will return header if it believes there's no payload encode(data, firstRecord, lastRecord); - _noPayload = temp; + _hasPayload = temp; } @@ -265,7 +266,7 @@ unsigned int NdefRecord::getIdLength() bool NdefRecord::hasPayload() { - return !_noPayload; + return _hasPayload; } const byte *NdefRecord::getType() @@ -311,7 +312,7 @@ void NdefRecord::setPayload(const byte *prefix, int prefixSize, const byte *payl if ( numBytes ) memcpy(_payload + prefixSize, payload, numBytes); _payloadLength = size; - _noPayload = false; + _hasPayload = true; } const byte *NdefRecord::getId() @@ -384,9 +385,11 @@ void NdefRecord::print() Serial.print(F(" Type "));PrintHexChar(_type, _typeLength); // TODO chunk large payloads so this is readable Serial.print(F(" Payload ")); - if (_noPayload) { + if (_hasPayload) { + PrintHexChar(_payload, _payloadLength); + }else { Serial.println(F("(not provided)")); - }else { PrintHexChar(_payload, _payloadLength); } + } if (_idLength) { Serial.print(F(" Id "));PrintHexChar(_id, _idLength); diff --git a/NdefRecord.h b/NdefRecord.h index e7945d3..e1e9cc9 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -57,7 +57,7 @@ class NdefRecord byte *_type; byte *_payload; byte *_id; - bool _noPayload; + bool _hasPayload; }; #endif \ No newline at end of file diff --git a/NfcTag.cpp b/NfcTag.cpp index bfe2eef..b2c5451 100644 --- a/NfcTag.cpp +++ b/NfcTag.cpp @@ -89,7 +89,7 @@ NdefMessage NfcTag::getNdefMessage() 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()); if (_ndefMessage == NULL) { Serial.println(F("\nNo NDEF Message")); diff --git a/tests/NdefMemoryTest/NdefMemoryTest.ino b/tests/NdefMemoryTest/NdefMemoryTest.ino index 4d004ad..71a98d6 100644 --- a/tests/NdefMemoryTest/NdefMemoryTest.ino +++ b/tests/NdefMemoryTest/NdefMemoryTest.ino @@ -1,8 +1,11 @@ +#define NDEF_USE_SERIAL + #include #include #include #include #include +#include void leakCheck(void (*callback)()) { @@ -53,10 +56,10 @@ void textRecord() 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 @@ -85,6 +88,7 @@ void printEmptyMessageNoNew() #endif } +// This is failing void messageWithTextRecord() { NdefMessage m = NdefMessage(); @@ -94,6 +98,7 @@ void messageWithTextRecord() #endif } +// This is failing void messageWithEmptyRecord() { NdefMessage m = NdefMessage(); @@ -193,12 +198,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..09d67ef 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)()) @@ -60,10 +61,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 +100,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); @@ -139,8 +140,8 @@ test(assign3) byte payload[s.length() + 1]; s.getBytes(payload, sizeof(payload)); - byte p[r.getPayloadLength()]; - r.getPayload(p); + //byte p[r.getPayloadLength()]; + const byte *p = r.getPayload(); assertBytesEqual(payload, p+3, s.length()); delete m2; diff --git a/tests/NdefUnitTest/NdefUnitTest.ino b/tests/NdefUnitTest/NdefUnitTest.ino index d1f8c93..6edfbdb 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++) { @@ -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) { diff --git a/tests/NfcTagTest/NfcTagTest.ino b/tests/NfcTagTest/NfcTagTest.ino index 0820bfd..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); From 92a411646eeeed47efb9f0125e900485d25e26f9 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Sun, 9 Apr 2017 15:49:38 -0700 Subject: [PATCH 24/35] Updates (untested) to ntag class. Work in progress. --- .../NdefWithoutPayload/NdefWithoutPayload.ino | 79 +++++---- ntag.cpp | 150 +++++++++++++++++- ntag.h | 15 +- 3 files changed, 212 insertions(+), 32 deletions(-) diff --git a/examples/NdefWithoutPayload/NdefWithoutPayload.ino b/examples/NdefWithoutPayload/NdefWithoutPayload.ino index 1b361f0..c0a533e 100644 --- a/examples/NdefWithoutPayload/NdefWithoutPayload.ino +++ b/examples/NdefWithoutPayload/NdefWithoutPayload.ino @@ -13,55 +13,78 @@ #include #include +#include -void setup(){ - Serial.begin(115200); - Serial.println("start"); +#define FD_PIN (4) +#define VOUT_PIN (2) +#define LED_PIN (13) - NdefMessage message = NdefMessage(); +// Troubleshooting +ISR(BADISR_vect) +{ + for (;;) UDR0='!'; +} + +Ntag ntag(Ntag::NTAG_I2C_2K, FD_PIN, VOUT_PIN); + +void setup(){ + pinMode(13, OUTPUT); + Serial.begin(9600); + Serial.println("start"); + // Create an NDEF record with a dummy payload and examine header - NdefRecord rec = NdefRecord(80); - Serial.print("ID length: "); - Serial.println(rec.getIdLength()); + NdefRecord rec = NdefRecord(3); rec.print(); unsigned int header_len = rec.getHeaderSize(); byte header[header_len]; rec.getHeader(header, true, true); - Serial.println(F("\n Record header: ")); + Serial.println(F("\nRecord header: ")); showBlockInHex(header, header_len); - Serial.print(F("\n Size of record in memory: ")); + Serial.print(F("\nSize of record in memory: ")); Serial.println(sizeof(rec)); + Serial.print(F("\nPayload size: ")); + Serial.println(rec.getPayloadLength()); + Serial.print(F("\nEncoded size of record: ")); + Serial.println(rec.getEncodedSize()); + + + NdefMessage message = NdefMessage(); if (message.addRecord(rec)) { Serial.println(F("Added record.")); } unsigned int msgHeader_len = message.getHeaderSize(); byte msgHeader[msgHeader_len]; message.getHeader(msgHeader); - Serial.println("\n Message header: "); + Serial.println(F("\nMessage header: ")); showBlockInHex(msgHeader, msgHeader_len); - Serial.print("Record count: "); + Serial.print(F("Record count: ")); Serial.println(message.getRecordCount()); - Serial.print(F("\n Size of message in memory: ")); + Serial.print(F("\nSize of message in memory: ")); Serial.println(sizeof(message)); - Serial.println(F("\n (Adding more copies of record to message)")); - if (message.addRecord(rec)) { Serial.println(F("Added record.")); } - if (message.addRecord(rec)) { Serial.println(F("Added record.")); } - - Serial.print("Record count: "); - Serial.println(message.getRecordCount()); - msgHeader_len = message.getHeaderSize(); - byte msgHeader2[msgHeader_len]; - message.getHeader(msgHeader2); - Serial.println("\n Message header: "); - showBlockInHex(msgHeader2, msgHeader_len); - - Serial.print("\n Size of message in memory: "); - Serial.println(sizeof(message)); + Serial.print(F("Message encoded size (excluding NDEF TLV): ")); + Serial.println(message.getEncodedSize()); + + delay(500); + // Now we'll actually write to an NTAG + Serial.print("Zeroing memory..."); + if (ntag.zeroEeprom()) { Serial.println("success."); } + Serial.println("Configuring NTAG..."); + if (ntag.setContainerClass()) { Serial.println("success."); } // Configure NTAG for an NDEF message to occupy all user EEPROM + Serial.println("Writing NDEF message..."); + if (ntag.writeNdef(16, message, true)) { Serial.println("success."); } // Write the message to EEPROM (which starts at global address 16) + + byte block[16]; + ntag.readConfigBlock(block); + Serial.println(F("\nConfig block: ")); + showBlockInHex(block, 16); + Serial.println(F("\nUser EEPROM first block: ")); + ntag.readEeprom(16, block, 16); + showBlockInHex(block, 16); } void loop() { @@ -69,10 +92,12 @@ void loop() { } + void showBlockInHex(byte* data, byte size){ for(int i=0;i 0xFB){ bt = USERMEM; } // See isAddressValid + // 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() +{ + word blockNr = 1; + byte data[NTAG_BLOCK_SIZE] = {0}; + while (writeBlock(USERMEM, blockNr, data)) { + blockNr++; + } + if (!isAddressValid(USERMEM, blockNr)) { return true; } // If we made it through all user mem + return false; +} + + bool Ntag::readSram(word address, byte *pdata, byte length) { return read(SRAM, address+SRAM_BASE_ADDR, pdata, length); @@ -303,10 +436,11 @@ bool Ntag::readRegister(REGISTER_NR regAddr, byte& value) return bRetVal; } + bool Ntag::writeRegister(REGISTER_NR regAddr, byte mask, byte regdat) { bool bRetVal=false; - if(regAddr>7 || !writeBlockAddress(REGISTER, 0xFE)){ + if(regAddr>7 || !writeBlockAddress(REGISTER, 0xFE)){ // Note that 0xFE is session registers, volatile if power cycles! return false; } if (HWire.write(regAddr)==1 && @@ -370,3 +504,13 @@ bool Ntag::isAddressValid(BLOCK_TYPE type, byte blocknr){ } return true; } + + +void Ntag::printHex(byte* data, uint8_t len) { + for(int i=0;i +#include + +#define NTAG_CC_NDEF_FULL {0xE1, 0x10, 0x6D, 0x00} // Container class to use all of sector 0 for NDEF + class Ntag { @@ -20,6 +24,7 @@ class Ntag I2C_CLOCK_STR, NS_REG }REGISTER_NR; + static const byte NTAG_BLOCK_SIZE=16; 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(); @@ -29,13 +34,18 @@ class Ntag bool isReaderPresent(); bool setSramMirrorRf(bool bEnable, byte mirrorBaseBlockNr); bool setFd_ReaderHandshake(); + bool readConfigBlock(byte *data); + bool setContainerClass(); + bool setContainerClass(byte* ccdata); + bool writeNdef(word address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 1) + bool zeroEeprom(); bool readEeprom(word address, byte* pdata, byte length);//starts at address 0 bool writeEeprom(word address, byte* pdata, byte length);//starts at address 0 bool readSram(word address, byte* pdata, byte length);//starts at address 0 bool writeSram(word address, byte* pdata, byte length);//starts at address 0 bool readRegister(REGISTER_NR regAddr, byte &value); bool writeRegister(REGISTER_NR regAddr, byte mask, byte regdat); - bool setLastNdefBlock(); + bool setLastNdefBlock(); // The block whose memory read governs FD pin toggle rules void releaseI2c(); private: typedef enum{ @@ -46,9 +56,9 @@ class Ntag }BLOCK_TYPE; static const byte UID_LENGTH=7; static const byte DEFAULT_I2C_ADDRESS=0x55; - static const byte NTAG_BLOCK_SIZE=16; static const word EEPROM_BASE_ADDR=0x1<<4; static const word SRAM_BASE_ADDR=0xF8<<4; + static const byte NXP_MFR_ID=0x04; bool write(BLOCK_TYPE bt, word address, byte* pdata, byte length); bool read(BLOCK_TYPE bt, word address, byte* pdata, byte length); bool readBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data, byte data_size); @@ -57,6 +67,7 @@ class Ntag 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; From 10754d78519bba2b5270faaccfc744d01b6ccb2d Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Sun, 9 Apr 2017 17:28:13 -0700 Subject: [PATCH 25/35] Memory leak troubleshooting in progress. --- tests/NdefUnitTest/NdefUnitTest.ino | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/NdefUnitTest/NdefUnitTest.ino b/tests/NdefUnitTest/NdefUnitTest.ino index 6edfbdb..e9c27a9 100644 --- a/tests/NdefUnitTest/NdefUnitTest.ino +++ b/tests/NdefUnitTest/NdefUnitTest.ino @@ -1,5 +1,8 @@ +#define NDEF_USE_SERIAL + #include #include +#include #include #include #include @@ -169,6 +172,19 @@ test(encoding_with_record_id) { assertBytesEqual(encodedBytes, expectedBytes, sizeof(encodedBytes)); } +test(create_text_record) { + NdefMessage message = NdefMessage(); + message.addTextRecord("This record is 100 characters.0123456789012345678901234567890123456789012345678901234567890123456789"); + NdefRecord rec = message.getRecord(0); + //assertFalse(rec.hasPayload()); + //byte payload[10] = {0xAA}; + //rec.setPayload(payload, 10); + Serial.print("hasPayload returns: "); + Serial.println(rec.hasPayload(), 10); + assertTrue(rec.hasPayload()); + rec.print(); +} + void loop() { Test::run(); } From 09c069882874ccba6d35d1f05fecf64609a7c569 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Sun, 9 Apr 2017 18:45:52 -0700 Subject: [PATCH 26/35] Apply unit test fixes so that all tests now pass. --- Ndef.h | 1 + NfcTag.cpp | 2 +- tests/NdefMemoryTest/NdefMemoryTest.ino | 16 ++++++-- tests/NdefMessageTest/NdefMessageTest.ino | 43 ++++++++++++++-------- tests/NdefUnitTest/NdefUnitTest.ino | 45 ++++++++++++----------- tests/NfcTagTest/NfcTagTest.ino | 1 + 6 files changed, 66 insertions(+), 42 deletions(-) diff --git a/Ndef.h b/Ndef.h index 43bd437..f5bed4f 100644 --- a/Ndef.h +++ b/Ndef.h @@ -8,6 +8,7 @@ #include //#define NDEF_DEBUG 1 +#define NDEF_USE_SERIAL #ifdef NDEF_USE_SERIAL void PrintHex(const byte *data, const long numBytes); diff --git a/NfcTag.cpp b/NfcTag.cpp index bfe2eef..ade66fc 100644 --- a/NfcTag.cpp +++ b/NfcTag.cpp @@ -89,7 +89,7 @@ NdefMessage NfcTag::getNdefMessage() 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")); diff --git a/tests/NdefMemoryTest/NdefMemoryTest.ino b/tests/NdefMemoryTest/NdefMemoryTest.ino index 4d004ad..e5671dd 100644 --- a/tests/NdefMemoryTest/NdefMemoryTest.ino +++ b/tests/NdefMemoryTest/NdefMemoryTest.ino @@ -3,6 +3,7 @@ #include #include #include +#include void leakCheck(void (*callback)()) { @@ -53,10 +54,10 @@ void textRecord() 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 @@ -193,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..ac6c88d 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; } diff --git a/tests/NdefUnitTest/NdefUnitTest.ino b/tests/NdefUnitTest/NdefUnitTest.ino index d1f8c93..6edfbdb 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++) { @@ -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) { diff --git a/tests/NfcTagTest/NfcTagTest.ino b/tests/NfcTagTest/NfcTagTest.ino index 0820bfd..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); From b957b9866ab4b22c7cc18e78302eaba8025663b6 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Sun, 9 Apr 2017 19:32:02 -0700 Subject: [PATCH 27/35] Implement NDEF TLV header creation and sketch of packaging function. All tests pass, with coverage of header sizing only. --- NdefMessage.cpp | 36 +++++++++++++++++++++++ NdefMessage.h | 5 ++++ NdefRecord.cpp | 4 +-- NdefRecord.h | 2 +- tests/NdefMessageTest/NdefMessageTest.ino | 26 ++++++++++++++++ 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 6d36620..58edde3 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -146,6 +146,42 @@ void NdefMessage::encode(uint8_t* data) } + +unsigned int 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) +{ + unsigned int 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; + } +} + +unsigned int NdefMessage::getPackagedSize() +{ + return getEncodedSize() + getHeaderSize() + 1; +} + + +uint8_t * NdefMessage::getPackaged() +{ + uint8_t *packaged; + Serial.println("Not yet implemented!") + return packaged; +} + + boolean NdefMessage::addRecord(NdefRecord& record) { diff --git a/NdefMessage.h b/NdefMessage.h index 4291be2..6e480d2 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -17,6 +17,11 @@ class NdefMessage int getEncodedSize(); // need so we can pass array to encode void encode(byte *data); + unsigned int getHeaderSize(); + void getHeader(byte* header); + unsigned int getPackagedSize(); + uint8_t * getPackaged(); + boolean addRecord(NdefRecord& record); void addMimeMediaRecord(const char *mimeType, const char *payload); diff --git a/NdefRecord.cpp b/NdefRecord.cpp index 9682cce..f48e454 100644 --- a/NdefRecord.cpp +++ b/NdefRecord.cpp @@ -114,9 +114,9 @@ NdefRecord& NdefRecord::operator=(const NdefRecord& rhs) } // size of records in bytes -int NdefRecord::getEncodedSize() +unsigned int NdefRecord::getEncodedSize() { - int size = 2; // tnf + typeLength + unsigned int size = 2; // tnf + typeLength if (_payloadLength > 0xFF) { size += 4; diff --git a/NdefRecord.h b/NdefRecord.h index 63a7b9f..3200492 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -22,7 +22,7 @@ class NdefRecord ~NdefRecord(); NdefRecord& operator=(const NdefRecord& rhs); - int getEncodedSize(); + unsigned int getEncodedSize(); void encode(byte *data, bool firstRecord, bool lastRecord); unsigned int getTypeLength(); diff --git a/tests/NdefMessageTest/NdefMessageTest.ino b/tests/NdefMessageTest/NdefMessageTest.ino index ac6c88d..eec5861 100644 --- a/tests/NdefMessageTest/NdefMessageTest.ino +++ b/tests/NdefMessageTest/NdefMessageTest.ino @@ -231,6 +231,32 @@ 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(aaa_printFreeMemoryAtStart) // warning: relies on fact tests are run in alphabetical order { Serial.println(F("---------------------")); From 286a9950e4d7c1cff1d4f852e54cf5cc1918d498 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Sun, 9 Apr 2017 20:55:07 -0700 Subject: [PATCH 28/35] Implement some unit tests for ntag interface class (requires real hardware attached). All tests pass. --- NdefMessage.cpp | 2 +- ntag.cpp | 151 +++++++++++++++++++- ntag.h | 15 +- tests/NtagHardwareTest/NtagHardwareTest.ino | 121 ++++++++++++++++ 4 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 tests/NtagHardwareTest/NtagHardwareTest.ino diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 58edde3..ecae9dc 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -177,7 +177,7 @@ unsigned int NdefMessage::getPackagedSize() uint8_t * NdefMessage::getPackaged() { uint8_t *packaged; - Serial.println("Not yet implemented!") + Serial.println("Not yet implemented!"); return packaged; } diff --git a/ntag.cpp b/ntag.cpp index cb04b66..79bf67b 100644 --- a/ntag.cpp +++ b/ntag.cpp @@ -50,12 +50,37 @@ bool Ntag::isReaderPresent() void Ntag::detectI2cDevices(){ for(byte i=0;i<0x80;i++){ + HWire.beginTransmission(i); if(HWire.endTransmission()==0) { - Serial.print("Found I²C device on : 0x"); + 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); } } @@ -113,7 +138,7 @@ bool Ntag::isRfBusy(){ if(_triggered && millis()<_rfBusyStartTime+30) { //a zero has been read, but monostable hasn't run out yet - return true; + return true; } return false; } @@ -131,6 +156,115 @@ bool Ntag::setSramMirrorRf(bool bEnable, byte mirrorBaseBlockNr){ return writeRegister(NC_REG, 0x42, bEnable ? 0x02 : 0x00); } +bool Ntag::readConfigBlock(byte *data) { + return readBlock(CONFIG, 0, data, NTAG_BLOCK_SIZE); +} + +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(word 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 + // 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() +{ + word blockNr = 1; + byte data[NTAG_BLOCK_SIZE] = {0}; + while (writeBlock(USERMEM, blockNr, data)) { + blockNr++; + } + if (!isAddressValid(USERMEM, blockNr)) { return true; } // If we made it through all user mem + return false; +} + + bool Ntag::readSram(word address, byte *pdata, byte length) { return read(SRAM, address+SRAM_BASE_ADDR, pdata, length); @@ -303,10 +437,11 @@ bool Ntag::readRegister(REGISTER_NR regAddr, byte& value) return bRetVal; } + bool Ntag::writeRegister(REGISTER_NR regAddr, byte mask, byte regdat) { bool bRetVal=false; - if(regAddr>7 || !writeBlockAddress(REGISTER, 0xFE)){ + if(regAddr>7 || !writeBlockAddress(REGISTER, 0xFE)){ // Note that 0xFE is session registers, volatile if power cycles! return false; } if (HWire.write(regAddr)==1 && @@ -370,3 +505,13 @@ bool Ntag::isAddressValid(BLOCK_TYPE type, byte blocknr){ } return true; } + + +void Ntag::printHex(byte* data, uint8_t len) { + for(int i=0;i +#include + +#define NTAG_CC_NDEF_FULL {0xE1, 0x10, 0x6D, 0x00} // Container class to use all of sector 0 for NDEF + class Ntag { @@ -20,6 +24,8 @@ class Ntag I2C_CLOCK_STR, NS_REG }REGISTER_NR; + static const byte 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(); @@ -29,13 +35,18 @@ class Ntag bool isReaderPresent(); bool setSramMirrorRf(bool bEnable, byte mirrorBaseBlockNr); bool setFd_ReaderHandshake(); + bool readConfigBlock(byte *data); + bool setContainerClass(); + bool setContainerClass(byte* ccdata); + bool writeNdef(word address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 1) + bool zeroEeprom(); bool readEeprom(word address, byte* pdata, byte length);//starts at address 0 bool writeEeprom(word address, byte* pdata, byte length);//starts at address 0 bool readSram(word address, byte* pdata, byte length);//starts at address 0 bool writeSram(word address, byte* pdata, byte length);//starts at address 0 bool readRegister(REGISTER_NR regAddr, byte &value); bool writeRegister(REGISTER_NR regAddr, byte mask, byte regdat); - bool setLastNdefBlock(); + bool setLastNdefBlock(); // The block whose memory read governs FD pin toggle rules void releaseI2c(); private: typedef enum{ @@ -46,7 +57,6 @@ class Ntag }BLOCK_TYPE; static const byte UID_LENGTH=7; static const byte DEFAULT_I2C_ADDRESS=0x55; - static const byte NTAG_BLOCK_SIZE=16; static const word EEPROM_BASE_ADDR=0x1<<4; static const word SRAM_BASE_ADDR=0xF8<<4; bool write(BLOCK_TYPE bt, word address, byte* pdata, byte length); @@ -57,6 +67,7 @@ class Ntag 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; diff --git a/tests/NtagHardwareTest/NtagHardwareTest.ino b/tests/NtagHardwareTest/NtagHardwareTest.ino new file mode 100644 index 0000000..518a141 --- /dev/null +++ b/tests/NtagHardwareTest/NtagHardwareTest.ino @@ -0,0 +1,121 @@ +#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); +} + + +void loop() { + Test::run(); +} \ No newline at end of file From d505bd458454864e466e7cd9b1e136ebc0e7a8a7 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Mon, 10 Apr 2017 00:54:35 -0700 Subject: [PATCH 29/35] Implement and test packaging of NDEF message and writing to NTAG. All tests pass but some cleanup needed. --- NdefMessage.cpp | 10 +++++---- NdefMessage.h | 2 +- ntag.cpp | 25 ++++++++++++++++++----- ntag.h | 4 +++- tests/NdefMessageTest/NdefMessageTest.ino | 22 ++++++++++++++++++++ 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index ecae9dc..c1d6017 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -174,11 +174,13 @@ unsigned int NdefMessage::getPackagedSize() } -uint8_t * NdefMessage::getPackaged() +void NdefMessage::getPackaged(uint8_t *data) { - uint8_t *packaged; - Serial.println("Not yet implemented!"); - return packaged; + uint8_t size = getPackagedSize(); + + getHeader(data); + encode(&data[getHeaderSize()]); + data[size-1] = 0xFE; // Termination byte for TLV } diff --git a/NdefMessage.h b/NdefMessage.h index 6e480d2..c6397a4 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -20,7 +20,7 @@ class NdefMessage unsigned int getHeaderSize(); void getHeader(byte* header); unsigned int getPackagedSize(); - uint8_t * getPackaged(); + void getPackaged(uint8_t *data); boolean addRecord(NdefRecord& record); diff --git a/ntag.cpp b/ntag.cpp index 79bf67b..dbd5be4 100644 --- a/ntag.cpp +++ b/ntag.cpp @@ -181,6 +181,21 @@ bool Ntag::writeNdef(word address, NdefMessage &message, bool sprint=true){ // 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 + + uint8_t size = message.getPackagedSize(); + byte data[size]; + if (size > 254) { + Serial.print("Packaging large message..."); + delay(100); + } + message.getPackaged(data); + if (size > 254) { + Serial.println("success. Now writing..."); + delay(100); + } + + return write(bt, address, data, size); + /* // Get & write the NDEF header, incrementing address uint8_t ndefHeaderSize = message.getHeaderSize(); byte ndefHeader[ndefHeaderSize]; @@ -250,7 +265,7 @@ bool Ntag::writeNdef(word address, NdefMessage &message, bool sprint=true){ Serial.println(address); } */ - return true; + //return true; } bool Ntag::zeroEeprom() @@ -332,7 +347,7 @@ bool Ntag::write(BLOCK_TYPE bt, word address, byte* pdata, byte length) wptr+=writeLength; blockNr++; } - _lastMemBlockWritten = --blockNr; + if (bt == USERMEM) { _lastMemBlockWritten = --blockNr; } return true; } @@ -420,7 +435,7 @@ bool Ntag::readRegister(REGISTER_NR regAddr, byte& value) { value=0; bool bRetVal=true; - if(regAddr>6 || !writeBlockAddress(REGISTER, 0xFE)){ + if(regAddr>6 || !writeBlockAddress(REGISTER, SESSION_REG_ADDR)){ return false; } if(HWire.write(regAddr)!=1){ @@ -441,7 +456,7 @@ bool Ntag::readRegister(REGISTER_NR regAddr, byte& value) bool Ntag::writeRegister(REGISTER_NR regAddr, byte mask, byte regdat) { bool bRetVal=false; - if(regAddr>7 || !writeBlockAddress(REGISTER, 0xFE)){ // Note that 0xFE is session registers, volatile if power cycles! + if(regAddr>7 || !writeBlockAddress(REGISTER, SESSION_REG_ADDR)){ // Note that 0xFE is session registers, volatile if power cycles! return false; } if (HWire.write(regAddr)==1 && @@ -496,7 +511,7 @@ bool Ntag::isAddressValid(BLOCK_TYPE type, byte blocknr){ } break; case REGISTER: - if(blocknr != 0xFE){ + if(blocknr != SESSION_REG_ADDR && blocknr != STARTUP_REG_ADDR){ return false; } break; diff --git a/ntag.h b/ntag.h index 99cabff..7fcfc50 100644 --- a/ntag.h +++ b/ntag.h @@ -38,7 +38,7 @@ class Ntag bool readConfigBlock(byte *data); bool setContainerClass(); bool setContainerClass(byte* ccdata); - bool writeNdef(word address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 1) + bool writeNdef(word address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 16) bool zeroEeprom(); bool readEeprom(word address, byte* pdata, byte length);//starts at address 0 bool writeEeprom(word address, byte* pdata, byte length);//starts at address 0 @@ -59,6 +59,8 @@ class Ntag static const byte DEFAULT_I2C_ADDRESS=0x55; static const word EEPROM_BASE_ADDR=0x1<<4; static const word SRAM_BASE_ADDR=0xF8<<4; + static const byte STARTUP_REG_ADDR=0x3A; + static const byte SESSION_REG_ADDR=0xFE; bool write(BLOCK_TYPE bt, word address, byte* pdata, byte length); bool read(BLOCK_TYPE bt, word address, byte* pdata, byte length); bool readBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data, byte data_size); diff --git a/tests/NdefMessageTest/NdefMessageTest.ino b/tests/NdefMessageTest/NdefMessageTest.ino index eec5861..2b98f0c 100644 --- a/tests/NdefMessageTest/NdefMessageTest.ino +++ b/tests/NdefMessageTest/NdefMessageTest.ino @@ -257,6 +257,28 @@ test(message_packaging_size) 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 +} + + test(aaa_printFreeMemoryAtStart) // warning: relies on fact tests are run in alphabetical order { Serial.println(F("---------------------")); From dec314db306d2d6f59c3fcf1cb83c2364e5d1b31 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Mon, 10 Apr 2017 03:18:19 -0700 Subject: [PATCH 30/35] Add test of complete NDEF encoding and writing to NTAG, and fix bug where bit width of sizes was 8 when 16 needed. --- NdefMessage.cpp | 8 ++-- NdefMessage.h | 4 +- ntag.cpp | 32 +++++++++---- tests/NdefMessageTest/NdefMessageTest.ino | 53 +++++++++++++++++++++ tests/NtagHardwareTest/NtagHardwareTest.ino | 24 ++++++++++ 5 files changed, 105 insertions(+), 16 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index c1d6017..6982010 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -121,10 +121,10 @@ unsigned int NdefMessage::getRecordCount() return _recordCount; } -int NdefMessage::getEncodedSize() +uint16_t NdefMessage::getEncodedSize() { - int size = 0; - for (unsigned int i = 0; i < _recordCount; i++) + uint16_t size = 0; + for (uint8_t i = 0; i < _recordCount; i++) { size += _records[i].getEncodedSize(); } @@ -168,7 +168,7 @@ void NdefMessage::getHeader(byte* header) } } -unsigned int NdefMessage::getPackagedSize() +uint16_t NdefMessage::getPackagedSize() { return getEncodedSize() + getHeaderSize() + 1; } diff --git a/NdefMessage.h b/NdefMessage.h index c6397a4..92925bc 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -15,11 +15,11 @@ class NdefMessage ~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); unsigned int getHeaderSize(); void getHeader(byte* header); - unsigned int getPackagedSize(); + uint16_t getPackagedSize(); void getPackaged(uint8_t *data); diff --git a/ntag.cpp b/ntag.cpp index dbd5be4..19037aa 100644 --- a/ntag.cpp +++ b/ntag.cpp @@ -8,6 +8,8 @@ HardWire HWire(1, I2C_REMAP);// | I2C_BUS_RESET); // I2c1 #endif #include +#include + Ntag::Ntag(DEVICE_TYPE dt, byte fd_pin, byte vout_pin, byte i2c_address): _dt(dt), @@ -182,17 +184,16 @@ bool Ntag::writeNdef(word address, NdefMessage &message, bool sprint=true){ BLOCK_TYPE bt = SRAM; if(address/NTAG_BLOCK_SIZE < 0xF8 || address/NTAG_BLOCK_SIZE > 0xFB){ bt = USERMEM; } // See isAddressValid - uint8_t size = message.getPackagedSize(); + uint16_t size = message.getPackagedSize(); byte data[size]; - if (size > 254) { - Serial.print("Packaging large message..."); - delay(100); - } message.getPackaged(data); - if (size > 254) { - Serial.println("success. Now writing..."); - delay(100); - } + +#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); /* @@ -416,6 +417,10 @@ bool Ntag::writeBlock(BLOCK_TYPE bt, byte memBlockAddress, byte *p_data) } } if(!end_transmission()){ +#ifdef NDEF_USE_SERIAL + Serial.print("NTAG: I2C block write failed at block 0x"); + Serial.println(memBlockAddress, HEX); +#endif return false; } switch(bt){ @@ -470,6 +475,13 @@ bool Ntag::writeRegister(REGISTER_NR regAddr, byte mask, byte regdat) 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); @@ -497,7 +509,7 @@ bool Ntag::isAddressValid(BLOCK_TYPE type, byte blocknr){ } break; case NTAG_I2C_2K: - if(blocknr < 1 || blocknr > 0x78){ + if(blocknr < 1 || blocknr > 0x78){ // Not consistent with observed failure at 0x3B return false; } break; diff --git a/tests/NdefMessageTest/NdefMessageTest.ino b/tests/NdefMessageTest/NdefMessageTest.ino index 2b98f0c..95dc6c1 100644 --- a/tests/NdefMessageTest/NdefMessageTest.ino +++ b/tests/NdefMessageTest/NdefMessageTest.ino @@ -279,6 +279,59 @@ test(message_packaged_content) } +// 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 = 256; + byte payload[len]; + payload[0] = 0xAA; + payload[len-1] = 0xBB; + + 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] = 0xCC; + 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] == 0xCC) { found = true; break; } + } + assertTrue(found); +} + + test(aaa_printFreeMemoryAtStart) // warning: relies on fact tests are run in alphabetical order { Serial.println(F("---------------------")); diff --git a/tests/NtagHardwareTest/NtagHardwareTest.ino b/tests/NtagHardwareTest/NtagHardwareTest.ino index 518a141..05ce6f4 100644 --- a/tests/NtagHardwareTest/NtagHardwareTest.ino +++ b/tests/NtagHardwareTest/NtagHardwareTest.ino @@ -116,6 +116,30 @@ test(eeprom_read_write) { } +test(write_big_ndef) { + NdefRecord r; + uint16_t len = 512; + byte payload[len]; + + r.setPayload(payload, len); + + assertEqual(len, r.getPayloadLength()); + + 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()); + + //byte data[m.getPackagedSize()]; + //m.getPackaged(data); + //ntag.writeEeprom(0, data, m.getPackagedSize()); + ntag.writeNdef(16, m, true); + Serial.println("Completed message write."); +} + + void loop() { Test::run(); } \ No newline at end of file From 0b59f8f51fdb34f3bb30e7e47768894caa7c8877 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Mon, 10 Apr 2017 18:29:43 -0700 Subject: [PATCH 31/35] Find and fix several errors caused by 8-bit index variables that should be 16-bit. --- NdefMessage.cpp | 2 +- NdefMessage.h | 2 +- ntag.cpp | 22 +++++++-------- ntag.h | 24 ++++++++--------- tests/NdefMessageTest/NdefMessageTest.ino | 30 +++++++++++++++++---- tests/NtagHardwareTest/NtagHardwareTest.ino | 25 ++++++++++++++--- 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 6982010..8e9b617 100644 --- a/NdefMessage.cpp +++ b/NdefMessage.cpp @@ -176,7 +176,7 @@ uint16_t NdefMessage::getPackagedSize() void NdefMessage::getPackaged(uint8_t *data) { - uint8_t size = getPackagedSize(); + uint16_t size = getPackagedSize(); getHeader(data); encode(&data[getHeaderSize()]); diff --git a/NdefMessage.h b/NdefMessage.h index 92925bc..e88232e 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -17,7 +17,7 @@ class NdefMessage uint16_t getEncodedSize(); // need so we can pass array to encode void encode(byte *data); - unsigned int getHeaderSize(); + uint16_t getHeaderSize(); void getHeader(byte* header); uint16_t getPackagedSize(); void getPackaged(uint8_t *data); diff --git a/ntag.cpp b/ntag.cpp index 19037aa..1dc0aa7 100644 --- a/ntag.cpp +++ b/ntag.cpp @@ -178,7 +178,7 @@ bool Ntag::setContainerClass(byte* ccdata) write(CONFIG, 0, config, NTAG_BLOCK_SIZE); } -bool Ntag::writeNdef(word address, NdefMessage &message, bool sprint=true){ +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; @@ -271,9 +271,9 @@ bool Ntag::writeNdef(word address, NdefMessage &message, bool sprint=true){ bool Ntag::zeroEeprom() { - word blockNr = 1; + byte blockNr = 1; byte data[NTAG_BLOCK_SIZE] = {0}; - while (writeBlock(USERMEM, blockNr, data)) { + while ( blockNr < 0x3B && writeBlock(USERMEM, blockNr, data)) { // Figure out why this is and code appropriately blockNr++; } if (!isAddressValid(USERMEM, blockNr)) { return true; } // If we made it through all user mem @@ -281,22 +281,22 @@ bool Ntag::zeroEeprom() } -bool Ntag::readSram(word address, byte *pdata, byte length) +bool Ntag::readSram(uint16_t address, byte *pdata, uint16_t length) { return read(SRAM, address+SRAM_BASE_ADDR, pdata, length); } -bool Ntag::writeSram(word address, byte *pdata, byte length) +bool Ntag::writeSram(uint16_t address, byte *pdata, uint16_t length) { return write(SRAM, address+SRAM_BASE_ADDR, pdata, length); } -bool Ntag::readEeprom(word address, byte *pdata, byte length) +bool Ntag::readEeprom(uint16_t address, byte *pdata, uint16_t length) { return read(USERMEM, address+EEPROM_BASE_ADDR, pdata, length); } -bool Ntag::writeEeprom(word address, byte *pdata, byte length) +bool Ntag::writeEeprom(uint16_t address, byte *pdata, uint16_t length) { return write(USERMEM, address+EEPROM_BASE_ADDR, pdata, length); } @@ -307,10 +307,10 @@ void Ntag::releaseI2c() writeRegister(NS_REG,0x40,0); } -bool Ntag::write(BLOCK_TYPE bt, word address, byte* pdata, byte length) +bool Ntag::write(BLOCK_TYPE bt, uint16_t address, byte* pdata, uint16_t length) { byte readbuffer[NTAG_BLOCK_SIZE]; - byte writeLength; + uint16_t writeLength; byte* wptr=pdata; byte blockNr=address/NTAG_BLOCK_SIZE; @@ -352,10 +352,10 @@ bool Ntag::write(BLOCK_TYPE bt, word address, byte* pdata, byte length) return true; } -bool Ntag::read(BLOCK_TYPE bt, word address, byte* pdata, byte length) +bool Ntag::read(BLOCK_TYPE bt, uint16_t address, byte* pdata, uint16_t length) { byte readbuffer[NTAG_BLOCK_SIZE]; - byte readLength; + uint16_t readLength; byte* wptr=pdata; readLength=min(NTAG_BLOCK_SIZE, (address % NTAG_BLOCK_SIZE) + length); diff --git a/ntag.h b/ntag.h index 7fcfc50..75970e4 100644 --- a/ntag.h +++ b/ntag.h @@ -24,7 +24,7 @@ class Ntag I2C_CLOCK_STR, NS_REG }REGISTER_NR; - static const byte NTAG_BLOCK_SIZE=16; + 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. @@ -38,12 +38,12 @@ class Ntag bool readConfigBlock(byte *data); bool setContainerClass(); bool setContainerClass(byte* ccdata); - bool writeNdef(word address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 16) + bool writeNdef(uint16_t address, NdefMessage &message, bool sprint); // absolute address (user mem starts at 16) bool zeroEeprom(); - bool readEeprom(word address, byte* pdata, byte length);//starts at address 0 - bool writeEeprom(word address, byte* pdata, byte length);//starts at address 0 - bool readSram(word address, byte* pdata, byte length);//starts at address 0 - bool writeSram(word address, byte* pdata, byte length);//starts at address 0 + 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 @@ -57,12 +57,12 @@ class Ntag }BLOCK_TYPE; static const byte UID_LENGTH=7; static const byte DEFAULT_I2C_ADDRESS=0x55; - static const word EEPROM_BASE_ADDR=0x1<<4; - static const word SRAM_BASE_ADDR=0xF8<<4; - static const byte STARTUP_REG_ADDR=0x3A; - static const byte SESSION_REG_ADDR=0xFE; - bool write(BLOCK_TYPE bt, word address, byte* pdata, byte length); - bool read(BLOCK_TYPE bt, word address, byte* pdata, byte length); + 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); diff --git a/tests/NdefMessageTest/NdefMessageTest.ino b/tests/NdefMessageTest/NdefMessageTest.ino index 95dc6c1..41206a0 100644 --- a/tests/NdefMessageTest/NdefMessageTest.ino +++ b/tests/NdefMessageTest/NdefMessageTest.ino @@ -283,10 +283,12 @@ test(message_packaged_content) test(big_record_handling) { NdefRecord r; - uint16_t len = 256; + uint16_t len = 300; byte payload[len]; - payload[0] = 0xAA; - payload[len-1] = 0xBB; + randomSeed(0xB8); + for (uint16_t i = 0; i < len; i++) { + payload[i] = random(len); + } r.setPayload(payload, len); r.setTnf(TNF_UNKNOWN); @@ -310,7 +312,7 @@ test(big_record_handling) NdefRecord r2; uint16_t len2 = 64; byte payload2[len2]; - payload2[0] = 0xCC; + payload2[0] = 0x12; r2.setPayload(payload2, len2); r2.setTnf(TNF_UNKNOWN); @@ -326,9 +328,27 @@ test(big_record_handling) bool found = false; for (uint16_t i = 0; i < m.getPackagedSize(); i++) { - if (package[i] == 0xCC) { found = true; break; } + 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]); + } } diff --git a/tests/NtagHardwareTest/NtagHardwareTest.ino b/tests/NtagHardwareTest/NtagHardwareTest.ino index 05ce6f4..9759575 100644 --- a/tests/NtagHardwareTest/NtagHardwareTest.ino +++ b/tests/NtagHardwareTest/NtagHardwareTest.ino @@ -120,6 +120,7 @@ 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); @@ -129,14 +130,30 @@ test(write_big_ndef) { m.addRecord(r); assertEqual(r.getEncodedSize(), m.getEncodedSize()); assertEqual(r.getEncodedSize() + 5, m.getPackagedSize()); - Serial.print("NDEF package size: "); - Serial.println(m.getPackagedSize()); - //byte data[m.getPackagedSize()]; - //m.getPackaged(data); + 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 } From c2a24d4d99fe47103b400024a2d1acff394c09c5 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Mon, 10 Apr 2017 20:09:46 -0700 Subject: [PATCH 32/35] Implement and test calculation of record payload offsets in packaged NDEF. More type conversion to uint8_t and uint16_t. --- NdefMessage.cpp | 47 +++++++++++++++-------- NdefMessage.h | 14 ++++--- tests/NdefMessageTest/NdefMessageTest.ino | 30 +++++++++++++++ 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/NdefMessage.cpp b/NdefMessage.cpp index 8e9b617..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) { @@ -34,7 +34,7 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) record.setTnf(tnf); index++; - int typeLength = data[index]; + uint8_t typeLength = data[index]; uint32_t payloadLength = 0; if (sr) @@ -52,7 +52,7 @@ NdefMessage::NdefMessage(const byte * data, const int numBytes) index += 4; } - int idLength = 0; + uint8_t idLength = 0; if (il) { index++; @@ -83,7 +83,7 @@ NdefMessage::NdefMessage(const NdefMessage& rhs) { _recordCount = rhs._recordCount; - for (unsigned int i = 0; i < _recordCount; i++) + for (uint8_t i = 0; i < _recordCount; i++) { _records[i] = rhs._records[i]; } @@ -101,14 +101,14 @@ NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) { // delete existing records - for (unsigned 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 (unsigned int i = 0; i < _recordCount; i++) + for (uint8_t i = 0; i < _recordCount; i++) { _records[i] = rhs._records[i]; } @@ -116,7 +116,7 @@ NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) return *this; } -unsigned int NdefMessage::getRecordCount() +uint8_t NdefMessage::getRecordCount() { return _recordCount; } @@ -136,18 +136,23 @@ void NdefMessage::encode(uint8_t* data) { // assert sizeof(data) >= getEncodedSize() uint8_t* data_ptr = &data[0]; + uint16_t offset = getHeaderSize(); - for (unsigned 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; } } -unsigned int NdefMessage::getHeaderSize() { +uint16_t NdefMessage::getHeaderSize() { return 2 + (getEncodedSize() > 254 ? 2 : 0); // TLV is 0x03 + 1 byte length // OR if size > 254, 0x03 + 3 byte length @@ -156,7 +161,7 @@ unsigned int NdefMessage::getHeaderSize() { void NdefMessage::getHeader(byte* header) { - unsigned int payloadLength = getEncodedSize(); + uint16_t payloadLength = getEncodedSize(); bool lengthy = payloadLength > 254; header[0] = 0x3; if (lengthy) { @@ -207,7 +212,7 @@ void NdefMessage::addMimeMediaRecord(const char *mimeType, const char* payload) addMimeMediaRecord(mimeType, reinterpret_cast(payload), strlen(payload)); } -void NdefMessage::addMimeMediaRecord(const char *mimeType, const byte* payload, int payloadLength) +void NdefMessage::addMimeMediaRecord(const char *mimeType, const byte* payload, uint16_t payloadLength) { NdefRecord r; r.setTnf(TNF_MIME_MEDIA); @@ -281,7 +286,7 @@ void NdefMessage::addExternalRecord(const char *type,const char* payload) addExternalRecord(type, reinterpret_cast(payload), strlen(payload)); } -void NdefMessage::addExternalRecord(const char *type, const byte *payload, int payloadLength) +void NdefMessage::addExternalRecord(const char *type, const byte *payload, uint16_t payloadLength) { NdefRecord r; r.setTnf(TNF_EXTERNAL_TYPE); @@ -304,7 +309,7 @@ void NdefMessage::addEmptyRecord() addRecord(r); } -NdefRecord NdefMessage::getRecord(int index) +NdefRecord NdefMessage::getRecord(uint8_t index) { if (index > -1 && index < static_cast(_recordCount)) { @@ -316,7 +321,15 @@ 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); } diff --git a/NdefMessage.h b/NdefMessage.h index e88232e..6c902c5 100644 --- a/NdefMessage.h +++ b/NdefMessage.h @@ -10,7 +10,7 @@ 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); @@ -25,13 +25,13 @@ class NdefMessage boolean addRecord(NdefRecord& record); void addMimeMediaRecord(const char *mimeType, const char *payload); - void addMimeMediaRecord(const char *mimeType, const byte *payload, int payloadLength); + 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, int payloadLength); + 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 @@ -45,15 +45,17 @@ class NdefMessage 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; }; diff --git a/tests/NdefMessageTest/NdefMessageTest.ino b/tests/NdefMessageTest/NdefMessageTest.ino index 41206a0..8d95cd8 100644 --- a/tests/NdefMessageTest/NdefMessageTest.ino +++ b/tests/NdefMessageTest/NdefMessageTest.ino @@ -352,6 +352,34 @@ test(big_record_handling) } +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("---------------------")); @@ -367,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 From 64e00ba97dbcc46c009aeee6e6ba0bee021f5a12 Mon Sep 17 00:00:00 2001 From: eecharlie Date: Tue, 11 Apr 2017 15:50:26 -0700 Subject: [PATCH 33/35] Update readme, back to stock. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 486bb7b..14dba0d 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,6 @@ NFC Data Exchange Format (NDEF) is a common data format that operates across all This code works with the [Adafruit NFC Shield](https://www.adafruit.com/products/789), [Seeed Studio NFC Shield v2.0](http://www.seeedstudio.com/depot/nfc-shield-v20-p-1370.html) and the [Seeed Studio NFC Shield](http://www.seeedstudio.com/depot/nfc-shield-p-916.html?cPath=73). The library supports I2C for the Adafruit shield and SPI with the Seeed shields. The Adafruit Shield can also be modified to use SPI. It should also work with the [Adafruit NFC Breakout Board](https://www.adafruit.com/products/364). -### A note on this fork -The purpose of this fork is to implement some memory-saving features to avoid stack overflows on Arduinos. Specifically, to make it possible to go through the process of defining NDEF records and messages and getting the header data, but without dragging payload data along with it. This will allow payload data to be moved around separately, and in whatever chunk size you desire. - ### Supports - Reading from Mifare Classic Tags with 4 byte UIDs. - Writing to Mifare Classic Tags with 4 byte UIDs. From b0393799e3a5dc021e755c3c5f482f7f27168b75 Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Thu, 1 Jun 2017 16:03:53 -0700 Subject: [PATCH 34/35] Add function to read startup & session config registers. --- ntag.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- ntag.h | 10 +++- 2 files changed, 153 insertions(+), 6 deletions(-) diff --git a/ntag.cpp b/ntag.cpp index 1dc0aa7..d8e0f7c 100644 --- a/ntag.cpp +++ b/ntag.cpp @@ -162,6 +162,142 @@ 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); @@ -273,9 +409,10 @@ bool Ntag::zeroEeprom() { byte blockNr = 1; byte data[NTAG_BLOCK_SIZE] = {0}; - while ( blockNr < 0x3B && writeBlock(USERMEM, blockNr, data)) { // Figure out why this is and code appropriately + 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; } @@ -440,7 +577,7 @@ bool Ntag::readRegister(REGISTER_NR regAddr, byte& value) { value=0; bool bRetVal=true; - if(regAddr>6 || !writeBlockAddress(REGISTER, SESSION_REG_ADDR)){ + if(regAddr>6 || !writeBlockAddress(REGISTER, STARTUP_REG_ADDR)){ return false; } if(HWire.write(regAddr)!=1){ @@ -461,7 +598,7 @@ bool Ntag::readRegister(REGISTER_NR regAddr, byte& value) bool Ntag::writeRegister(REGISTER_NR regAddr, byte mask, byte regdat) { bool bRetVal=false; - if(regAddr>7 || !writeBlockAddress(REGISTER, SESSION_REG_ADDR)){ // Note that 0xFE is session registers, volatile if power cycles! + 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 && @@ -523,8 +660,10 @@ bool Ntag::isAddressValid(BLOCK_TYPE type, byte blocknr){ } break; case REGISTER: - if(blocknr != SESSION_REG_ADDR && blocknr != STARTUP_REG_ADDR){ - return false; + if(blocknr == SESSION_REG_ADDR || blocknr == STARTUP_REG_ADDR + || (blocknr >= 56 && blocknr <= 58) + || blocknr == 254) { + return true; } break; default: diff --git a/ntag.h b/ntag.h index 75970e4..4c629a2 100644 --- a/ntag.h +++ b/ntag.h @@ -5,8 +5,9 @@ #include #include -#define NTAG_CC_NDEF_FULL {0xE1, 0x10, 0x6D, 0x00} // Container class to use all of sector 0 for NDEF +#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 { @@ -33,9 +34,16 @@ class Ntag 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) From 7b0460068aa90e27e463eadc824f9b5a49928d0f Mon Sep 17 00:00:00 2001 From: Charlie Matlack Date: Mon, 10 Jul 2017 14:35:19 -0700 Subject: [PATCH 35/35] Add record header getter functions. All tests pass. --- NdefRecord.cpp | 64 +++++++++++++++++------------ NdefRecord.h | 2 + tests/NdefUnitTest/NdefUnitTest.ino | 32 ++++++++++++++- 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/NdefRecord.cpp b/NdefRecord.cpp index f48e454..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 -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; +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) diff --git a/NdefRecord.h b/NdefRecord.h index 3200492..72cc909 100644 --- a/NdefRecord.h +++ b/NdefRecord.h @@ -22,6 +22,8 @@ class NdefRecord ~NdefRecord(); NdefRecord& operator=(const NdefRecord& rhs); + byte getHeaderSize(); + void getHeader(byte *headerData, bool firstRecord, bool lastRecord); unsigned int getEncodedSize(); void encode(byte *data, bool firstRecord, bool lastRecord); diff --git a/tests/NdefUnitTest/NdefUnitTest.ino b/tests/NdefUnitTest/NdefUnitTest.ino index 6edfbdb..8904345 100644 --- a/tests/NdefUnitTest/NdefUnitTest.ino +++ b/tests/NdefUnitTest/NdefUnitTest.ino @@ -11,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) { @@ -169,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