to colorize a colorful pixel with a simple touch using nfc technology
Dependencies: Chainable_RGB_LED mbed
use Arch, NFC Shield and Grove - Chainable RGB LED to DIY a touch pixel. Then use an Android with NFC support to colorize it.
The project is on https://github.com/Seeed-Studio/TouchPixel
Revision 0:88960f3eeb2c, committed 2013-12-27
- Comitter:
- yihui
- Date:
- Fri Dec 27 01:46:32 2013 +0000
- Commit message:
- initial
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Chainable_RGB_LED.lib Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/seeed/code/Chainable_RGB_LED/#24631ceaa38a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,109 @@ + +#include "mbed.h" +#include "ChainableLED.h" +#include "PN532_SPI.h" +#include "emulatetag.h" +#include "NdefMessage.h" + +#include <string.h> + +#define LOG(args...) + +BusOut leds(LED1, LED2, LED3, LED4); + +ChainableLED rgbled(P1_14, P1_13, 1); + +SPI spi(P1_22, P1_21, P1_20); +PN532_SPI interface(spi, P0_2); +EmulateTag nfc(interface); + +uint8_t ndefBuf[120]; +NdefMessage message; +int messageSize; + +uint8_t uid[3] = { 0x12, 0x34, 0x56 }; + +uint32_t getColor(uint8_t *buf) +{ + uint32_t x = 0; + for (uint8_t i = 0; i < 8; i++) { + uint8_t c = *buf; + if (c >= '0' && c <= '9') { + x *= 16; + x += c - '0'; + } else if (c >= 'A' && c <= 'F') { + x *= 16; + x += (c - 'A') + 10; + } else if (c >= 'a' && c <= 'f') { + x *= 16; + x += (c - 'a') + 10; + } else + break; + + buf++; + } + + return x; +} + +void processNewNdef(uint8_t *buf, uint16_t length) +{ + NdefMessage msg = NdefMessage(buf, length); + + NdefRecord record = msg.getRecord(0); + uint8_t recordbuf[32]; + record.getType(recordbuf); + if (!memcmp(recordbuf, "text/c", 6)) { + uint8_t r, g, b; + uint32_t color; + record.getPayload(recordbuf); + color = getColor(recordbuf); + r = (color >> 16) & 0xFF; + g = (color >> 8) & 0xFF; + b = color & 0xFF; + + rgbled.setColorRGB(0, r, g, b); + leds = r; + } +} + +int main() +{ + LOG("------- Emulate Tag --------\n"); + rgbled.setColorRGB(0, 0, 0, 0); + + NdefRecord aarRecord = NdefRecord(); + const uint8_t aarType[] = "android.com:pkg"; + const uint8_t aarPayload[] = "com.seeedstudio.android.nfc.touchpixel"; + + aarRecord.setTnf(TNF_EXTERNAL_TYPE); + aarRecord.setType(aarType, sizeof(aarType) - 1); + aarRecord.setPayload(aarPayload, sizeof(aarPayload) - 1); + + message = NdefMessage(); + message.addMimeMediaRecord("text/c", "FF000000"); + message.addRecord(aarRecord); + messageSize = message.getEncodedSize(); + if (messageSize > sizeof(ndefBuf)) { + while (1) { } + } + + LOG("Ndef encoded message size: %d\n", messageSize); + + message.encode(ndefBuf); + + // comment out this command for no ndef message + nfc.setNdefFile(ndefBuf, messageSize); + + // uid must be 3 bytes! + nfc.setUid(uid); + + nfc.init(); + + nfc.attach(processNewNdef); + + while (1) { + //nfc.setNdefFile(ndefBuf, messageSize); + nfc.emulate(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/a9913a65894f \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/MifareClassic.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,276 @@ +#include "MifareClassic.h" +#include "PN532_debug.h" +#include "string.h" + +#define BLOCK_SIZE 16 +#define LONG_TLV_SIZE 4 +#define SHORT_TLV_SIZE 2 + +#define MIFARE_CLASSIC ("Mifare Classic") + +MifareClassic::MifareClassic(PN532& nfcShield) +{ + _nfcShield = &nfcShield; +} + +MifareClassic::~MifareClassic() +{ +} + +NfcTag MifareClassic::read(uint8_t *uid, unsigned int uidLength) +{ + uint8_t key[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; + int currentBlock = 4; + int messageStartIndex = 0; + int messageLength = 0; + uint8_t data[BLOCK_SIZE]; + + // read first block to get message length + int success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); + if (success) + { + success = _nfcShield->mifareclassic_ReadDataBlock(currentBlock, data); + if (success) + { + if (!decodeTlv(data, messageLength, messageStartIndex)) { + return NfcTag(uid, uidLength, "ERROR"); // TODO should the error message go in NfcTag? + } + } + else + { + DMSG("Error. Failed read block "); + DMSG_INT(currentBlock); + return NfcTag(uid, uidLength, MIFARE_CLASSIC); + } + } + else + { + DMSG("Tag is not NDEF formatted."); + // TODO set tag.isFormatted = false + return NfcTag(uid, uidLength, MIFARE_CLASSIC); + } + + // this should be nested in the message length loop + int index = 0; + int bufferSize = getBufferSize(messageLength); + uint8_t buffer[bufferSize]; + + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Message Length "); + DMSG_INT(messageLength); + DMSG("Buffer Size "); + DMSG_INT(bufferSize); + #endif + + while (index < bufferSize) + { + + // authenticate on every sector + if (_nfcShield->mifareclassic_IsFirstBlock(currentBlock)) + { + success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); + if (!success) + { + DMSG("Error. Block Authentication failed for "); + DMSG_INT(currentBlock); + // TODO error handling + } + } + + // read the data + success = _nfcShield->mifareclassic_ReadDataBlock(currentBlock, &buffer[index]); + if (success) + { + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Block "); + DMSG_INT(currentBlock); + _nfcShield->PrintHexChar(&buffer[index], BLOCK_SIZE); + #endif + } + else + { + DMSG("Read failed "); + DMSG_INT(currentBlock); + // TODO handle errors here + } + + index += BLOCK_SIZE; + currentBlock++; + + // skip the trailer block + if (_nfcShield->mifareclassic_IsTrailerBlock(currentBlock)) + { + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Skipping block "); + DMSG_INT(currentBlock); + #endif + currentBlock++; + } + } + + return NfcTag(uid, uidLength, MIFARE_CLASSIC, &buffer[messageStartIndex], messageLength); +} + +int MifareClassic::getBufferSize(int messageLength) +{ + + int bufferSize = messageLength; + + // TLV header is 2 or 4 uint8_ts, TLV terminator is 1 uint8_t. + if (messageLength < 0xFF) + { + bufferSize += SHORT_TLV_SIZE + 1; + } + else + { + bufferSize += LONG_TLV_SIZE + 1; + } + + // bufferSize needs to be a multiple of BLOCK_SIZE + if (bufferSize % BLOCK_SIZE != 0) + { + bufferSize = ((bufferSize / BLOCK_SIZE) + 1) * BLOCK_SIZE; + } + + return bufferSize; +} + +// skip null tlvs (0x0) before the real message +// technically unlimited null tlvs, but we assume +// T & L of TLV in the first block we read +int MifareClassic::getNdefStartIndex(uint8_t *data) +{ + + for (int i = 0; i < BLOCK_SIZE; i++) + { + if (data[i] == 0x0) + { + // do nothing, skip + } + else if (data[i] == 0x3) + { + return i; + } + else + { + DMSG("Unknown TLV "); + DMSG_HEX(data[i]); + return -2; + } + } + + return -1; +} + +// Decode the NDEF data length from the Mifare TLV +// Leading null TLVs (0x0) are skipped +// Assuming T & L of TLV will be in the first block +// messageLength and messageStartIndex written to the parameters +// success or failure status is returned +// +// { 0x3, LENGTH } +// { 0x3, 0xFF, LENGTH, LENGTH } +bool MifareClassic::decodeTlv(uint8_t *data, int &messageLength, int &messageStartIndex) +{ + int i = getNdefStartIndex(data); + + if (i < 0 || data[i] != 0x3) + { + DMSG("Error. Can't decode message length."); + return false; + } + else + { + if (data[i+1] == 0xFF) + { + messageLength = ((0xFF & data[i+2]) << 8) | (0xFF & data[i+3]); + messageStartIndex = i + LONG_TLV_SIZE; + } + else + { + messageLength = data[i+1]; + messageStartIndex = i + SHORT_TLV_SIZE; + } + } + + return true; +} + +bool MifareClassic::write(NdefMessage& m, uint8_t * uid, unsigned int uidLength) +{ + + uint8_t encoded[m.getEncodedSize()]; + m.encode(encoded); + + uint8_t buffer[getBufferSize(sizeof(encoded))]; + memset(buffer, 0, sizeof(buffer)); + + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("sizeof(encoded) "));DMSG(sizeof(encoded); + DMSG("sizeof(buffer) "));DMSG(sizeof(buffer); + #endif + + if (sizeof(encoded) < 0xFF) + { + buffer[0] = 0x3; + buffer[1] = sizeof(encoded); + memcpy(&buffer[2], encoded, sizeof(encoded)); + buffer[2+sizeof(encoded)] = 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 + } + + // Write to tag + int index = 0; + int currentBlock = 4; + uint8_t key[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; // this is Sector 1 - 15 key + + while (index < sizeof(buffer)) + { + + if (_nfcShield->mifareclassic_IsFirstBlock(currentBlock)) + { + int success = _nfcShield->mifareclassic_AuthenticateBlock(uid, uidLength, currentBlock, 0, key); + if (!success) + { + DMSG("Error. Block Authentication failed for ");DMSG_INT(currentBlock); + return false; + } + } + + int write_success = _nfcShield->mifareclassic_WriteDataBlock (currentBlock, &buffer[index]); + if (write_success) + { + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Wrote block ");Serial.print(currentBlock);DMSG(" - "); + _nfcShield->PrintHexChar(&buffer[index], BLOCK_SIZE); + #endif + } + else + { + DMSG("Write failed ");DMSG_INT(currentBlock); + return false; + } + index += BLOCK_SIZE; + currentBlock++; + + if (_nfcShield->mifareclassic_IsTrailerBlock(currentBlock)) + { + // can't write to trailer block + #ifdef MIFARE_CLASSIC_DEBUG + DMSG("Skipping block ");DMSG(currentBlock); + #endif + currentBlock++; + } + + } + + return true; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/MifareClassic.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,22 @@ +#ifndef MifareClassic_h +#define MifareClassic_h + +#include <PN532.h> +#include <Ndef.h> +#include <NfcTag.h> + +class MifareClassic +{ + public: + MifareClassic(PN532& nfcShield); + ~MifareClassic(); + NfcTag read(uint8_t *uid, unsigned int uidLength); + bool write(NdefMessage& ndefMessage, uint8_t *uid, unsigned int uidLength); + private: + PN532* _nfcShield; + int getBufferSize(int messageLength); + int getNdefStartIndex(uint8_t *data); + bool decodeTlv(uint8_t *data, int &messageLength, int &messageStartIndex); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/MifareUltralight.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,165 @@ +#include "MifareUltralight.h" + +#include "PN532_debug.h" + +#define ULTRALIGHT_PAGE_SIZE 4 +#define ULTRALIGHT_READ_SIZE 4 // we should be able to read 16 uint8_ts at a time + +#define ULTRALIGHT_DATA_START_PAGE 4 +#define ULTRALIGHT_MESSAGE_LENGTH_INDEX 1 +#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) +{ + nfc = &nfcShield; + ndefStartIndex = 0; + messageLength = 0; +} + +MifareUltralight::~MifareUltralight() +{ +} + +NfcTag MifareUltralight::read(uint8_t * uid, unsigned int uidLength) +{ + if (isUnformatted()) + { + DMSG("WARNING: Tag is not formatted."); + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2); + } + + readCapabilityContainer(); // meta info for tag + findNdefMessage(); + calculateBufferSize(); + + if (messageLength == 0) { // data is 0x44 0x03 0x00 0xFE + NdefMessage message = NdefMessage(); + message.addEmptyRecord(); + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, message); + } + + bool success; + uint8_t page; + uint8_t index = 0; + uint8_t buffer[bufferSize]; + for (page = ULTRALIGHT_DATA_START_PAGE; page < ULTRALIGHT_MAX_PAGE; page++) + { + // read the data + success = nfc->mifareultralight_ReadPage(page, &buffer[index]); + if (success) + { + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("Page ");Serial.print(page);DMSG(" "); + nfc->PrintHexChar(&buffer[index], ULTRALIGHT_PAGE_SIZE); + #endif + } + else + { + DMSG("Read failed ");DMSG_INT(page); + // TODO error handling + messageLength = 0; + break; + } + + if (index >= (messageLength + ndefStartIndex)) + { + break; + } + + index += ULTRALIGHT_PAGE_SIZE; + } + + NdefMessage ndefMessage = NdefMessage(&buffer[ndefStartIndex], messageLength); + return NfcTag(uid, uidLength, NFC_FORUM_TAG_TYPE_2, ndefMessage); + +} + +bool MifareUltralight::isUnformatted() +{ + uint8_t page = 4; + uint8_t data[ULTRALIGHT_READ_SIZE]; + bool success = nfc->mifareultralight_ReadPage (page, data); + if (success) + { + return (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF); + } + else + { + DMSG("Error. Failed read page ");DMSG_INT(page); + return false; + } +} + +// page 3 has tag capabilities +void MifareUltralight::readCapabilityContainer() +{ + uint8_t data[ULTRALIGHT_PAGE_SIZE]; + int success = nfc->mifareultralight_ReadPage (3, data); + if (success) + { + // See AN1303 - different rules for Mifare Family uint8_t2 = (additional data + 48)/8 + tagCapacity = data[2] * 8; + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("Tag capacity "));Serial.print(tagCapacity);DMSG(F(" uint8_ts"); + #endif + + // TODO future versions should get lock information + } +} + +// read enough of the message to find the ndef message length +void MifareUltralight::findNdefMessage() +{ + int page; + uint8_t data[12]; // 3 pages + uint8_t* data_ptr = &data[0]; + + // the nxp read command reads 4 pages, unfortunately adafruit give me one page at a time + bool success = true; + for (page = 4; page < 6; page++) + { + success = success && nfc->mifareultralight_ReadPage(page, data_ptr); + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("Page "));Serial.print(page);Serial.print(F(" - "); + nfc->PrintHexChar(data_ptr, 4); + #endif + data_ptr += ULTRALIGHT_PAGE_SIZE; + } + + if (success) + { + if (data[0] == 0x03) + { + messageLength = data[1]; + ndefStartIndex = 2; + } + else if (data[5] == 0x3) // page 5 uint8_t 1 + { + // TODO should really read the lock control TLV to ensure uint8_t[5] is correct + messageLength = data[6]; + ndefStartIndex = 7; + } + } + + #ifdef MIFARE_ULTRALIGHT_DEBUG + DMSG("messageLength ");DMSG(messageLength); + DMSG("ndefStartIndex ");DMSG(ndefStartIndex); + #endif +} + +// buffer is larger than the message, need to handle some data before and after +// message and need to ensure we read full pages +void MifareUltralight::calculateBufferSize() +{ + // TLV terminator 0xFE is 1 uint8_t + bufferSize = messageLength + ndefStartIndex + 1; + + if (bufferSize % ULTRALIGHT_READ_SIZE != 0) + { + // buffer must be an increment of page size + bufferSize = ((bufferSize / ULTRALIGHT_READ_SIZE) + 1) * ULTRALIGHT_READ_SIZE; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/MifareUltralight.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,26 @@ +#ifndef MifareUltralight_h +#define MifareUltralight_h + +#include <PN532.h> +#include <NfcTag.h> +#include <Ndef.h> + +class MifareUltralight +{ + public: + MifareUltralight(PN532& nfcShield); + ~MifareUltralight(); + NfcTag read(uint8_t *uid, unsigned int uidLength); + private: + PN532* nfc; + unsigned int tagCapacity; + unsigned int messageLength; + unsigned int bufferSize; + unsigned int ndefStartIndex; + bool isUnformatted(); + void readCapabilityContainer(); + void findNdefMessage(); + void calculateBufferSize(); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/Ndef.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,52 @@ +#include "Ndef.h" +#include "PN532_debug.h" + +// Borrowed from Adafruit_NFCShield_I2C +void PrintHex(const uint8_t * data, const long numuint8_ts) +{ + uint32_t szPos; + for (szPos=0; szPos < numuint8_ts; szPos++) + { + DMSG("0x"); + // Append leading 0 for small values + if (data[szPos] <= 0xF) + DMSG("0"); + DMSG_HEX(data[szPos]&0xff); + if ((numuint8_ts > 1) && (szPos != numuint8_ts - 1)) + { + DMSG(" "); + } + } + DMSG(""); +} + +// Borrowed from Adafruit_NFCShield_I2C +void PrintHexChar(const uint8_t * data, const long numuint8_ts) +{ + uint32_t szPos; + for (szPos=0; szPos < numuint8_ts; szPos++) + { + // Append leading 0 for small values + DMSG_HEX(data[szPos]); + } + DMSG(" "); + for (szPos=0; szPos < numuint8_ts; szPos++) + { + if (data[szPos] <= 0x1F) + DMSG("."); + else + DMSG("%c", (char)data[szPos]); + } + DMSG("\n"); +} + +// Note if buffer % blockSize != 0, last block will not be written +void DumpHex(const uint8_t * data, const long numuint8_ts, const unsigned int blockSize) +{ + int i; + for (i = 0; i < (numuint8_ts / blockSize); i++) + { + PrintHexChar(data, blockSize); + data += blockSize; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/Ndef.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,13 @@ +#ifndef Ndef_h +#define Ndef_h + +/* NOTE: To use the Ndef library in your code, don't include Ndef.h + See README.md for details on which files to include in your sketch. +*/ +#include <stdint.h> + +void PrintHex(const uint8_t *data, const long numuint8_ts); +void PrintHexChar(const uint8_t *data, const long numuint8_ts); +void DumpHex(const uint8_t *data, const long numuint8_ts, const int blockSize); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NdefMessage.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,278 @@ +#include "NdefMessage.h" +#include "PN532_debug.h" + +NdefMessage::NdefMessage(void) +{ + _recordCount = 0; +} + +NdefMessage::NdefMessage(const uint8_t * data, const int numuint8_ts) +{ + #ifdef NDEF_DEBUG + DMSG("Decoding "));Serial.print(numuint8_ts);DMSG(F(" uint8_ts"); + PrintHexChar(data, numuint8_ts); + //DumpHex(data, numuint8_ts, 16); + #endif + + _recordCount = 0; + + int index = 0; + + while (index <= numuint8_ts) + { + + // decode tnf - first uint8_t is tnf with bit flags + // see the NFDEF spec for more info + uint8_t tnf_uint8_t = data[index]; + bool mb = (tnf_uint8_t & 0x80) != 0; + bool me = (tnf_uint8_t & 0x40) != 0; + bool cf = (tnf_uint8_t & 0x20) != 0; + bool sr = (tnf_uint8_t & 0x10) != 0; + bool il = (tnf_uint8_t & 0x8) != 0; + uint8_t tnf = (tnf_uint8_t & 0x7); + + NdefRecord record = NdefRecord(); + record.setTnf(tnf); + + index++; + int typeLength = data[index]; + + int payloadLength = 0; + if (sr) + { + index++; + payloadLength = data[index]; + } + else + { + payloadLength = ((0xFF & data[++index]) << 24) | ((0xFF & data[++index]) << 26) | + ((0xFF & data[++index]) << 8) | (0xFF & data[++index]); + } + + int idLength = 0; + if (il) + { + index++; + idLength = data[index]; + } + + index++; + record.setType(&data[index], typeLength); + index += typeLength; + + if (il) + { + record.setId(&data[index], idLength); + index += idLength; + } + + record.setPayload(&data[index], payloadLength); + index += payloadLength; + + addRecord(record); + + if (me) break; // last message + } + +} + +NdefMessage::NdefMessage(const NdefMessage& rhs) +{ + + _recordCount = rhs._recordCount; + for (int i = 0; i < _recordCount; i++) + { + _records[i] = rhs._records[i]; + } + +} + +NdefMessage::~NdefMessage() +{ +} + +NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) +{ + + if (this != &rhs) + { + + // delete existing records + for (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++) + { + _records[i] = rhs._records[i]; + } + } + return *this; +} + +unsigned int NdefMessage::getRecordCount() +{ + return _recordCount; +} + +int NdefMessage::getEncodedSize() +{ + int size = 0; + for (int i = 0; i < _recordCount; i++) + { + size += _records[i].getEncodedSize(); + } + return size; +} + +// TODO change this to return uint8_t* +void NdefMessage::encode(uint8_t* data) +{ + // assert sizeof(data) >= getEncodedSize() + uint8_t* data_ptr = &data[0]; + + for (int 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(); + } + +} + +bool NdefMessage::addRecord(NdefRecord& record) +{ + + if (_recordCount < MAX_NDEF_RECORDS) + { + _records[_recordCount] = record; + _recordCount++; + return true; + } + else + { + DMSG("WARNING: Too many records. Increase MAX_NDEF_RECORDS."); + return false; + } +} + +void NdefMessage::addMimeMediaRecord(string mimeType, string payload) +{ + + uint8_t payloaduint8_ts[payload.length() + 1]; + payload.copy((char*)payloaduint8_ts, sizeof(payloaduint8_ts)); + + addMimeMediaRecord(mimeType, payloaduint8_ts, payload.length()); +} + +void NdefMessage::addMimeMediaRecord(string mimeType, uint8_t* payload, int payloadLength) +{ + NdefRecord r = NdefRecord(); + r.setTnf(TNF_MIME_MEDIA); + + uint8_t type[mimeType.length() + 1]; + mimeType.copy((char*)type, sizeof(type)); + r.setType(type, mimeType.length()); + + r.setPayload(payload, payloadLength); + + addRecord(r); +} + +void NdefMessage::addTextRecord(string text) +{ + addTextRecord(text, "en"); +} + +void NdefMessage::addTextRecord(string text, string encoding) +{ + NdefRecord r = NdefRecord(); + 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; + + uint8_t payload[payloadString.length() + 1]; + payloadString.copy((char*)payload, sizeof(payload)); + + // replace X with the real encoding length + payload[0] = encoding.length(); + + r.setPayload(payload, payloadString.length()); + + addRecord(r); +} + +void NdefMessage::addUriRecord(string uri) +{ + NdefRecord* r = new NdefRecord(); + 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; + + uint8_t payload[payloadString.length() + 1]; + payloadString.copy((char*)payload, sizeof(payload)); + + // add identifier code 0x0, meaning no prefix substitution + payload[0] = 0x0; + + r->setPayload(payload, payloadString.length()); + + addRecord(*r); + delete(r); +} + +void NdefMessage::addEmptyRecord() +{ + NdefRecord* r = new NdefRecord(); + r->setTnf(TNF_EMPTY); + addRecord(*r); + delete(r); +} + +NdefRecord NdefMessage::getRecord(int index) +{ + if (index > -1 && index < _recordCount) + { + return _records[index]; + } + else + { + return NdefRecord(); // would rather return NULL + } +} + +NdefRecord NdefMessage::operator[](int index) +{ + return getRecord(index); +} + +void NdefMessage::print() +{ + DMSG("\nNDEF Message "); + DMSG_INT(_recordCount); + DMSG(" record"); + if (_recordCount == 1) { + DMSG(", "); + } else { + DMSG("s, "); + } + DMSG_INT(getEncodedSize()); + + int i; + for (i = 0; i < _recordCount; i++) + { + _records[i].print(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NdefMessage.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,39 @@ +#ifndef NdefMessage_h +#define NdefMessage_h + +#include <Ndef.h> +#include <NdefRecord.h> + +#define MAX_NDEF_RECORDS 4 + +class NdefMessage +{ + public: + NdefMessage(void); + NdefMessage(const uint8_t *data, const int numuint8_ts); + NdefMessage(const NdefMessage& rhs); + ~NdefMessage(); + NdefMessage& operator=(const NdefMessage& rhs); + + int getEncodedSize(); // need so we can pass array to encode + void encode(uint8_t *data); + + bool addRecord(NdefRecord& record); + void addMimeMediaRecord(string mimeType, string payload); + void addMimeMediaRecord(string mimeType, uint8_t *payload, int payloadLength); + void addTextRecord(string text); + void addTextRecord(string text, string encoding); + void addUriRecord(string uri); + void addEmptyRecord(); + + unsigned int getRecordCount(); + NdefRecord getRecord(int index); + NdefRecord operator[](int index); + + void print(); + private: + NdefRecord _records[MAX_NDEF_RECORDS]; + unsigned int _recordCount; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NdefRecord.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,363 @@ + +#include <string> +#include <string.h> +#include <stdlib.h> + +#include "NdefRecord.h" +#include "PN532_debug.h" + +NdefRecord::NdefRecord() +{ + //DMSG("NdefRecord Constructor 1"); + _tnf = 0; + _typeLength = 0; + _payloadLength = 0; + _idLength = 0; + _type = (uint8_t *)NULL; + _payload = (uint8_t *)NULL; + _id = (uint8_t *)NULL; +} + +NdefRecord::NdefRecord(const NdefRecord& rhs) +{ + //DMSG("NdefRecord Constructor 2 (copy)"); + + _tnf = rhs._tnf; + _typeLength = rhs._typeLength; + _payloadLength = rhs._payloadLength; + _idLength = rhs._idLength; + _type = (uint8_t *)NULL; + _payload = (uint8_t *)NULL; + _id = (uint8_t *)NULL; + + if (_typeLength) + { + _type = (uint8_t*)malloc(_typeLength); + memcpy(_type, rhs._type, _typeLength); + } + + if (_payloadLength) + { + _payload = (uint8_t*)malloc(_payloadLength); + memcpy(_payload, rhs._payload, _payloadLength); + } + + if (_idLength) + { + _id = (uint8_t*)malloc(_idLength); + memcpy(_id, rhs._id, _idLength); + } + +} + +// TODO NdefRecord::NdefRecord(tnf, type, payload, id) + +NdefRecord::~NdefRecord() +{ + //DMSG("NdefRecord Destructor"); + if (_typeLength) + { + free(_type); + } + + if (_payloadLength) + { + free(_payload); + } + + if (_idLength) + { + free(_id); + } +} + +NdefRecord& NdefRecord::operator=(const NdefRecord& rhs) +{ + //DMSG("NdefRecord ASSIGN"); + + if (this != &rhs) + { + // free existing + if (_typeLength) + { + free(_type); + } + + if (_payloadLength) + { + free(_payload); + } + + if (_idLength) + { + free(_id); + } + + _tnf = rhs._tnf; + _typeLength = rhs._typeLength; + _payloadLength = rhs._payloadLength; + _idLength = rhs._idLength; + + if (_typeLength) + { + _type = (uint8_t*)malloc(_typeLength); + memcpy(_type, rhs._type, _typeLength); + } + + if (_payloadLength) + { + _payload = (uint8_t*)malloc(_payloadLength); + memcpy(_payload, rhs._payload, _payloadLength); + } + + if (_idLength) + { + _id = (uint8_t*)malloc(_idLength); + memcpy(_id, rhs._id, _idLength); + } + } + return *this; +} + +// size of records in uint8_ts +int NdefRecord::getEncodedSize() +{ + int size = 2; // tnf + typeLength + if (_payloadLength > 0xFF) + { + size += 4; + } + else + { + size += 1; + } + + if (_idLength) + { + size += 1; + } + + size += (_typeLength + _payloadLength + _idLength); + + return size; +} + +void NdefRecord::encode(uint8_t *data, bool firstRecord, bool lastRecord) +{ + // assert data > getEncodedSize() + + uint8_t* data_ptr = &data[0]; + + *data_ptr = getTnfuint8_t(firstRecord, lastRecord); + data_ptr += 1; + + *data_ptr = _typeLength; + data_ptr += 1; + + if (_payloadLength <= 0xFF) { // short record + *data_ptr = _payloadLength; + data_ptr += 1; + } else { // long format + // 4 uint8_ts but we store length as an int + data_ptr[0] = 0x0; // (_payloadLength >> 24) & 0xFF; + data_ptr[1] = 0x0; // (_payloadLength >> 16) & 0xFF; + data_ptr[2] = (_payloadLength >> 8) & 0xFF; + data_ptr[3] = _payloadLength & 0xFF; + data_ptr += 4; + } + + if (_idLength) + { + *data_ptr = _idLength; + data_ptr += 1; + } + + //DMSG(2); + memcpy(data_ptr, _type, _typeLength); + data_ptr += _typeLength; + + memcpy(data_ptr, _payload, _payloadLength); + data_ptr += _payloadLength; + + if (_idLength) + { + memcpy(data_ptr, _id, _idLength); + data_ptr += _idLength; + } +} + +uint8_t NdefRecord::getTnfuint8_t(bool firstRecord, bool lastRecord) +{ + int value = _tnf; + + if (firstRecord) { // mb + value = value | 0x80; + } + + if (lastRecord) { // + value = value | 0x40; + } + + // chunked flag is always false for now + // if (cf) { + // value = value | 0x20; + // } + + if (_payloadLength <= 0xFF) { + value = value | 0x10; + } + + if (_idLength) { + value = value | 0x8; + } + + return value; +} + +uint8_t NdefRecord::getTnf() +{ + return _tnf; +} + +void NdefRecord::setTnf(uint8_t tnf) +{ + _tnf = tnf; +} + +unsigned int NdefRecord::getTypeLength() +{ + return _typeLength; +} + +int NdefRecord::getPayloadLength() +{ + return _payloadLength; +} + +unsigned int NdefRecord::getIdLength() +{ + return _idLength; +} + +string NdefRecord::getType() +{ + char type[_typeLength + 1]; + memcpy(type, _type, _typeLength); + type[_typeLength] = '\0'; // null terminate + return string(type); +} + +// this assumes the caller created type correctly +void NdefRecord::getType(uint8_t* type) +{ + memcpy(type, _type, _typeLength); +} + +void NdefRecord::setType(const uint8_t * type, const unsigned int numuint8_ts) +{ + if(_typeLength) + { + free(_type); + } + + _type = (uint8_t*)malloc(numuint8_ts); + memcpy(_type, type, numuint8_ts); + _typeLength = numuint8_ts; +} + +// assumes the caller sized payload properly +void NdefRecord::getPayload(uint8_t *payload) +{ + memcpy(payload, _payload, _payloadLength); +} + +void NdefRecord::setPayload(const uint8_t * payload, const int numuint8_ts) +{ + if (_payloadLength) + { + free(_payload); + } + + _payload = (uint8_t*)malloc(numuint8_ts); + memcpy(_payload, payload, numuint8_ts); + _payloadLength = numuint8_ts; +} + +string NdefRecord::getId() +{ + char id[_idLength + 1]; + memcpy(id, _id, _idLength); + id[_idLength] = '\0'; // null terminate + return string(id); +} + +void NdefRecord::getId(uint8_t *id) +{ + memcpy(id, _id, _idLength); +} + +void NdefRecord::setId(const uint8_t * id, const unsigned int numuint8_ts) +{ + if (_idLength) + { + free(_id); + } + + _id = (uint8_t*)malloc(numuint8_ts); + memcpy(_id, id, numuint8_ts); + _idLength = numuint8_ts; +} + +void NdefRecord::print() +{ + DMSG(" NDEF Record"); + DMSG(" TNF 0x"); + DMSG_HEX(_tnf); + DMSG(" "); + switch (_tnf) { + case TNF_EMPTY: + DMSG("Empty"); + break; + case TNF_WELL_KNOWN: + DMSG("Well Known"); + break; + case TNF_MIME_MEDIA: + DMSG("Mime Media"); + break; + case TNF_ABSOLUTE_URI: + DMSG("Absolute URI"); + break; + case TNF_EXTERNAL_TYPE: + DMSG("External"); + break; + case TNF_UNKNOWN: + DMSG("Unknown"); + break; + case TNF_UNCHANGED: + DMSG("Unchanged"); + break; + case TNF_RESERVED: + DMSG("Reserved"); + break; + default: + DMSG("\n"); + } + DMSG(" Type Length 0x"); + DMSG_HEX(_typeLength); + DMSG(" Payload Length 0x"); + DMSG_HEX(_payloadLength); + if (_idLength) + { + DMSG(" Id Length 0x"); + DMSG_HEX(_idLength); + } + DMSG(" Type ");PrintHexChar(_type, _typeLength); + // TODO chunk large payloads so this is readable + DMSG(" Payload ");PrintHexChar(_payload, _payloadLength); + if (_idLength) + { + DMSG(" Id ");PrintHexChar(_id, _idLength); + } + DMSG(" Record is "); + DMSG_INT(getEncodedSize()); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NdefRecord.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,59 @@ +#ifndef NdefRecord_h +#define NdefRecord_h + +#include <string> +#include <Ndef.h> + +using namespace std; + +#define TNF_EMPTY 0x0 +#define TNF_WELL_KNOWN 0x01 +#define TNF_MIME_MEDIA 0x02 +#define TNF_ABSOLUTE_URI 0x03 +#define TNF_EXTERNAL_TYPE 0x04 +#define TNF_UNKNOWN 0x05 +#define TNF_UNCHANGED 0x06 +#define TNF_RESERVED 0x07 + +class NdefRecord +{ + public: + NdefRecord(); + NdefRecord(const NdefRecord& rhs); + ~NdefRecord(); + NdefRecord& operator=(const NdefRecord& rhs); + + int getEncodedSize(); + void encode(uint8_t *data, bool firstRecord, bool lastRecord); + + unsigned int getTypeLength(); + int getPayloadLength(); + unsigned int getIdLength(); + + uint8_t getTnf(); + void getType(uint8_t *type); + void getPayload(uint8_t *payload); + void getId(uint8_t *id); + + // convenience methods + string getType(); + string getId(); + + void setTnf(uint8_t tnf); + void setType(const uint8_t *type, const unsigned int numuint8_ts); + void setPayload(const uint8_t *payload, const int numuint8_ts); + void setId(const uint8_t *id, const unsigned int numuint8_ts); + + void print(); + private: + uint8_t getTnfuint8_t(bool firstRecord, bool lastRecord); + uint8_t _tnf; // 3 bit + unsigned int _typeLength; + int _payloadLength; + unsigned int _idLength; + uint8_t *_type; + uint8_t *_payload; + uint8_t *_id; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NfcAdapter.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,128 @@ +#include <NfcAdapter.h> +#include <PN532_debug.h> + +NfcAdapter::NfcAdapter(PN532Interface &interface) +{ + shield = new PN532(interface); +} + +NfcAdapter::~NfcAdapter(void) +{ + delete shield; +} + +void NfcAdapter::begin() +{ + shield->begin(); + + uint32_t versiondata = shield->getFirmwareVersion(); + if (! versiondata) { + DMSG("Didn't find PN53x board"); + while (1); // halt + } + + DMSG("Found chip PN5%2X\r\n", versiondata >> 24); + DMSG("Firmware V%d.%d\r\n", (versiondata >> 16) & 0xFF, (versiondata >> 8) & 0xFF); + + // configure board to read RFID tags + shield->SAMConfig(); +} + +bool NfcAdapter::tagPresent() +{ + uint8_t success; + uidLength = 0; + + // TODO is cast of uidLength OK? + success = shield->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, (uint8_t*)&uidLength); + + // if (success) + // { + // DMSG("Found an ISO14443A card"); + // DMSG(" UID Length: ");Serial.print(uidLength, DEC);DMSG(" uint8_ts"); + // DMSG(" UID Value: "); + // shield->PrintHex(uid, uidLength); + // DMSG(""); + // } + + return success; +} + +NfcTag NfcAdapter::read() +{ + + uint8_t type = guessTagType(); + + // TODO need an abstraction of Driver + if (type == TAG_TYPE_MIFARE_CLASSIC) + { + #ifdef NDEF_DEBUG + DMSG("Reading Mifare Classic"); + #endif + MifareClassic mifareClassic = MifareClassic(*shield); + return mifareClassic.read(uid, uidLength); + } + else if (type == TAG_TYPE_2) + { + #ifdef NDEF_DEBUG + DMSG("Reading Mifare Ultralight"); + #endif + MifareUltralight ultralight = MifareUltralight(*shield); + return ultralight.read(uid, uidLength); + } + else if (type == TAG_TYPE_UNKNOWN) + { + DMSG("Can not determine tag type"); + //DMSG("Can not determine tag type for ATQA 0x"); + //Serial.print(atqa, HEX);DMSG(" SAK 0x");DMSG(sak, HEX); + return NfcTag(uid, uidLength); + } + else + { + DMSG("No driver for card type "); + DMSG_INT(type); + // TODO should set type here + return NfcTag(uid, uidLength); + } + +} + +bool NfcAdapter::write(NdefMessage& ndefMessage) +{ + bool success; + + if (uidLength == 4) + { + MifareClassic mifareClassic = MifareClassic(*shield); + success = mifareClassic.write(ndefMessage, uid, uidLength); + } + else + { + DMSG("Unsupported Tag"); + success = false; + } + return success; +} + +// TODO this should return a Driver MifareClassic, MifareUltralight, Type 4, Unknown +// Guess Tag Type by looking at the ATQA and SAK values +// Need to follow spec for Card Identification. Maybe AN1303, AN1305 and ??? +unsigned int NfcAdapter::guessTagType() +{ + + // 4 uint8_t id - Mifare Classic + // - ATQA 0x4 && SAK 0x8 + // 7 uint8_t id + // - ATQA 0x44 && SAK 0x8 - Mifare Classic + // - ATQA 0x44 && SAK 0x0 - Mifare Ultralight NFC Forum Type 2 + // - ATQA 0x344 && SAK 0x20 - NFC Forum Type 4 + + if (uidLength == 4) + { + return TAG_TYPE_MIFARE_CLASSIC; + } + else + { + return TAG_TYPE_2; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NfcAdapter.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,43 @@ +#ifndef NfcAdapter_h +#define NfcAdapter_h + +#include <PN532Interface.h> +#include <PN532.h> +#include <NfcTag.h> +#include <Ndef.h> + +// Drivers +#include <MifareClassic.h> +#include <MifareUltralight.h> + +#define TAG_TYPE_MIFARE_CLASSIC (0) +#define TAG_TYPE_1 (1) +#define TAG_TYPE_2 (2) +#define TAG_TYPE_3 (3) +#define TAG_TYPE_4 (4) +#define TAG_TYPE_UNKNOWN (99) + +#define IRQ (2) +#define RESET (3) // Not connected by default on the NFC Shield + +class NfcAdapter { + public: + NfcAdapter(PN532Interface &interface); + + ~NfcAdapter(void); + void begin(void); + bool tagPresent(); // tagAvailable + NfcTag read(); + bool write(NdefMessage& ndefMessage); + // FUTURE bool share(NdefMessage& ndefMessage); + // FUTURE bool unshare(); + // FUTURE bool erase(); + // FUTURE bool format(); + private: + PN532* shield; + uint8_t uid[7]; // Buffer to store the returned UID + unsigned int uidLength; // Length of the UID (4 or 7 uint8_ts depending on ISO14443A card type) + unsigned int guessTagType(); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NfcDriver.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,9 @@ +// eventually the NFC drivers should extend this class +class NfcDriver +{ + public: + virtual NfcTag read(uint8_t * uid, int uidLength) = 0; + virtual bool write(NdefMessage& message, uint8_t * uid, int uidLength) = 0; + // erase() + // format() +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NfcTag.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,127 @@ +#include <NfcTag.h> +#include <string.h> +#include <PN532_debug.h> + +NfcTag::NfcTag() +{ + _uid = 0; + _uidLength = 0; + _tagType = "Unknown"; + _ndefMessage = (NdefMessage*)NULL; +} + +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = "Unknown"; + _ndefMessage = (NdefMessage*)NULL; +} + +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength, string tagType) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = tagType; + _ndefMessage = (NdefMessage*)NULL; +} + +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, NdefMessage& ndefMessage) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = tagType; + _ndefMessage = new NdefMessage(ndefMessage); +} + +// I don't like this version, but it will use less memory +NfcTag::NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, const uint8_t *ndefData, const int ndefDataLength) +{ + _uid = uid; + _uidLength = uidLength; + _tagType = tagType; + _ndefMessage = new NdefMessage(ndefData, ndefDataLength); +} + +NfcTag::~NfcTag() +{ + delete _ndefMessage; +} + +NfcTag& NfcTag::operator=(const NfcTag& rhs) +{ + if (this != &rhs) + { + delete _ndefMessage; + _uid = rhs._uid; + _uidLength = rhs._uidLength; + _tagType = rhs._tagType; + // TODO do I need a copy here? + _ndefMessage = rhs._ndefMessage; + } + return *this; +} + +uint8_t NfcTag::getUidLength() +{ + return _uidLength; +} + +void NfcTag::getUid(uint8_t *uid, unsigned int uidLength) +{ + memcpy(_uid, uid, uidLength); +} + +string NfcTag::getUidString() +{ + string uidString = ""; +#if 0 + for (int i = 0; i < _uidLength; i++) + { + if (i > 0) + { + uidString += " "; + } + + if (_uid[i] < 0xF) + { + uidString += "0"; + } + + uidString += string((unsigned int)_uid[i], 16); + } + uidString.toUpperCase(); +#endif + return uidString; +} + +string NfcTag::getTagType() +{ + return _tagType; +} + +bool NfcTag::hasNdefMessage() +{ + return (_ndefMessage != NULL); +} + +NdefMessage NfcTag::getNdefMessage() +{ + return *_ndefMessage; +} + +void NfcTag::print() +{ + DMSG("NFC Tag - "); + DMSG_INT(_tagType); + DMSG("UID - "); + DMSG(getUidString().c_str()); + if (_ndefMessage == NULL) + { + DMSG("\nNo NDEF Message"); + } + else + { + _ndefMessage->print(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/NfcTag.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,33 @@ +#ifndef NfcTag_h +#define NfcTag_h + +#include <stdint.h> +#include <NdefMessage.h> + +class NfcTag +{ + public: + NfcTag(); + NfcTag(uint8_t *uid, unsigned int uidLength); + NfcTag(uint8_t *uid, unsigned int uidLength, string tagType); + NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, NdefMessage& ndefMessage); + NfcTag(uint8_t *uid, unsigned int uidLength, string tagType, const uint8_t *ndefData, const int ndefDataLength); + ~NfcTag(void); + NfcTag& operator=(const NfcTag& rhs); + uint8_t getUidLength(); + void getUid(uint8_t *uid, unsigned int uidLength); + string getUidString(); + string getTagType(); + bool hasNdefMessage(); + NdefMessage getNdefMessage(); + void print(); + private: + uint8_t *_uid; + unsigned int _uidLength; + string _tagType; // Mifare Classic, NFC Forum Type {1,2,3,4}, Unknown + NdefMessage* _ndefMessage; + // TODO capacity + // TODO isFormatted +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/PN532.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,851 @@ +/**************************************************************************/ +/*! + @file PN532.cpp + @author Adafruit Industries & Seeed Studio + @license BSD +*/ +/**************************************************************************/ + +#include "PN532.h" +#include "PN532_debug.h" +#include <string.h> + +#ifdef ARDUINO +#include "Arduino.h" +#else +#include <stdio.h> +#endif + +#define HAL(func) (_interface->func) + +PN532::PN532(PN532Interface &interface) +{ + _interface = &interface; +} + +/**************************************************************************/ +/*! + @brief Setups the HW +*/ +/**************************************************************************/ +void PN532::begin() +{ + HAL(begin)(); + HAL(wakeup)(); +} + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters + + @param data Pointer to the uint8_t data + @param numBytes Data length in bytes +*/ +/**************************************************************************/ +void PN532::PrintHex(const uint8_t *data, const uint32_t numBytes) +{ +#ifdef ARDUINO + for (uint8_t i = 0; i < numBytes; i++) { + if (data[i] < 0x10) { + Serial.print(" 0"); + } else { + Serial.print(' '); + } + Serial.print(data[i], HEX); + } + Serial.println(""); +#else + for (uint8_t i = 0; i < numBytes; i++) { + printf(" %2X", data[i]); + } + printf("\n"); +#endif +} + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters, along with + the char equivalents in the following format + + 00 00 00 00 00 00 ...... + + @param data Pointer to the data + @param numBytes Data length in bytes +*/ +/**************************************************************************/ +void PN532::PrintHexChar(const uint8_t *data, const uint32_t numBytes) +{ +#ifdef ARDUINO + for (uint8_t i = 0; i < numBytes; i++) { + if (data[i] < 0x10) { + Serial.print(" 0"); + } else { + Serial.print(' '); + } + Serial.print(data[i], HEX); + } + Serial.print(" "); + for (uint8_t i = 0; i < numBytes; i++) { + char c = data[i]; + if (c <= 0x1f || c > 0x7f) { + Serial.print('.'); + } else { + Serial.print(c); + } + } + Serial.println(""); +#else + for (uint8_t i = 0; i < numBytes; i++) { + printf(" %2X", data[i]); + } + printf(" "); + for (uint8_t i = 0; i < numBytes; i++) { + char c = data[i]; + if (c <= 0x1f || c > 0x7f) { + printf("."); + } else { + printf("%c", c); + } + printf("\n"); + } +#endif +} + +/**************************************************************************/ +/*! + @brief Checks the firmware version of the PN5xx chip + + @returns The chip's firmware version and ID +*/ +/**************************************************************************/ +uint32_t PN532::getFirmwareVersion(void) +{ + uint32_t response; + + pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION; + + if (HAL(writeCommand)(pn532_packetbuffer, 1)) { + return 0; + } + + // read data packet + int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + if (0 > status) { + return 0; + } + + response = pn532_packetbuffer[0]; + response <<= 8; + response |= pn532_packetbuffer[1]; + response <<= 8; + response |= pn532_packetbuffer[2]; + response <<= 8; + response |= pn532_packetbuffer[3]; + + return response; +} + + +/**************************************************************************/ +/*! + Writes an 8-bit value that sets the state of the PN532's GPIO pins + + @warning This function is provided exclusively for board testing and + is dangerous since it will throw an error if any pin other + than the ones marked "Can be used as GPIO" are modified! All + pins that can not be used as GPIO should ALWAYS be left high + (value = 1) or the system will become unstable and a HW reset + will be required to recover the PN532. + + pinState[0] = P30 Can be used as GPIO + pinState[1] = P31 Can be used as GPIO + pinState[2] = P32 *** RESERVED (Must be 1!) *** + pinState[3] = P33 Can be used as GPIO + pinState[4] = P34 *** RESERVED (Must be 1!) *** + pinState[5] = P35 Can be used as GPIO + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::writeGPIO(uint8_t pinstate) +{ + // Make sure pinstate does not try to toggle P32 or P34 + pinstate |= (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34); + + // Fill command buffer + pn532_packetbuffer[0] = PN532_COMMAND_WRITEGPIO; + pn532_packetbuffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate; // P3 Pins + pn532_packetbuffer[2] = 0x00; // P7 GPIO Pins (not used ... taken by I2C) + + DMSG("Writing P3 GPIO: "); + DMSG_HEX(pn532_packetbuffer[1]); + DMSG("\n"); + + // Send the WRITEGPIO command (0x0E) + if (HAL(writeCommand)(pn532_packetbuffer, 3)) + return 0; + + return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +/**************************************************************************/ +/*! + Reads the state of the PN532's GPIO pins + + @returns An 8-bit value containing the pin state where: + + pinState[0] = P30 + pinState[1] = P31 + pinState[2] = P32 + pinState[3] = P33 + pinState[4] = P34 + pinState[5] = P35 +*/ +/**************************************************************************/ +uint8_t PN532::readGPIO(void) +{ + pn532_packetbuffer[0] = PN532_COMMAND_READGPIO; + + // Send the READGPIO command (0x0C) + if (HAL(writeCommand)(pn532_packetbuffer, 1)) + return 0x0; + + HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + /* READGPIO response without prefix and suffix should be in the following format: + + byte Description + ------------- ------------------------------------------ + b0 P3 GPIO Pins + b1 P7 GPIO Pins (not used ... taken by I2C) + b2 Interface Mode Pins (not used ... bus select pins) + */ + + + DMSG("P3 GPIO: "); DMSG_HEX(pn532_packetbuffer[7]); + DMSG("P7 GPIO: "); DMSG_HEX(pn532_packetbuffer[8]); + DMSG("I0I1 GPIO: "); DMSG_HEX(pn532_packetbuffer[9]); + DMSG("\n"); + + return pn532_packetbuffer[0]; +} + +/**************************************************************************/ +/*! + @brief Configures the SAM (Secure Access Module) +*/ +/**************************************************************************/ +bool PN532::SAMConfig(void) +{ + pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; // normal mode; + pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second + pn532_packetbuffer[3] = 0x01; // use IRQ pin! + + DMSG("SAMConfig\n"); + + if (HAL(writeCommand)(pn532_packetbuffer, 4)) + return false; + + return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +/**************************************************************************/ +/*! + Sets the MxRtyPassiveActivation uint8_t of the RFConfiguration register + + @param maxRetries 0xFF to wait forever, 0x00..0xFE to timeout + after mxRetries + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::setPassiveActivationRetries(uint8_t maxRetries) +{ + pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; // Config item 5 (MaxRetries) + pn532_packetbuffer[2] = 0xFF; // MxRtyATR (default = 0xFF) + pn532_packetbuffer[3] = 0x01; // MxRtyPSL (default = 0x01) + pn532_packetbuffer[4] = maxRetries; + + if (HAL(writeCommand)(pn532_packetbuffer, 5)) + return 0x0; // no ACK + + return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +/***** ISO14443A Commands ******/ + +/**************************************************************************/ +/*! + Waits for an ISO14443A target to enter the field + + @param cardBaudRate Baud rate of the card + @param uid Pointer to the array that will be populated + with the card's UID (up to 7 bytes) + @param uidLength Pointer to the variable that will hold the + length of the card's UID. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout) +{ + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) + pn532_packetbuffer[2] = cardbaudrate; + + if (HAL(writeCommand)(pn532_packetbuffer, 3)) { + return 0x0; // command failed + } + + // read data packet + if (HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { + return 0x0; + } + + // check some basic stuff + /* ISO14443A card response should be in the following format: + + byte Description + ------------- ------------------------------------------ + b0 Tags Found + b1 Tag Number (only one used in this example) + b2..3 SENS_RES + b4 SEL_RES + b5 NFCID Length + b6..NFCIDLen NFCID + */ + + if (pn532_packetbuffer[0] != 1) + return 0; + + uint16_t sens_res = pn532_packetbuffer[2]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[3]; + + DMSG("ATQA: 0x"); DMSG_HEX(sens_res); + DMSG("SAK: 0x"); DMSG_HEX(pn532_packetbuffer[4]); + DMSG("\n"); + + /* Card appears to be Mifare Classic */ + *uidLength = pn532_packetbuffer[5]; + + for (uint8_t i = 0; i < pn532_packetbuffer[5]; i++) { + uid[i] = pn532_packetbuffer[6 + i]; + } + + return 1; +} + + +/***** Mifare Classic Functions ******/ + +/**************************************************************************/ +/*! + Indicates whether the specified block number is the first block + in the sector (block 0 relative to the current sector) +*/ +/**************************************************************************/ +bool PN532::mifareclassic_IsFirstBlock (uint32_t uiBlock) +{ + // Test if we are in the small or big sectors + if (uiBlock < 128) + return ((uiBlock) % 4 == 0); + else + return ((uiBlock) % 16 == 0); +} + +/**************************************************************************/ +/*! + Indicates whether the specified block number is the sector trailer +*/ +/**************************************************************************/ +bool PN532::mifareclassic_IsTrailerBlock (uint32_t uiBlock) +{ + // Test if we are in the small or big sectors + if (uiBlock < 128) + return ((uiBlock + 1) % 4 == 0); + else + return ((uiBlock + 1) % 16 == 0); +} + +/**************************************************************************/ +/*! + Tries to authenticate a block of memory on a MIFARE card using the + INDATAEXCHANGE command. See section 7.3.8 of the PN532 User Manual + for more information on sending MIFARE and other commands. + + @param uid Pointer to a byte array containing the card UID + @param uidLen The length (in bytes) of the card's UID (Should + be 4 for MIFARE Classic) + @param blockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param keyNumber Which key type to use during authentication + (0 = MIFARE_CMD_AUTH_A, 1 = MIFARE_CMD_AUTH_B) + @param keyData Pointer to a byte array containing the 6 bytes + key value + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t PN532::mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) +{ + uint8_t i; + + // Hang on to the key and uid data + memcpy (_key, keyData, 6); + memcpy (_uid, uid, uidLen); + _uidLen = uidLen; + + // Prepare the authentication command // + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ + pn532_packetbuffer[1] = 1; /* Max card numbers */ + pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_packetbuffer[3] = blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ + memcpy (pn532_packetbuffer + 4, _key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_packetbuffer[10 + i] = _uid[i]; /* 4 bytes card ID */ + } + + if (HAL(writeCommand)(pn532_packetbuffer, 10 + _uidLen)) + return 0; + + // Read the response packet + HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + // Check if the response is valid and we are authenticated??? + // for an auth success it should be bytes 5-7: 0xD5 0x41 0x00 + // Mifare auth error is technically byte 7: 0x14 but anything other and 0x00 is not good + if (pn532_packetbuffer[0] != 0x00) { + DMSG("Authentification failed\n"); + return 0; + } + + return 1; +} + +/**************************************************************************/ +/*! + Tries to read an entire 16-bytes data block at the specified block + address. + + @param blockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param data Pointer to the byte array that will hold the + retrieved data (if any) + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t PN532::mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) +{ + DMSG("Trying to read 16 bytes from block "); + DMSG_INT(blockNumber); + + /* Prepare the command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + + /* Send the command */ + if (HAL(writeCommand)(pn532_packetbuffer, 4)) { + return 0; + } + + /* Read the response packet */ + HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[0] != 0x00) { + return 0; + } + + /* Copy the 16 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + memcpy (data, pn532_packetbuffer + 1, 16); + + return 1; +} + +/**************************************************************************/ +/*! + Tries to write an entire 16-bytes data block at the specified block + address. + + @param blockNumber The block number to authenticate. (0..63 for + 1KB cards, and 0..255 for 4KB cards). + @param data The byte array that contains the data to write. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t PN532::mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) +{ + /* Prepare the first command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */ + pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + memcpy (pn532_packetbuffer + 4, data, 16); /* Data Payload */ + + /* Send the command */ + if (HAL(writeCommand)(pn532_packetbuffer, 20)) { + return 0; + } + + /* Read the response packet */ + return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +/**************************************************************************/ +/*! + Formats a Mifare Classic card to store NDEF Records + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t PN532::mifareclassic_FormatNDEF (void) +{ + uint8_t sectorbuffer1[16] = {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; + uint8_t sectorbuffer2[16] = {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; + uint8_t sectorbuffer3[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + // Note 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 must be used for key A + // for the MAD sector in NDEF records (sector 0) + + // Write block 1 and 2 to the card + if (!(mifareclassic_WriteDataBlock (1, sectorbuffer1))) + return 0; + if (!(mifareclassic_WriteDataBlock (2, sectorbuffer2))) + return 0; + // Write key A and access rights card + if (!(mifareclassic_WriteDataBlock (3, sectorbuffer3))) + return 0; + + // Seems that everything was OK (?!) + return 1; +} + +/**************************************************************************/ +/*! + Writes an NDEF URI Record to the specified sector (1..15) + + Note that this function assumes that the Mifare Classic card is + already formatted to work as an "NFC Forum Tag" and uses a MAD1 + file system. You can use the NXP TagWriter app on Android to + properly format cards for this. + + @param sectorNumber The sector that the URI record should be written + to (can be 1..15 for a 1K card) + @param uriIdentifier The uri identifier code (0 = none, 0x01 = + "http://www.", etc.) + @param url The uri text to write (max 38 characters). + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +uint8_t PN532::mifareclassic_WriteNDEFURI (uint8_t sectorNumber, uint8_t uriIdentifier, const char *url) +{ + // Figure out how long the string is + uint8_t len = strlen(url); + + // Make sure we're within a 1K limit for the sector number + if ((sectorNumber < 1) || (sectorNumber > 15)) + return 0; + + // Make sure the URI payload is between 1 and 38 chars + if ((len < 1) || (len > 38)) + return 0; + + // Note 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 must be used for key A + // in NDEF records + + // Setup the sector buffer (w/pre-formatted TLV wrapper and NDEF message) + uint8_t sectorbuffer1[16] = {0x00, 0x00, 0x03, len + 5, 0xD1, 0x01, len + 1, 0x55, uriIdentifier, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t sectorbuffer2[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t sectorbuffer3[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t sectorbuffer4[16] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + if (len <= 6) { + // Unlikely we'll get a url this short, but why not ... + memcpy (sectorbuffer1 + 9, url, len); + sectorbuffer1[len + 9] = 0xFE; + } else if (len == 7) { + // 0xFE needs to be wrapped around to next block + memcpy (sectorbuffer1 + 9, url, len); + sectorbuffer2[0] = 0xFE; + } else if ((len > 7) || (len <= 22)) { + // Url fits in two blocks + memcpy (sectorbuffer1 + 9, url, 7); + memcpy (sectorbuffer2, url + 7, len - 7); + sectorbuffer2[len - 7] = 0xFE; + } else if (len == 23) { + // 0xFE needs to be wrapped around to final block + memcpy (sectorbuffer1 + 9, url, 7); + memcpy (sectorbuffer2, url + 7, len - 7); + sectorbuffer3[0] = 0xFE; + } else { + // Url fits in three blocks + memcpy (sectorbuffer1 + 9, url, 7); + memcpy (sectorbuffer2, url + 7, 16); + memcpy (sectorbuffer3, url + 23, len - 24); + sectorbuffer3[len - 22] = 0xFE; + } + + // Now write all three blocks back to the card + if (!(mifareclassic_WriteDataBlock (sectorNumber * 4, sectorbuffer1))) + return 0; + if (!(mifareclassic_WriteDataBlock ((sectorNumber * 4) + 1, sectorbuffer2))) + return 0; + if (!(mifareclassic_WriteDataBlock ((sectorNumber * 4) + 2, sectorbuffer3))) + return 0; + if (!(mifareclassic_WriteDataBlock ((sectorNumber * 4) + 3, sectorbuffer4))) + return 0; + + // Seems that everything was OK (?!) + return 1; +} + +/***** Mifare Ultralight Functions ******/ + +/**************************************************************************/ +/*! + Tries to read an entire 4-bytes page at the specified address. + + @param page The page number (0..63 in most cases) + @param buffer Pointer to the byte array that will hold the + retrieved data (if any) +*/ +/**************************************************************************/ +uint8_t PN532::mifareultralight_ReadPage (uint8_t page, uint8_t *buffer) +{ + if (page >= 64) { + DMSG("Page value out of range\n"); + return 0; + } + + /* Prepare the command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 in most cases) */ + + /* Send the command */ + if (HAL(writeCommand)(pn532_packetbuffer, 4)) { + return 0; + } + + /* Read the response packet */ + HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[0] == 0x00) { + /* Copy the 4 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + /* Note that the command actually reads 16 bytes or 4 */ + /* pages at a time ... we simply discard the last 12 */ + /* bytes */ + memcpy (buffer, pn532_packetbuffer + 1, 4); + } else { + return 0; + } + + // Return OK signal + return 1; +} + +/**************************************************************************/ +/*! + @brief Exchanges an APDU with the currently inlisted peer + + @param send Pointer to data to send + @param sendLength Length of the data to send + @param response Pointer to response data + @param responseLength Pointer to the response data length +*/ +/**************************************************************************/ +bool PN532::inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength) +{ + uint8_t i; + + pn532_packetbuffer[0] = 0x40; // PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = inListedTag; + + if (HAL(writeCommand)(pn532_packetbuffer, 2, send, sendLength)) { + return false; + } + + int16_t status = HAL(readResponse)(response, *responseLength, 1000); + if (status < 0) { + return false; + } + + if ((response[0] & 0x3f) != 0) { + DMSG("Status code indicates an error\n"); + return false; + } + + uint8_t length = status; + length -= 1; + + if (length > *responseLength) { + length = *responseLength; // silent truncation... + } + + for (uint8_t i = 0; i < length; i++) { + response[i] = response[i + 1]; + } + *responseLength = length; + + return true; +} + +/**************************************************************************/ +/*! + @brief 'InLists' a passive target. PN532 acting as reader/initiator, + peer acting as card/responder. +*/ +/**************************************************************************/ +bool PN532::inListPassiveTarget() +{ + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = 0; + + DMSG("inList passive target\n"); + + if (HAL(writeCommand)(pn532_packetbuffer, 3)) { + return false; + } + + int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 30000); + if (status < 0) { + return false; + } + + if (pn532_packetbuffer[0] != 1) { + return false; + } + + inListedTag = pn532_packetbuffer[1]; + + return true; +} + +int8_t PN532::tgInitAsTarget(const uint8_t* command, const uint8_t len, const uint16_t timeout){ + + int8_t status = HAL(writeCommand)(command, len); + if (status < 0) { + return -1; + } + + status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout); + if (status > 0) { + return 1; + } else if (PN532_TIMEOUT == status) { + return 0; + } else { + return -2; + } +} + +/** + * Peer to Peer + */ +int8_t PN532::tgInitAsTarget(uint16_t timeout) +{ + const uint8_t command[] = { + PN532_COMMAND_TGINITASTARGET, + 0, + 0x00, 0x00, //SENS_RES + 0x00, 0x00, 0x00, //NFCID1 + 0x40, //SEL_RES + + 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, // POL_RES + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, + + 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, 0x00, 0x00, //NFCID3t: Change this to desired value + + 0x06, 0x46, 0x66, 0x6D, 0x01, 0x01, 0x10, 0x00// LLCP magic number and version parameter + }; + return tgInitAsTarget(command, sizeof(command), timeout); +} + +int16_t PN532::tgGetData(uint8_t *buf, uint8_t len) +{ + buf[0] = PN532_COMMAND_TGGETDATA; + + if (HAL(writeCommand)(buf, 1)) { + return -1; + } + + int16_t status = HAL(readResponse)(buf, len, 3000); + if (0 >= status) { + return status; + } + + uint16_t length = status - 1; + + + if (buf[0] != 0) { + DMSG("status is not ok\n"); + return -5; + } + + for (uint8_t i = 0; i < length; i++) { + buf[i] = buf[i + 1]; + } + + return length; +} + +bool PN532::tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + if (hlen > (sizeof(pn532_packetbuffer) - 1)) { + if ((body != 0) || (header == pn532_packetbuffer)) { + DMSG("tgSetData:buffer too small\n"); + return false; + } + + pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA; + if (HAL(writeCommand)(pn532_packetbuffer, 1, header, hlen)) { + return false; + } + } else { + for (int8_t i = hlen - 1; i >= 0; i--){ + pn532_packetbuffer[i + 1] = header[i]; + } + pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA; + + if (HAL(writeCommand)(pn532_packetbuffer, hlen + 1, body, blen)) { + return false; + } + } + + if (0 > HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 3000)) { + return false; + } + + if (0 != pn532_packetbuffer[0]) { + return false; + } + + return true; +} + +int16_t PN532::inRelease(const uint8_t relevantTarget){ + + pn532_packetbuffer[0] = PN532_COMMAND_INRELEASE; + pn532_packetbuffer[1] = relevantTarget; + + if (HAL(writeCommand)(pn532_packetbuffer, 2)) { + return 0; + } + + // read data packet + return HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/PN532.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,177 @@ +/**************************************************************************/ +/*! + @file PN532.h + @author Adafruit Industries & Seeed Studio + @license BSD +*/ +/**************************************************************************/ + +#ifndef __PN532_H__ +#define __PN532_H__ + +#include <stdint.h> +#include "PN532Interface.h" + +// PN532 Commands +#define PN532_COMMAND_DIAGNOSE (0x00) +#define PN532_COMMAND_GETFIRMWAREVERSION (0x02) +#define PN532_COMMAND_GETGENERALSTATUS (0x04) +#define PN532_COMMAND_READREGISTER (0x06) +#define PN532_COMMAND_WRITEREGISTER (0x08) +#define PN532_COMMAND_READGPIO (0x0C) +#define PN532_COMMAND_WRITEGPIO (0x0E) +#define PN532_COMMAND_SETSERIALBAUDRATE (0x10) +#define PN532_COMMAND_SETPARAMETERS (0x12) +#define PN532_COMMAND_SAMCONFIGURATION (0x14) +#define PN532_COMMAND_POWERDOWN (0x16) +#define PN532_COMMAND_RFCONFIGURATION (0x32) +#define PN532_COMMAND_RFREGULATIONTEST (0x58) +#define PN532_COMMAND_INJUMPFORDEP (0x56) +#define PN532_COMMAND_INJUMPFORPSL (0x46) +#define PN532_COMMAND_INLISTPASSIVETARGET (0x4A) +#define PN532_COMMAND_INATR (0x50) +#define PN532_COMMAND_INPSL (0x4E) +#define PN532_COMMAND_INDATAEXCHANGE (0x40) +#define PN532_COMMAND_INCOMMUNICATETHRU (0x42) +#define PN532_COMMAND_INDESELECT (0x44) +#define PN532_COMMAND_INRELEASE (0x52) +#define PN532_COMMAND_INSELECT (0x54) +#define PN532_COMMAND_INAUTOPOLL (0x60) +#define PN532_COMMAND_TGINITASTARGET (0x8C) +#define PN532_COMMAND_TGSETGENERALBYTES (0x92) +#define PN532_COMMAND_TGGETDATA (0x86) +#define PN532_COMMAND_TGSETDATA (0x8E) +#define PN532_COMMAND_TGSETMETADATA (0x94) +#define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88) +#define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90) +#define PN532_COMMAND_TGGETTARGETSTATUS (0x8A) + +#define PN532_RESPONSE_INDATAEXCHANGE (0x41) +#define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B) + + +#define PN532_MIFARE_ISO14443A (0x00) + +// Mifare Commands +#define MIFARE_CMD_AUTH_A (0x60) +#define MIFARE_CMD_AUTH_B (0x61) +#define MIFARE_CMD_READ (0x30) +#define MIFARE_CMD_WRITE (0xA0) +#define MIFARE_CMD_TRANSFER (0xB0) +#define MIFARE_CMD_DECREMENT (0xC0) +#define MIFARE_CMD_INCREMENT (0xC1) +#define MIFARE_CMD_STORE (0xC2) + +// Prefixes for NDEF Records (to identify record type) +#define NDEF_URIPREFIX_NONE (0x00) +#define NDEF_URIPREFIX_HTTP_WWWDOT (0x01) +#define NDEF_URIPREFIX_HTTPS_WWWDOT (0x02) +#define NDEF_URIPREFIX_HTTP (0x03) +#define NDEF_URIPREFIX_HTTPS (0x04) +#define NDEF_URIPREFIX_TEL (0x05) +#define NDEF_URIPREFIX_MAILTO (0x06) +#define NDEF_URIPREFIX_FTP_ANONAT (0x07) +#define NDEF_URIPREFIX_FTP_FTPDOT (0x08) +#define NDEF_URIPREFIX_FTPS (0x09) +#define NDEF_URIPREFIX_SFTP (0x0A) +#define NDEF_URIPREFIX_SMB (0x0B) +#define NDEF_URIPREFIX_NFS (0x0C) +#define NDEF_URIPREFIX_FTP (0x0D) +#define NDEF_URIPREFIX_DAV (0x0E) +#define NDEF_URIPREFIX_NEWS (0x0F) +#define NDEF_URIPREFIX_TELNET (0x10) +#define NDEF_URIPREFIX_IMAP (0x11) +#define NDEF_URIPREFIX_RTSP (0x12) +#define NDEF_URIPREFIX_URN (0x13) +#define NDEF_URIPREFIX_POP (0x14) +#define NDEF_URIPREFIX_SIP (0x15) +#define NDEF_URIPREFIX_SIPS (0x16) +#define NDEF_URIPREFIX_TFTP (0x17) +#define NDEF_URIPREFIX_BTSPP (0x18) +#define NDEF_URIPREFIX_BTL2CAP (0x19) +#define NDEF_URIPREFIX_BTGOEP (0x1A) +#define NDEF_URIPREFIX_TCPOBEX (0x1B) +#define NDEF_URIPREFIX_IRDAOBEX (0x1C) +#define NDEF_URIPREFIX_FILE (0x1D) +#define NDEF_URIPREFIX_URN_EPC_ID (0x1E) +#define NDEF_URIPREFIX_URN_EPC_TAG (0x1F) +#define NDEF_URIPREFIX_URN_EPC_PAT (0x20) +#define NDEF_URIPREFIX_URN_EPC_RAW (0x21) +#define NDEF_URIPREFIX_URN_EPC (0x22) +#define NDEF_URIPREFIX_URN_NFC (0x23) + +#define PN532_GPIO_VALIDATIONBIT (0x80) +#define PN532_GPIO_P30 (0) +#define PN532_GPIO_P31 (1) +#define PN532_GPIO_P32 (2) +#define PN532_GPIO_P33 (3) +#define PN532_GPIO_P34 (4) +#define PN532_GPIO_P35 (5) + +class PN532 +{ +public: + PN532(PN532Interface &interface); + + void begin(void); + + // Generic PN532 functions + bool SAMConfig(void); + uint32_t getFirmwareVersion(void); + bool writeGPIO(uint8_t pinstate); + uint8_t readGPIO(void); + bool setPassiveActivationRetries(uint8_t maxRetries); + + /** + * @brief Init PN532 as a target + * @param timeout max time to wait, 0 means no timeout + * @return > 0 success + * = 0 timeout + * < 0 failed + */ + int8_t tgInitAsTarget(uint16_t timeout = 0); + int8_t tgInitAsTarget(const uint8_t* command, const uint8_t len, const uint16_t timeout = 0); + + int16_t tgGetData(uint8_t *buf, uint8_t len); + bool tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); + + int16_t inRelease(const uint8_t relevantTarget = 0); + + // ISO14443A functions + bool inListPassiveTarget(); + bool readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 1000); + bool inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength); + + // Mifare Classic functions + bool mifareclassic_IsFirstBlock (uint32_t uiBlock); + bool mifareclassic_IsTrailerBlock (uint32_t uiBlock); + uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData); + uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data); + uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data); + uint8_t mifareclassic_FormatNDEF (void); + uint8_t mifareclassic_WriteNDEFURI (uint8_t sectorNumber, uint8_t uriIdentifier, const char *url); + + // Mifare Ultralight functions + uint8_t mifareultralight_ReadPage (uint8_t page, uint8_t *buffer); + + // Help functions to display formatted text + static void PrintHex(const uint8_t *data, const uint32_t numBytes); + static void PrintHexChar(const uint8_t *pbtData, const uint32_t numBytes); + + uint8_t *getBuffer(uint8_t *len) { + *len = sizeof(pn532_packetbuffer) - 4; + return pn532_packetbuffer; + }; + +private: + uint8_t _uid[7]; // ISO14443A uid + uint8_t _uidLen; // uid len + uint8_t _key[6]; // Mifare Classic key + uint8_t inListedTag; // Tg number of inlisted tag. + + uint8_t pn532_packetbuffer[64]; + + PN532Interface *_interface; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/PN532Interface.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,56 @@ + + +#ifndef __PN532_INTERFACE_H__ +#define __PN532_INTERFACE_H__ + +#include <stdint.h> + +#define PN532_PREAMBLE (0x00) +#define PN532_STARTCODE1 (0x00) +#define PN532_STARTCODE2 (0xFF) +#define PN532_POSTAMBLE (0x00) + +#define PN532_HOSTTOPN532 (0xD4) +#define PN532_PN532TOHOST (0xD5) + +#define PN532_ACK_WAIT_TIME (10) // ms, timeout of waiting for ACK + +#define PN532_INVALID_ACK (-1) +#define PN532_TIMEOUT (-2) +#define PN532_INVALID_FRAME (-3) +#define PN532_NO_SPACE (-4) + +#define REVERSE_BITS_ORDER(b) b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; \ + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; \ + b = (b & 0xAA) >> 1 | (b & 0x55) << 1 + +class PN532Interface +{ +public: + virtual void begin() = 0; + virtual void wakeup() = 0; + + /** + * @brief write a command and check ack + * @param header packet header + * @param hlen length of header + * @param body packet body + * @param blen length of body + * @return 0 success + * not 0 failed + */ + virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) = 0; + + /** + * @brief read the response of a command, strip prefix and suffix + * @param buf to contain the response data + * @param len lenght to read + * @param timeout max time to wait, 0 means no timeout + * @return >=0 length of response without prefix and suffix + * <0 failed to read response + */ + virtual int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 1000) = 0; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/PN532_SPI.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,209 @@ + +#include "PN532_SPI.h" +#include "PN532_debug.h" + +#define STATUS_READ 2 +#define DATA_WRITE 1 +#define DATA_READ 3 + +PN532_SPI::PN532_SPI(SPI &spi, PinName ss) : _ss(ss) +{ + command = 0; + _spi = &spi; + _spi->format(8, 0); + _spi->frequency(2000000); + + _ss = 1; +} + +PN532_SPI::PN532_SPI(SPI *spi, PinName ss) : _ss(ss) +{ + command = 0; + _spi = spi; + _spi->format(8, 0); + _spi->frequency(2000000); + + _ss = 1; +} + +void PN532_SPI::begin() +{ + +} + +void PN532_SPI::wakeup() +{ + _ss = 0; + wait_ms(2); + _ss = 1; +} + +int8_t PN532_SPI::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + command = header[0]; + writeFrame(header, hlen, body, blen); + + uint8_t timeout = PN532_ACK_WAIT_TIME; + while (!isReady()) { + wait_ms(1); + timeout--; + if (0 == timeout) { + DMSG("Time out when waiting for ACK\n"); + return -2; + } + } + if (readAckFrame()) { + DMSG("Invalid ACK\n"); + return PN532_INVALID_ACK; + } + return 0; +} + +int16_t PN532_SPI::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) +{ + uint16_t time = 0; + while (!isReady()) { + wait_ms(1); + time++; + if (timeout > 0 && time > timeout) { + return PN532_TIMEOUT; + } + } + + _ss = 0; + wait_ms(1); + + int16_t result; + do { + write(DATA_READ); + + if (0x00 != read() || // PREAMBLE + 0x00 != read() || // STARTCODE1 + 0xFF != read() // STARTCODE2 + ) { + + result = PN532_INVALID_FRAME; + break; + } + + uint8_t length = read(); + if (0 != (uint8_t)(length + read())) { // checksum of length + result = PN532_INVALID_FRAME; + break; + } + + uint8_t cmd = command + 1; // response command + if (PN532_PN532TOHOST != read() || (cmd) != read()) { + result = PN532_INVALID_FRAME; + break; + } + + DMSG("read: "); + DMSG_HEX(cmd); + + length -= 2; + if (length > len) { + for (uint8_t i = 0; i < length; i++) { + DMSG_HEX(read()); // dump message + } + DMSG("\nNot enough space\n"); + read(); + read(); + result = PN532_NO_SPACE; // not enough space + break; + } + + uint8_t sum = PN532_PN532TOHOST + cmd; + for (uint8_t i = 0; i < length; i++) { + buf[i] = read(); + sum += buf[i]; + + DMSG_HEX(buf[i]); + } + DMSG("\n"); + + uint8_t checksum = read(); + if (0 != (uint8_t)(sum + checksum)) { + DMSG("checksum is not ok\n"); + result = PN532_INVALID_FRAME; + break; + } + read(); // POSTAMBLE + + result = length; + } while (0); + + _ss = 1; + + return result; +} + +bool PN532_SPI::isReady() +{ + _ss = 0; + + write(STATUS_READ); + uint8_t status = read() & 1; + _ss = 1; + return status; +} + +void PN532_SPI::writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + _ss = 0; + wait_ms(2); // wake up PN532 + + write(DATA_WRITE); + write(PN532_PREAMBLE); + write(PN532_STARTCODE1); + write(PN532_STARTCODE2); + + uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA + write(length); + write(~length + 1); // checksum of length + + write(PN532_HOSTTOPN532); + uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA + + DMSG("write: "); + + for (uint8_t i = 0; i < hlen; i++) { + write(header[i]); + sum += header[i]; + + DMSG_HEX(header[i]); + } + for (uint8_t i = 0; i < blen; i++) { + write(body[i]); + sum += body[i]; + + DMSG_HEX(header[i]); + } + + uint8_t checksum = ~sum + 1; // checksum of TFI + DATA + write(checksum); + write(PN532_POSTAMBLE); + + _ss = 1; + + DMSG("\n"); +} + +int8_t PN532_SPI::readAckFrame() +{ + const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; + + uint8_t ackBuf[sizeof(PN532_ACK)]; + + _ss = 0; + wait_ms(1); + write(DATA_READ); + + for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) { + ackBuf[i] = read(); + } + + _ss = 1; + + return memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/PN532_SPI.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,39 @@ + +#ifndef __PN532_SPI_H__ +#define __PN532_SPI_H__ + +#include "mbed.h" +#include "PN532Interface.h" + +class PN532_SPI : public PN532Interface +{ +public: + PN532_SPI(SPI &spi, PinName ss); + PN532_SPI(SPI *spi, PinName ss); + + virtual void begin(); + virtual void wakeup(); + virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen); + virtual int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); + +private: + SPI *_spi; + DigitalOut _ss; + uint8_t command; + + bool isReady(); + void writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen); + int8_t readAckFrame(); + + inline void write(uint8_t data) { + REVERSE_BITS_ORDER(data); + _spi->write(data); + } + inline uint8_t read() { + uint8_t data = _spi->write(0); + REVERSE_BITS_ORDER(data); + return data; + } +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/PN532_debug.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,24 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +//#define DEBUG + +#ifdef DEBUG + +#include <stdio.h> + +#define DMSG(args...) printf(args) +#define DMSG_STR(str) printf("%s\n", str) +#define DMSG_INT(num) printf("%d\n", num) +#define DMSG_HEX(num) printf("%2X ", num) + +#else + +#define DMSG(args...) +#define DMSG_STR(str) +#define DMSG_INT(num) +#define DMSG_HEX(num) + +#endif // DEBUG + +#endif // __DEBUG_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/emulatetag.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,255 @@ +/**************************************************************************/ +/*! + @file emulatetag.cpp + @author Armin Wieser + @license BSD +*/ +/**************************************************************************/ + +#include "emulatetag.h" +#include "PN532_debug.h" + +#include <string.h> + +#define MAX_TGREAD + + +// Command APDU +#define C_APDU_CLA 0 +#define C_APDU_INS 1 // instruction +#define C_APDU_P1 2 // parameter 1 +#define C_APDU_P2 3 // parameter 2 +#define C_APDU_LC 4 // length command +#define C_APDU_DATA 5 // data + +#define C_APDU_P1_SELECT_BY_ID 0x00 +#define C_APDU_P1_SELECT_BY_NAME 0x04 + +// Response APDU +#define R_APDU_SW1_COMMAND_COMPLETE 0x90 +#define R_APDU_SW2_COMMAND_COMPLETE 0x00 + +#define R_APDU_SW1_NDEF_TAG_NOT_FOUND 0x6a +#define R_APDU_SW2_NDEF_TAG_NOT_FOUND 0x82 + +#define R_APDU_SW1_FUNCTION_NOT_SUPPORTED 0x6A +#define R_APDU_SW2_FUNCTION_NOT_SUPPORTED 0x81 + +#define R_APDU_SW1_MEMORY_FAILURE 0x65 +#define R_APDU_SW2_MEMORY_FAILURE 0x81 + +#define R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x62 +#define R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x82 + +// ISO7816-4 commands +#define ISO7816_SELECT_FILE 0xA4 +#define ISO7816_READ_BINARY 0xB0 +#define ISO7816_UPDATE_BINARY 0xD6 + +typedef enum { NONE, CC, NDEF } tag_file; // CC ... Compatibility Container + +bool EmulateTag::init(){ + pn532.begin(); + return pn532.SAMConfig(); +} + +void EmulateTag::setNdefFile(const uint8_t* ndef, const int16_t ndefLength){ + if(ndefLength > (NDEF_MAX_LENGTH -2)){ + DMSG("ndef file too large (> NDEF_MAX_LENGHT -2) - aborting"); + return; + } + + ndef_file[0] = ndefLength >> 8; + ndef_file[1] = ndefLength & 0xFF; + memcpy(ndef_file+2, ndef, ndefLength); +} + +void EmulateTag::setUid(uint8_t* uid){ + uidPtr = uid; +} + +bool EmulateTag::emulate(const uint16_t tgInitAsTargetTimeout){ + + uint8_t command[] = { + PN532_COMMAND_TGINITASTARGET, + 5, // MODE: PICC only, Passive only + + 0x04, 0x00, // SENS_RES + 0x00, 0x00, 0x00, // NFCID1 + 0x20, // SEL_RES + + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, // FeliCaParams + 0,0, + + 0,0,0,0,0,0,0,0,0,0, // NFCID3t + + 0, // length of general bytes + 0 // length of historical bytes + }; + + if(uidPtr != 0){ // if uid is set copy 3 bytes to nfcid1 + memcpy(command + 4, uidPtr, 3); + } + + if(1 != pn532.tgInitAsTarget(command,sizeof(command), tgInitAsTargetTimeout)){ + DMSG("tgInitAsTarget failed or timed out!"); + return false; + } + + uint8_t compatibility_container[] = { + 0, 0x0F, + 0x20, + 0, 0x54, + 0, 0xFF, + 0x04, // T + 0x06, // L + 0xE1, 0x04, // File identifier + ((NDEF_MAX_LENGTH & 0xFF00) >> 8), (NDEF_MAX_LENGTH & 0xFF), // maximum NDEF file size + 0x00, // read access 0x0 = granted + 0x00 // write access 0x0 = granted | 0xFF = deny + }; + + if(tagWriteable == false){ + compatibility_container[14] = 0xFF; + } + + tagWrittenByInitiator = false; + + uint8_t rwbuf[128]; + uint8_t sendlen; + int16_t status; + tag_file currentFile = NONE; + uint16_t cc_size = sizeof(compatibility_container); + bool runLoop = true; + + while(runLoop){ + status = pn532.tgGetData(rwbuf, sizeof(rwbuf)); + if(status < 0){ + DMSG("tgGetData failed!\n"); + pn532.inRelease(); + return true; + } + + uint8_t p1 = rwbuf[C_APDU_P1]; + uint8_t p2 = rwbuf[C_APDU_P2]; + uint8_t lc = rwbuf[C_APDU_LC]; + uint16_t p1p2_length = ((int16_t) p1 << 8) + p2; + + switch(rwbuf[C_APDU_INS]){ + case ISO7816_SELECT_FILE: + switch(p1){ + case C_APDU_P1_SELECT_BY_ID: + if(p2 != 0x0c){ + DMSG("C_APDU_P2 != 0x0c\n"); + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + } else if(lc == 2 && rwbuf[C_APDU_DATA] == 0xE1 && (rwbuf[C_APDU_DATA+1] == 0x03 || rwbuf[C_APDU_DATA+1] == 0x04)){ + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + if(rwbuf[C_APDU_DATA+1] == 0x03){ + currentFile = CC; + } else if(rwbuf[C_APDU_DATA+1] == 0x04){ + currentFile = NDEF; + } + } else { + setResponse(TAG_NOT_FOUND, rwbuf, &sendlen); + } + break; + case C_APDU_P1_SELECT_BY_NAME: + const uint8_t ndef_tag_application_name_v2[] = {0, 0x7, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 }; + if(0 == memcmp(ndef_tag_application_name_v2, rwbuf + C_APDU_P2, sizeof(ndef_tag_application_name_v2))){ + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + } else{ + DMSG("function not supported\n"); + setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); + } + break; + } + break; + case ISO7816_READ_BINARY: + switch(currentFile){ + case NONE: + setResponse(TAG_NOT_FOUND, rwbuf, &sendlen); + break; + case CC: + if( p1p2_length > NDEF_MAX_LENGTH){ + setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen); + }else { + memcpy(rwbuf,compatibility_container + p1p2_length, lc); + setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc); + } + break; + case NDEF: + if( p1p2_length > NDEF_MAX_LENGTH){ + setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen); + }else { + memcpy(rwbuf, ndef_file + p1p2_length, lc); + setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc); + } + break; + } + break; + case ISO7816_UPDATE_BINARY: + if(!tagWriteable){ + setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); + } else{ + if( p1p2_length > NDEF_MAX_LENGTH){ + setResponse(MEMORY_FAILURE, rwbuf, &sendlen); + } + else{ + memcpy(ndef_file + p1p2_length, rwbuf + C_APDU_DATA, lc); + setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); + tagWrittenByInitiator = true; + + uint16_t ndef_length = (ndef_file[0] << 8) + ndef_file[1]; + if ((ndef_length > 0) && (updateNdefCallback != 0)) { + updateNdefCallback(ndef_file + 2, ndef_length); + } + } + } + break; + default: + DMSG("Command not supported!"); + DMSG_HEX(rwbuf[C_APDU_INS]); + DMSG("\n"); + setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); + } + status = pn532.tgSetData(rwbuf, sendlen); + if(status < 0){ + DMSG("tgSetData failed\n!"); + pn532.inRelease(); + return true; + } + } + pn532.inRelease(); + return true; +} + +void EmulateTag::setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset){ + switch(cmd){ + case COMMAND_COMPLETE: + buf[0] = R_APDU_SW1_COMMAND_COMPLETE; + buf[1] = R_APDU_SW2_COMMAND_COMPLETE; + *sendlen = 2 + sendlenOffset; + break; + case TAG_NOT_FOUND: + buf[0] = R_APDU_SW1_NDEF_TAG_NOT_FOUND; + buf[1] = R_APDU_SW2_NDEF_TAG_NOT_FOUND; + *sendlen = 2; + break; + case FUNCTION_NOT_SUPPORTED: + buf[0] = R_APDU_SW1_FUNCTION_NOT_SUPPORTED; + buf[1] = R_APDU_SW2_FUNCTION_NOT_SUPPORTED; + *sendlen = 2; + break; + case MEMORY_FAILURE: + buf[0] = R_APDU_SW1_MEMORY_FAILURE; + buf[1] = R_APDU_SW2_MEMORY_FAILURE; + *sendlen = 2; + break; + case END_OF_FILE_BEFORE_REACHED_LE_BYTES: + buf[0] = R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES; + buf[1] = R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES; + *sendlen= 2; + break; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/emulatetag.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,71 @@ +/**************************************************************************/ +/*! + @file emulatetag.h + @author Armin Wieser + @license BSD + + Implemented using NFC forum documents & library of libnfc +*/ +/**************************************************************************/ + +#ifndef __EMULATETAG_H__ +#define __EMULATETAG_H__ + +#include "PN532.h" + +#define NDEF_MAX_LENGTH 128 // altough ndef can handle up to 0xfffe in size, arduino cannot. +typedef enum {COMMAND_COMPLETE, TAG_NOT_FOUND, FUNCTION_NOT_SUPPORTED, MEMORY_FAILURE, END_OF_FILE_BEFORE_REACHED_LE_BYTES} responseCommand; + +class EmulateTag{ + +public: +EmulateTag(PN532Interface &interface) : pn532(interface), uidPtr(0), tagWrittenByInitiator(false), tagWriteable(true), updateNdefCallback(0) { } + + bool init(); + + bool emulate(const uint16_t tgInitAsTargetTimeout = 0); + + /* + * @param uid pointer to byte array of length 3 (uid is 4 bytes - first byte is fixed) or zero for uid + */ + void setUid(uint8_t* uid = 0); + + void setNdefFile(const uint8_t* ndef, const int16_t ndefLength); + + void getContent(uint8_t** buf, uint16_t* length){ + *buf = ndef_file + 2; // first 2 bytes = length + *length = (ndef_file[0] << 8) + ndef_file[1]; + } + + bool writeOccured(){ + return tagWrittenByInitiator; + } + + void setTagWriteable(bool setWriteable){ + tagWriteable = setWriteable; + } + + uint8_t* getNdefFilePtr(){ + return ndef_file; + } + + uint8_t getNdefMaxLength(){ + return NDEF_MAX_LENGTH; + } + + void attach(void (*func)(uint8_t *buf, uint16_t length)) { + updateNdefCallback = func; + }; + +private: + PN532 pn532; + uint8_t ndef_file[NDEF_MAX_LENGTH]; + uint8_t* uidPtr; + bool tagWrittenByInitiator; + bool tagWriteable; + void (*updateNdefCallback)(uint8_t *ndef, uint16_t length); + + void setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset = 0); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/llcp.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,309 @@ + +#include "llcp.h" +#include "PN532_debug.h" + +// LLCP PDU Type Values +#define PDU_SYMM 0x00 +#define PDU_PAX 0x01 +#define PDU_CONNECT 0x04 +#define PDU_DISC 0x05 +#define PDU_CC 0x06 +#define PDU_DM 0x07 +#define PDU_I 0x0c +#define PDU_RR 0x0d + +uint8_t LLCP::SYMM_PDU[2] = {0, 0}; + +inline uint8_t getPType(const uint8_t *buf) +{ + return ((buf[0] & 0x3) << 2) + (buf[1] >> 6); +} + +inline uint8_t getSSAP(const uint8_t *buf) +{ + return buf[1] & 0x3f; +} + +inline uint8_t getDSAP(const uint8_t *buf) +{ + return buf[0] >> 2; +} + +int8_t LLCP::activate(uint16_t timeout) +{ + return link.activateAsTarget(timeout); +} + +int8_t LLCP::waitForConnection(uint16_t timeout) +{ + uint8_t type; + + mode = 1; + ns = 0; + nr = 0; + + // Get CONNECT PDU + DMSG("wait for a CONNECT PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_CONNECT == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + // Put CC PDU + DMSG("put a CC(Connection Complete) PDU to response the CONNECT PDU\n"); + ssap = getDSAP(headerBuf); + dsap = getSSAP(headerBuf); + headerBuf[0] = (dsap << 2) + ((PDU_CC >> 2) & 0x3); + headerBuf[1] = ((PDU_CC & 0x3) << 6) + ssap; + if (!link.write(headerBuf, 2)) { + return -2; + } + + return 1; +} + +int8_t LLCP::waitForDisconnection(uint16_t timeout) +{ + uint8_t type; + + // Get DISC PDU + DMSG("wait for a DISC PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_DISC == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + // Put DM PDU + DMSG("put a DM(Disconnect Mode) PDU to response the DISC PDU\n"); + // ssap = getDSAP(headerBuf); + // dsap = getSSAP(headerBuf); + headerBuf[0] = (dsap << 2) + (PDU_DM >> 2); + headerBuf[1] = ((PDU_DM & 0x3) << 6) + ssap; + if (!link.write(headerBuf, 2)) { + return -2; + } + + return 1; +} + +int8_t LLCP::connect(uint16_t timeout) +{ + uint8_t type; + + mode = 0; + dsap = LLCP_DEFAULT_DSAP; + ssap = LLCP_DEFAULT_SSAP; + ns = 0; + nr = 0; + + // try to get a SYMM PDU + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + type = getPType(headerBuf); + if (PDU_SYMM != type) { + return -1; + } + + // put a CONNECT PDU + headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_CONNECT >> 2); + headerBuf[1] = ((PDU_CONNECT & 0x03) << 6) + LLCP_DEFAULT_SSAP; + uint8_t body[] = " urn:nfc:sn:snep"; + body[0] = 0x06; + body[1] = sizeof(body) - 2 - 1; + if (!link.write(headerBuf, 2, body, sizeof(body) - 1)) { + return -2; + } + + // wait for a CC PDU + DMSG("wait for a CC PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_CC == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + return 1; +} + +int8_t LLCP::disconnect(uint16_t timeout) +{ + uint8_t type; + + // try to get a SYMM PDU + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + type = getPType(headerBuf); + if (PDU_SYMM != type) { + return -1; + } + + // put a DISC PDU + headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_DISC >> 2); + headerBuf[1] = ((PDU_DISC & 0x03) << 6) + LLCP_DEFAULT_SSAP; + if (!link.write(headerBuf, 2)) { + return -2; + } + + // wait for a DM PDU + DMSG("wait for a DM PDU\n"); + do { + if (2 > link.read(headerBuf, headerBufLen)) { + return -1; + } + + type = getPType(headerBuf); + if (PDU_CC == type) { + break; + } else if (PDU_DM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + return 1; +} + +bool LLCP::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + uint8_t type; + uint8_t buf[3]; + + if (mode) { + // Get a SYMM PDU + if (2 != link.read(buf, sizeof(buf))) { + return false; + } + } + + if (headerBufLen < (hlen + 3)) { + return false; + } + + for (int8_t i = hlen - 1; i >= 0; i--) { + headerBuf[i + 3] = header[i]; + } + + headerBuf[0] = (dsap << 2) + (PDU_I >> 2); + headerBuf[1] = ((PDU_I & 0x3) << 6) + ssap; + headerBuf[2] = (ns << 4) + nr; + if (!link.write(headerBuf, 3 + hlen, body, blen)) { + return false; + } + + ns++; + + // Get a RR PDU + int16_t status; + do { + status = link.read(headerBuf, headerBufLen); + if (2 > status) { + return false; + } + + type = getPType(headerBuf); + if (PDU_RR == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return false; + } + } else { + return false; + } + } while (1); + + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return false; + } + + return true; +} + +int16_t LLCP::read(uint8_t *buf, uint8_t length) +{ + uint8_t type; + uint16_t status; + + // Get INFO PDU + do { + status = link.read(buf, length); + if (2 > status) { + return -1; + } + + type = getPType(buf); + if (PDU_I == type) { + break; + } else if (PDU_SYMM == type) { + if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) { + return -2; + } + } else { + return -3; + } + + } while (1); + + uint8_t len = status - 3; + ssap = getDSAP(buf); + dsap = getSSAP(buf); + + headerBuf[0] = (dsap << 2) + (PDU_RR >> 2); + headerBuf[1] = ((PDU_RR & 0x3) << 6) + ssap; + headerBuf[2] = (buf[2] >> 4) + 1; + if (!link.write(headerBuf, 3)) { + return -2; + } + + for (uint8_t i = 0; i < len; i++) { + buf[i] = buf[i + 3]; + } + + nr++; + + return len; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/llcp.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,75 @@ + +#ifndef __LLCP_H__ +#define __LLCP_H__ + +#include "mac_link.h" + +#define LLCP_DEFAULT_TIMEOUT 20000 +#define LLCP_DEFAULT_DSAP 0x04 +#define LLCP_DEFAULT_SSAP 0x20 + +class LLCP { +public: + LLCP(PN532Interface &interface) : link(interface) { + headerBuf = link.getHeaderBuffer(&headerBufLen); + ns = 0; + nr = 0; + }; + + /** + * @brief Actiave PN532 as a target + * @param timeout max time to wait, 0 means no timeout + * @return > 0 success + * = 0 timeout + * < 0 failed + */ + int8_t activate(uint16_t timeout = 0); + + int8_t waitForConnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + int8_t waitForDisconnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + int8_t connect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + int8_t disconnect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); + + /** + * @brief write a packet, the packet should be less than (255 - 2) bytes + * @param header packet header + * @param hlen length of header + * @param body packet body + * @param blen length of body + * @return true success + * false failed + */ + bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); + + /** + * @brief read a packet, the packet will be less than (255 - 2) bytes + * @param buf the buffer to contain the packet + * @param len lenght of the buffer + * @return >=0 length of the packet + * <0 failed + */ + int16_t read(uint8_t *buf, uint8_t len); + + uint8_t *getHeaderBuffer(uint8_t *len) { + uint8_t *buf = link.getHeaderBuffer(len); + len -= 3; // I PDU header has 3 bytes + return buf; + }; + +private: + MACLink link; + uint8_t mode; + uint8_t ssap; + uint8_t dsap; + uint8_t *headerBuf; + uint8_t headerBufLen; + uint8_t ns; // Number of I PDU Sent + uint8_t nr; // Number of I PDU Received + + static uint8_t SYMM_PDU[2]; +}; + +#endif // __LLCP_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/mac_link.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,20 @@ + +#include "mac_link.h" +#include "PN532_debug.h" + +int8_t MACLink::activateAsTarget(uint16_t timeout) +{ + pn532.begin(); + pn532.SAMConfig(); + return pn532.tgInitAsTarget(timeout); +} + +bool MACLink::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) +{ + return pn532.tgSetData(header, hlen, body, blen); +} + +int16_t MACLink::read(uint8_t *buf, uint8_t len) +{ + return pn532.tgGetData(buf, len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/mac_link.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,51 @@ + + +#ifndef __MAC_LINK_H__ +#define __MAC_LINK_H__ + +#include "PN532.h" + +class MACLink { +public: + MACLink(PN532Interface &interface) : pn532(interface) { + + }; + + /** + * @brief Activate PN532 as a target + * @param timeout max time to wait, 0 means no timeout + * @return > 0 success + * = 0 timeout + * < 0 failed + */ + int8_t activateAsTarget(uint16_t timeout = 0); + + /** + * @brief write a PDU packet, the packet should be less than (255 - 2) bytes + * @param header packet header + * @param hlen length of header + * @param body packet body + * @param blen length of body + * @return true success + * false failed + */ + bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); + + /** + * @brief read a PDU packet, the packet will be less than (255 - 2) bytes + * @param buf the buffer to contain the PDU packet + * @param len lenght of the buffer + * @return >=0 length of the PDU packet + * <0 failed + */ + int16_t read(uint8_t *buf, uint8_t len); + + uint8_t *getHeaderBuffer(uint8_t *len) { + return pn532.getBuffer(len); + }; + +private: + PN532 pn532; +}; + +#endif // __MAC_LINK_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/snep.cpp Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,106 @@ + +#include "snep.h" +#include "PN532_debug.h" + +int8_t SNEP::write(const uint8_t *buf, uint8_t len, uint16_t timeout) +{ + if (0 >= llcp.activate(timeout)) { + DMSG("failed to activate PN532 as a target\n"); + return -1; + } + + if (0 >= llcp.connect(timeout)) { + DMSG("failed to set up a connection\n"); + return -2; + } + + // response a success SNEP message + headerBuf[0] = SNEP_DEFAULT_VERSION; + headerBuf[1] = SNEP_REQUEST_PUT; + headerBuf[2] = 0; + headerBuf[3] = 0; + headerBuf[4] = 0; + headerBuf[5] = len; + if (0 >= llcp.write(headerBuf, 6, buf, len)) { + return -3; + } + + uint8_t rbuf[16]; + if (6 > llcp.read(rbuf, sizeof(rbuf))) { + return -4; + } + + // check SNEP version + if (SNEP_DEFAULT_VERSION != rbuf[0]) { + DMSG("The received SNEP message's major version is different\n"); + // To-do: send Unsupported Version response + return -4; + } + + // expect a put request + if (SNEP_RESPONSE_SUCCESS != rbuf[1]) { + DMSG("Expect a success response\n"); + return -4; + } + + llcp.disconnect(timeout); + + return 1; +} + +int16_t SNEP::read(uint8_t *buf, uint8_t len, uint16_t timeout) +{ + if (0 >= llcp.activate(timeout)) { + DMSG("failed to activate PN532 as a target\n"); + return -1; + } + + if (0 >= llcp.waitForConnection(timeout)) { + DMSG("failed to set up a connection\n"); + return -2; + } + + uint16_t status = llcp.read(buf, len); + if (6 > status) { + return -3; + } + + + // check SNEP version + if (SNEP_DEFAULT_VERSION != buf[0]) { + DMSG("The received SNEP message's major version is different\n"); + // To-do: send Unsupported Version response + return -4; + } + + // expect a put request + if (SNEP_REQUEST_PUT != buf[1]) { + DMSG("Expect a put request\n"); + return -4; + } + + // check message's length + uint32_t length = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8) + buf[5]; + // length should not be more than 244 (header + body < 255, header = 6 + 3 + 2) + if (length > (status - 6)) { + DMSG("The SNEP message is too large: "); + DMSG_INT(length); + DMSG_INT(status - 6); + DMSG("\n"); + return -4; + } + for (uint8_t i = 0; i < length; i++) { + buf[i] = buf[i + 6]; + } + + // response a success SNEP message + headerBuf[0] = SNEP_DEFAULT_VERSION; + headerBuf[1] = SNEP_RESPONSE_SUCCESS; + headerBuf[2] = 0; + headerBuf[3] = 0; + headerBuf[4] = 0; + headerBuf[5] = 0; + llcp.write(headerBuf, 6); + + return length; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nfc/snep.h Fri Dec 27 01:46:32 2013 +0000 @@ -0,0 +1,49 @@ + + +#ifndef __SNEP_H__ +#define __SNEP_H__ + +#include "llcp.h" + +#define SNEP_DEFAULT_VERSION 0x10 // Major: 1, Minor: 0 + +#define SNEP_REQUEST_PUT 0x02 +#define SNEP_REQUEST_GET 0x01 + +#define SNEP_RESPONSE_SUCCESS 0x81 +#define SNEP_RESPONSE_REJECT 0xFF + +class SNEP { +public: + SNEP(PN532Interface &interface) : llcp(interface) { + headerBuf = llcp.getHeaderBuffer(&headerBufLen); + }; + + /** + * @brief write a SNEP packet, the packet should be less than (255 - 2 - 3) bytes + * @param buf the buffer to contain the packet + * @param len lenght of the buffer + * @param timeout max time to wait, 0 means no timeout + * @return >0 success + * =0 timeout + * <0 failed + */ + int8_t write(const uint8_t *buf, uint8_t len, uint16_t timeout = 0); + + /** + * @brief read a SNEP packet, the packet will be less than (255 - 2 - 3) bytes + * @param buf the buffer to contain the packet + * @param len lenght of the buffer + * @param timeout max time to wait, 0 means no timeout + * @return >=0 length of the packet + * <0 failed + */ + int16_t read(uint8_t *buf, uint8_t len, uint16_t timeout = 0); + +private: + LLCP llcp; + uint8_t *headerBuf; + uint8_t headerBufLen; +}; + +#endif // __SNEP_H__