├── Due.h ├── NFCLinkLayer.cpp ├── NFCLinkLayer.h ├── NFCReader.h ├── Ndef.cpp ├── Ndef.h ├── NdefMessage.cpp ├── NdefMessage.h ├── NdefRecord.cpp ├── NdefRecord.h ├── NfcTag.cpp ├── NfcTag.h ├── PN5321.cpp ├── PN5321.h ├── README.md ├── SNEP.cpp ├── SNEP.h ├── debug.h ├── example └── nfc_ndef_push_url │ └── nfc_ndef_push_url.ino ├── keywords.txt └── license.txt /Due.h: -------------------------------------------------------------------------------- 1 | // redefine some stuff so code works on Due 2 | // http://arduino.cc/forum/index.php?&topic=153761.0 3 | 4 | #ifndef Due_h 5 | #define Due_h 6 | 7 | #if defined(__SAM3X8E__) 8 | #define PROGMEM 9 | #define pgm_read_byte(x) (*((char *)x)) 10 | // #define pgm_read_word(x) (*((short *)(x & 0xfffffffe))) 11 | #define pgm_read_word(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) 12 | #define pgm_read_byte_near(x) (*((char *)x)) 13 | #define pgm_read_byte_far(x) (*((char *)x)) 14 | // #define pgm_read_word_near(x) (*((short *)(x & 0xfffffffe)) 15 | // #define pgm_read_word_far(x) (*((short *)(x & 0xfffffffe))) 16 | #define pgm_read_word_near(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x))) 17 | #define pgm_read_word_far(x) ( ((*((unsigned char *)x + 1)) << 8) + (*((unsigned char *)x)))) 18 | #define PSTR(x) x 19 | #if defined F 20 | #undef F 21 | #endif 22 | #define F(X) (X) 23 | #endif 24 | 25 | #endif -------------------------------------------------------------------------------- /NFCLinkLayer.cpp: -------------------------------------------------------------------------------- 1 | #include "NFCLinkLayer.h" 2 | 3 | #include "debug.h" 4 | 5 | uint8_t SYMM_PDU[] = {0, 0}; 6 | 7 | NFCLinkLayer::NFCLinkLayer(NFCReader *nfcReader) 8 | : _nfcReader(nfcReader) 9 | { 10 | } 11 | 12 | NFCLinkLayer::~NFCLinkLayer() 13 | { 14 | 15 | } 16 | 17 | /* 18 | * the conversation of opening SNEP server link 19 | * 20 | * <- SYMM 21 | * -> CONN [LLCP_CONNECTING] 22 | * <- SYMM [option] 23 | * -> SYMM [option] 24 | * <- CC [LLCP_CONNECTED] 25 | */ 26 | uint32_t NFCLinkLayer::openSNEPClientLink(void) 27 | { 28 | PDU *targetPayload; 29 | PDU *recievedPDU; 30 | uint8_t PDUBuffer[64]; 31 | uint8_t DataIn[64]; 32 | 33 | targetPayload = (PDU *) PDUBuffer; 34 | 35 | DMSG(F("Opening SNEP Client Link.\n")); 36 | 37 | uint32_t result = _nfcReader->configurePeerAsTarget(SNEP_CLIENT); 38 | 39 | if (IS_ERROR(result)) 40 | { 41 | return result; 42 | } 43 | 44 | recievedPDU = ( PDU *) DataIn; 45 | uint32_t rx_result = _nfcReader->targetRxData(DataIn); 46 | if (IS_ERROR(rx_result)) 47 | { 48 | DMSG(F("Connection Failed.\n")); 49 | 50 | return CONNECT_RX_FAILURE; 51 | } 52 | 53 | uint8_t PDU[2] ; 54 | 55 | /* 56 | * Send CONNECTION PDU 57 | */ 58 | PDU[0] = 0x11; 59 | PDU[1] = 0x20; 60 | if (IS_ERROR(_nfcReader->targetTxData(PDU, 2))) { 61 | return CONNECT_TX_FAILURE; 62 | } 63 | 64 | 65 | _nfcReader->targetRxData(DataIn); 66 | PDU[0] = 0; 67 | PDU[1] = 0; 68 | int count = 0; 69 | while (recievedPDU->getPTYPE() == SYMM_PTYPE) { 70 | if (IS_ERROR(_nfcReader->targetTxData(PDU, 2))) { 71 | return CONNECT_TX_FAILURE; 72 | } 73 | 74 | if (IS_ERROR(_nfcReader->targetRxData(DataIn))) { 75 | return CONNECT_RX_FAILURE; 76 | } 77 | 78 | count++; 79 | if (count > 10) { 80 | return CONNECT_TX_FAILURE; 81 | } 82 | } 83 | 84 | 85 | /* 86 | * get a CONNECTION COMPLETE PDU 87 | */ 88 | if (recievedPDU->getPTYPE() != CONNECTION_COMPLETE_PTYPE) 89 | { 90 | DMSG(F("Connection Complete Failed.\n")); 91 | 92 | return UNEXPECTED_PDU_FAILURE; 93 | } 94 | 95 | DSAP = recievedPDU->getSSAP(); 96 | SSAP = recievedPDU->getDSAP(); 97 | 98 | return RESULT_SUCCESS; 99 | } 100 | 101 | uint32_t NFCLinkLayer::closeSNEPClientLink() 102 | { 103 | 104 | } 105 | 106 | /* 107 | * the conversation of opening SNEP server link 108 | * 109 | * -> SYMM 110 | * <- SYMM 111 | * ... SYMM repeat 112 | * <- CONN [LLCP_CONNECTING] 113 | * -> CC [LLCP_CONNECTED] 114 | */ 115 | 116 | uint32_t NFCLinkLayer::openSNEPServerLink(void) 117 | { 118 | uint8_t status[2]; 119 | uint8_t DataIn[64]; 120 | PDU *recievedPDU; 121 | PDU targetPayload; 122 | uint32_t result; 123 | 124 | DMSG(F("Opening Server Link.\n")); 125 | 126 | result = _nfcReader->configurePeerAsTarget(SNEP_CLIENT); 127 | if (IS_ERROR(result)) 128 | { 129 | return result; 130 | } 131 | 132 | uint8_t symm[] = {0, 0}; 133 | recievedPDU = (PDU *)DataIn; 134 | do 135 | { 136 | result = _nfcReader->targetRxData(DataIn); 137 | 138 | if (IS_ERROR(result)) 139 | { 140 | return result; 141 | } 142 | 143 | if (recievedPDU->getPTYPE() == CONNECT_PTYPE) { 144 | break; 145 | } else { 146 | result = _nfcReader->targetTxData(symm, sizeof(symm)); 147 | } 148 | 149 | } while (RESULT_OK(result)); 150 | 151 | targetPayload.setDSAP(recievedPDU->getSSAP()); 152 | targetPayload.setPTYPE(CONNECTION_COMPLETE_PTYPE); 153 | targetPayload.setSSAP(recievedPDU->getDSAP()); 154 | 155 | if (IS_ERROR(_nfcReader->targetTxData((uint8_t *)&targetPayload, 2))) 156 | { 157 | DMSG(F("Connection Complete Failed.\n")); 158 | 159 | return CONNECT_COMPLETE_TX_FAILURE; 160 | } 161 | 162 | return RESULT_SUCCESS; 163 | } 164 | 165 | uint32_t NFCLinkLayer::closeSNEPServerLink() 166 | { 167 | uint8_t DataIn[64]; 168 | PDU *recievedPDU; 169 | 170 | recievedPDU = (PDU *)DataIn; 171 | 172 | uint32_t result = _nfcReader->targetRxData(DataIn); 173 | 174 | if (_nfcReader->isTargetReleasedError(result)) 175 | { 176 | return RESULT_SUCCESS; 177 | } 178 | else if (IS_ERROR(result)) 179 | { 180 | return result; 181 | } 182 | 183 | //Serial.println(F("Recieved disconnect Message.")); 184 | 185 | return result; 186 | } 187 | 188 | uint32_t NFCLinkLayer::serverLinkRxData(uint8_t *&Data) 189 | { 190 | uint8_t len; 191 | PDU ackPDU; 192 | uint32_t result; 193 | uint8_t count = 0; 194 | PDU *recievedPDU = (PDU *) Data; 195 | 196 | do { 197 | result = _nfcReader->targetRxData(Data); 198 | if (IS_ERROR(result)) 199 | { 200 | DMSG(F("Failed to Recieve NDEF Message.\n")); 201 | 202 | return NDEF_MESSAGE_RX_FAILURE; 203 | } 204 | 205 | if (recievedPDU->getPTYPE() == INFORMATION_PTYPE) 206 | { 207 | break; 208 | } 209 | 210 | if (recievedPDU->getPTYPE() == SYMM_PTYPE) { 211 | result = _nfcReader->targetTxData(SYMM_PDU, sizeof(SYMM_PDU)); 212 | if (IS_ERROR(result)) 213 | { 214 | DMSG(F("Failed to Recieve NDEF Message.\n")); 215 | 216 | return NDEF_MESSAGE_RX_FAILURE; 217 | } 218 | } 219 | 220 | count++; 221 | if (count > 10) { 222 | return NDEF_MESSAGE_RX_FAILURE; 223 | } 224 | } while (1); 225 | 226 | len = (uint8_t) result - 2; 227 | 228 | Data = &Data[3]; 229 | 230 | // Acknowledge reciept of Information PDU 231 | ackPDU.setDSAP(recievedPDU->getSSAP()); 232 | ackPDU.setPTYPE(RECEIVE_READY_TYPE); 233 | ackPDU.setSSAP(recievedPDU->getDSAP()); 234 | 235 | ackPDU.params.sequence = (recievedPDU->params.sequence + 1)& 0x0F; 236 | 237 | result = _nfcReader->targetTxData((uint8_t *)&ackPDU, 3); 238 | if (IS_ERROR(result)) 239 | { 240 | DMSG(F("Ack Failed.")); 241 | 242 | return result; 243 | } 244 | 245 | 246 | // Receive SYMM PDU 247 | uint8_t rwbuf[9]; 248 | _nfcReader->targetRxData(rwbuf, sizeof(rwbuf)); 249 | 250 | // Send INFO PDU to confirm SNEP 251 | rwbuf[0] = (recievedPDU->getSSAP() << 2) + 0x3; 252 | rwbuf[1] = recievedPDU->getDSAP(); 253 | rwbuf[2] = 0x01; 254 | rwbuf[3] = 0x10; 255 | rwbuf[4] = 0x81; 256 | rwbuf[5] = 0x00; 257 | rwbuf[6] = 0x00; 258 | rwbuf[7] = 0x00; 259 | rwbuf[8] = 0x00; 260 | _nfcReader->targetTxData(rwbuf, 9); 261 | 262 | // Receive RR PDU 263 | _nfcReader->targetRxData(rwbuf, sizeof(rwbuf)); 264 | 265 | // Send SYMM PDU 266 | rwbuf[0] = 0x00; 267 | rwbuf[1] = 0x00; 268 | _nfcReader->targetTxData(rwbuf, 2); 269 | 270 | // Receive DISC PDU 271 | _nfcReader->targetRxData(rwbuf, sizeof(rwbuf)); 272 | 273 | // Send DM PDU 274 | rwbuf[0] = (recievedPDU->getSSAP() << 2) + 0x1; 275 | rwbuf[1] = (0x1 << 6) + recievedPDU->getDSAP(); 276 | rwbuf[2] = 0x0; 277 | _nfcReader->targetTxData(rwbuf, 3); 278 | 279 | return len; 280 | } 281 | 282 | uint32_t NFCLinkLayer::clientLinkTxData(uint8_t *snepMessage, uint32_t len) 283 | { 284 | PDU *infoPDU = (PDU *) ALLOCATE_HEADER_SPACE(snepMessage, 3); 285 | infoPDU->setDSAP(DSAP); 286 | infoPDU->setSSAP(SSAP); 287 | infoPDU->setPTYPE(INFORMATION_PTYPE); 288 | 289 | infoPDU->params.sequence = 0; 290 | 291 | uint8_t buf[32]; 292 | 293 | uint8_t symm[] = {0, 0}; 294 | _nfcReader->targetTxData(symm, sizeof(symm)); 295 | 296 | // _nfcReader->targetRxData(buf); 297 | 298 | 299 | if (IS_ERROR(_nfcReader->targetTxData((uint8_t *)infoPDU, len + 3))) 300 | { 301 | DMSG(F("Sending NDEF Message Failed.")); 302 | 303 | return NDEF_MESSAGE_TX_FAILURE; 304 | } 305 | 306 | 307 | _nfcReader->targetRxData(buf); 308 | 309 | _nfcReader->targetTxData(symm, sizeof(symm)); 310 | 311 | PDU disconnect; 312 | disconnect.setDSAP(DSAP); 313 | disconnect.setSSAP(SSAP); 314 | disconnect.setPTYPE(DISCONNECT_PTYPE); 315 | 316 | _nfcReader->targetTxData((uint8_t *)&disconnect, 2); 317 | 318 | _nfcReader->targetRxData(buf); 319 | 320 | DMSG(F("Sent NDEF Message")); 321 | 322 | return RESULT_SUCCESS; 323 | } 324 | 325 | inline bool PDU::isConnectClientRequest() 326 | { 327 | #if 0 328 | return ((getPTYPE() == CONNECT_PTYPE) && 329 | (params.length == CONNECT_SERVICE_NAME_LEN) && 330 | (strncmp((char *)params.data, CONNECT_SERVICE_NAME, CONNECT_SERVICE_NAME_LEN) == 0)); 331 | #else 332 | return (getPTYPE() == CONNECT_PTYPE); 333 | #endif 334 | } 335 | 336 | PDU::PDU() 337 | { 338 | field[0] = 0; 339 | field[1] = 0; 340 | params.type = 0; 341 | params.length = 0; 342 | } 343 | 344 | uint8_t PDU::getDSAP() 345 | { 346 | return (field[0] >> 2); 347 | } 348 | 349 | uint8_t PDU::getSSAP() 350 | { 351 | return (field[1] & 0x3F); 352 | } 353 | 354 | uint8_t PDU::getPTYPE() 355 | { 356 | return (((field[0] & 0x03) << 2) | ((field[1] & 0xC0) >> 6)); 357 | } 358 | 359 | void PDU::setDSAP(uint8_t DSAP) 360 | { 361 | field[0] &= 0x03; // Clear the DSAP bits 362 | field[0] |= ((DSAP & 0x3F) << 2); // Set the bits 363 | } 364 | 365 | void PDU::setSSAP(uint8_t SSAP) 366 | { 367 | field[1] &= (0xC0); // Clear the SSAP bits 368 | field[1] |= (0x3F & SSAP); 369 | } 370 | 371 | void PDU::setPTYPE(uint8_t PTYPE) 372 | { 373 | field[0] &= 0xFC; // Clear the last two bits that contain the PTYPE 374 | field[1] &= 0x3F; // Clear the upper two bits that contain the PTYPE 375 | field[0] |= (PTYPE & 0x0C) >> 2; 376 | field[1] |= (PTYPE & 0x03) << 6; 377 | } 378 | -------------------------------------------------------------------------------- /NFCLinkLayer.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef NFC_LINK_LAYER_H 3 | #define NFC_LINK_LAYER_H 4 | 5 | #include "Arduino.h" 6 | #include "NFCReader.h" 7 | 8 | #define SYMM_PTYPE 0x00 9 | #define PAX_PTYPE 0x01 10 | #define AGGREGATE_PTYPE 0x02 11 | #define UNNUMBERED_INFORMATION_PTYPE 0x03 12 | #define CONNECT_PTYPE 0x04 13 | #define DISCONNECT_PTYPE 0x05 14 | #define CONNECTION_COMPLETE_PTYPE 0x06 15 | #define DISCONNECTED_MODE_PTYPE 0x07 16 | #define FRAME_REJECT_PTYPE 0X08 17 | #define INFORMATION_PTYPE 0x0C 18 | #define RECEIVE_READY_TYPE 0x0D 19 | #define RECEIVE_NOT_READY_TYPE 0x0E 20 | 21 | #define SERVICE_NAME_PARAM_TYPE 0x06 22 | 23 | #define CONNECT_SERVICE_NAME_LEN 0x0F 24 | #define CONNECT_SERVICE_NAME "com.android.npp" 25 | 26 | #define CONNECT_SERVER_PDU_LEN CONNECT_SERVICE_NAME_LEN + 4 27 | 28 | 29 | struct PARAMETER_DESCR { 30 | union { 31 | uint8_t type; 32 | uint8_t sequence; 33 | }; 34 | uint8_t length; 35 | uint8_t data[0]; 36 | }; 37 | 38 | struct PDU { 39 | public: 40 | uint8_t field[2]; 41 | PARAMETER_DESCR params; 42 | 43 | 44 | PDU(); 45 | 46 | uint8_t getDSAP(); 47 | uint8_t getSSAP(); 48 | uint8_t getPTYPE(); 49 | 50 | void setDSAP(uint8_t DSAP); 51 | void setSSAP(uint8_t SSAP); 52 | void setPTYPE(uint8_t PTYPE); 53 | bool isConnectClientRequest(); 54 | }; 55 | 56 | class NFCLinkLayer { 57 | public: 58 | NFCLinkLayer(NFCReader *nfcReader); 59 | ~NFCLinkLayer(); 60 | 61 | uint32_t openSNEPServerLink(void); 62 | uint32_t closeSNEPServerLink(void); 63 | 64 | uint32_t openSNEPClientLink(void); 65 | uint32_t closeSNEPClientLink(void); 66 | 67 | uint32_t serverLinkRxData(uint8_t *&Data); 68 | uint32_t clientLinkTxData(uint8_t *nppMessage, uint32_t len); 69 | private: 70 | NFCReader *_nfcReader; 71 | uint8_t DSAP; 72 | uint8_t SSAP; 73 | }; 74 | 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /NFCReader.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef NFC_READER_H 4 | #define NFC_READER_H 5 | 6 | #include "Arduino.h" 7 | 8 | #define NFC_READER_CFG_BAUDRATE_106_KPS 0 9 | #define NFC_READER_CFG_BAUDRATE_201_KPS 1 10 | #define NFC_READER_CFG_BAUDRATE_424_KPS 2 11 | 12 | 13 | #define SNEP_CLIENT 1 14 | #define SNEP_SERVER 2 15 | 16 | enum RESULTS 17 | { 18 | RESULT_SUCCESS = 0x00000001, 19 | GEN_ERROR = 0x80000000, 20 | CONFIGURE_HARDWARE_ERROR, 21 | NFC_READER_COMMAND_FAILURE, 22 | NFC_READER_RESPONSE_FAILURE, 23 | CONNECT_RX_FAILURE, 24 | CONNECT_TX_FAILURE, 25 | CONNECT_COMPLETE_RX_FAILURE, 26 | CONNECT_COMPLETE_TX_FAILURE, 27 | UNEXPECTED_PDU_FAILURE, 28 | NDEF_MESSAGE_RX_FAILURE, 29 | NDEF_MESSAGE_TX_FAILURE, 30 | SNEP_UNSUPPORTED_VERSION, 31 | SNEP_INVALID_NUM_ENTRIES, 32 | SNEP_INVALID_ACTION_CODE, 33 | SEND_COMMAND_TX_TIMEOUT_ERROR, 34 | SEND_COMMAND_RX_ACK_ERROR, 35 | SEND_COMMAND_RX_TIMEOUT_ERROR, 36 | INVALID_CHECKSUM_RX, 37 | INVALID_RESPONSE, 38 | INVALID_POSTAMBLE, 39 | CLIENT_REQ_ERROR 40 | 41 | }; 42 | 43 | 44 | #define IS_ERROR(result) ((result) & 0x80000000) 45 | #define RESULT_OK(result) !IS_ERROR(result) 46 | class NFCReader { 47 | 48 | public: 49 | virtual void initializeReader() = 0; 50 | virtual uint32_t SAMConfig(void) = 0; 51 | virtual uint32_t getFirmwareVersion(void) = 0; 52 | 53 | virtual uint32_t readPassiveTargetID(uint8_t cardbaudrate) = 0; 54 | virtual uint32_t authenticateBlock( uint8_t cardnumber /*1 or 2*/, 55 | uint32_t cid /*Card NUID*/, 56 | uint8_t blockaddress /*0 to 63*/, 57 | uint8_t authtype /*Either KEY_A or KEY_B */, 58 | uint8_t * keys) = 0; 59 | 60 | virtual uint32_t readMemoryBlock(uint8_t cardnumber /*1 or 2*/, 61 | uint8_t blockaddress /*0 to 63*/, 62 | uint8_t * block) = 0; 63 | 64 | virtual uint32_t writeMemoryBlock(uint8_t cardnumber /*1 or 2*/, 65 | uint8_t blockaddress /*0 to 63*/, 66 | uint8_t * block); 67 | 68 | virtual uint32_t configurePeerAsInitiator(uint8_t baudrate) = 0; 69 | virtual uint32_t configurePeerAsTarget(uint8_t type) = 0; 70 | 71 | 72 | virtual uint32_t initiatorTxRxData(uint8_t *DataOut, 73 | uint32_t dataSize, 74 | uint8_t *response) = 0; 75 | 76 | virtual uint32_t targetRxData(uint8_t *response) = 0; 77 | virtual uint32_t targetRxData(uint8_t *response, uint32_t len) = 0; 78 | virtual uint32_t targetTxData(uint8_t *DataOut, uint32_t dataSize) = 0; 79 | 80 | virtual uint32_t getTargetStatus(uint8_t *statusOut) = 0; 81 | 82 | virtual uint32_t sendCommandCheckAck(uint8_t *cmd, 83 | uint8_t cmdlen, 84 | uint16_t timeout = 1000) = 0; 85 | 86 | virtual boolean isTargetReleasedError(uint32_t result) = 0; 87 | 88 | }; 89 | 90 | 91 | #define ALLOCATE_HEADER_SPACE(buffer, numHdrBytes) &buffer[-((int)numHdrBytes)]; 92 | #endif // NFC_READER_H 93 | -------------------------------------------------------------------------------- /Ndef.cpp: -------------------------------------------------------------------------------- 1 | #include "Ndef.h" 2 | 3 | // Borrowed from Adafruit_NFCShield_I2C 4 | void PrintHex(const byte * data, const uint32_t numBytes) 5 | { 6 | uint32_t szPos; 7 | for (szPos=0; szPos < numBytes; szPos++) 8 | { 9 | Serial.print("0x"); 10 | // Append leading 0 for small values 11 | if (data[szPos] <= 0xF) 12 | Serial.print("0"); 13 | Serial.print(data[szPos]&0xff, HEX); 14 | if ((numBytes > 1) && (szPos != numBytes - 1)) 15 | { 16 | Serial.print(" "); 17 | } 18 | } 19 | Serial.println(""); 20 | } 21 | 22 | // Borrowed from Adafruit_NFCShield_I2C 23 | void PrintHexChar(const byte * data, const uint32_t numBytes) 24 | { 25 | uint32_t szPos; 26 | for (szPos=0; szPos < numBytes; szPos++) 27 | { 28 | // Append leading 0 for small values 29 | if (data[szPos] <= 0xF) 30 | Serial.print("0"); 31 | Serial.print(data[szPos], HEX); 32 | if ((numBytes > 1) && (szPos != numBytes - 1)) 33 | { 34 | Serial.print(" "); 35 | } 36 | } 37 | Serial.print(" "); 38 | for (szPos=0; szPos < numBytes; szPos++) 39 | { 40 | if (data[szPos] <= 0x1F) 41 | Serial.print("."); 42 | else 43 | Serial.print((char)data[szPos]); 44 | } 45 | Serial.println(""); 46 | } 47 | 48 | // Note if buffer % blockSize != 0, last block will not be written 49 | void DumpHex(const byte * data, const uint32_t numBytes, const uint8_t blockSize) 50 | { 51 | int i; 52 | for (i = 0; i < (numBytes / blockSize); i++) 53 | { 54 | PrintHexChar(data, blockSize); 55 | data += blockSize; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Ndef.h: -------------------------------------------------------------------------------- 1 | #ifndef Ndef_h 2 | #define Ndef_h 3 | 4 | #include 5 | 6 | #define NULL (void *)0 7 | 8 | void PrintHex(const byte * data, const uint32_t numBytes); 9 | void PrintHexChar(const byte * data, const uint32_t numBytes); 10 | void DumpHex(const byte * data, const uint32_t numBytes, const uint8_t blockSize); 11 | 12 | 13 | 14 | 15 | // RTD_TEXT: [0x54], // "T" 16 | // RTD_URI: [0x55], // "U" 17 | // RTD_SMART_POSTER: [0x53, 0x70], // "Sp" 18 | // RTD_ALTERNATIVE_CARRIER: [0x61, 0x63], // "ac" 19 | // RTD_HANDOVER_CARRIER: [0x48, 0x63], // "Hc" 20 | // RTD_HANDOVER_REQUEST: [0x48, 0x72], // "Hr" 21 | // RTD_HANDOVER_SELECT: [0x48, 0x73], // "Hs" 22 | 23 | /* 24 | // Maybe for something like NdefField NdefRecord::getType() 25 | // Less useful for Payload 26 | // Really need subclasses of NdefRecord with additonal constructors and getters 27 | class NdefField 28 | { 29 | public: 30 | NdefField(); 31 | ~NdefField(); 32 | NdefField& operator=(const NdefField& rhs); 33 | int length(); 34 | int getLength(); 35 | void getBytes(uint8_t* data); 36 | String getValueAsString(); 37 | void setValue(String value); 38 | void setValue(uint8_t* data, int dataLength); 39 | }; 40 | */ 41 | 42 | #endif -------------------------------------------------------------------------------- /NdefMessage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NdefMessage::NdefMessage(void) 4 | { 5 | _recordCount = 0; 6 | } 7 | 8 | NdefMessage::NdefMessage(const byte * data, const int numBytes) 9 | { 10 | #ifdef NDEF_DEBUG 11 | Serial.print(F("Decoding "));Serial.print(numBytes);Serial.println(F(" bytes")); 12 | PrintHexChar(data, numBytes); 13 | //DumpHex(data, numBytes, 16); 14 | #endif 15 | 16 | _recordCount = 0; 17 | 18 | int index = 0; 19 | 20 | while (index <= numBytes) { 21 | 22 | // decode tnf - first byte is tnf with bit flags 23 | // see the NFDEF spec for more info 24 | byte tnf_byte = data[index]; 25 | bool mb = (tnf_byte & 0x80) != 0; 26 | bool me = (tnf_byte & 0x40) != 0; 27 | bool cf = (tnf_byte & 0x20) != 0; 28 | bool sr = (tnf_byte & 0x10) != 0; 29 | bool il = (tnf_byte & 0x8) != 0; 30 | byte tnf = (tnf_byte & 0x7); 31 | 32 | NdefRecord record = NdefRecord(); 33 | record.setTnf(tnf); 34 | 35 | index++; 36 | int typeLength = data[index]; 37 | 38 | int payloadLength = 0; 39 | if (sr) 40 | { 41 | index++; 42 | payloadLength = data[index]; 43 | } 44 | else 45 | { 46 | payloadLength = ((0xFF & data[++index]) << 24) | ((0xFF & data[++index]) << 26) | 47 | ((0xFF & data[++index]) << 8) | (0xFF & data[++index]); 48 | } 49 | 50 | int idLength = 0; 51 | if (il) 52 | { 53 | index++; 54 | idLength = data[index]; 55 | } 56 | 57 | index++; 58 | record.setType(&data[index], typeLength); 59 | index += typeLength; 60 | 61 | if (il) 62 | { 63 | record.setId(&data[index], idLength); 64 | index += idLength; 65 | } 66 | 67 | record.setPayload(&data[index], payloadLength); 68 | index += payloadLength; 69 | 70 | addRecord(record); 71 | 72 | if (me) break; // last message 73 | } 74 | 75 | } 76 | 77 | NdefMessage::NdefMessage(const NdefMessage& rhs) 78 | { 79 | 80 | _recordCount = rhs._recordCount; 81 | for (int i = 0; i < _recordCount; i++) 82 | { 83 | _records[i] = rhs._records[i]; 84 | } 85 | 86 | } 87 | 88 | NdefMessage::~NdefMessage() 89 | { 90 | } 91 | 92 | NdefMessage& NdefMessage::operator=(const NdefMessage& rhs) 93 | { 94 | 95 | if (this != &rhs) 96 | { 97 | 98 | // delete existing records 99 | for (int i = 0; i < _recordCount; i++) 100 | { 101 | // TODO Dave: is this the right way to delete existing records? 102 | _records[i] = NdefRecord(); 103 | } 104 | 105 | _recordCount = rhs._recordCount; 106 | for (int i = 0; i < _recordCount; i++) 107 | { 108 | _records[i] = rhs._records[i]; 109 | } 110 | } 111 | return *this; 112 | } 113 | 114 | uint8_t NdefMessage::getRecordCount() 115 | { 116 | return _recordCount; 117 | } 118 | 119 | int NdefMessage::getEncodedSize() 120 | { 121 | int size = 0; 122 | for (int i = 0; i < _recordCount; i++) 123 | { 124 | size += _records[i].getEncodedSize(); 125 | } 126 | return size; 127 | } 128 | 129 | // TODO change this to return uint8_t* 130 | void NdefMessage::encode(uint8_t* data) 131 | { 132 | // assert sizeof(data) >= getEncodedSize() 133 | uint8_t* data_ptr = &data[0]; 134 | 135 | for (int i = 0; i < _recordCount; i++) 136 | { 137 | _records[i].encode(data_ptr, i == 0, (i + 1) == _recordCount); 138 | // TODO can NdefRecord.encode return the record size? 139 | data_ptr += _records[i].getEncodedSize(); 140 | } 141 | 142 | } 143 | 144 | boolean NdefMessage::addRecord(NdefRecord& record) 145 | { 146 | 147 | if (_recordCount < MAX_NDEF_RECORDS) 148 | { 149 | _records[_recordCount] = record; 150 | _recordCount++; 151 | return true; 152 | } 153 | else 154 | { 155 | Serial.println(F("WARNING: Too many records. Increase MAX_NDEF_RECORDS.")); 156 | return false; 157 | } 158 | } 159 | 160 | void NdefMessage::addMimeMediaRecord(String mimeType, String payload) 161 | { 162 | 163 | byte payloadBytes[payload.length() + 1]; 164 | payload.getBytes(payloadBytes, sizeof(payloadBytes)); 165 | 166 | addMimeMediaRecord(mimeType, payloadBytes, payload.length()); 167 | } 168 | 169 | void NdefMessage::addMimeMediaRecord(String mimeType, uint8_t* payload, int payloadLength) 170 | { 171 | NdefRecord r = NdefRecord(); 172 | r.setTnf(TNF_MIME_MEDIA); 173 | 174 | byte type[mimeType.length() + 1]; 175 | mimeType.getBytes(type, sizeof(type)); 176 | r.setType(type, mimeType.length()); 177 | 178 | r.setPayload(payload, payloadLength); 179 | 180 | addRecord(r); 181 | } 182 | 183 | void NdefMessage::addTextRecord(String text) 184 | { 185 | addTextRecord(text, "en"); 186 | } 187 | 188 | void NdefMessage::addTextRecord(String text, String encoding) 189 | { 190 | NdefRecord r = NdefRecord(); 191 | r.setTnf(TNF_WELL_KNOWN); 192 | 193 | uint8_t RTD_TEXT[1] = { 0x54 }; // TODO this should be a constant or preprocessor 194 | r.setType(RTD_TEXT, sizeof(RTD_TEXT)); 195 | 196 | // X is a placeholder for encoding length 197 | // TODO is it more efficient to build w/o string concatenation? 198 | String payloadString = "X" + encoding + text; 199 | 200 | byte payload[payloadString.length() + 1]; 201 | payloadString.getBytes(payload, sizeof(payload)); 202 | 203 | // replace X with the real encoding length 204 | payload[0] = encoding.length(); 205 | 206 | r.setPayload(payload, payloadString.length()); 207 | 208 | addRecord(r); 209 | } 210 | 211 | void NdefMessage::addUriRecord(String uri) 212 | { 213 | NdefRecord* r = new NdefRecord(); 214 | r->setTnf(TNF_WELL_KNOWN); 215 | 216 | uint8_t RTD_URI[1] = { 0x55 }; // TODO this should be a constant or preprocessor 217 | r->setType(RTD_URI, sizeof(RTD_URI)); 218 | 219 | // X is a placeholder for identifier code 220 | String payloadString = "X" + uri; 221 | 222 | byte payload[payloadString.length() + 1]; 223 | payloadString.getBytes(payload, sizeof(payload)); 224 | 225 | // add identifier code 0x0, meaning no prefix substitution 226 | payload[0] = 0x0; 227 | 228 | r->setPayload(payload, payloadString.length()); 229 | 230 | addRecord(*r); 231 | delete(r); 232 | } 233 | 234 | void NdefMessage::addEmptyRecord() 235 | { 236 | NdefRecord* r = new NdefRecord(); 237 | r->setTnf(TNF_EMPTY); 238 | addRecord(*r); 239 | delete(r); 240 | } 241 | 242 | NdefRecord NdefMessage::getRecord(uint8_t index) 243 | { 244 | if (index > -1 && index < _recordCount) 245 | { 246 | return _records[index]; 247 | } 248 | else 249 | { 250 | return NdefRecord(); // would rather return NULL 251 | } 252 | } 253 | 254 | NdefRecord NdefMessage::operator[](uint8_t index) 255 | { 256 | return getRecord(index); 257 | } 258 | 259 | 260 | void NdefMessage::print() 261 | { 262 | Serial.print(F("\nNDEF Message "));Serial.print(_recordCount);Serial.print(F(" record")); 263 | _recordCount == 1 ? Serial.print(", ") : Serial.print("s, "); 264 | Serial.print(getEncodedSize());Serial.println(F(" bytes")); 265 | 266 | int i; 267 | for (i = 0; i < _recordCount; i++) 268 | { 269 | _records[i].print(); 270 | } 271 | } -------------------------------------------------------------------------------- /NdefMessage.h: -------------------------------------------------------------------------------- 1 | #ifndef NdefMessage_h 2 | #define NdefMessage_h 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_NDEF_RECORDS 4 8 | 9 | class NdefMessage 10 | { 11 | public: 12 | NdefMessage(void); 13 | NdefMessage(const uint8_t * data, const int numBytes); 14 | NdefMessage(const NdefMessage& rhs); 15 | ~NdefMessage(); 16 | NdefMessage& operator=(const NdefMessage& rhs); 17 | 18 | int getEncodedSize(); // need so we can pass array to encode 19 | void encode(uint8_t* data); 20 | // TODO encode(uint8_t* data, int numberBytes); ??? 21 | 22 | boolean addRecord(NdefRecord& record); 23 | void addMimeMediaRecord(String mimeType, String payload); 24 | void addMimeMediaRecord(String mimeType, uint8_t* payload, int payloadLength); 25 | void addTextRecord(String text); 26 | void addTextRecord(String text, String encoding); 27 | void addUriRecord(String uri); 28 | void addEmptyRecord(); 29 | 30 | uint8_t getRecordCount(); 31 | NdefRecord getRecord(uint8_t index); 32 | NdefRecord operator[](uint8_t index); 33 | 34 | void print(); 35 | private: 36 | NdefRecord _records[MAX_NDEF_RECORDS]; 37 | uint8_t _recordCount; 38 | }; 39 | 40 | #endif -------------------------------------------------------------------------------- /NdefRecord.cpp: -------------------------------------------------------------------------------- 1 | #include "NdefRecord.h" 2 | 3 | NdefRecord::NdefRecord() 4 | { 5 | //Serial.println("NdefRecord Constructor 1"); 6 | _tnf = 0; 7 | _typeLength = 0; 8 | _payloadLength = 0; 9 | _idLength = 0; 10 | _type = (uint8_t *)NULL; 11 | _payload = (uint8_t *)NULL; 12 | _id = (uint8_t *)NULL; 13 | } 14 | 15 | NdefRecord::NdefRecord(const NdefRecord& rhs) 16 | { 17 | //Serial.println("NdefRecord Constructor 2 (copy)"); 18 | 19 | _tnf = rhs._tnf; 20 | _typeLength = rhs._typeLength; 21 | _payloadLength = rhs._payloadLength; 22 | _idLength = rhs._idLength; 23 | _type = (uint8_t *)NULL; 24 | _payload = (uint8_t *)NULL; 25 | _id = (uint8_t *)NULL; 26 | 27 | if (_typeLength) 28 | { 29 | _type = (uint8_t*)malloc(_typeLength); 30 | memcpy(_type, rhs._type, _typeLength); 31 | } 32 | 33 | if (_payloadLength) 34 | { 35 | _payload = (uint8_t*)malloc(_payloadLength); 36 | memcpy(_payload, rhs._payload, _payloadLength); 37 | } 38 | 39 | if (_idLength) 40 | { 41 | _id = (uint8_t*)malloc(_idLength); 42 | memcpy(_id, rhs._id, _idLength); 43 | } 44 | 45 | } 46 | 47 | // TODO NdefRecord::NdefRecord(tnf, type, payload, id) 48 | 49 | NdefRecord::~NdefRecord() 50 | { 51 | //Serial.println("NdefRecord Destructor"); 52 | if (_typeLength) 53 | { 54 | free(_type); 55 | } 56 | 57 | if (_payloadLength) 58 | { 59 | free(_payload); 60 | } 61 | 62 | if (_idLength) 63 | { 64 | free(_id); 65 | } 66 | } 67 | 68 | NdefRecord& NdefRecord::operator=(const NdefRecord& rhs) 69 | { 70 | //Serial.println("NdefRecord ASSIGN"); 71 | 72 | if (this != &rhs) 73 | { 74 | // free existing 75 | if (_typeLength) 76 | { 77 | free(_type); 78 | } 79 | 80 | if (_payloadLength) 81 | { 82 | free(_payload); 83 | } 84 | 85 | if (_idLength) 86 | { 87 | free(_id); 88 | } 89 | 90 | _tnf = rhs._tnf; 91 | _typeLength = rhs._typeLength; 92 | _payloadLength = rhs._payloadLength; 93 | _idLength = rhs._idLength; 94 | 95 | if (_typeLength) 96 | { 97 | _type = (uint8_t*)malloc(_typeLength); 98 | memcpy(_type, rhs._type, _typeLength); 99 | } 100 | 101 | if (_payloadLength) 102 | { 103 | _payload = (uint8_t*)malloc(_payloadLength); 104 | memcpy(_payload, rhs._payload, _payloadLength); 105 | } 106 | 107 | if (_idLength) 108 | { 109 | _id = (uint8_t*)malloc(_idLength); 110 | memcpy(_id, rhs._id, _idLength); 111 | } 112 | } 113 | return *this; 114 | } 115 | 116 | // size of records in bytes 117 | int NdefRecord::getEncodedSize() 118 | { 119 | int size = 2; // tnf + typeLength 120 | if (_payloadLength > 0xFF) 121 | { 122 | size += 4; 123 | } 124 | else 125 | { 126 | size += 1; 127 | } 128 | 129 | if (_idLength) 130 | { 131 | size += 1; 132 | } 133 | 134 | size += (_typeLength + _payloadLength + _idLength); 135 | 136 | return size; 137 | } 138 | 139 | void NdefRecord::encode(uint8_t* data, bool firstRecord, bool lastRecord) 140 | { 141 | // assert data > getEncodedSize() 142 | 143 | uint8_t* data_ptr = &data[0]; 144 | 145 | *data_ptr = getTnfByte(firstRecord, lastRecord); 146 | data_ptr += 1; 147 | 148 | *data_ptr = _typeLength; 149 | data_ptr += 1; 150 | 151 | *data_ptr = _payloadLength; // TODO handle sr == false 152 | data_ptr += 1; 153 | if (_idLength) 154 | { 155 | *data_ptr = _idLength; 156 | data_ptr += 1; 157 | } 158 | 159 | //Serial.println(2); 160 | memcpy(data_ptr, _type, _typeLength); 161 | data_ptr += _typeLength; 162 | 163 | memcpy(data_ptr, _payload, _payloadLength); 164 | data_ptr += _payloadLength; 165 | 166 | if (_idLength) 167 | { 168 | memcpy(data_ptr, _id, _idLength); 169 | data_ptr += _idLength; 170 | } 171 | } 172 | 173 | uint8_t NdefRecord::getTnfByte(bool firstRecord, bool lastRecord) 174 | { 175 | int value = _tnf; 176 | 177 | if (firstRecord) { // mb 178 | value = value | 0x80; 179 | } 180 | 181 | if (lastRecord) { // 182 | value = value | 0x40; 183 | } 184 | 185 | // chunked flag is always false for now 186 | // if (cf) { 187 | // value = value | 0x20; 188 | // } 189 | 190 | if (_typeLength <= 0xFF) { // TODO test 0xFF on tag 191 | value = value | 0x10; 192 | } 193 | 194 | if (_idLength) { 195 | value = value | 0x8; 196 | } 197 | 198 | return value; 199 | } 200 | 201 | uint8_t NdefRecord::getTnf() 202 | { 203 | return _tnf; 204 | } 205 | 206 | void NdefRecord::setTnf(uint8_t tnf) 207 | { 208 | _tnf = tnf; 209 | } 210 | 211 | uint8_t NdefRecord::getTypeLength() 212 | { 213 | return _typeLength; 214 | } 215 | 216 | int NdefRecord::getPayloadLength() 217 | { 218 | return _payloadLength; 219 | } 220 | 221 | uint8_t NdefRecord::getIdLength() 222 | { 223 | return _idLength; 224 | } 225 | 226 | String NdefRecord::getType() 227 | { 228 | // TODO is there a simpler way to do this? 229 | char* type = (char*)malloc(_typeLength); 230 | memset(type, 0, sizeof(type)); 231 | memcpy(type, _type, _typeLength); 232 | String typeString = String(type); 233 | free(type); 234 | return typeString; 235 | } 236 | 237 | // this assumes the caller created type correctly 238 | void NdefRecord::getType(uint8_t* type) 239 | { 240 | memcpy(type, _type, _typeLength); 241 | } 242 | 243 | void NdefRecord::setType(const uint8_t * type, const uint8_t numBytes) 244 | { 245 | if(_typeLength) 246 | { 247 | free(_type); 248 | } 249 | 250 | _type = (uint8_t*)malloc(numBytes); 251 | memcpy(_type, type, numBytes); 252 | _typeLength = numBytes; 253 | } 254 | 255 | // caller is responsible for deleting array 256 | uint8_t* NdefRecord::getPayload() 257 | { 258 | uint8_t* payload = (uint8_t*)malloc(_payloadLength); 259 | memcpy(payload, _payload, _payloadLength); 260 | return payload; 261 | } 262 | 263 | // assumes the caller sized payload properly 264 | void NdefRecord::getPayload(uint8_t* payload) 265 | { 266 | memcpy(payload, _payload, _payloadLength); 267 | } 268 | 269 | void NdefRecord::setPayload(const uint8_t * payload, const int numBytes) 270 | { 271 | if (_payloadLength) 272 | { 273 | free(_payload); 274 | } 275 | 276 | _payload = (uint8_t*)malloc(numBytes); 277 | memcpy(_payload, payload, numBytes); 278 | _payloadLength = numBytes; 279 | } 280 | 281 | String NdefRecord::getId() 282 | { 283 | // TODO is there a simpler way to do this? 284 | char* id = (char*)malloc(_idLength); 285 | memset(id, 0, sizeof(id)); 286 | memcpy(id, _id, _idLength); 287 | String idString = String(id); 288 | free(id); 289 | return idString; 290 | } 291 | 292 | void NdefRecord::getId(uint8_t * id) 293 | { 294 | memcpy(id, _id, _idLength); 295 | } 296 | 297 | void NdefRecord::setId(const uint8_t * id, const uint8_t numBytes) 298 | { 299 | if (_idLength) 300 | { 301 | free(_id); 302 | } 303 | 304 | _id = (uint8_t*)malloc(numBytes); 305 | memcpy(_id, id, numBytes); 306 | _idLength = numBytes; 307 | } 308 | 309 | /* 310 | void NdefRecord::print() 311 | { 312 | Serial.println(" NDEF Record"); 313 | Serial.print(" TNF 0x");Serial.println(_tnf, HEX); 314 | Serial.print(" Type Length 0x");Serial.println(_typeLength, HEX); 315 | Serial.print(" Payload Length 0x");Serial.println(_payloadLength, HEX); 316 | if (_idLength) 317 | { 318 | Serial.print(" Id Length 0x");Serial.println(_idLength, HEX); 319 | } 320 | Serial.print(" Type ");PrintHex(_type, _typeLength); 321 | Serial.print(" Payload ");PrintHex(_payload, _payloadLength); 322 | if (_idLength) 323 | { 324 | Serial.print(" Id ");PrintHex(_id, _idLength); 325 | } 326 | Serial.print(" Record is ");Serial.print(getEncodedSize());Serial.println(" bytes"); 327 | 328 | } 329 | */ 330 | 331 | void NdefRecord::print() 332 | { 333 | Serial.println(F(" NDEF Record")); 334 | Serial.print(F(" TNF 0x"));Serial.print(_tnf, HEX);Serial.print(" "); 335 | switch (_tnf) { 336 | case TNF_EMPTY: 337 | Serial.println(F("Empty")); 338 | break; 339 | case TNF_WELL_KNOWN: 340 | Serial.println(F("Well Known")); 341 | break; 342 | case TNF_MIME_MEDIA: 343 | Serial.println(F("Mime Media")); 344 | break; 345 | case TNF_ABSOLUTE_URI: 346 | Serial.println(F("Absolute URI")); 347 | break; 348 | case TNF_EXTERNAL_TYPE: 349 | Serial.println(F("External")); 350 | break; 351 | case TNF_UNKNOWN: 352 | Serial.println(F("Unknown")); 353 | break; 354 | case TNF_UNCHANGED: 355 | Serial.println(F("Unchanged")); 356 | break; 357 | case TNF_RESERVED: 358 | Serial.println(F("Reserved")); 359 | break; 360 | default: 361 | Serial.println(); 362 | } 363 | Serial.print(F(" Type Length 0x"));Serial.print(_typeLength, HEX);Serial.print(" ");Serial.println(_typeLength); 364 | Serial.print(F(" Payload Length 0x"));Serial.print(_payloadLength, HEX);;Serial.print(" ");Serial.println(_payloadLength); 365 | if (_idLength) 366 | { 367 | Serial.print(F(" Id Length 0x"));Serial.println(_idLength, HEX); 368 | } 369 | Serial.print(F(" Type "));PrintHexChar(_type, _typeLength); 370 | // TODO chunk large payloads so this is readable 371 | Serial.print(F(" Payload "));PrintHexChar(_payload, _payloadLength); 372 | if (_idLength) 373 | { 374 | Serial.print(F(" Id "));PrintHexChar(_id, _idLength); 375 | } 376 | Serial.print(F(" Record is "));Serial.print(getEncodedSize());Serial.println(" bytes"); 377 | 378 | } -------------------------------------------------------------------------------- /NdefRecord.h: -------------------------------------------------------------------------------- 1 | #ifndef NdefRecord_h 2 | #define NdefRecord_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define TNF_EMPTY 0x0 9 | #define TNF_WELL_KNOWN 0x01 10 | #define TNF_MIME_MEDIA 0x02 11 | #define TNF_ABSOLUTE_URI 0x03 12 | #define TNF_EXTERNAL_TYPE 0x04 13 | #define TNF_UNKNOWN 0x05 14 | #define TNF_UNCHANGED 0x06 15 | #define TNF_RESERVED 0x07 16 | 17 | class NdefRecord 18 | { 19 | public: 20 | NdefRecord(); 21 | NdefRecord(const NdefRecord& rhs); 22 | ~NdefRecord(); 23 | NdefRecord& operator=(const NdefRecord& rhs); 24 | int getEncodedSize(); 25 | void encode(uint8_t* data, bool firstRecord, bool lastRecord); 26 | 27 | uint8_t getTnf(); 28 | 29 | uint8_t getTypeLength(); 30 | int getPayloadLength(); 31 | uint8_t getIdLength(); 32 | 33 | String getType(); 34 | uint8_t* getPayload(); 35 | String getId(); 36 | 37 | void getType(uint8_t* type); 38 | void getPayload(uint8_t* payload); 39 | void getId(uint8_t* id); 40 | 41 | void setTnf(uint8_t tnf); 42 | void setType(const uint8_t * type, const uint8_t numBytes); 43 | void setPayload(const uint8_t * payload, const int numBytes); 44 | void setId(const uint8_t * id, const uint8_t numBytes); 45 | 46 | void print(); 47 | private: 48 | uint8_t getTnfByte(bool firstRecord, bool lastRecord); 49 | uint8_t _tnf; // 3 bit 50 | uint8_t _typeLength; 51 | int _payloadLength; 52 | uint8_t _idLength; 53 | uint8_t * _type; 54 | uint8_t * _payload; 55 | uint8_t * _id; 56 | }; 57 | 58 | #endif -------------------------------------------------------------------------------- /NfcTag.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NfcTag::NfcTag() 4 | { 5 | _uid = 0; 6 | _uidLength = 0; 7 | _tagType = "Unknown"; 8 | _ndefMessage = (NdefMessage*)NULL; 9 | } 10 | 11 | NfcTag::NfcTag(uint8_t* uid, uint8_t uidLength) 12 | { 13 | _uid = uid; 14 | _uidLength = uidLength; 15 | _tagType = "Unknown"; 16 | _ndefMessage = (NdefMessage*)NULL; 17 | } 18 | 19 | NfcTag::NfcTag(uint8_t* uid, uint8_t uidLength, String tagType) 20 | { 21 | _uid = uid; 22 | _uidLength = uidLength; 23 | _tagType = tagType; 24 | _ndefMessage = (NdefMessage*)NULL; 25 | } 26 | 27 | NfcTag::NfcTag(uint8_t* uid, uint8_t uidLength, String tagType, NdefMessage& ndefMessage) 28 | { 29 | _uid = uid; 30 | _uidLength = uidLength; 31 | _tagType = tagType; 32 | _ndefMessage = new NdefMessage(ndefMessage); 33 | } 34 | 35 | // I don't like this version, but it will use less memory 36 | NfcTag::NfcTag(uint8_t* uid, uint8_t uidLength, String tagType, const uint8_t * ndefData, const int ndefDataLength) 37 | { 38 | _uid = uid; 39 | _uidLength = uidLength; 40 | _tagType = tagType; 41 | _ndefMessage = new NdefMessage(ndefData, ndefDataLength); 42 | } 43 | 44 | NfcTag::~NfcTag() 45 | { 46 | delete _ndefMessage; 47 | } 48 | 49 | NfcTag& NfcTag::operator=(const NfcTag& rhs) 50 | { 51 | if (this != &rhs) 52 | { 53 | delete _ndefMessage; 54 | _uid = rhs._uid; 55 | _uidLength = rhs._uidLength; 56 | _tagType = rhs._tagType; 57 | // TODO do I need a copy here? 58 | _ndefMessage = rhs._ndefMessage; 59 | } 60 | return *this; 61 | } 62 | 63 | uint8_t NfcTag::getUidLength() 64 | { 65 | return _uidLength; 66 | } 67 | 68 | void NfcTag::getUid(uint8_t* uid, uint8_t uidLength) 69 | { 70 | memcpy(_uid, uid, uidLength); 71 | } 72 | 73 | String NfcTag::getUidString() 74 | { 75 | String uidString = ""; 76 | for (int i = 0; i < _uidLength; i++) 77 | { 78 | if (i > 0) 79 | { 80 | uidString += " "; 81 | } 82 | 83 | if (_uid[i] < 0xF) 84 | { 85 | uidString += "0"; 86 | } 87 | 88 | uidString += String(_uid[i], HEX); 89 | } 90 | uidString.toUpperCase(); 91 | return uidString; 92 | } 93 | 94 | String NfcTag::getTagType() 95 | { 96 | return _tagType; 97 | } 98 | 99 | boolean NfcTag::hasNdefMessage() 100 | { 101 | return (_ndefMessage != NULL); 102 | } 103 | 104 | NdefMessage NfcTag::getNdefMessage() 105 | { 106 | return *_ndefMessage; 107 | } 108 | 109 | void NfcTag::print() 110 | { 111 | Serial.print(F("NFC Tag - "));Serial.println(_tagType); 112 | Serial.print(F("UID "));Serial.println(getUidString()); 113 | if (_ndefMessage == NULL) 114 | { 115 | Serial.println(F("\nNo NDEF Message")); 116 | } 117 | else 118 | { 119 | _ndefMessage->print(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /NfcTag.h: -------------------------------------------------------------------------------- 1 | #ifndef NfcTag_h 2 | #define NfcTag_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class NfcTag 9 | { 10 | public: 11 | NfcTag(); 12 | NfcTag(uint8_t* uid, uint8_t uidLength); 13 | NfcTag(uint8_t* uid, uint8_t uidLength, String tagType); 14 | NfcTag(uint8_t* uid, uint8_t uidLength, String tagType, NdefMessage& ndefMessage); 15 | NfcTag(uint8_t* uid, uint8_t uidLength, String tagType, const uint8_t * ndefData, const int ndefDataLength); 16 | ~NfcTag(void); 17 | NfcTag& operator=(const NfcTag& rhs); 18 | uint8_t getUidLength(); 19 | void getUid(uint8_t* uid, uint8_t uidLength); 20 | String getUidString(); 21 | String getTagType(); 22 | boolean hasNdefMessage(); 23 | NdefMessage getNdefMessage(); 24 | void print(); 25 | private: 26 | uint8_t* _uid; 27 | uint8_t _uidLength; 28 | String _tagType; // Mifare Classic, NFC Forum Type {1,2,3,4}, Unknown 29 | NdefMessage* _ndefMessage; 30 | // TODO capacity 31 | // TODO isFormatted 32 | }; 33 | 34 | #endif -------------------------------------------------------------------------------- /PN5321.cpp: -------------------------------------------------------------------------------- 1 | // PN532 library by adafruit/ladyada 2 | // MIT license 3 | 4 | // authenticateBlock, readMemoryBlock, writeMemoryBlock contributed 5 | // by Seeed Technology Inc (www.seeedstudio.com) 6 | 7 | #include "PN5321.h" 8 | 9 | #include "debug.h" 10 | 11 | uint8_t pn532ack[] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}; 12 | uint8_t pn532response_firmwarevers[] = {0x00, 0xFF, 0x06, 0xFA, 0xD5, 0x03}; 13 | 14 | #define COMMAND_RESPONSE_SIZE 3 15 | #define TS_GET_DATA_IN_MAX_SIZE 262 + 3 16 | 17 | uint8_t pn532_packetbuffer[TS_GET_DATA_IN_MAX_SIZE]; 18 | 19 | PN532::PN532(HardwareSerial &serial) 20 | { 21 | _serial = &serial; 22 | command = 0; 23 | } 24 | 25 | void PN532::initializeReader() 26 | { 27 | _serial -> begin(115200); 28 | wakeup(); 29 | 30 | getFirmwareVersion(); 31 | } 32 | 33 | void PN532::wakeup() 34 | { 35 | _serial->write(0x55); 36 | _serial->write(0x55); 37 | _serial->write(0); 38 | _serial->write(0); 39 | _serial->write(0); 40 | 41 | /** dump serial buffer */ 42 | if(_serial->available()){ 43 | DMSG("Dump serial buffer: "); 44 | } 45 | while(_serial->available()){ 46 | uint8_t ret = _serial->read(); 47 | DMSG_HEX(ret); 48 | } 49 | } 50 | 51 | int8_t PN532::receive(uint8_t *buf, int len, uint16_t timeout) 52 | { 53 | int read_bytes = 0; 54 | int ret; 55 | unsigned long start_millis; 56 | 57 | while (read_bytes < len) { 58 | start_millis = millis(); 59 | do { 60 | ret = _serial->read(); 61 | if (ret >= 0) { 62 | break; 63 | } 64 | } while( (millis()- start_millis ) < timeout || !timeout); 65 | 66 | if (ret < 0) { 67 | if(read_bytes){ 68 | return read_bytes; 69 | }else{ 70 | return -1; 71 | } 72 | } 73 | buf[read_bytes] = (uint8_t)ret; 74 | DMSG_HEX(ret); 75 | read_bytes++; 76 | } 77 | return read_bytes; 78 | } 79 | 80 | int8_t PN532::readAckFrame(uint16_t timeout) 81 | { 82 | const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; 83 | uint8_t ackBuf[sizeof(PN532_ACK)]; 84 | 85 | DMSG("Read Ack\n"); 86 | 87 | if( receive(ackBuf, sizeof(PN532_ACK), timeout) <= 0 ){ 88 | DMSG("Read ACK Timeout\n"); 89 | return SEND_COMMAND_RX_TIMEOUT_ERROR; 90 | } 91 | 92 | if( memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)) ){ 93 | DMSG("Invalid ACK\n"); 94 | return SEND_COMMAND_RX_ACK_ERROR; 95 | } 96 | return RESULT_SUCCESS; 97 | } 98 | 99 | // default timeout of one second 100 | uint32_t PN532::sendCommandCheckAck(uint8_t *cmd, 101 | uint8_t cmdlen, 102 | uint16_t timeout) 103 | { 104 | 105 | /** dump serial buffer */ 106 | if(_serial->available()){ 107 | DMSG("Dump serial buffer: "); 108 | } 109 | while(_serial->available()){ 110 | uint8_t ret = _serial->read(); 111 | DMSG_HEX(ret); 112 | } 113 | 114 | command = cmd[0]; 115 | 116 | _serial->write(PN532_PREAMBLE); 117 | _serial->write(PN532_STARTCODE1); 118 | _serial->write(PN532_STARTCODE2); 119 | 120 | uint8_t length = cmdlen + 1; // length of data field: TFI + DATA 121 | _serial->write(length); 122 | _serial->write(~length + 1); // checksum of length 123 | 124 | _serial->write(PN532_HOSTTOPN532); 125 | uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA 126 | 127 | _serial->write(cmd, cmdlen); 128 | for (uint8_t i = 0; i < cmdlen; i++) { 129 | sum += cmd[i]; 130 | } 131 | 132 | uint8_t checksum = ~sum + 1; // checksum of TFI + DATA 133 | _serial->write(checksum); 134 | _serial->write(PN532_POSTAMBLE); 135 | 136 | return readAckFrame(timeout); 137 | } 138 | 139 | uint32_t PN532::readspicommand(uint8_t cmdCode, PN532_CMD_RESPONSE *response, uint16_t timeout) 140 | { 141 | uint8_t tmp[3]; 142 | 143 | DMSG("Read response: "); 144 | 145 | /** Frame Preamble and Start Code */ 146 | if(receive(tmp, 3, timeout)<=0){ 147 | return SEND_COMMAND_RX_TIMEOUT_ERROR; 148 | } 149 | if(0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]){ 150 | DMSG("Preamble error"); 151 | return INVALID_RESPONSE; 152 | } 153 | response->header[0] = tmp[1]; 154 | response->header[1] = tmp[2]; 155 | 156 | 157 | /** receive length and check */ 158 | uint8_t length[2]; 159 | if(receive(length, 2, timeout) <= 0){ 160 | return SEND_COMMAND_RX_TIMEOUT_ERROR; 161 | } 162 | if( 0 != (uint8_t)(length[0] + length[1]) ){ 163 | DMSG("Length error"); 164 | return INVALID_RESPONSE; 165 | } 166 | response->len = length[0]; 167 | response->len_chksum = length[1]; 168 | response->data_len = length[0] - 2; 169 | 170 | /** receive command byte */ 171 | uint8_t cmd = cmdCode+1; // response command 172 | if(receive(tmp, 2, timeout) <= 0){ 173 | return SEND_COMMAND_RX_TIMEOUT_ERROR; 174 | } 175 | if( PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]){ 176 | DMSG("Command error"); 177 | return INVALID_RESPONSE; 178 | } 179 | response->direction = tmp[0]; 180 | response->responseCode = tmp[1]; 181 | 182 | if(receive(response->data, response->data_len, timeout) != response->data_len){ 183 | return SEND_COMMAND_RX_TIMEOUT_ERROR; 184 | } 185 | uint8_t sum = PN532_PN532TOHOST + cmd; 186 | for(uint8_t i=0; idata_len; i++){ 187 | sum += response->data[i]; 188 | } 189 | 190 | /** checksum and postamble */ 191 | if(receive(tmp, 2, timeout) <= 0){ 192 | return SEND_COMMAND_RX_TIMEOUT_ERROR; 193 | } 194 | if( 0 != (uint8_t)(sum + tmp[0]) || 0 != tmp[1] ){ 195 | DMSG("Checksum error"); 196 | return INVALID_POSTAMBLE; 197 | } 198 | 199 | DMSG("\n"); 200 | 201 | return RESULT_SUCCESS; 202 | } 203 | 204 | /****************************************************************************/ 205 | uint32_t PN532::getFirmwareVersion(void) 206 | { 207 | uint32_t version; 208 | 209 | DMSG("Get Version\n"); 210 | 211 | pn532_packetbuffer[0] = PN532_FIRMWAREVERSION; 212 | 213 | if (IS_ERROR(sendCommandCheckAck(pn532_packetbuffer, 1))) 214 | { 215 | DMSG("Ack Failed\n"); 216 | return 0; 217 | } 218 | 219 | // read response Packet 220 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 221 | if (IS_ERROR(readspicommand(PN532_FIRMWAREVERSION, response))) 222 | { 223 | DMSG("Response Failed\n"); 224 | return 0; 225 | } 226 | 227 | //response->printResponse(); 228 | 229 | version = response->data[0]; 230 | version <<= 8; 231 | version |= response->data[1]; 232 | version <<= 8; 233 | version |= response->data[2]; 234 | version <<= 8; 235 | version |= response->data[3]; 236 | 237 | return version; 238 | } 239 | 240 | uint32_t PN532::SAMConfig(void) 241 | { 242 | pn532_packetbuffer[0] = PN532_SAMCONFIGURATION; 243 | pn532_packetbuffer[1] = 0x01; // normal mode; 244 | pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second 245 | pn532_packetbuffer[3] = 0x01; // use IRQ pin! 246 | 247 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 4); 248 | 249 | if (IS_ERROR(result)) 250 | { 251 | return result; 252 | } 253 | 254 | // read data packet 255 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 256 | return readspicommand(PN532_SAMCONFIGURATION, response); 257 | } 258 | 259 | uint32_t PN532::configurePeerAsInitiator(uint8_t baudrate) 260 | { 261 | // Does not support 106 262 | if (baudrate != NFC_READER_CFG_BAUDRATE_201_KPS && baudrate != NFC_READER_CFG_BAUDRATE_424_KPS) 263 | { 264 | return CONFIGURE_HARDWARE_ERROR; 265 | } 266 | 267 | pn532_packetbuffer[0] = PN532_INJUMPFORDEP; 268 | pn532_packetbuffer[1] = 0x01; //Active Mode 269 | pn532_packetbuffer[2] = baudrate; // Use 1 or 2. //0 i.e 106kps is not supported yet 270 | pn532_packetbuffer[3] = 0x01; //Indicates Optional Payload is present 271 | 272 | //Polling request payload 273 | pn532_packetbuffer[4] = 0x00; 274 | pn532_packetbuffer[5] = 0xFF; 275 | pn532_packetbuffer[6] = 0xFF; 276 | pn532_packetbuffer[7] = 0x00; 277 | pn532_packetbuffer[8] = 0x00; 278 | 279 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 9); 280 | 281 | if (IS_ERROR(result)) 282 | { 283 | return result; 284 | } 285 | 286 | // read data packet 287 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 288 | result = readspicommand(PN532_INJUMPFORDEP, response); 289 | if (IS_ERROR(result)) 290 | { 291 | return result; 292 | } 293 | 294 | if (response->data[0] != 0x00) 295 | { 296 | return (GEN_ERROR | response->data[0]); 297 | } 298 | 299 | return RESULT_SUCCESS; //No error 300 | } 301 | 302 | 303 | uint32_t PN532::initiatorTxRxData(uint8_t *DataOut, 304 | uint32_t dataSize, 305 | uint8_t *DataIn) 306 | { 307 | pn532_packetbuffer[0] = PN532_INDATAEXCHANGE; 308 | pn532_packetbuffer[1] = 0x01; //Target 01 309 | 310 | for(uint8_t iter=(2+0);iter<(2+dataSize);iter++) 311 | { 312 | pn532_packetbuffer[iter] = DataOut[iter-2]; //pack the data to send to target 313 | } 314 | 315 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, dataSize+2); 316 | 317 | if (IS_ERROR(result)) 318 | { 319 | return result; 320 | } 321 | 322 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 323 | if (!readspicommand(PN532_INDATAEXCHANGE, response)) 324 | { 325 | return false; 326 | } 327 | 328 | #if IS_DEBUG 329 | response->printResponse(); 330 | #endif 331 | 332 | if (response->data[0] != 0x00) 333 | { 334 | return (GEN_ERROR | response->data[0]); 335 | } 336 | return RESULT_SUCCESS; //No error 337 | } 338 | 339 | uint32_t PN532::configurePeerAsTarget(uint8_t type) 340 | { 341 | static const uint8_t command[] = { PN532_TGINITASTARGET, 342 | 0, 343 | 0x00, 0x00, //SENS_RES 344 | 0x00, 0x00, 0x00, //NFCID1 345 | 0x40, //SEL_RES 346 | 347 | 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, // POL_RES 348 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 349 | 0xFF, 0xFF, 350 | 351 | 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, 0x00, 0x00, //NFCID3t: Change this to desired value 352 | 353 | 0x06, 0x46, 0x66, 0x6D, 0x01, 0x01, 0x10, 0x00// LLCP magic number and version parameter 354 | }; 355 | 356 | 357 | uint32_t result; 358 | result = sendCommandCheckAck((uint8_t *)command, sizeof(command)); 359 | 360 | if (IS_ERROR(result)) 361 | { 362 | return result; 363 | } 364 | 365 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 366 | return readspicommand(PN532_TGINITASTARGET, response); 367 | } 368 | 369 | uint32_t PN532::getTargetStatus(uint8_t *DataIn) 370 | { 371 | pn532_packetbuffer[0] = PN532_TGTARGETSTATUS; 372 | 373 | if (IS_ERROR(sendCommandCheckAck(pn532_packetbuffer, 1))) { 374 | return 0; 375 | } 376 | 377 | // read data packet 378 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 379 | if (RESULT_OK(readspicommand(PN532_TGTARGETSTATUS, response))) 380 | { 381 | memcpy(DataIn, response->data, response->data_len); 382 | return response->data_len; 383 | } 384 | 385 | return 0; 386 | } 387 | 388 | uint32_t PN532::targetRxData(uint8_t *DataIn) 389 | { 390 | ///////////////////////////////////// Receiving from Initiator /////////////////////////// 391 | pn532_packetbuffer[0] = PN532_TGGETDATA; 392 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 1, 1000); 393 | if (IS_ERROR(result)) { 394 | //Serial.println(F("SendCommandCheck Ack Failed")); 395 | return NFC_READER_COMMAND_FAILURE; 396 | } 397 | 398 | // read data packet 399 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 400 | 401 | result = readspicommand(PN532_TGGETDATA, response); 402 | 403 | if (IS_ERROR(result)) 404 | { 405 | return NFC_READER_RESPONSE_FAILURE; 406 | } 407 | 408 | if (response->data[0] == 0x00) 409 | { 410 | uint32_t ret_len = response->data_len - 1; 411 | memcpy(DataIn, &(response->data[1]), ret_len); 412 | return ret_len; 413 | } 414 | 415 | return (GEN_ERROR | response->data[0]); 416 | } 417 | 418 | uint32_t PN532::targetRxData(uint8_t *DataIn, uint32_t DataSize) 419 | { 420 | ///////////////////////////////////// Receiving from Initiator /////////////////////////// 421 | pn532_packetbuffer[0] = PN532_TGGETDATA; 422 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 1, 1000); 423 | if (IS_ERROR(result)) { 424 | //Serial.println(F("SendCommandCheck Ack Failed")); 425 | return NFC_READER_COMMAND_FAILURE; 426 | } 427 | 428 | // read data packet 429 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 430 | 431 | result = readspicommand(PN532_TGGETDATA, response); 432 | 433 | if (IS_ERROR(result)) 434 | { 435 | return NFC_READER_RESPONSE_FAILURE; 436 | } 437 | 438 | if (response->data[0] == 0x00) 439 | { 440 | uint32_t ret_len = response->data_len - 1; 441 | if (ret_len > DataSize) { 442 | ret_len = DataSize; 443 | } 444 | memcpy(DataIn, &(response->data[1]), ret_len); 445 | return ret_len; 446 | } 447 | 448 | return (GEN_ERROR | response->data[0]); 449 | } 450 | 451 | 452 | 453 | uint32_t PN532::targetTxData(uint8_t *DataOut, uint32_t dataSize) 454 | { 455 | ///////////////////////////////////// Sending to Initiator /////////////////////////// 456 | pn532_packetbuffer[0] = PN532_TGSETDATA; 457 | uint8_t commandBufferSize = (1 + dataSize); 458 | for(uint8_t iter=(1+0);iter < commandBufferSize; ++iter) 459 | { 460 | pn532_packetbuffer[iter] = DataOut[iter-1]; //pack the data to send to target 461 | } 462 | 463 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, commandBufferSize, 1000); 464 | if (IS_ERROR(result)) { 465 | DMSG(F("TX_Target Command Failed.")); 466 | 467 | return result; 468 | } 469 | 470 | 471 | // read data packet 472 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 473 | 474 | result = readspicommand(PN532_TGSETDATA, response); 475 | if (IS_ERROR(result)) 476 | { 477 | return result; 478 | } 479 | 480 | if (response->data[0] != 0x00) 481 | { 482 | return (GEN_ERROR | response->data[0]); 483 | } 484 | return RESULT_SUCCESS; //No error 485 | } 486 | 487 | 488 | uint32_t PN532::authenticateBlock(uint8_t cardnumber /*1 or 2*/, 489 | uint32_t cid /*Card NUID*/, 490 | uint8_t blockaddress /*0 to 63*/, 491 | uint8_t authtype/*Either KEY_A or KEY_B */, 492 | uint8_t * keys) 493 | { 494 | pn532_packetbuffer[0] = PN532_INDATAEXCHANGE; 495 | pn532_packetbuffer[1] = cardnumber; // either card 1 or 2 (tested for card 1) 496 | if(authtype == KEY_A) 497 | { 498 | pn532_packetbuffer[2] = PN532_AUTH_WITH_KEYA; 499 | } 500 | else 501 | { 502 | pn532_packetbuffer[2] = PN532_AUTH_WITH_KEYB; 503 | } 504 | pn532_packetbuffer[3] = blockaddress; //This address can be 0-63 for MIFARE 1K card 505 | 506 | pn532_packetbuffer[4] = keys[0]; 507 | pn532_packetbuffer[5] = keys[1]; 508 | pn532_packetbuffer[6] = keys[2]; 509 | pn532_packetbuffer[7] = keys[3]; 510 | pn532_packetbuffer[8] = keys[4]; 511 | pn532_packetbuffer[9] = keys[5]; 512 | 513 | pn532_packetbuffer[10] = ((cid >> 24) & 0xFF); 514 | pn532_packetbuffer[11] = ((cid >> 16) & 0xFF); 515 | pn532_packetbuffer[12] = ((cid >> 8) & 0xFF); 516 | pn532_packetbuffer[13] = ((cid >> 0) & 0xFF); 517 | 518 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 14); 519 | 520 | if (IS_ERROR(result)) 521 | { 522 | return result; 523 | } 524 | 525 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 526 | result = readspicommand(PN532_INDATAEXCHANGE, response); 527 | if (IS_ERROR(result)) 528 | { 529 | return result; 530 | } 531 | 532 | 533 | if((response->data[0] == 0x41) && (response->data[1] == 0x00)) 534 | { 535 | return RESULT_SUCCESS; 536 | } 537 | 538 | return (GEN_ERROR | response->data[1]); 539 | } 540 | 541 | uint32_t PN532::readMemoryBlock(uint8_t cardnumber /*1 or 2*/, 542 | uint8_t blockaddress /*0 to 63*/, 543 | uint8_t * block) 544 | { 545 | pn532_packetbuffer[0] = PN532_INDATAEXCHANGE; 546 | pn532_packetbuffer[1] = cardnumber; // either card 1 or 2 (tested for card 1) 547 | pn532_packetbuffer[2] = PN532_MIFARE_READ; 548 | pn532_packetbuffer[3] = blockaddress; //This address can be 0-63 for MIFARE 1K card 549 | 550 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 4); 551 | 552 | if (IS_ERROR(result)) { 553 | return result; 554 | } 555 | 556 | // read data packet 557 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 558 | result = readspicommand(PN532_INDATAEXCHANGE, response); 559 | if (IS_ERROR(result)) 560 | { 561 | return result; 562 | } 563 | 564 | if((response->data[0] == 0x41) && (response->data[1] == 0x00)) 565 | { 566 | return RESULT_SUCCESS; //read successful 567 | } 568 | 569 | return (GEN_ERROR | response->data[1]); 570 | } 571 | 572 | //Do not write to Sector Trailer Block unless you know what you are doing. 573 | uint32_t PN532::writeMemoryBlock(uint8_t cardnumber /*1 or 2*/, 574 | uint8_t blockaddress /*0 to 63*/, 575 | uint8_t * block) 576 | { 577 | pn532_packetbuffer[0] = PN532_INDATAEXCHANGE; 578 | pn532_packetbuffer[1] = cardnumber; // either card 1 or 2 (tested for card 1) 579 | pn532_packetbuffer[2] = PN532_MIFARE_WRITE; 580 | pn532_packetbuffer[3] = blockaddress; 581 | 582 | for(uint8_t i =0; i <16; i++) 583 | { 584 | pn532_packetbuffer[4+i] = block[i]; 585 | } 586 | 587 | uint32_t result = sendCommandCheckAck(pn532_packetbuffer, 20); 588 | 589 | if (IS_ERROR(result)) 590 | { 591 | return result; 592 | } 593 | 594 | // read data packet 595 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 596 | result = readspicommand(PN532_INDATAEXCHANGE, response); 597 | if (IS_ERROR(result)) 598 | { 599 | return result; 600 | } 601 | 602 | if((response->data[0] == 0x41) && (response->data[1] == 0x00)) 603 | { 604 | return RESULT_SUCCESS; //read successful 605 | } 606 | 607 | return (GEN_ERROR | response->data[1]); 608 | } 609 | 610 | uint32_t PN532::readPassiveTargetID(uint8_t cardbaudrate) 611 | { 612 | uint32_t cid; 613 | 614 | pn532_packetbuffer[0] = PN532_INLISTPASSIVETARGET; 615 | pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) 616 | pn532_packetbuffer[2] = cardbaudrate; 617 | 618 | if (IS_ERROR(sendCommandCheckAck(pn532_packetbuffer, 3))) 619 | { 620 | return 0; // no cards read 621 | } 622 | 623 | // read data packet 624 | PN532_CMD_RESPONSE *response = (PN532_CMD_RESPONSE *) pn532_packetbuffer; 625 | if (IS_ERROR(readspicommand(PN532_INDATAEXCHANGE, response))) 626 | { 627 | return 0; 628 | } 629 | 630 | // check some basic stuff 631 | Serial.print(F("Found ")); 632 | Serial.print(response->data[2], DEC); 633 | Serial.println(F(" tags")); 634 | 635 | if (response->data[2] != 1) 636 | { 637 | return 0; 638 | } 639 | 640 | uint16_t sens_res = response->data[4]; 641 | sens_res <<= 8; 642 | sens_res |= response->data[5]; 643 | 644 | Serial.print(F("Sens Response: 0x")); 645 | Serial.println(sens_res, HEX); 646 | Serial.print(F("Sel Response: 0x")); 647 | Serial.println(response->data[6], HEX); 648 | 649 | cid = 0; 650 | for (uint8_t i = 0; i < response->data[7]; i++) 651 | { 652 | cid <<= 8; 653 | cid |= response->data[8 + i]; 654 | Serial.print(F(" 0x")); 655 | Serial.print(response->data[8 + i], HEX); 656 | } 657 | 658 | return cid; 659 | } 660 | 661 | 662 | inline boolean PN532::isTargetReleasedError(uint32_t result) 663 | { 664 | return result == (GEN_ERROR | TARGET_RELEASED_ERROR); 665 | } 666 | 667 | boolean PN532_CMD_RESPONSE::verifyResponse(uint32_t cmdCode) 668 | { 669 | return ( header[0] == 0x00 && 670 | header[1] == 0xFF && 671 | ((uint8_t)(len + len_chksum)) == 0x00 && 672 | direction == 0xD5 && 673 | (cmdCode + 1) == responseCode); 674 | } 675 | 676 | void PN532_CMD_RESPONSE::printResponse() 677 | { 678 | Serial.print("response: "); 679 | 680 | uint8_t *ptr = (uint8_t *) this; 681 | for (uint8_t i = 0; i < sizeof(PN532_CMD_RESPONSE); i++) 682 | { 683 | if ((ptr + i) == &data_len) 684 | { 685 | continue; 686 | } 687 | Serial.print(ptr[i], HEX); 688 | Serial.print(' '); 689 | } 690 | 691 | for (uint8_t i = 0; i < data_len; ++i) 692 | { 693 | Serial.print(data[i], HEX); 694 | Serial.print(' '); 695 | } 696 | Serial.println(); 697 | } 698 | -------------------------------------------------------------------------------- /PN5321.h: -------------------------------------------------------------------------------- 1 | // PN532 library by adafruit/ladyada 2 | // MIT license 3 | 4 | // authenticateBlock, readMemoryBlock, writeMemoryBlock contributed 5 | // by Seeed Technology Inc (www.seeedstudio.com) 6 | 7 | 8 | #include "Arduino.h" 9 | #include "NFCReader.h" 10 | 11 | #include "debug.h" 12 | 13 | #define PN532_PREAMBLE 0x00 14 | #define PN532_STARTCODE1 0x00 15 | #define PN532_STARTCODE2 0xFF 16 | #define PN532_POSTAMBLE 0x00 17 | 18 | #define PN532_HOSTTOPN532 0xD4 19 | #define PN532_PN532TOHOST 0xD5 20 | 21 | #define PN532_FIRMWAREVERSION 0x02 22 | #define PN532_GETGENERALSTATUS 0x04 23 | #define PN532_SAMCONFIGURATION 0x14 24 | #define PN532_INLISTPASSIVETARGET 0x4A 25 | #define PN532_INDATAEXCHANGE 0x40 26 | #define PN532_INJUMPFORDEP 0x56 27 | #define PN532_TGINITASTARGET 0x8C 28 | #define PN532_TGGETDATA 0x86 29 | #define PN532_TGSETDATA 0x8E 30 | #define PN532_TGTARGETSTATUS 0x8A 31 | 32 | #define PN532_MIFARE_READ 0x30 33 | #define PN532_MIFARE_WRITE 0xA0 34 | 35 | #define PN532_AUTH_WITH_KEYA 0x60 36 | #define PN532_AUTH_WITH_KEYB 0x61 37 | 38 | 39 | #define PN532_WAKEUP 0x55 40 | 41 | #define PN532_SPI_STATREAD 0x02 42 | #define PN532_SPI_DATAWRITE 0x01 43 | #define PN532_SPI_DATAREAD 0x03 44 | #define PN532_SPI_READY 0x01 45 | 46 | #define PN532_MIFARE_ISO14443A 0x0 47 | 48 | #define KEY_A 1 49 | #define KEY_B 2 50 | 51 | #define PN532_SEL_RES_DEP 0x40 52 | #define PN532_SEL_RES_PICC 0x20 53 | #define PN532_SEL_RES_BOTH 0x60 54 | 55 | #define PN532_MODE_PICC_ONLY 0x04 56 | #define PN532_MODE_DEP_ONLY 0x02 57 | #define PN532_MODE_PASSIVE_ONLY 0x01 58 | 59 | #define PN532_MODE_PICC_ONLY_OFF 0x00 60 | #define PN532_MODE_DEP_ONLY_OFF 0x00 61 | #define PN532_MODE_PASSIVE_ONLY_OFF 0x00 62 | 63 | #define PN532_MODE_AS_TARGET (PN532_MODE_PICC_ONLY_OFF | PN532_MODE_DEP_ONLY_OFF | PN532_MODE_PASSIVE_ONLY_OFF) 64 | 65 | enum READER_ERRORS 66 | { 67 | TIME_OUT_ERROR = 0x01, 68 | CRC_ERROR, 69 | PARITY_ERROR, 70 | ANTI_COLLISION_BIT_COUNT_ERROR, 71 | MIFARE_FRAMING_ERROR, 72 | ANTI_COLLISION_BIT_COLLISION_ERROR, 73 | INSUFFICIENT_BUFFER_ERROR, 74 | RF_BUFFER_OVERFLOW_ERROR, 75 | RF_FIELD_MISSING_ERROR, 76 | RF_PROTOCOL_ERROR, 77 | TEMPERATURE_ERROR = 0x0D, 78 | INTERNAL_BUFFER_ERROR, 79 | INVALID_PARAM_ERROR = 0x10, 80 | DEP_UNSUPPORTED_COMMAND_ERROR = 0x12, 81 | DATA_FORMAT_ERROR, 82 | MIFARE_AUTH_ERROR, 83 | UID_CHECK_BYTE_ERROR = 0X23, 84 | DEP_INVALID_DEVICE_STATE_ERROR = 0x25, 85 | OPERATION_NOT_ALLOWED_ERROR, 86 | TARGET_RELEASED_ERROR = 0x29, 87 | CARD_ID_ERROR, 88 | CARD_INACTIVE_ERROR, 89 | NFCID3_MISMATCH_ERROR, 90 | OVER_CURRENT_ERROR, 91 | DEP_NAD_MISSING, 92 | }; 93 | 94 | struct PN532_CMD_RESPONSE { 95 | uint8_t header[2]; // 0x00 0xFF 96 | uint8_t len; 97 | uint8_t len_chksum; // len + len_chksum = 0x00 98 | uint8_t data_len; 99 | uint8_t direction; 100 | uint8_t responseCode; 101 | uint8_t data[0]; 102 | 103 | boolean verifyResponse(uint32_t cmdCode); 104 | void printResponse(); 105 | 106 | }; 107 | 108 | class PN532 : public NFCReader { 109 | public: 110 | PN532(HardwareSerial &serial); 111 | 112 | uint32_t SAMConfig(void); 113 | void initializeReader(); 114 | 115 | uint32_t getFirmwareVersion(void); 116 | uint32_t readPassiveTargetID(uint8_t cardbaudrate); 117 | uint32_t authenticateBlock( uint8_t cardnumber /*1 or 2*/, 118 | uint32_t cid, 119 | uint8_t blockaddress, 120 | uint8_t authtype /*Either KEY_A or KEY_B */, 121 | uint8_t * keys); 122 | 123 | uint32_t readMemoryBlock(uint8_t cardnumber /*1 or 2*/, 124 | uint8_t blockaddress /*0 to 63*/, 125 | uint8_t * block); 126 | 127 | uint32_t writeMemoryBlock(uint8_t cardnumber /*1 or 2*/, 128 | uint8_t blockaddress /*0 to 63*/, 129 | uint8_t * block); 130 | 131 | uint32_t configurePeerAsInitiator(uint8_t baudrate); 132 | uint32_t configurePeerAsTarget(uint8_t type); 133 | 134 | uint32_t getTargetStatus(uint8_t *response); 135 | 136 | uint32_t sendCommandCheckAck(uint8_t *cmd, 137 | uint8_t cmdlen, 138 | uint16_t timeout = 1000); 139 | 140 | uint32_t initiatorTxRxData(uint8_t *DataOut, 141 | uint32_t dataSize, 142 | uint8_t *response); 143 | 144 | uint32_t targetTxData(uint8_t *DataOut, 145 | uint32_t dataSize); 146 | 147 | uint32_t targetRxData(uint8_t *response); 148 | uint32_t targetRxData(uint8_t *DataIn, uint32_t DataSize); 149 | boolean isTargetReleasedError(uint32_t result); 150 | 151 | // boolean isReady(void); 152 | uint32_t readspicommand(uint8_t cmdCode, PN532_CMD_RESPONSE *reponse, uint16_t timeout=3000); 153 | 154 | private: 155 | HardwareSerial* _serial; 156 | uint8_t command; 157 | void wakeup(); 158 | 159 | 160 | int8_t readAckFrame(uint16_t timeout = 1000); 161 | int8_t receive(uint8_t *buf, int len, uint16_t timeout=1000); 162 | }; 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFC Library for Arduino to communicate with Android 2 | 3 | This library is developed by [SeeedStudio](https://github.com/Seeed-Shield/NFC_Shield_DEV). [Elechouse](http://elechouse.com) changes the driver from `Spi` to `Serial`, in order to support the [NFC Module](http://goo.gl/i0EQgd) of elechouse. 4 | 5 | ## How To 6 | 7 | 1. Make the NFC module works in HSU mode. 8 | 2. Connect NFC Module with Arduino(At least 2 serials) 9 | 10 | PN532 Arduino 11 | VCC --> 5V 12 | GND --> GND 13 | RXD --> Serial1-TX 14 | TXD --> Serail1-RX 15 | 3. [Download zip file][zip], extract it to the `Arduino libraries` folder. 16 | 4. Open example `nfc_ndef_push_url`, upload it to Arduino. 17 | 5. Enable NFC function of your Android device, approximate it to NFC module, then the will be opened. (The url can be changed in code.) 18 | 19 | [zip]:https://github.com/elechouse/NFC_Module_DEV/archive/HSU.zip 20 | 21 | -------------------------------------------------------------------------------- /SNEP.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "SNEP.h" 3 | 4 | SNEP::SNEP(NFCLinkLayer *linkLayer) : 5 | _linkLayer(linkLayer) 6 | { 7 | } 8 | 9 | SNEP::~SNEP() 10 | { 11 | } 12 | 13 | uint32_t SNEP::rxNDEFPayload(uint8_t *&data) 14 | { 15 | uint32_t result = _linkLayer->openSNEPServerLink(); 16 | 17 | if(RESULT_OK(result)) //if connection is error-free 18 | { 19 | //Serial.println(F("CONNECTED.")); 20 | result = _linkLayer->serverLinkRxData(data); 21 | if (RESULT_OK(result)) 22 | { 23 | // TODO: to check header of SNEP packet 24 | uint8_t *ptr = data; 25 | uint8_t version = *ptr; 26 | uint8_t require = *(ptr + 1); 27 | uint32_t length = (ptr[2] << 24) + (ptr[3] << 16) + (ptr[4] << 8) + ptr[5]; 28 | 29 | data = ptr + 6; 30 | // Serial.print("NDEF message length: "); 31 | // Serial.println(length); 32 | return length; 33 | } 34 | } 35 | return result; 36 | } 37 | 38 | uint32_t SNEP::pushPayload(uint8_t *NDEFMessage, uint32_t length) 39 | { 40 | SNEP_MESSAGE *snepMessage = (SNEP_MESSAGE *) ALLOCATE_HEADER_SPACE(NDEFMessage, SNEP_MESSAGE_HDR_LEN); 41 | 42 | snepMessage->version = 0x10; 43 | snepMessage->action = 0x02; 44 | snepMessage->length = MODIFY_ENDIAN(length); 45 | 46 | uint32_t result = _linkLayer->openSNEPClientLink(); 47 | 48 | 49 | 50 | if(RESULT_OK(result)) //if connection is error-free 51 | { 52 | result = _linkLayer->clientLinkTxData((uint8_t *)snepMessage, length + SNEP_MESSAGE_HDR_LEN); 53 | } 54 | 55 | 56 | 57 | return result; 58 | } 59 | -------------------------------------------------------------------------------- /SNEP.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef SNEP_H 4 | #define SNEP_H 5 | 6 | #include "Arduino.h" 7 | #include "NFCLinkLayer.h" 8 | 9 | #define MODIFY_ENDIAN(x) ((((x) >> 24) & 0xFF) /* move byte 3 to byte 0 */ \ 10 | | (((x) << 24) & 0xFF000000) /* move byte 0 to byte 3 */ \ 11 | | (((x) << 8) & 0xFF0000) /* move byte 1 to byte 2 */ \ 12 | | (((x) >> 8) & 0xFF00)) /* move byte 2 to byte 1 */ 13 | 14 | 15 | #define NDEF_MESSAGE_BEGIN_FLAG 0x80 16 | #define NDEF_MESSAGE_END_FLAG 0x40 17 | #define NDEF_MESSAGE_CHUNK_FLAG 0X20 18 | #define NDEF_MESSAGE_SHORT_RECORD 0X10 19 | #define NDEF_MESSAGE_ID_LENGTH_PRESENT 0X08 20 | #define NDEF_MESSAGE_TYPENAME_FORMAT 0x07 21 | 22 | 23 | #define TYPE_FORMAT_EMPTY 0x00 24 | #define TYPE_FORMAT_NFC_FORUM_TYPE 0x01 25 | #define TYPE_FORMAT_MEDIA_TYPE 0x02 26 | #define TYPE_FORMAT_ABSOLUTE_URI 0x03 27 | #define TYPE_FORMAT_NFC_FORUM_EXTERNAL_TYPE 0x04 28 | #define TYPE_FORMAT_UNKNOWN_TYPE 0x05 29 | #define TYPE_FORMAT_UNCHANGED_TYPE 0x06 30 | #define TYPE_FORMAT_RESERVED_TYPE 0x07 31 | 32 | 33 | #define NFC_FORUM_TEXT_TYPE 0x54 34 | 35 | 36 | enum NDEFState 37 | { 38 | NDEF_TYPE_LEN, 39 | NDEF_PAYLOAD_LEN, 40 | NDEF_ID_LEN, 41 | NDEF_TYPE, 42 | NDEF_ID, 43 | NDEF_PAYLOAD, 44 | NDEF_FINISHED 45 | }; 46 | 47 | #define SNEP_MESSAGE_HDR_LEN 0x6 48 | struct SNEP_MESSAGE 49 | { 50 | uint8_t version; 51 | uint8_t action; 52 | uint32_t length; 53 | uint8_t message[0]; 54 | }; 55 | 56 | 57 | /*struct TEXT_PAYLOAD 58 | { 59 | uint8_t textType; // ASCII, UTF-8, ect. 60 | uint8_t locale[2]; 61 | uint8_t text[0]; 62 | }; 63 | */ 64 | 65 | class SNEP 66 | { 67 | 68 | public: 69 | SNEP(NFCLinkLayer *); 70 | ~SNEP(); 71 | 72 | uint32_t openRxConnection(uint32_t timeout); 73 | uint32_t closeRxConnection(); 74 | 75 | uint32_t rxNDEFPayload(uint8_t *&NDEFMessage); 76 | uint32_t pushPayload(uint8_t *NDEFMessage, uint32_t length); 77 | private: 78 | NFCLinkLayer *_linkLayer; 79 | }; 80 | 81 | #endif -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __DEBUG_H__ 3 | #define __DEBUG_H__ 4 | 5 | #define IS_DEBUG 1 6 | 7 | #if IS_DEBUG 8 | #define DMSG(msg) Serial.print(msg) 9 | #define DMSG_HEX(n) Serial.print(' '); Serial.print(n, HEX) 10 | #else 11 | #define DMSG(msg) 12 | #define DMSG_HEX(n) 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /example/nfc_ndef_push_url/nfc_ndef_push_url.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This example demonstrates pushing a NDEF message from Arduino + NFC Shield to Android 4.0+ device 3 | * 4 | * This demo does not support UNO, because UNO board has only one HardwareSerial. 5 | * Do not try to use SoftwareSerial to control PN532, it won't work. 6 | * SotfwareSerial is not fast and stable enough. 7 | * 8 | * This demo only supports the Arduino board which has at least 2 Serial, 9 | * Like Leonard(1 USB serial and 1 Hardware serial), Mega ect. 10 | * 11 | * Make sure your PN532 board is in HSU(High Speed Uart) mode. 12 | * 13 | * This demo is tested with Leonard. 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | /** Use hardware serial to control PN532 */ 24 | // PN532 Arduino 25 | // VCC --> 5V 26 | // GND --> GND 27 | // RXD --> Serial1-TX 28 | // TXD --> Serail1-RX 29 | /** Serial1 can be */ 30 | PN532 nfc(Serial1); 31 | NFCLinkLayer linkLayer(&nfc); 32 | SNEP snep(&linkLayer); 33 | 34 | 35 | // NDEF messages 36 | #define MAX_PKT_HEADER_SIZE 50 37 | #define MAX_PKT_PAYLOAD_SIZE 100 38 | uint8_t txNDEFMessage[MAX_PKT_HEADER_SIZE + MAX_PKT_PAYLOAD_SIZE]; 39 | uint8_t *txNDEFMessagePtr; 40 | uint8_t txLen; 41 | 42 | void setup(void) { 43 | Serial.begin(115200); 44 | while(!Serial); 45 | Serial.println(F("----------------- nfc ndef push url --------------------")); 46 | 47 | 48 | txNDEFMessagePtr = &txNDEFMessage[MAX_PKT_HEADER_SIZE]; 49 | NdefMessage message = NdefMessage(); 50 | message.addUriRecord("http://elechouse.com"); 51 | txLen = message.getEncodedSize(); 52 | if (txLen <= MAX_PKT_PAYLOAD_SIZE) { 53 | message.encode(txNDEFMessagePtr); 54 | } 55 | else { 56 | Serial.println("Tx Buffer is too small."); 57 | while (1) { 58 | } 59 | } 60 | 61 | nfc.initializeReader(); 62 | 63 | uint32_t versiondata = nfc.getFirmwareVersion(); 64 | if (! versiondata) { 65 | Serial.print("Didn't find PN53x board"); 66 | while (1); // halt 67 | } 68 | // Got ok data, print it out! 69 | Serial.print("Found chip PN5"); 70 | Serial.println((versiondata>>24) & 0xFF, HEX); 71 | Serial.print("Firmware ver. "); 72 | Serial.print((versiondata>>16) & 0xFF, DEC); 73 | Serial.print('.'); 74 | Serial.println((versiondata>>8) & 0xFF, DEC); 75 | Serial.print("Supports "); 76 | Serial.println(versiondata & 0xFF, HEX); 77 | 78 | nfc.SAMConfig(); 79 | } 80 | 81 | void loop(void) 82 | { 83 | Serial.println(); 84 | Serial.println(F("---------------- LOOP ----------------------")); 85 | Serial.println(); 86 | 87 | uint32_t txResult = GEN_ERROR; 88 | 89 | if (IS_ERROR(nfc.configurePeerAsTarget(SNEP_SERVER))) { 90 | extern uint8_t pn532_packetbuffer[]; 91 | 92 | Serial.println(F("\nSNEP Sever:Blocking wait response.")); 93 | nfc.readspicommand(PN532_TGINITASTARGET, (PN532_CMD_RESPONSE *)pn532_packetbuffer, 0); 94 | } 95 | 96 | txResult = snep.pushPayload(txNDEFMessagePtr, txLen); 97 | Serial.print(F("Result: 0x")); 98 | Serial.println(txResult, HEX); 99 | if(txResult == 0x00000001){ 100 | delay(3000); 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Ndef 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | NdefMessage KEYWORD1 10 | NdefRecord KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | getTnf KEYWORD2 17 | getType KEYWORD2 18 | getPayload KEYWORD2 19 | getId KEYWORD2 20 | setTnf KEYWORD2 21 | setType KEYWORD2 22 | setPayload KEYWORD2 23 | setId KEYWORD2 24 | print KEYWORD2 25 | recordCount KEYWORD2 26 | encode KEYWORD2 27 | add KEYWORD2 28 | get KEYWORD2 29 | 30 | ####################################### 31 | # Instances (KEYWORD2) 32 | ####################################### 33 | 34 | ####################################### 35 | # Constants (LITERAL1) 36 | ####################################### -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Berkeley Software Distribution license 3 | * 4 | * Copyright (c) 2013, Seeed Technology Inc. 5 | * All rights reserved. 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of the University of California, Berkeley nor the 15 | * names of its contributors may be used to endorse or promote products 16 | * derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY 19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ --------------------------------------------------------------------------------