├── doc ├── AN12650.pdf ├── MF0ICU1.pdf ├── sloa138.pdf ├── SL2S2602.pdf ├── ISO15693-1.pdf ├── ISO15693-2.pdf ├── ISO15693-3.pdf ├── PN5180-NFC.png ├── FritzingLayout.jpg ├── PN5180A0XX-C3.pdf ├── PN5180A0XX-C1-C2.pdf ├── ESP32 PN5180 Wiring.fzz └── PN5180 R1.1-170710_SCH.PDF ├── library.properties ├── library.json ├── Debug.h ├── README.md ├── PN5180ISO14443.h ├── Debug.cpp ├── keywords.txt ├── PN5180ISO15693.h ├── PN5180.h ├── examples ├── PN5180-LPCD-ESP32-deep-sleep │ └── PN5180-LPCD-ESP32-deep-sleep.ino ├── PN5180-ISO14443 │ └── PN5180-ISO14443.ino ├── PN5180-ReadUID │ └── PN5180-ReadUID.ino ├── PN5180-Library │ └── PN5180-Library.ino └── PN5180-LowPowerCardDetection │ └── PN5180-LowPowerCardDetection.ino ├── PN5180ISO14443.cpp ├── LICENSE ├── PN5180.cpp └── PN5180ISO15693.cpp /doc/AN12650.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/AN12650.pdf -------------------------------------------------------------------------------- /doc/MF0ICU1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/MF0ICU1.pdf -------------------------------------------------------------------------------- /doc/sloa138.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/sloa138.pdf -------------------------------------------------------------------------------- /doc/SL2S2602.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/SL2S2602.pdf -------------------------------------------------------------------------------- /doc/ISO15693-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/ISO15693-1.pdf -------------------------------------------------------------------------------- /doc/ISO15693-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/ISO15693-2.pdf -------------------------------------------------------------------------------- /doc/ISO15693-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/ISO15693-3.pdf -------------------------------------------------------------------------------- /doc/PN5180-NFC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/PN5180-NFC.png -------------------------------------------------------------------------------- /doc/FritzingLayout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/FritzingLayout.jpg -------------------------------------------------------------------------------- /doc/PN5180A0XX-C3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/PN5180A0XX-C3.pdf -------------------------------------------------------------------------------- /doc/PN5180A0XX-C1-C2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/PN5180A0XX-C1-C2.pdf -------------------------------------------------------------------------------- /doc/ESP32 PN5180 Wiring.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/ESP32 PN5180 Wiring.fzz -------------------------------------------------------------------------------- /doc/PN5180 R1.1-170710_SCH.PDF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playfultechnology/PN5180-Library/HEAD/doc/PN5180 R1.1-170710_SCH.PDF -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PN5180 Library 2 | version=1.9.0 3 | author=Andreas Trappmann 4 | maintainer=Alastair Aitchison 5 | sentence=NXP NFC PN5180 Library 6 | paragraph=NXP NFC PN5180 Library 7 | category=Sensors 8 | url=https://github.com/playfultechnology/PN5180-Library 9 | architectures=* 10 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PN5180-Library", 3 | "frameworks": "Arduino", 4 | "keywords": "NXP NFC PN5180", 5 | "description": "NXP NFC PN5180 Library", 6 | "authors": 7 | [ 8 | { 9 | "name": "Andreas Trappmann", 10 | "email": "andreas.trappmann@t-online.de", 11 | "url": "https://github.com/ATrappmann/PN5180-Library", 12 | "maintainer": true 13 | } 14 | ], 15 | "repository": 16 | { 17 | "type": "git", 18 | "url": "https://github.com/playfultechnology/PN5180-Library" 19 | } 20 | } -------------------------------------------------------------------------------- /Debug.h: -------------------------------------------------------------------------------- 1 | // NAME: Debug.h 2 | // 3 | // DESC: Helper methods for debugging 4 | // 5 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | #ifndef DEBUG_H 20 | #define DEBUG_H 21 | 22 | #ifdef DEBUG 23 | #define PN5180DEBUG(msg) Serial.print(msg) 24 | #else 25 | #define PN5180DEBUG(msg) 26 | #endif 27 | 28 | #ifdef DEBUG 29 | extern char * formatHex(const uint8_t val); 30 | extern char * formatHex(const uint16_t val); 31 | extern char * formatHex(const uint32_t val); 32 | #endif 33 | 34 | #endif /* DEBUG_H */ 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PN5180-Library 2 | 3 | Arduino Uno / Arduino ESP-32 library for PN5180-NFC Module from NXP Semiconductors 4 | 5 | ![PN5180-NFC module](./doc/PN5180-NFC.png) 6 | ![PN5180 Schematics](./doc/FritzingLayout.jpg) 7 | 8 | Release Notes: 9 | 10 | Version 1.8.1 - 19.08.2021 11 | 12 | * Added changes from Nettermann90 13 | 14 | Version 1.7 - 12.07.2021 15 | 16 | * Migrated branch from Dirk Carstensen for ISO14443 tags to the library. 17 | * See https://github.com/tueddy/PN5180-Library/tree/ISO14443 18 | 19 | Version 1.6 - 13.03.2021 20 | 21 | * Added PN5180::writeEEPROM 22 | 23 | Version 1.5 - 29.01.2020 24 | 25 | * Fixed offset in readSingleBlock. Was off by 1. 26 | 27 | Version 1.4 - 13.11.2019 28 | 29 | * ICODE SLIX2 specific commands, see https://www.nxp.com/docs/en/data-sheet/SL2S2602.pdf 30 | * Example usage, currently outcommented 31 | 32 | Version 1.3 - 21.05.2019 33 | 34 | * Initialized Reset pin with HIGH 35 | * Made readBuffer static 36 | * Typo fixes 37 | * Data type corrections for length parameters 38 | 39 | Version 1.2 - 28.01.2019 40 | 41 | * Cleared Option bit in PN5180ISO15693::readSingleBlock and ::writeSingleBlock 42 | 43 | Version 1.1 - 26.10.2018 44 | 45 | * Cleanup, bug fixing, refactoring 46 | * Automatic check for Arduino vs. ESP-32 platform via compiler switches 47 | * Added open pull requests 48 | * Working on documentation 49 | 50 | Version 1.0.x - 21.09.2018 51 | 52 | * Initial versions 53 | -------------------------------------------------------------------------------- /PN5180ISO14443.h: -------------------------------------------------------------------------------- 1 | // NAME: PN5180ISO14443.h 2 | // 3 | // DESC: ISO14443 protocol on NXP Semiconductors PN5180 module for Arduino. 4 | // 5 | // Copyright (c) 2019 by Dirk Carstensen. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | #ifndef PN5180ISO14443_H 20 | #define PN5180ISO14443_H 21 | 22 | #include "PN5180.h" 23 | 24 | 25 | class PN5180ISO14443 : public PN5180 { 26 | 27 | public: 28 | PN5180ISO14443(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi=SPI); 29 | 30 | private: 31 | uint16_t rxBytesReceived(); 32 | uint32_t GetNumberOfBytesReceivedAndValidBits(); 33 | public: 34 | // Mifare TypeA 35 | int8_t activateTypeA(uint8_t *buffer, uint8_t kind); 36 | bool mifareBlockRead(uint8_t blockno,uint8_t *buffer); 37 | uint8_t mifareBlockWrite16(uint8_t blockno, uint8_t *buffer); 38 | bool mifareHalt(); 39 | /* 40 | * Helper functions 41 | */ 42 | public: 43 | bool setupRF(); 44 | int8_t readCardSerial(uint8_t *buffer); 45 | bool isCardPresent(); 46 | }; 47 | 48 | #endif /* PN5180ISO14443_H */ 49 | -------------------------------------------------------------------------------- /Debug.cpp: -------------------------------------------------------------------------------- 1 | // NAME: Debug.cpp 2 | // 3 | // DESC: Helper functions for debugging 4 | // 5 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | #include 20 | #include 21 | #include "Debug.h" 22 | 23 | #ifdef DEBUG 24 | 25 | static const char hexChar[] = "0123456789ABCDEF"; 26 | static char hexBuffer[9]; 27 | 28 | char * formatHex(const uint8_t val) { 29 | hexBuffer[0] = hexChar[val >> 4]; 30 | hexBuffer[1] = hexChar[val & 0x0f]; 31 | hexBuffer[2] = '\0'; 32 | return hexBuffer; 33 | } 34 | 35 | char * formatHex(const uint16_t val) { 36 | hexBuffer[0] = hexChar[(val >> 12) & 0x0f]; 37 | hexBuffer[1] = hexChar[(val >> 8) & 0x0f]; 38 | hexBuffer[2] = hexChar[(val >> 4) & 0x0f]; 39 | hexBuffer[3] = hexChar[val & 0x0f]; 40 | hexBuffer[4] = '\0'; 41 | return hexBuffer; 42 | } 43 | 44 | char * formatHex(uint32_t val) { 45 | for (int i=7; i>=0; i--) { 46 | hexBuffer[i] = hexChar[val & 0x0f]; 47 | val = val >> 4; 48 | } 49 | hexBuffer[8] = '\0'; 50 | return hexBuffer; 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For PN5180-Library 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | PN5180 KEYWORD1 8 | PN5180ISO15693 KEYWORD1 9 | PN5180ISO14443 KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions 13 | ####################################### 14 | 15 | writeRegister KEYWORD2 16 | writeRegisterWithOrMask KEYWORD2 17 | writeRegisterWithAndMask KEYWORD2 18 | readRegister KEYWORD2 19 | readEprom KEYWORD2 20 | sendData KEYWORD2 21 | readData KEYWORD2 22 | loadRFConfig KEYWORD2 23 | setRF_on KEYWORD2 24 | setRF_off KEYWORD2 25 | getIRQStatus KEYWORD2 26 | getTransceiveState KEYWORD2 27 | transceiveCommand KEYWORD2 28 | 29 | issueISO15693Command KEYWORD2 30 | getInventory KEYWORD2 31 | getInventoryMultiple KEYWORD2 32 | getInventoryPoll KEYWORD2 33 | readSingleBlock KEYWORD2 34 | writeSingleBlock KEYWORD2 35 | readMultipleBlock KEYWORD2 36 | getSystemInfo KEYWORD2 37 | setupRF KEYWORD2 38 | 39 | ####################################### 40 | # Constants 41 | ####################################### 42 | 43 | PN5180_NSS LITERAL1 44 | PN5180_BUSY LITERAL1 45 | PN5180_RST LITERAL1 46 | 47 | PN5180_SPI_SETTINGS LITERAL1 48 | 49 | PN5180TransceiveStat LITERAL1 50 | PN5180_TS_Idle LITERAL1 51 | PN5180_TS_WaitTransmit LITERAL1 52 | PN5180_TS_Transmitting LITERAL1 53 | PN5180_TS_WaitReceive LITERAL1 54 | PN5180_TS_WaitForData LITERAL1 55 | PN5180_TS_Receiving LITERAL1 56 | PN5180_TS_LoopBack LITERAL1 57 | PN5180_TS_RESERVED LITERAL1 58 | 59 | SYSTEM_CONFIG LITERAL1 60 | IRQ_ENABLE LITERAL1 61 | IRQ_STATUS LITERAL1 62 | IRQ_CLEAR LITERAL1 63 | TRANSCEIVE_CONTROL LITERAL1 64 | TIMER1_RELOAD LITERAL1 65 | TIMER1_CONFIG LITERAL1 66 | RX_WAIT_CONFIG LITERAL1 67 | CRC_RX_CONFIG LITERAL1 68 | RX_STATUS LITERAL1 69 | RF_STATUS LITERAL1 70 | SYSTEM_STATUS LITERAL1 71 | TEMP_CONTROL LITERAL1 72 | 73 | DIE_IDENTIFIER LITERAL1 74 | PRODUCT_VERSION LITERAL1 75 | FIRMWARE_VERSION LITERAL1 76 | EEPROM_VERSION LITERAL1 77 | IRQ_PIN_CONFIG LITERAL1 78 | -------------------------------------------------------------------------------- /PN5180ISO15693.h: -------------------------------------------------------------------------------- 1 | // NAME: PN5180ISO15693.h 2 | // 3 | // DESC: ISO15693 protocol on NXP Semiconductors PN5180 module for Arduino. 4 | // 5 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | #ifndef PN5180ISO15693_H 20 | #define PN5180ISO15693_H 21 | 22 | #include "PN5180.h" 23 | 24 | enum ISO15693ErrorCode { 25 | EC_NO_CARD = -1, 26 | ISO15693_EC_OK = 0, 27 | ISO15693_EC_NOT_SUPPORTED = 0x01, 28 | ISO15693_EC_NOT_RECOGNIZED = 0x02, 29 | ISO15693_EC_OPTION_NOT_SUPPORTED = 0x03, 30 | ISO15693_EC_UNKNOWN_ERROR = 0x0f, 31 | ISO15693_EC_BLOCK_NOT_AVAILABLE = 0x10, 32 | ISO15693_EC_BLOCK_ALREADY_LOCKED = 0x11, 33 | ISO15693_EC_BLOCK_IS_LOCKED = 0x12, 34 | ISO15693_EC_BLOCK_NOT_PROGRAMMED = 0x13, 35 | ISO15693_EC_BLOCK_NOT_LOCKED = 0x14, 36 | ISO15693_EC_CUSTOM_CMD_ERROR = 0xA0 37 | }; 38 | 39 | class PN5180ISO15693 : public PN5180 { 40 | 41 | public: 42 | PN5180ISO15693(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi=SPI); 43 | 44 | private: 45 | ISO15693ErrorCode issueISO15693Command(uint8_t *cmd, uint8_t cmdLen, uint8_t **resultPtr); 46 | ISO15693ErrorCode inventoryPoll(uint8_t *uid, uint8_t maxTags, uint8_t *numCard, uint8_t *numCol, uint16_t *collision); 47 | public: 48 | ISO15693ErrorCode getInventory(uint8_t *uid); 49 | ISO15693ErrorCode getInventoryMultiple(uint8_t *uid, uint8_t maxTags, uint8_t *numCard); 50 | ISO15693ErrorCode readSingleBlock(uint8_t *uid, uint8_t blockNo, uint8_t *blockData, uint8_t blockSize); 51 | ISO15693ErrorCode writeSingleBlock(uint8_t *uid, uint8_t blockNo, uint8_t *blockData, uint8_t blockSize); 52 | ISO15693ErrorCode readMultipleBlock(uint8_t *uid, uint8_t blockNo, uint8_t numBlock, uint8_t *blockData, uint8_t blockSize); 53 | ISO15693ErrorCode getSystemInfo(uint8_t *uid, uint8_t *blockSize, uint8_t *numBlocks); 54 | 55 | // ICODE SLIX2 specific commands, see https://www.nxp.com/docs/en/data-sheet/SL2S2602.pdf 56 | ISO15693ErrorCode getRandomNumber(uint8_t *randomData); 57 | ISO15693ErrorCode setPassword(uint8_t identifier, uint8_t *password, uint8_t *random); 58 | ISO15693ErrorCode enablePrivacy(uint8_t *password, uint8_t *random); 59 | // helpers 60 | ISO15693ErrorCode enablePrivacyMode(uint8_t *password); 61 | ISO15693ErrorCode disablePrivacyMode(uint8_t *password); 62 | /* 63 | * Helper functions 64 | */ 65 | public: 66 | bool setupRF(); 67 | const __FlashStringHelper *strerror(ISO15693ErrorCode err); 68 | 69 | }; 70 | 71 | #endif /* PN5180ISO15693_H */ 72 | -------------------------------------------------------------------------------- /PN5180.h: -------------------------------------------------------------------------------- 1 | // NAME: PN5180.h 2 | // 3 | // DESC: NFC Communication with NXP Semiconductors PN5180 module for Arduino. 4 | // 5 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | #ifndef PN5180_H 20 | #define PN5180_H 21 | 22 | #include 23 | 24 | // PN5180 1-Byte Direct Commands 25 | // see 11.4.3.3 Host Interface Command List 26 | // https://www.nxp.com/docs/en/data-sheet/PN5180A0XX-C1-C2.pdf 27 | #define PN5180_WRITE_REGISTER (0x00) 28 | #define PN5180_WRITE_REGISTER_OR_MASK (0x01) 29 | #define PN5180_WRITE_REGISTER_AND_MASK (0x02) 30 | #define WRITE_REGISTER_MULTIPLE (0x03) 31 | #define PN5180_READ_REGISTER (0x04) 32 | #define READ_REGISTER_MULTIPLE (0x05) 33 | #define PN5180_WRITE_EEPROM (0x06) 34 | #define PN5180_READ_EEPROM (0x07) 35 | #define WRITE_TX_DATA (0x08) 36 | #define PN5180_SEND_DATA (0x09) 37 | #define PN5180_READ_DATA (0x0A) 38 | #define PN5180_SWITCH_MODE (0x0B) 39 | #define MIFARE_AUTHENTICATE (0x0C) 40 | #define EPC_INVENTORY (0x0D) 41 | #define EPC_RESUME_INVENTORY (0x0E) 42 | #define EPC_RETRIEVE_INVENTORY_RESULT_SIZE (0x0F) 43 | #define EPC_RETRIEVE_INVENTORY_RESULT (0x10) 44 | #define PN5180_LOAD_RF_CONFIG (0x11) 45 | #define UPDATE_RF_CONFIG (0x12) 46 | #define RETRIEVE_RF_CONFIG_SIZE (0x13) 47 | #define RETRIEVE_RF_CONFIG (0x14) 48 | // Reserved for future use (0x15) 49 | #define PN5180_RF_ON (0x16) 50 | #define PN5180_RF_OFF (0x17) 51 | 52 | // PN5180 Registers 53 | #define SYSTEM_CONFIG (0x00) 54 | #define IRQ_ENABLE (0x01) 55 | #define IRQ_STATUS (0x02) 56 | #define IRQ_CLEAR (0x03) 57 | #define TRANSCEIVE_CONTROL (0x04) 58 | #define TIMER1_RELOAD (0x0c) 59 | #define TIMER1_CONFIG (0x0f) 60 | #define RX_WAIT_CONFIG (0x11) 61 | #define CRC_RX_CONFIG (0x12) 62 | #define RX_STATUS (0x13) 63 | #define TX_WAIT_CONFIG (0x17) 64 | #define TX_CONFIG (0x18) 65 | #define CRC_TX_CONFIG (0x19) 66 | #define RF_STATUS (0x1d) 67 | #define SYSTEM_STATUS (0x24) 68 | #define TEMP_CONTROL (0x25) 69 | #define AGC_REF_CONFIG (0x26) 70 | 71 | // PN5180 EEPROM Addresses 72 | #define DIE_IDENTIFIER (0x00) 73 | #define PRODUCT_VERSION (0x10) 74 | #define FIRMWARE_VERSION (0x12) 75 | #define EEPROM_VERSION (0x14) 76 | #define IRQ_PIN_CONFIG (0x1A) 77 | 78 | enum PN5180TransceiveStat { 79 | PN5180_TS_Idle = 0, 80 | PN5180_TS_WaitTransmit = 1, 81 | PN5180_TS_Transmitting = 2, 82 | PN5180_TS_WaitReceive = 3, 83 | PN5180_TS_WaitForData = 4, 84 | PN5180_TS_Receiving = 5, 85 | PN5180_TS_LoopBack = 6, 86 | PN5180_TS_RESERVED = 7 87 | }; 88 | 89 | // PN5180 IRQ_STATUS 90 | #define RX_IRQ_STAT (1<<0) // End of RF receiption IRQ 91 | #define TX_IRQ_STAT (1<<1) // End of RF transmission IRQ 92 | #define IDLE_IRQ_STAT (1<<2) // IDLE IRQ 93 | #define RFOFF_DET_IRQ_STAT (1<<6) // RF Field OFF detection IRQ 94 | #define RFON_DET_IRQ_STAT (1<<7) // RF Field ON detection IRQ 95 | #define TX_RFOFF_IRQ_STAT (1<<8) // RF Field OFF in PCD IRQ 96 | #define TX_RFON_IRQ_STAT (1<<9) // RF Field ON in PCD IRQ 97 | #define RX_SOF_DET_IRQ_STAT (1<<14) // RF SOF Detection IRQ 98 | #define GENERAL_ERROR_IRQ_STAT (1<<17) // General error IRQ 99 | #define LPCD_IRQ_STAT (1<<19) // LPCD Detection IRQ 100 | 101 | class PN5180 { 102 | private: 103 | uint8_t PN5180_NSS; // active low 104 | uint8_t PN5180_BUSY; 105 | uint8_t PN5180_RST; 106 | SPIClass& PN5180_SPI; 107 | 108 | SPISettings SPI_SETTINGS; 109 | static uint8_t readBuffer[508]; 110 | 111 | public: 112 | PN5180(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi=SPI); 113 | 114 | void begin(); 115 | void end(); 116 | 117 | /* 118 | * PN5180 direct commands with host interface 119 | */ 120 | public: 121 | /* cmd 0x00 */ 122 | bool writeRegister(uint8_t reg, uint32_t value); 123 | /* cmd 0x01 */ 124 | bool writeRegisterWithOrMask(uint8_t addr, uint32_t mask); 125 | /* cmd 0x02 */ 126 | bool writeRegisterWithAndMask(uint8_t addr, uint32_t mask); 127 | 128 | /* cmd 0x04 */ 129 | bool readRegister(uint8_t reg, uint32_t *value); 130 | 131 | /* cmd 0x06 */ 132 | bool writeEEprom(uint8_t addr, uint8_t *buffer, uint8_t len); 133 | /* cmd 0x07 */ 134 | bool readEEprom(uint8_t addr, uint8_t *buffer, int len); 135 | 136 | /* cmd 0x09 */ 137 | bool sendData(uint8_t *data, int len, uint8_t validBits = 0); 138 | /* cmd 0x0a */ 139 | uint8_t * readData(int len); 140 | bool readData(uint8_t len, uint8_t *buffer); 141 | /* prepare LPCD registers */ 142 | bool prepareLPCD(); 143 | /* cmd 0x0B */ 144 | bool switchToLPCD(uint16_t wakeupCounterInMs); 145 | /* cmd 0x11 */ 146 | bool loadRFConfig(uint8_t txConf, uint8_t rxConf); 147 | 148 | /* cmd 0x16 */ 149 | bool setRF_on(); 150 | /* cmd 0x17 */ 151 | bool setRF_off(); 152 | 153 | /* 154 | * Helper functions 155 | */ 156 | public: 157 | void reset(); 158 | 159 | uint8_t commandTimeout = 250; 160 | uint32_t getIRQStatus(); 161 | bool clearIRQStatus(uint32_t irqMask); 162 | 163 | PN5180TransceiveStat getTransceiveState(); 164 | 165 | /* 166 | * Private methods, called within an SPI transaction 167 | */ 168 | private: 169 | bool transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_t *recvBuffer = 0, size_t recvBufferLen = 0); 170 | 171 | }; 172 | 173 | #endif /* PN5180_H */ 174 | -------------------------------------------------------------------------------- /examples/PN5180-LPCD-ESP32-deep-sleep/PN5180-LPCD-ESP32-deep-sleep.ino: -------------------------------------------------------------------------------- 1 | //for reference https://github.com/tueddy/PN5180-Library/blob/master/examples/PN5180-LowPowerCardDetection/PN5180-LPCD-ESP32-deep-sleep.ino 2 | 3 | /* 4 | * demo for low power card detection to wake-up the ESP-32 from deep-sleep mode 5 | * 6 | */ 7 | #include 8 | #define PN5180_NSS 21 9 | #define PN5180_BUSY 39 10 | #define PN5180_RST 12 11 | #define PN5180_IRQ 36 12 | 13 | #define LED_BUILTIN 22 14 | 15 | // SPI controller used in ESP32 (ESP32-DevKitC v4 board (ESP32 WROOM 32D Module)) to conduct SPI communication is SPI3/VSPI. 16 | // So by default SCLK is Pin 18,MISO is Pin 19, MOSI is Pin 23 17 | 18 | 19 | 20 | /* 21 | * Method to print the reason by which ESP32 has been awaken from sleep 22 | */ 23 | void print_wakeup_reason(){ 24 | esp_sleep_wakeup_cause_t wakeup_reason; 25 | 26 | wakeup_reason = esp_sleep_get_wakeup_cause(); 27 | 28 | switch(wakeup_reason) 29 | { 30 | case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; 31 | case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; 32 | case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break; 33 | case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break; 34 | case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break; 35 | default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; 36 | } 37 | } 38 | 39 | /* 40 | * Method to print the GPIO that triggered the wakeup 41 | */ 42 | void print_GPIO_wake_up(){ 43 | int GPIO_reason = esp_sleep_get_ext1_wakeup_status(); 44 | Serial.print("GPIO that triggered the wake up: GPIO "); 45 | Serial.println((log(GPIO_reason))/log(2), 0); 46 | } 47 | 48 | PN5180 nfc(PN5180_NSS, PN5180_BUSY, PN5180_RST); 49 | 50 | uint16_t wakeupCounterInMs = 0x3FF; // must be in the range of 0x0 - 0xA82. max wake-up time is 2960 ms. 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | Serial.println(F("PN5180 Demo Sketch for wake-up ESP-32 from deep-sleep with LPCD interrupt")); 55 | Serial.println(""); 56 | pinMode(12, INPUT); // turn NeoPixel off 57 | // turn on LED 58 | pinMode(LED_BUILTIN, OUTPUT); 59 | digitalWrite(LED_BUILTIN, LOW); 60 | Serial.print("LED_BUILTIN: "); 61 | Serial.println(LED_BUILTIN); 62 | // print wakeup reason 63 | print_wakeup_reason(); 64 | // print wakeup pin 65 | print_GPIO_wake_up(); 66 | 67 | // disable pin hold from deep sleep 68 | gpio_deep_sleep_hold_dis(); 69 | gpio_hold_dis(gpio_num_t(PN5180_NSS)); // NSS 70 | gpio_hold_dis(gpio_num_t(PN5180_RST)); // RST 71 | 72 | // Init PN 5180 73 | pinMode(PN5180_IRQ, INPUT); 74 | nfc.begin(); 75 | 76 | Serial.println(F("----------------------------------")); 77 | Serial.println(F("PN5180 Hard-Reset...")); 78 | nfc.reset(); 79 | 80 | Serial.println(F("----------------------------------")); 81 | Serial.println(F("Reading product version...")); 82 | uint8_t productVersion[2]; 83 | nfc.readEEprom(PRODUCT_VERSION, productVersion, sizeof(productVersion)); 84 | Serial.print(F("Product version=")); 85 | Serial.print(productVersion[1]); 86 | Serial.print("."); 87 | Serial.println(productVersion[0]); 88 | 89 | nfc.clearIRQStatus(0xffffffff); 90 | Serial.println(digitalRead(PN5180_IRQ)); //reads 0 because IRQ pin pin config is set to active high (eeprom@0x1A) //should read 1 because when interrupt is raised GPIO4 is LOW 91 | Serial.println(F("Reading IRQ-Pin...")); 92 | uint8_t irqPin[1]; 93 | nfc.readEEprom(IRQ_PIN_CONFIG, irqPin, sizeof(irqPin)); 94 | Serial.print(F("irqPin=")); 95 | Serial.println(irqPin[0]); //should read 1 i.e. pin IRQ is high(bolean 1/3.3v) when active(interrupted) 96 | 97 | if (nfc.prepareLPCD()) { 98 | Serial.println("prepareLPCD success"); 99 | } 100 | Serial.print("IRQ-pin:"); Serial.println(digitalRead(PN5180_IRQ)); 101 | 102 | // turn on LPCD 103 | if (nfc.switchToLPCD(wakeupCounterInMs)) { 104 | Serial.println("switchToLPCD success"); 105 | // configure wakeup pin for deep-sleep wake-up 106 | // esp_sleep_enable_ext0_wakeup(gpio_num_t(PN5180_IRQ), 1); //1 = High, 0 = Low 107 | esp_sleep_enable_ext1_wakeup((1ULL << (PN5180_IRQ)), ESP_EXT1_WAKEUP_ANY_HIGH); 108 | // freeze pin states in deep sleep 109 | gpio_hold_en(gpio_num_t(PN5180_NSS)); // NSS 110 | gpio_hold_en(gpio_num_t(PN5180_RST)); // RST 111 | gpio_deep_sleep_hold_en(); 112 | Serial.println("Good night.."); 113 | esp_deep_sleep_start(); 114 | Serial.println("This will never be printed"); 115 | } else { 116 | Serial.println("switchToLPCD failed"); 117 | } 118 | // ++ go to sleep ++ 119 | 120 | } 121 | 122 | void loop() { 123 | if (digitalRead(PN5180_IRQ) == HIGH) { 124 | showIRQStatus(nfc.getIRQStatus()); 125 | uint32_t registerValue; 126 | nfc.readRegister(0x26, ®isterValue); Serial.print(registerValue); 127 | delay(2000); 128 | nfc.reset(); //very important to have for lpcd to work 129 | 130 | // read card data here! 131 | // .. 132 | 133 | // turn back on LPCD 134 | if (nfc.switchToLPCD(wakeupCounterInMs)) { 135 | // LPCD success 136 | Serial.println("switchToLPCD success"); 137 | // prepare deep sleep 138 | Serial.println("Going to sleep now"); 139 | digitalWrite(LED_BUILTIN, HIGH); 140 | pinMode(LED_BUILTIN, INPUT); 141 | // configure wakeup pin for deep-sleep wake-up 142 | //esp_sleep_enable_ext0_wakeup(gpio_num_t(PN5180_IRQ), 1); //1 = High, 0 = Low 143 | esp_sleep_enable_ext1_wakeup(WAKEUP_PIN_MASK, ESP_EXT1_WAKEUP_ANY_HIGH); 144 | // freeze pin states in deep sleep 145 | gpio_hold_en(gpio_num_t(PN5180_NSS)); // NSS 146 | gpio_hold_en(gpio_num_t(PN5180_RST)); // RST 147 | gpio_deep_sleep_hold_en(); 148 | Serial.println("Good night.."); 149 | esp_deep_sleep_start(); 150 | Serial.println("This will never be printed"); 151 | } else { 152 | Serial.println("switchToLPCD failed"); 153 | } 154 | } 155 | } 156 | void showIRQStatus(uint32_t irqStatus) { 157 | Serial.print(F("IRQ-Status 0x")); 158 | Serial.print(irqStatus, HEX); 159 | Serial.print(": [ "); 160 | if (irqStatus & (1 << 0)) Serial.print(F("RQ | ")); 161 | if (irqStatus & (1 << 1)) Serial.print(F("TX | ")); 162 | if (irqStatus & (1 << 2)) Serial.print(F("IDLE | ")); 163 | if (irqStatus & (1 << 3)) Serial.print(F("MODE_DETECTED | ")); 164 | if (irqStatus & (1 << 4)) Serial.print(F("CARD_ACTIVATED | ")); 165 | if (irqStatus & (1 << 5)) Serial.print(F("STATE_CHANGE | ")); 166 | if (irqStatus & (1 << 6)) Serial.print(F("RFOFF_DET ")); 167 | if (irqStatus & (1 << 7)) Serial.print(F("RFON_DET ")); 168 | if (irqStatus & (1 << 8)) Serial.print(F("TX_RFOFF | ")); 169 | if (irqStatus & (1 << 9)) Serial.print(F("TX_RFON | ")); 170 | if (irqStatus & (1 << 10)) Serial.print(F("RF_ACTIVE_ERROR ")); 171 | if (irqStatus & (1 << 11)) Serial.print(F("TIMER0 ")); 172 | if (irqStatus & (1 << 12)) Serial.print(F("TIMER1 ")); 173 | if (irqStatus & (1 << 13)) Serial.print(F("TIMER2 ")); 174 | if (irqStatus & (1 << 14)) Serial.print(F("RX_SOF_DET ")); 175 | if (irqStatus & (1 << 15)) Serial.print(F("RX_SC_DET ")); 176 | if (irqStatus & (1 << 16)) Serial.print(F("TEMPSENS_ERROR ")); 177 | if (irqStatus & (1 << 17)) Serial.print(F("GENERAL_ERROR ")); 178 | if (irqStatus & (1 << 18)) Serial.print(F("HV_ERROR ")); 179 | if (irqStatus & (1 << 19)) Serial.print(F("LPCD")); 180 | Serial.println("]"); 181 | } 182 | -------------------------------------------------------------------------------- /examples/PN5180-ISO14443/PN5180-ISO14443.ino: -------------------------------------------------------------------------------- 1 | // NAME: PN5180-ISO14443.ino 2 | // 3 | // DESC: Example usage of the PN5180 library for the PN5180-NFC Module 4 | // from NXP Semiconductors. 5 | // 6 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 7 | // Copyright (c) 2019 by Dirk Carstensen. 8 | // 9 | // This file is part of the PN5180 library for the Arduino environment. 10 | // 11 | // This library is free software; you can redistribute it and/or 12 | // modify it under the terms of the GNU Lesser General Public 13 | // License as published by the Free Software Foundation; either 14 | // version 2.1 of the License, or (at your option) any later version. 15 | // 16 | // This library is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | // Lesser General Public License for more details. 20 | // 21 | // BEWARE: SPI with an Arduino to a PN5180 module has to be at a level of 3.3V 22 | // use of logic-level converters from 5V->3.3V is absolutly neccessary 23 | // on most Arduinos for all input pins of PN5180! 24 | // If used with an ESP-32, there is no need for a logic-level converter, since 25 | // it operates on 3.3V already. 26 | // 27 | // Arduino <-> Level Converter <-> PN5180 pin mapping: 28 | // 5V <--> 5V 29 | // 3.3V <--> 3.3V 30 | // GND <--> GND 31 | // 5V <-> HV 32 | // GND <-> GND (HV) 33 | // LV <-> 3.3V 34 | // GND (LV) <-> GND 35 | // SCLK,13 <-> HV1 - LV1 --> SCLK 36 | // MISO,12 <--- <-- MISO 37 | // MOSI,11 <-> HV3 - LV3 --> MOSI 38 | // SS,10 <-> HV4 - LV4 --> NSS (=Not SS -> active LOW) 39 | // BUSY,9 <--- BUSY 40 | // Reset,7 <-> HV2 - LV2 --> RST 41 | // 42 | // ESP-32 <--> PN5180 pin mapping: 43 | // 3.3V <--> 3.3V 44 | // GND <--> GND 45 | // SCLK, 18 --> SCLK 46 | // MISO, 19 <-- MISO 47 | // MOSI, 23 --> MOSI 48 | // SS, 16 --> NSS (=Not SS -> active LOW) 49 | // BUSY, 5 <-- BUSY 50 | // Reset, 17 --> RST 51 | // 52 | 53 | /* 54 | * Pins on ICODE2 Reader Writer: 55 | * 56 | * ICODE2 | PN5180 57 | * pin label | pin I/O name 58 | * 1 +5V 59 | * 2 +3,3V 60 | * 3 RST 10 I RESET_N (low active) 61 | * 4 NSS 1 I SPI NSS 62 | * 5 MOSI 3 I SPI MOSI 63 | * 6 MISO 5 O SPI MISO 64 | * 7 SCK 7 I SPI Clock 65 | * 8 BUSY 8 O Busy Signal 66 | * 9 GND 9 Supply VSS - Ground 67 | * 10 GPIO 38 O GPO1 - Control for external DC/DC 68 | * 11 IRQ 39 O IRQ 69 | * 12 AUX 40 O AUX1 - Analog/Digital test signal 70 | * 13 REQ 2? I/O AUX2 - Analog test bus or download 71 | * 72 | */ 73 | 74 | //#define WRITE_ENABLED 1 75 | 76 | #include 77 | #include 78 | 79 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_AVR_NANO) 80 | 81 | #define PN5180_NSS 10 82 | #define PN5180_BUSY 9 83 | #define PN5180_RST 7 84 | 85 | #elif defined(ARDUINO_ARCH_ESP32) 86 | 87 | #define PN5180_NSS 16 // swapped with BUSY 88 | #define PN5180_BUSY 5 // swapped with NSS 89 | #define PN5180_RST 17 90 | 91 | #else 92 | #error Please define your pinout here! 93 | #endif 94 | 95 | PN5180ISO14443 nfc(PN5180_NSS, PN5180_BUSY, PN5180_RST); 96 | 97 | void setup() { 98 | Serial.begin(115200); 99 | Serial.println(F("==================================")); 100 | Serial.println(F("Uploaded: " __DATE__ " " __TIME__)); 101 | Serial.println(F("PN5180 ISO14443 Demo Sketch")); 102 | 103 | nfc.begin(); 104 | 105 | Serial.println(F("----------------------------------")); 106 | Serial.println(F("PN5180 Hard-Reset...")); 107 | nfc.reset(); 108 | 109 | Serial.println(F("----------------------------------")); 110 | Serial.println(F("Reading product version...")); 111 | uint8_t productVersion[2]; 112 | nfc.readEEprom(PRODUCT_VERSION, productVersion, sizeof(productVersion)); 113 | Serial.print(F("Product version=")); 114 | Serial.print(productVersion[1]); 115 | Serial.print("."); 116 | Serial.println(productVersion[0]); 117 | 118 | if (0xff == productVersion[1]) { // if product version 255, the initialization failed 119 | Serial.println(F("Initialization failed!?")); 120 | Serial.println(F("Press reset to restart...")); 121 | Serial.flush(); 122 | exit(-1); // halt 123 | } 124 | 125 | Serial.println(F("----------------------------------")); 126 | Serial.println(F("Reading firmware version...")); 127 | uint8_t firmwareVersion[2]; 128 | nfc.readEEprom(FIRMWARE_VERSION, firmwareVersion, sizeof(firmwareVersion)); 129 | Serial.print(F("Firmware version=")); 130 | Serial.print(firmwareVersion[1]); 131 | Serial.print("."); 132 | Serial.println(firmwareVersion[0]); 133 | 134 | Serial.println(F("----------------------------------")); 135 | Serial.println(F("Reading EEPROM version...")); 136 | uint8_t eepromVersion[2]; 137 | nfc.readEEprom(EEPROM_VERSION, eepromVersion, sizeof(eepromVersion)); 138 | Serial.print(F("EEPROM version=")); 139 | Serial.print(eepromVersion[1]); 140 | Serial.print("."); 141 | Serial.println(eepromVersion[0]); 142 | 143 | Serial.println(F("----------------------------------")); 144 | Serial.println(F("Enable RF field...")); 145 | nfc.setupRF(); 146 | } 147 | 148 | uint32_t loopCnt = 0; 149 | bool errorFlag = false; 150 | 151 | 152 | // ISO 14443 loop 153 | void loop() { 154 | if (errorFlag) { 155 | uint32_t irqStatus = nfc.getIRQStatus(); 156 | showIRQStatus(irqStatus); 157 | 158 | if (0 == (RX_SOF_DET_IRQ_STAT & irqStatus)) { // no card detected 159 | Serial.println(F("*** No card detected!")); 160 | } 161 | 162 | nfc.reset(); 163 | nfc.setupRF(); 164 | 165 | errorFlag = false; 166 | delay(10); 167 | } 168 | Serial.println(F("----------------------------------")); 169 | Serial.print(F("Loop #")); 170 | Serial.println(loopCnt++); 171 | if (!nfc.isCardPresent()) { 172 | Serial.print(F("no card found")); 173 | return; 174 | } 175 | uint8_t uid[8]; 176 | if (!nfc.readCardSerial(uid)) { 177 | Serial.print(F("Error in readCardSerial: ")); 178 | errorFlag = true; 179 | return; 180 | } 181 | Serial.print(F("card serial successful, UID=")); 182 | for (int i=0; i<8; i++) { 183 | Serial.print(uid[i], HEX); 184 | if (i < 2) Serial.print(":"); 185 | } 186 | Serial.println(); 187 | 188 | Serial.println(F("----------------------------------")); 189 | 190 | delay(1000); 191 | } 192 | 193 | 194 | 195 | void showIRQStatus(uint32_t irqStatus) { 196 | Serial.print(F("IRQ-Status 0x")); 197 | Serial.print(irqStatus, HEX); 198 | Serial.print(": [ "); 199 | if (irqStatus & (1<< 0)) Serial.print(F("RQ ")); 200 | if (irqStatus & (1<< 1)) Serial.print(F("TX ")); 201 | if (irqStatus & (1<< 2)) Serial.print(F("IDLE ")); 202 | if (irqStatus & (1<< 3)) Serial.print(F("MODE_DETECTED ")); 203 | if (irqStatus & (1<< 4)) Serial.print(F("CARD_ACTIVATED ")); 204 | if (irqStatus & (1<< 5)) Serial.print(F("STATE_CHANGE ")); 205 | if (irqStatus & (1<< 6)) Serial.print(F("RFOFF_DET ")); 206 | if (irqStatus & (1<< 7)) Serial.print(F("RFON_DET ")); 207 | if (irqStatus & (1<< 8)) Serial.print(F("TX_RFOFF ")); 208 | if (irqStatus & (1<< 9)) Serial.print(F("TX_RFON ")); 209 | if (irqStatus & (1<<10)) Serial.print(F("RF_ACTIVE_ERROR ")); 210 | if (irqStatus & (1<<11)) Serial.print(F("TIMER0 ")); 211 | if (irqStatus & (1<<12)) Serial.print(F("TIMER1 ")); 212 | if (irqStatus & (1<<13)) Serial.print(F("TIMER2 ")); 213 | if (irqStatus & (1<<14)) Serial.print(F("RX_SOF_DET ")); 214 | if (irqStatus & (1<<15)) Serial.print(F("RX_SC_DET ")); 215 | if (irqStatus & (1<<16)) Serial.print(F("TEMPSENS_ERROR ")); 216 | if (irqStatus & (1<<17)) Serial.print(F("GENERAL_ERROR ")); 217 | if (irqStatus & (1<<18)) Serial.print(F("HV_ERROR ")); 218 | if (irqStatus & (1<<19)) Serial.print(F("LPCD ")); 219 | Serial.println("]"); 220 | } 221 | -------------------------------------------------------------------------------- /examples/PN5180-ReadUID/PN5180-ReadUID.ino: -------------------------------------------------------------------------------- 1 | // NAME: PN5180-ReadUID.ino 2 | // 3 | // DESC: Example usage of the PN5180 library for the PN5180-NFC Module 4 | // from NXP Semiconductors. 5 | // 6 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 7 | // Copyright (c) 2019 by Dirk Carstensen. 8 | // 9 | // This file is part of the PN5180 library for the Arduino environment. 10 | // 11 | // This library is free software; you can redistribute it and/or 12 | // modify it under the terms of the GNU Lesser General Public 13 | // License as published by the Free Software Foundation; either 14 | // version 2.1 of the License, or (at your option) any later version. 15 | // 16 | // This library is distributed in the hope that it will be useful, 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | // Lesser General Public License for more details. 20 | // 21 | // BEWARE: SPI with an Arduino to a PN5180 module has to be at a level of 3.3V 22 | // use of logic-level converters from 5V->3.3V is absolutly neccessary 23 | // on most Arduinos for all input pins of PN5180! 24 | // If used with an ESP-32, there is no need for a logic-level converter, since 25 | // it operates on 3.3V already. 26 | // 27 | // Arduino <-> Level Converter <-> PN5180 pin mapping: 28 | // 5V <--> 5V 29 | // 3.3V <--> 3.3V 30 | // GND <--> GND 31 | // 5V <-> HV 32 | // GND <-> GND (HV) 33 | // LV <-> 3.3V 34 | // GND (LV) <-> GND 35 | // SCLK,13 <-> HV1 - LV1 --> SCLK 36 | // MISO,12 <--- <-- MISO 37 | // MOSI,11 <-> HV3 - LV3 --> MOSI 38 | // SS,10 <-> HV4 - LV4 --> NSS (=Not SS -> active LOW) 39 | // BUSY,9 <--- BUSY 40 | // Reset,7 <-> HV2 - LV2 --> RST 41 | // 42 | // ESP-32 <--> PN5180 pin mapping: 43 | // 3.3V <--> 3.3V 44 | // GND <--> GND 45 | // SCLK, 18 --> SCLK 46 | // MISO, 19 <-- MISO 47 | // MOSI, 23 --> MOSI 48 | // SS, 16 --> NSS (=Not SS -> active LOW) 49 | // BUSY, 5 <-- BUSY 50 | // Reset, 17 --> RST 51 | // 52 | 53 | /* 54 | * Pins on ICODE2 Reader Writer: 55 | * 56 | * ICODE2 | PN5180 57 | * pin label | pin I/O name 58 | * 1 +5V 59 | * 2 +3,3V 60 | * 3 RST 10 I RESET_N (low active) 61 | * 4 NSS 1 I SPI NSS 62 | * 5 MOSI 3 I SPI MOSI 63 | * 6 MISO 5 O SPI MISO 64 | * 7 SCK 7 I SPI Clock 65 | * 8 BUSY 8 O Busy Signal 66 | * 9 GND 9 Supply VSS - Ground 67 | * 10 GPIO 38 O GPO1 - Control for external DC/DC 68 | * 11 IRQ 39 O IRQ 69 | * 12 AUX 40 O AUX1 - Analog/Digital test signal 70 | * 13 REQ 2? I/O AUX2 - Analog test bus or download 71 | * 72 | */ 73 | 74 | 75 | #include 76 | #include 77 | #include 78 | 79 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_AVR_NANO) 80 | 81 | #define PN5180_NSS 10 82 | #define PN5180_BUSY 9 83 | #define PN5180_RST 7 84 | 85 | #elif defined(ARDUINO_ARCH_ESP32) 86 | 87 | #define PN5180_NSS 16 88 | #define PN5180_BUSY 5 89 | #define PN5180_RST 17 90 | 91 | #else 92 | #error Please define your pinout here! 93 | #endif 94 | 95 | 96 | PN5180ISO14443 nfc14443(PN5180_NSS, PN5180_BUSY, PN5180_RST); 97 | PN5180ISO15693 nfc15693(PN5180_NSS, PN5180_BUSY, PN5180_RST); 98 | 99 | void setup() { 100 | Serial.begin(115200); 101 | Serial.println(F("==================================")); 102 | Serial.println(F("Uploaded: " __DATE__ " " __TIME__)); 103 | Serial.println(F("PN5180 ISO14443 Demo Sketch")); 104 | 105 | nfc14443.begin(); 106 | 107 | Serial.println(F("----------------------------------")); 108 | Serial.println(F("PN5180 Hard-Reset...")); 109 | nfc14443.reset(); 110 | 111 | Serial.println(F("----------------------------------")); 112 | Serial.println(F("Reading product version...")); 113 | uint8_t productVersion[2]; 114 | nfc14443.readEEprom(PRODUCT_VERSION, productVersion, sizeof(productVersion)); 115 | Serial.print(F("Product version=")); 116 | Serial.print(productVersion[1]); 117 | Serial.print("."); 118 | Serial.println(productVersion[0]); 119 | 120 | if (0xff == productVersion[1]) { // if product version 255, the initialization failed 121 | Serial.println(F("Initialization failed!?")); 122 | Serial.println(F("Press reset to restart...")); 123 | Serial.flush(); 124 | exit(-1); // halt 125 | } 126 | 127 | Serial.println(F("----------------------------------")); 128 | Serial.println(F("Reading firmware version...")); 129 | uint8_t firmwareVersion[2]; 130 | nfc14443.readEEprom(FIRMWARE_VERSION, firmwareVersion, sizeof(firmwareVersion)); 131 | Serial.print(F("Firmware version=")); 132 | Serial.print(firmwareVersion[1]); 133 | Serial.print("."); 134 | Serial.println(firmwareVersion[0]); 135 | 136 | Serial.println(F("----------------------------------")); 137 | Serial.println(F("Reading EEPROM version...")); 138 | uint8_t eepromVersion[2]; 139 | nfc14443.readEEprom(EEPROM_VERSION, eepromVersion, sizeof(eepromVersion)); 140 | Serial.print(F("EEPROM version=")); 141 | Serial.print(eepromVersion[1]); 142 | Serial.print("."); 143 | Serial.println(eepromVersion[0]); 144 | 145 | Serial.println(F("----------------------------------")); 146 | Serial.println(F("Enable RF field...")); 147 | nfc14443.setupRF(); 148 | } 149 | 150 | uint32_t loopCnt = 0; 151 | 152 | 153 | // read cards loop 154 | void loop() { 155 | Serial.println(F("----------------------------------")); 156 | Serial.print(F("Loop #")); 157 | Serial.println(loopCnt++); 158 | #if defined(ARDUINO_ARCH_ESP32) 159 | Serial.println("Free heap: " + String(ESP.getFreeHeap())); 160 | #endif 161 | uint8_t uid[10]; 162 | unsigned long StartTime, ElapsedTime; 163 | // check for ISO-14443 card 164 | StartTime = millis(); 165 | nfc14443.reset(); 166 | nfc14443.setupRF(); 167 | if (nfc14443.isCardPresent()) { 168 | uint8_t uidLength = nfc14443.readCardSerial(uid); 169 | if (uidLength > 0) { 170 | Serial.print(F("ISO-14443 card found, UID=")); 171 | for (int i=0; i3.3V is absolutely necessary 22 | // on most Arduinos for all input pins of PN5180! 23 | // If used with an ESP-32, there is no need for a logic-level converter, since 24 | // it operates on 3.3V already. 25 | // 26 | // Arduino <-> Level Converter <-> PN5180 pin mapping: 27 | // 5V <--> 5V 28 | // 3.3V <--> 3.3V 29 | // GND <--> GND 30 | // 5V <-> HV 31 | // GND <-> GND (HV) 32 | // LV <-> 3.3V 33 | // GND (LV) <-> GND 34 | // SCLK,13 <-> HV1 - LV1 --> SCLK 35 | // MISO,12 <--- <-- MISO 36 | // MOSI,11 <-> HV3 - LV3 --> MOSI 37 | // SS,10 <-> HV4 - LV4 --> NSS (=Not SS -> active LOW) 38 | // BUSY,9 <--- BUSY 39 | // Reset,7 <-> HV2 - LV2 --> RST 40 | // 41 | // ESP-32 <--> PN5180 pin mapping: 42 | // 3.3V <--> 3.3V 43 | // GND <--> GND 44 | // SCLK, 18 --> SCLK 45 | // MISO, 19 <-- MISO 46 | // MOSI, 23 --> MOSI 47 | // SS, 16 --> NSS (=Not SS -> active LOW) 48 | // BUSY, 5 <-- BUSY 49 | // Reset, 17 --> RST 50 | // 51 | 52 | /* 53 | * Pins on ICODE2 Reader Writer: 54 | * 55 | * ICODE2 | PN5180 56 | * pin label | pin I/O name 57 | * 1 +5V 58 | * 2 +3,3V 59 | * 3 RST 10 I RESET_N (low active) 60 | * 4 NSS 1 I SPI NSS 61 | * 5 MOSI 3 I SPI MOSI 62 | * 6 MISO 5 O SPI MISO 63 | * 7 SCK 7 I SPI Clock 64 | * 8 BUSY 8 O Busy Signal 65 | * 9 GND 9 Supply VSS - Ground 66 | * 10 GPIO 38 O GPO1 - Control for external DC/DC 67 | * 11 IRQ 39 O IRQ 68 | * 12 AUX 40 O AUX1 - Analog/Digital test signal 69 | * 13 REQ 2? I/O AUX2 - Analog test bus or download 70 | * 71 | */ 72 | 73 | //#define WRITE_ENABLED 1 74 | 75 | #include 76 | #include 77 | 78 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_AVR_NANO) 79 | 80 | #define PN5180_NSS 10 81 | #define PN5180_BUSY 9 82 | #define PN5180_RST 7 83 | 84 | #elif defined(ARDUINO_ARCH_ESP32) 85 | 86 | #define PN5180_NSS 16 87 | #define PN5180_BUSY 5 88 | #define PN5180_RST 17 89 | 90 | #else 91 | #error Please define your pinout here! 92 | #endif 93 | 94 | PN5180ISO15693 nfc(PN5180_NSS, PN5180_BUSY, PN5180_RST); 95 | 96 | void setup() { 97 | Serial.begin(115200); 98 | Serial.println(F("==================================")); 99 | Serial.println(F("Uploaded: " __DATE__ " " __TIME__)); 100 | Serial.println(F("PN5180 ISO15693 Demo Sketch")); 101 | 102 | nfc.begin(); 103 | 104 | Serial.println(F("----------------------------------")); 105 | Serial.println(F("PN5180 Hard-Reset...")); 106 | nfc.reset(); 107 | 108 | Serial.println(F("----------------------------------")); 109 | Serial.println(F("Reading product version...")); 110 | uint8_t productVersion[2]; 111 | nfc.readEEprom(PRODUCT_VERSION, productVersion, sizeof(productVersion)); 112 | Serial.print(F("Product version=")); 113 | Serial.print(productVersion[1]); 114 | Serial.print("."); 115 | Serial.println(productVersion[0]); 116 | 117 | if (0xff == productVersion[1]) { // if product version 255, the initialization failed 118 | Serial.println(F("Initialization failed!?")); 119 | Serial.println(F("Press reset to restart...")); 120 | Serial.flush(); 121 | exit(-1); // halt 122 | } 123 | 124 | Serial.println(F("----------------------------------")); 125 | Serial.println(F("Reading firmware version...")); 126 | uint8_t firmwareVersion[2]; 127 | nfc.readEEprom(FIRMWARE_VERSION, firmwareVersion, sizeof(firmwareVersion)); 128 | Serial.print(F("Firmware version=")); 129 | Serial.print(firmwareVersion[1]); 130 | Serial.print("."); 131 | Serial.println(firmwareVersion[0]); 132 | 133 | Serial.println(F("----------------------------------")); 134 | Serial.println(F("Reading EEPROM version...")); 135 | uint8_t eepromVersion[2]; 136 | nfc.readEEprom(EEPROM_VERSION, eepromVersion, sizeof(eepromVersion)); 137 | Serial.print(F("EEPROM version=")); 138 | Serial.print(eepromVersion[1]); 139 | Serial.print("."); 140 | Serial.println(eepromVersion[0]); 141 | 142 | /* 143 | Serial.println(F("----------------------------------")); 144 | Serial.println(F("Reading IRQ pin config...")); 145 | uint8_t irqConfig; 146 | nfc.readEEprom(IRQ_PIN_CONFIG, &irqConfig, 1)); 147 | Serial.print(F("IRQ_PIN_CONFIG=0x")); 148 | Serial.println(irqConfig, HEX); 149 | 150 | Serial.println(F("----------------------------------")); 151 | Serial.println(F("Reading IRQ_ENABLE register...")); 152 | uint32_t irqEnable; 153 | nfc.readRegister(IRQ_ENABLE, &irqEnable)); 154 | Serial.print(F("IRQ_ENABLE=0x")); 155 | Serial.println(irqConfig, HEX); 156 | */ 157 | 158 | Serial.println(F("----------------------------------")); 159 | Serial.println(F("Enable RF field...")); 160 | nfc.setupRF(); 161 | } 162 | 163 | uint32_t loopCnt = 0; 164 | bool errorFlag = false; 165 | 166 | void loop() { 167 | if (errorFlag) { 168 | uint32_t irqStatus = nfc.getIRQStatus(); 169 | showIRQStatus(irqStatus); 170 | 171 | if (0 == (RX_SOF_DET_IRQ_STAT & irqStatus)) { // no card detected 172 | Serial.println(F("*** No card detected!")); 173 | } 174 | 175 | nfc.reset(); 176 | nfc.setupRF(); 177 | 178 | errorFlag = false; 179 | } 180 | 181 | Serial.println(F("----------------------------------")); 182 | Serial.print(F("Loop #")); 183 | Serial.println(loopCnt++); 184 | 185 | /* 186 | // code for unlocking an ICODE SLIX2 protected tag 187 | uint8_t password[] = {0x01, 0x02, 0x03, 0x04}; // put your privacy password here 188 | ISO15693ErrorCode myrc = nfc.unlockICODESLIX2(password); 189 | if (ISO15693_EC_OK == myrc) { 190 | Serial.println("unlockICODESLIX2 successful"); 191 | } 192 | */ 193 | 194 | uint8_t uid[8]; 195 | ISO15693ErrorCode rc = nfc.getInventory(uid); 196 | if (ISO15693_EC_OK != rc) { 197 | Serial.print(F("Error in getInventory: ")); 198 | Serial.println(nfc.strerror(rc)); 199 | errorFlag = true; 200 | return; 201 | } 202 | Serial.print(F("Inventory successful, UID=")); 203 | for (int i=0; i<8; i++) { 204 | Serial.print(uid[7-i], HEX); // LSB is first 205 | if (i < 2) Serial.print(":"); 206 | } 207 | Serial.println(); 208 | 209 | Serial.println(F("----------------------------------")); 210 | uint8_t blockSize, numBlocks; 211 | rc = nfc.getSystemInfo(uid, &blockSize, &numBlocks); 212 | if (ISO15693_EC_OK != rc) { 213 | Serial.print(F("Error in getSystemInfo: ")); 214 | Serial.println(nfc.strerror(rc)); 215 | errorFlag = true; 216 | return; 217 | } 218 | Serial.print(F("System Info retrieved: blockSize=")); 219 | Serial.print(blockSize); 220 | Serial.print(F(", numBlocks=")); 221 | Serial.println(numBlocks); 222 | 223 | Serial.println(F("----------------------------------")); 224 | uint8_t readBuffer[blockSize]; 225 | for (int no=0; no 22 | #include "PN5180ISO14443.h" 23 | #include "PN5180.h" 24 | #include "Debug.h" 25 | 26 | PN5180ISO14443::PN5180ISO14443(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi) 27 | : PN5180(SSpin, BUSYpin, RSTpin, spi) { 28 | } 29 | 30 | bool PN5180ISO14443::setupRF() { 31 | PN5180DEBUG(F("Loading RF-Configuration...\n")); 32 | if (loadRFConfig(0x00, 0x80)) { // ISO14443 parameters 33 | PN5180DEBUG(F("done.\n")); 34 | } 35 | else return false; 36 | 37 | PN5180DEBUG(F("Turning ON RF field...\n")); 38 | if (setRF_on()) { 39 | PN5180DEBUG(F("done.\n")); 40 | } 41 | else return false; 42 | 43 | return true; 44 | } 45 | 46 | uint16_t PN5180ISO14443::rxBytesReceived() { 47 | uint32_t rxStatus; 48 | uint16_t len = 0; 49 | readRegister(RX_STATUS, &rxStatus); 50 | // Lower 9 bits has length 51 | len = (uint16_t)(rxStatus & 0x000001ff); 52 | return len; 53 | } 54 | 55 | 56 | 57 | /* 58 | * buffer : must be 10 byte array 59 | * buffer[0-1] is ATQA 60 | * buffer[2] is sak 61 | * buffer[3..6] is 4 byte UID 62 | * buffer[7..9] is remaining 3 bytes of UID for 7 Byte UID tags 63 | * kind : 0 we send REQA, 1 we send WUPA 64 | * 65 | * return value: the uid length: 66 | * - zero if no tag was recognized 67 | * - -1 general error 68 | * - -2 card in field but with error 69 | * - single Size UID (4 byte) 70 | * - double Size UID (7 byte) 71 | * - triple Size UID (10 byte) - not yet supported 72 | */ 73 | int8_t PN5180ISO14443::activateTypeA(uint8_t *buffer, uint8_t kind) { 74 | uint8_t cmd[7]; 75 | uint8_t uidLength = 0; 76 | 77 | // Load standard TypeA protocol already done in reset() 78 | if (!loadRFConfig(0x0, 0x80)) { 79 | PN5180DEBUG(F("*** ERROR: Load standard TypeA protocol failed!\n")); 80 | return -1; 81 | } 82 | // activate RF field 83 | setRF_on(); 84 | // wait RF-field to ramp-up 85 | delay(10); 86 | 87 | // OFF Crypto 88 | if (!writeRegisterWithAndMask(SYSTEM_CONFIG, 0xFFFFFFBF)) { 89 | PN5180DEBUG(F("*** ERROR: OFF Crypto failed!\n")); 90 | return -1; 91 | } 92 | // clear RX CRC 93 | if (!writeRegisterWithAndMask(CRC_RX_CONFIG, 0xFFFFFFFE)) { 94 | PN5180DEBUG(F("*** ERROR: Clear RX CRC failed!\n")); 95 | return -1; 96 | } 97 | // clear TX CRC 98 | if (!writeRegisterWithAndMask(CRC_TX_CONFIG, 0xFFFFFFFE)) { 99 | PN5180DEBUG(F("*** ERROR: Clear TX CRC failed!\n")); 100 | return -1; 101 | } 102 | 103 | // set the PN5180 into IDLE state 104 | if (!writeRegisterWithAndMask(SYSTEM_CONFIG, 0xFFFFFFF8)) { 105 | PN5180DEBUG(F("*** ERROR: set IDLE state failed!\n")); 106 | return -1; 107 | } 108 | 109 | // activate TRANSCEIVE routine 110 | if (!writeRegisterWithOrMask(SYSTEM_CONFIG, 0x00000003)) { 111 | PN5180DEBUG(F("*** ERROR: Activates TRANSCEIVE routine failed!\n")); 112 | return -1; 113 | } 114 | 115 | // wait for wait-transmit state 116 | PN5180TransceiveStat transceiveState = getTransceiveState(); 117 | if (PN5180_TS_WaitTransmit != transceiveState) { 118 | PN5180DEBUG(F("*** ERROR: Transceiver not in state WaitTransmit!?\n")); 119 | return -1; 120 | } 121 | 122 | /* uint8_t irqConfig = 0b0000000; // Set IRQ active low + clear IRQ-register 123 | writeEEprom(IRQ_PIN_CONFIG, &irqConfig, 1); 124 | // enable only RX_IRQ_STAT, TX_IRQ_STAT and general error IRQ 125 | writeRegister(IRQ_ENABLE, RX_IRQ_STAT | TX_IRQ_STAT | GENERAL_ERROR_IRQ_STAT); 126 | */ 127 | 128 | // clear all IRQs 129 | clearIRQStatus(0xffffffff); 130 | 131 | //Send REQA/WUPA, 7 bits in last byte 132 | cmd[0] = (kind == 0) ? 0x26 : 0x52; 133 | if (!sendData(cmd, 1, 0x07)) { 134 | PN5180DEBUG(F("*** ERROR: Send REQA/WUPA failed!\n")); 135 | return 0; 136 | } 137 | 138 | // wait some mSecs for end of RF receiption 139 | delay(10); 140 | 141 | // READ 2 bytes ATQA into buffer 142 | if (!readData(2, buffer)) { 143 | PN5180DEBUG(F("*** ERROR: READ 2 bytes ATQA failed!\n")); 144 | return 0; 145 | } 146 | 147 | // 148 | unsigned long startedWaiting = millis(); 149 | while (PN5180_TS_WaitTransmit != getTransceiveState()) { 150 | if (millis() - startedWaiting > 200) { 151 | PN5180DEBUG(F("*** ERROR: timeout in PN5180_TS_WaitTransmit!\n")); 152 | return -1; 153 | } 154 | } 155 | 156 | // clear all IRQs 157 | clearIRQStatus(0xffffffff); 158 | 159 | // send Anti collision 1, 8 bits in last byte 160 | cmd[0] = 0x93; 161 | cmd[1] = 0x20; 162 | if (!sendData(cmd, 2, 0x00)) { 163 | PN5180DEBUG(F("*** ERROR: Send Anti collision 1 failed!\n")); 164 | return -2; 165 | } 166 | 167 | // wait some mSecs for end of RF receiption 168 | delay(5); 169 | 170 | uint8_t numBytes = rxBytesReceived(); 171 | if (numBytes != 5) { 172 | PN5180DEBUG(F("*** ERROR: Read 5 bytes sak failed!\n")); 173 | return -2; 174 | }; 175 | // read 5 bytes sak, we will store at offset 2 for later usage 176 | if (!readData(5, cmd+2)) { 177 | Serial.println("Read 5 bytes failed!"); 178 | return -2; 179 | } 180 | // We do have a card now! enable CRC and send anticollision 181 | // save the first 4 bytes of UID 182 | for (int i = 0; i < 4; i++) buffer[i] = cmd[2 + i]; 183 | 184 | //Enable RX CRC calculation 185 | if (!writeRegisterWithOrMask(CRC_RX_CONFIG, 0x01)) 186 | return -2; 187 | //Enable TX CRC calculation 188 | if (!writeRegisterWithOrMask(CRC_TX_CONFIG, 0x01)) 189 | return -2; 190 | 191 | 192 | //Send Select anti collision 1, the remaining bytes are already in offset 2 onwards 193 | cmd[0] = 0x93; 194 | cmd[1] = 0x70; 195 | if (!sendData(cmd, 7, 0x00)) { 196 | // no remaining bytes, we have a 4 byte UID 197 | return 4; 198 | } 199 | //Read 1 byte SAK into buffer[2] 200 | if (!readData(1, buffer+2)) 201 | return -2; 202 | // Check if the tag is 4 Byte UID or 7 byte UID and requires anti collision 2 203 | // If Bit 3 is 0 it is 4 Byte UID 204 | if ((buffer[2] & 0x04) == 0) { 205 | // Take first 4 bytes of anti collision as UID store at offset 3 onwards. job done 206 | for (int i = 0; i < 4; i++) buffer[3+i] = cmd[2 + i]; 207 | uidLength = 4; 208 | } 209 | else { 210 | // Take First 3 bytes of UID, Ignore first byte 88(CT) 211 | if (cmd[2] != 0x88) 212 | return 0; 213 | for (int i = 0; i < 3; i++) buffer[3+i] = cmd[3 + i]; 214 | // Clear RX CRC 215 | if (!writeRegisterWithAndMask(CRC_RX_CONFIG, 0xFFFFFFFE)) 216 | return -2; 217 | // Clear TX CRC 218 | if (!writeRegisterWithAndMask(CRC_TX_CONFIG, 0xFFFFFFFE)) 219 | return -2; 220 | // Do anti collision 2 221 | cmd[0] = 0x95; 222 | cmd[1] = 0x20; 223 | if (!sendData(cmd, 2, 0x00)) 224 | return -2; 225 | //Read 5 bytes. we will store at offset 2 for later use 226 | if (!readData(5, cmd+2)) 227 | return -2; 228 | // first 4 bytes belongs to last 4 UID bytes, we keep it. 229 | for (int i = 0; i < 4; i++) { 230 | buffer[6 + i] = cmd[2+i]; 231 | } 232 | //Enable RX CRC calculation 233 | if (!writeRegisterWithOrMask(CRC_RX_CONFIG, 0x01)) 234 | return -2; 235 | //Enable TX CRC calculation 236 | if (!writeRegisterWithOrMask(CRC_TX_CONFIG, 0x01)) 237 | return -2; 238 | //Send Select anti collision 2 239 | cmd[0] = 0x95; 240 | cmd[1] = 0x70; 241 | if (!sendData(cmd, 7, 0x00)) 242 | return -2; 243 | //Read 1 byte SAK into buffer[2] 244 | if (!readData(1, buffer + 2)) 245 | return -2; 246 | uidLength = 7; 247 | } 248 | return uidLength; 249 | } 250 | 251 | bool PN5180ISO14443::mifareBlockRead(uint8_t blockno, uint8_t *buffer) { 252 | bool success = false; 253 | uint16_t len; 254 | uint8_t cmd[2]; 255 | // Send mifare command 30,blockno 256 | cmd[0] = 0x30; 257 | cmd[1] = blockno; 258 | if (!sendData(cmd, 2, 0x00)) 259 | return false; 260 | //Check if we have received any data from the tag 261 | delay(5); 262 | len = rxBytesReceived(); 263 | if (len == 16) { 264 | // READ 16 bytes into buffer 265 | if (readData(16, buffer)) 266 | success = true; 267 | } 268 | return success; 269 | } 270 | 271 | 272 | uint8_t PN5180ISO14443::mifareBlockWrite16(uint8_t blockno, uint8_t *buffer) { 273 | uint8_t cmd[2]; 274 | // Clear RX CRC 275 | writeRegisterWithAndMask(CRC_RX_CONFIG, 0xFFFFFFFE); 276 | 277 | // Mifare write part 1 278 | cmd[0] = 0xA0; 279 | cmd[1] = blockno; 280 | sendData(cmd, 2, 0x00); 281 | readData(1, cmd); 282 | 283 | // Mifare write part 2 284 | sendData(buffer,16, 0x00); 285 | delay(10); 286 | 287 | // Read ACK/NAK 288 | readData(1, cmd); 289 | 290 | //Enable RX CRC calculation 291 | writeRegisterWithOrMask(CRC_RX_CONFIG, 0x1); 292 | return cmd[0]; 293 | } 294 | 295 | bool PN5180ISO14443::mifareHalt() { 296 | uint8_t cmd[2]; 297 | //mifare Halt 298 | cmd[0] = 0x50; 299 | cmd[1] = 0x00; 300 | sendData(cmd, 2, 0x00); 301 | return true; 302 | } 303 | 304 | int8_t PN5180ISO14443::readCardSerial(uint8_t *buffer) { 305 | 306 | uint8_t response[10]; 307 | int8_t uidLength; 308 | // Always return 10 bytes 309 | // Offset 0..1 is ATQA 310 | // Offset 2 is SAK. 311 | // UID 4 bytes : offset 3 to 6 is UID, offset 7 to 9 to Zero 312 | // UID 7 bytes : offset 3 to 9 is UID 313 | for (int i = 0; i < 10; i++) response[i] = 0; 314 | // try to activate Type A until response or timeout 315 | uidLength = activateTypeA(response, 0); 316 | 317 | 318 | if (uidLength <= 0) 319 | return uidLength; 320 | // UID length must be at least 4 bytes 321 | if (uidLength < 4) 322 | return 0; 323 | if ((response[0] == 0xFF) && (response[1] == 0xFF)) 324 | uidLength = 0; 325 | 326 | // first UID byte should not be 0x00 or 0xFF 327 | if ((response[3] == 0x00) || (response[3] == 0xFF)) 328 | uidLength = 0; 329 | 330 | // check for valid uid, skip first byte (0x04) 331 | // 0x04 0x00 0xFF 0x00 => invalid uid 332 | bool validUID = false; 333 | for (int i = 1; i < uidLength; i++) { 334 | if ((response[i+3] != 0x00) && (response[i+3] != 0xFF)) { 335 | validUID = true; 336 | break; 337 | } 338 | } 339 | if (uidLength == 4) { 340 | if ((response[3] == 0x88)) { 341 | // must not be the CT-flag (0x88)! 342 | validUID = false; 343 | }; 344 | } 345 | if (uidLength == 7) { 346 | if ((response[6] == 0x88)) { 347 | // must not be the CT-flag (0x88)! 348 | validUID = false; 349 | }; 350 | if ((response[6] == 0x00) && (response[7] == 0x00) && (response[8] == 0x00) && (response[9] == 0x00)) { 351 | validUID = false; 352 | }; 353 | if ((response[6] == 0xFF) && (response[7] == 0xFF) && (response[8] == 0xFF) && (response[9] == 0xFF)) { 354 | validUID = false; 355 | }; 356 | }; 357 | // mifareHalt(); 358 | if (validUID) { 359 | for (int i = 0; i < uidLength; i++) buffer[i] = response[i+3]; 360 | return uidLength; 361 | } else { 362 | return 0; 363 | } 364 | } 365 | 366 | bool PN5180ISO14443::isCardPresent() { 367 | 368 | uint8_t buffer[10]; 369 | return (readCardSerial(buffer) >=4); 370 | } 371 | 372 | -------------------------------------------------------------------------------- /examples/PN5180-LowPowerCardDetection/PN5180-LowPowerCardDetection.ino: -------------------------------------------------------------------------------- 1 | // NAME: LowPowerCardDetection.ino 2 | // 3 | // DESC: Example usage of the PN5180 library for the PN5180-NFC Module 4 | // from NXP Semiconductors. 5 | // This example uses the PN5180 low power card detection mode (LPCD) 6 | // 7 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 8 | // Copyright (c) 2019 by Dirk Carstensen. 9 | // 10 | // This file is part of the PN5180 library for the Arduino environment. 11 | // 12 | // This library is free software; you can redistribute it and/or 13 | // modify it under the terms of the GNU Lesser General Public 14 | // License as published by the Free Software Foundation; either 15 | // version 2.1 of the License, or (at your option) any later version. 16 | // 17 | // This library is distributed in the hope that it will be useful, 18 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | // Lesser General Public License for more details. 21 | // 22 | // BEWARE: SPI with an Arduino to a PN5180 module has to be at a level of 3.3V 23 | // use of logic-level converters from 5V->3.3V is absolutly neccessary 24 | // on most Arduinos for all input pins of PN5180! 25 | // If used with an ESP-32, there is no need for a logic-level converter, since 26 | // it operates on 3.3V already. 27 | // 28 | // Arduino <-> Level Converter <-> PN5180 pin mapping: 29 | // 5V <--> 5V 30 | // 3.3V <--> 3.3V 31 | // GND <--> GND 32 | // 5V <-> HV 33 | // GND <-> GND (HV) 34 | // LV <-> 3.3V 35 | // GND (LV) <-> GND 36 | // SCLK,13 <-> HV1 - LV1 --> SCLK 37 | // MISO,12 <--- <-- MISO 38 | // MOSI,11 <-> HV3 - LV3 --> MOSI 39 | // SS,10 <-> HV4 - LV4 --> NSS (=Not SS -> active LOW) 40 | // BUSY,9 <--- BUSY 41 | // Reset,7 <-> HV2 - LV2 --> RST 42 | // IRQ,6 <--- IRQ 43 | // 44 | // ESP-32 <--> PN5180 pin mapping: 45 | // 3.3V <--> 3.3V 46 | // 5V <--> 3.3V 47 | // GND <--> GND 48 | // SCLK, 18 --> SCLK 49 | // MISO, 19 <-- MISO 50 | // MOSI, 23 --> MOSI 51 | // SS, 16 --> NSS (=Not SS -> active LOW) 52 | // BUSY, 5 <-- BUSY 53 | // Reset, 17 --> RST 54 | // GPIO_NUM_34 <--- IRQ 55 | // 56 | 57 | 58 | #if defined(ARDUINO_ARCH_ESP32) 59 | #include 60 | #endif 61 | #include 62 | #include 63 | #include 64 | 65 | #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_AVR_NANO) 66 | 67 | #define PN5180_NSS 10 68 | #define PN5180_BUSY 9 69 | #define PN5180_RST 7 70 | #define PN5180_IRQ 6 71 | 72 | #elif defined(ARDUINO_ARCH_ESP32) 73 | 74 | #define PN5180_NSS 16 75 | #define PN5180_BUSY 5 76 | #define PN5180_RST 17 77 | #define PN5180_IRQ GPIO_NUM_15 78 | 79 | #else 80 | #error Please define your pinout here! 81 | #endif 82 | 83 | #if defined(ARDUINO_ARCH_ESP32) 84 | /* 85 | * Method to print the reason by which ESP32 has been awaken from sleep 86 | */ 87 | void print_wakeup_reason(){ 88 | esp_sleep_wakeup_cause_t wakeup_reason; 89 | 90 | wakeup_reason = esp_sleep_get_wakeup_cause(); 91 | 92 | switch(wakeup_reason) 93 | { 94 | case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; 95 | case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; 96 | case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break; 97 | case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break; 98 | case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break; 99 | default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; 100 | } 101 | } 102 | #endif 103 | 104 | 105 | PN5180ISO14443 nfc(PN5180_NSS, PN5180_BUSY, PN5180_RST); 106 | PN5180ISO15693 nfc15693(PN5180_NSS, PN5180_BUSY, PN5180_RST); 107 | 108 | uint16_t sleepTimeMS = 0x3FF; 109 | 110 | void setup() { 111 | Serial.begin(115200); 112 | Serial.println(F("==================================")); 113 | Serial.println(F("Uploaded: " __DATE__ " " __TIME__)); 114 | #if defined(ARDUINO_ARCH_ESP32) 115 | // save power 116 | WiFi.mode(WIFI_OFF); 117 | btStop(); 118 | // configure wakeup pin for deep-sleep mode 119 | esp_sleep_enable_ext0_wakeup(PN5180_IRQ, 1); //1 = High, 0 = Low 120 | 121 | Serial.println(F("============================================")); 122 | Serial.println(F("LPCD detection demo with deep-sleep on ESP32")); 123 | print_wakeup_reason(); 124 | Serial.println(F("============================================")); 125 | #else 126 | Serial.println(F("PN5180 LPCD Demo Sketch")); 127 | #endif 128 | Serial.println(""); 129 | pinMode(PN5180_IRQ, INPUT); 130 | nfc.begin(); 131 | nfc15693.begin(); 132 | 133 | Serial.println(F("----------------------------------")); 134 | Serial.println(F("PN5180 Hard-Reset...")); 135 | nfc.reset(); 136 | 137 | Serial.println(F("----------------------------------")); 138 | Serial.println(F("Reading product version...")); 139 | uint8_t productVersion[2]; 140 | nfc.readEEprom(PRODUCT_VERSION, productVersion, sizeof(productVersion)); 141 | Serial.print(F("Product version=")); 142 | Serial.print(productVersion[1]); 143 | Serial.print("."); 144 | Serial.println(productVersion[0]); 145 | 146 | if (0xff == productVersion[1]) { // if product version 255, the initialization failed 147 | Serial.println(F("Initialization failed!?")); 148 | Serial.println(F("Press reset to restart...")); 149 | Serial.flush(); 150 | exit(-1); // halt 151 | } 152 | 153 | Serial.println(F("----------------------------------")); 154 | Serial.println(F("Reading firmware version...")); 155 | uint8_t firmwareVersion[2]; 156 | nfc.readEEprom(FIRMWARE_VERSION, firmwareVersion, sizeof(firmwareVersion)); 157 | Serial.print(F("Firmware version=")); 158 | Serial.print(firmwareVersion[1]); 159 | Serial.print("."); 160 | Serial.println(firmwareVersion[0]); 161 | 162 | if (firmwareVersion[1] < 4) { 163 | Serial.println("This LPCD demo might work only for firmware version 4.0!!!"); 164 | }; 165 | 166 | Serial.println(F("----------------------------------")); 167 | Serial.println(F("Reading EEPROM version...")); 168 | uint8_t eepromVersion[2]; 169 | nfc.readEEprom(EEPROM_VERSION, eepromVersion, sizeof(eepromVersion)); 170 | Serial.print(F("EEPROM version=")); 171 | Serial.print(eepromVersion[1]); 172 | Serial.print("."); 173 | Serial.println(eepromVersion[0]); 174 | Serial.println(F("----------------------------------")); 175 | Serial.println(F("Reading IRQ-Pin...")); 176 | uint8_t irqPin[1]; 177 | nfc.readEEprom(IRQ_PIN_CONFIG, irqPin, sizeof(irqPin)); 178 | Serial.print(F("irqPin=")); 179 | Serial.println(irqPin[0]); 180 | 181 | Serial.println(F("----------------------------------")); 182 | Serial.println(F("start LPCD...")); 183 | 184 | 185 | uint8_t data[255]; 186 | uint8_t response[256]; 187 | // LPCD_FIELD_ON_TIME (0x36) 188 | uint8_t fieldOn = 0xF0; 189 | data[0] = fieldOn; 190 | nfc.writeEEprom(0x36, data, 1); 191 | nfc.readEEprom(0x36, response, 1); 192 | fieldOn = response[0]; 193 | Serial.print("LPCD-fieldOn time: "); 194 | Serial.println(fieldOn, HEX); 195 | 196 | // LPCD_THRESHOLD (0x37) 197 | uint8_t threshold = 0x04; 198 | data[0] = threshold; 199 | nfc.writeEEprom(0x37, data, 1); 200 | nfc.readEEprom(0x37, response, 1); 201 | threshold = response[0]; 202 | Serial.print("LPCD-threshold: "); 203 | Serial.println(threshold, HEX); 204 | 205 | // LPCD_REFVAL_GPO_CONTROL (0x38) 206 | uint8_t lpcdMode = 0x01; // 1 = LPCD SELF CALIBRATION 207 | data[0] = lpcdMode; 208 | nfc.writeEEprom(0x38, data, 1); 209 | nfc.readEEprom(0x38, response, 1); 210 | lpcdMode = response[0]; 211 | Serial.print("lpcdMode: "); 212 | Serial.println(lpcdMode, HEX); 213 | 214 | // LPCD_GPO_TOGGLE_BEFORE_FIELD_ON (0x39) 215 | uint8_t beforeFieldOn = 0xF0; 216 | data[0] = beforeFieldOn; 217 | nfc.writeEEprom(0x39, data, 1); 218 | nfc.readEEprom(0x39, response, 1); 219 | beforeFieldOn = response[0]; 220 | Serial.print("beforeFieldOn: "); 221 | Serial.println(beforeFieldOn, HEX); 222 | 223 | // LPCD_GPO_TOGGLE_AFTER_FIELD_ON (0x3A) 224 | uint8_t afterFieldOn = 0xF0; 225 | data[0] = afterFieldOn; 226 | nfc.writeEEprom(0x3A, data, 1); 227 | nfc.readEEprom(0x3A, response, 1); 228 | afterFieldOn = response[0]; 229 | Serial.print("afterFieldOn: "); 230 | Serial.println(afterFieldOn, HEX); 231 | delay(100); 232 | 233 | // turn on LPCD in self calibration mode 234 | if (nfc.switchToLPCD(sleepTimeMS)) { 235 | Serial.println("switchToLPCD success"); 236 | } else { 237 | Serial.println("switchToLPCD failed"); 238 | } 239 | // ++ go to sleep ++ 240 | } 241 | 242 | uint32_t loopCnt = 0; 243 | 244 | 245 | // read cards loop 246 | void loop() { 247 | if (digitalRead(PN5180_IRQ) == HIGH) { 248 | // LPCD detection irq 249 | showIRQStatus(nfc.getIRQStatus()); 250 | uint32_t u; 251 | nfc.readRegister(0x26, &u); 252 | Serial.print("LPCD_REFERENCE_VALUE: "); 253 | Serial.println(u, HEX); 254 | nfc.clearIRQStatus(0xffffffff); 255 | nfc.reset(); 256 | // try to read the UID for an ISO-14443 card 257 | uint8_t uid[10]; 258 | nfc.setupRF(); 259 | if (nfc.isCardPresent()) { 260 | uint8_t uidLength = nfc.readCardSerial(uid); 261 | if (uidLength > 0) { 262 | Serial.print(F("ISO14443 card found, UID=")); 263 | for (int i=0; i 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /PN5180.cpp: -------------------------------------------------------------------------------- 1 | // NAME: PN5180.cpp 2 | // 3 | // DESC: Implementation of PN5180 class. 4 | // 5 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | //#define DEBUG 1 20 | 21 | #include 22 | #include "PN5180.h" 23 | #include "Debug.h" 24 | 25 | uint8_t PN5180::readBuffer[508]; 26 | 27 | PN5180::PN5180(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi) : 28 | PN5180_NSS(SSpin), 29 | PN5180_BUSY(BUSYpin), 30 | PN5180_RST(RSTpin), 31 | PN5180_SPI(spi) 32 | { 33 | /* 34 | * 11.4.1 Physical Host Interface 35 | * The interface of the PN5180 to a host microcontroller is based on a SPI interface, 36 | * extended by signal line BUSY. The maximum SPI speed is 7 Mbps and fixed to CPOL 37 | * = 0 and CPHA = 0. 38 | */ 39 | // Settings for PN5180: 7Mbps, MSB first, SPI_MODE0 (CPOL=0, CPHA=0) 40 | SPI_SETTINGS = SPISettings(7000000, MSBFIRST, SPI_MODE0); 41 | } 42 | 43 | 44 | void PN5180::begin() { 45 | pinMode(PN5180_NSS, OUTPUT); 46 | pinMode(PN5180_BUSY, INPUT); 47 | pinMode(PN5180_RST, OUTPUT); 48 | 49 | digitalWrite(PN5180_NSS, HIGH); // disable 50 | digitalWrite(PN5180_RST, HIGH); // no reset 51 | 52 | PN5180_SPI.begin(); 53 | PN5180DEBUG(F("SPI pinout: ")); 54 | PN5180DEBUG(F("SS=")); PN5180DEBUG(SS); 55 | PN5180DEBUG(F(", MOSI=")); PN5180DEBUG(MOSI); 56 | PN5180DEBUG(F(", MISO=")); PN5180DEBUG(MISO); 57 | PN5180DEBUG(F(", SCK=")); PN5180DEBUG(SCK); 58 | PN5180DEBUG("\n"); 59 | } 60 | 61 | void PN5180::end() { 62 | digitalWrite(PN5180_NSS, HIGH); // disable 63 | PN5180_SPI.end(); 64 | } 65 | 66 | /* 67 | * WRITE_REGISTER - 0x00 68 | * This command is used to write a 32-bit value (little endian) to a configuration register. 69 | * The address of the register must exist. If the condition is not fulfilled, an exception is 70 | * raised. 71 | */ 72 | bool PN5180::writeRegister(uint8_t reg, uint32_t value) { 73 | /* 74 | For all 4 byte command parameter transfers (e.g. register values), the payload 75 | parameters passed follow the little endian approach (Least Significant Byte first). 76 | */ 77 | uint8_t *p = (uint8_t*)&value; 78 | uint8_t buf[6] = { PN5180_WRITE_REGISTER, reg, p[0], p[1], p[2], p[3] }; 79 | 80 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 81 | transceiveCommand(buf, 6); 82 | PN5180_SPI.endTransaction(); 83 | 84 | return true; 85 | } 86 | 87 | /* 88 | * WRITE_REGISTER_OR_MASK - 0x01 89 | * This command modifies the content of a register using a logical OR operation. The 90 | * content of the register is read and a logical OR operation is performed with the provided 91 | * mask. The modified content is written back to the register. 92 | * The address of the register must exist. If the condition is not fulfilled, an exception is 93 | * raised. 94 | */ 95 | bool PN5180::writeRegisterWithOrMask(uint8_t reg, uint32_t mask) { 96 | uint8_t *p = (uint8_t*)&mask; 97 | uint8_t buf[6] = { PN5180_WRITE_REGISTER_OR_MASK, reg, p[0], p[1], p[2], p[3] }; 98 | 99 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 100 | transceiveCommand(buf, 6); 101 | PN5180_SPI.endTransaction(); 102 | 103 | return true; 104 | } 105 | 106 | /* 107 | * WRITE _REGISTER_AND_MASK - 0x02 108 | * This command modifies the content of a register using a logical AND operation. The 109 | * content of the register is read and a logical AND operation is performed with the provided 110 | * mask. The modified content is written back to the register. 111 | * The address of the register must exist. If the condition is not fulfilled, an exception is 112 | * raised. 113 | */ 114 | bool PN5180::writeRegisterWithAndMask(uint8_t reg, uint32_t mask) { 115 | uint8_t *p = (uint8_t*)&mask; 116 | uint8_t buf[6] = { PN5180_WRITE_REGISTER_AND_MASK, reg, p[0], p[1], p[2], p[3] }; 117 | 118 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 119 | transceiveCommand(buf, 6); 120 | PN5180_SPI.endTransaction(); 121 | 122 | return true; 123 | } 124 | 125 | /* 126 | * READ_REGISTER - 0x04 127 | * This command is used to read the content of a configuration register. The content of the 128 | * register is returned in the 4 byte response. 129 | * The address of the register must exist. If the condition is not fulfilled, an exception is 130 | * raised. 131 | */ 132 | bool PN5180::readRegister(uint8_t reg, uint32_t *value) { 133 | uint8_t cmd[2] = { PN5180_READ_REGISTER, reg }; 134 | 135 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 136 | transceiveCommand(cmd, 2, (uint8_t*)value, 4); 137 | PN5180_SPI.endTransaction(); 138 | 139 | #ifdef DEBUG 140 | switch (reg) { 141 | case IRQ_STATUS: 142 | PN5180DEBUG(F("=> IRQ-Status=0x")); 143 | PN5180DEBUG(formatHex(*value)); 144 | PN5180DEBUG(F(":")); 145 | if (*value & 0x80000) PN5180DEBUG(" LPCD"); 146 | if (*value & 0x40000) PN5180DEBUG(" HVErr"); 147 | if (*value & 0x20000) PN5180DEBUG(" GeneralErr"); 148 | if (*value & 0x10000) PN5180DEBUG(" TempSensErr"); 149 | if (*value & 0x8000) PN5180DEBUG(" RxSCDet"); 150 | if (*value & 0x4000) PN5180DEBUG(" RxSOFDet"); 151 | if (*value & 0x2000) PN5180DEBUG(" Timer2"); 152 | if (*value & 0x1000) PN5180DEBUG(" Timer1"); 153 | if (*value & 0x800) PN5180DEBUG(" Timer0"); 154 | if (*value & 0x400) PN5180DEBUG(" RFActiveErr"); 155 | if (*value & 0x200) PN5180DEBUG(" TxRFOn"); 156 | if (*value & 0x100) PN5180DEBUG(" TxRFOff"); 157 | if (*value & 0x80) PN5180DEBUG(" RFOnDet"); 158 | if (*value & 0x40) PN5180DEBUG(" RFOffDet"); 159 | if (*value & 0x20) PN5180DEBUG(" StateChange"); 160 | if (*value & 0x10) PN5180DEBUG(" CardAct"); 161 | if (*value & 0x8) PN5180DEBUG(" ModeDet"); 162 | if (*value & 0x4) PN5180DEBUG(" Idle"); 163 | if (*value & 0x2) PN5180DEBUG(" TxDone"); 164 | if (*value & 0x1) PN5180DEBUG(" RxDone"); 165 | PN5180DEBUG("\n"); 166 | break; 167 | 168 | case RF_STATUS: { 169 | PN5180DEBUG(F("=> RF-Status=0x")); 170 | PN5180DEBUG(formatHex(*value)); 171 | PN5180DEBUG(F(": ")); 172 | uint8_t xstate = (*value >> 24) & 0x07; 173 | switch (xstate) { 174 | #define S(s) case PN5180_TS_ ## s: PN5180DEBUG(#s); break; 175 | S(Idle) 176 | S(WaitTransmit) 177 | S(Transmitting) 178 | S(WaitReceive) 179 | S(WaitForData) 180 | S(Receiving) 181 | S(LoopBack) 182 | S(RESERVED) 183 | #undef S 184 | default: 185 | PN5180DEBUG(F("xstate=0x")); 186 | PN5180DEBUG(formatHex((uint8_t) xstate)); 187 | break; 188 | } 189 | 190 | PN5180DEBUG(" DPC=0x"); 191 | PN5180DEBUG(formatHex(uint8_t((*value >> 20) & 0x0F))); 192 | if (*value & 0x80000) PN5180DEBUG(" DPLL"); 193 | if (*value & 0x40000) PN5180DEBUG(" CRC-ok"); 194 | if (*value & 0x20000) PN5180DEBUG(" TxRF"); 195 | if (*value & 0x10000) PN5180DEBUG(" RFDet"); 196 | 197 | switch ((*value >> 13) & 0x07) { 198 | case 1: PN5180DEBUG(" [Ext RF in TIDT]"); break; 199 | case 2: PN5180DEBUG(" [Ext RF in TADT]"); break; 200 | case 3: PN5180DEBUG(" [NO ext RF in TADT]"); break; 201 | case 4: PN5180DEBUG(" [Ext RF cutoff]"); break; 202 | } 203 | 204 | if (*value & 0x1000) PN5180DEBUG(" Rx-en"); 205 | if (*value & 0x800) PN5180DEBUG(" Tx-act"); 206 | if (*value & 0x400) PN5180DEBUG(" Rx-act"); 207 | PN5180DEBUG(" AGC=0x"); 208 | PN5180DEBUG(*value & 0x3FF); 209 | PN5180DEBUG("\n"); 210 | break; 211 | } 212 | 213 | case RX_STATUS: { 214 | PN5180DEBUG(F("=> RX-Status=0x")); 215 | PN5180DEBUG(formatHex(*value)); 216 | PN5180DEBUG(F(":")); 217 | 218 | if (*value & 0x20000) PN5180DEBUG(" ProtoErr"); 219 | if (*value & 0x10000) PN5180DEBUG(" DataErr"); 220 | if (*value & 0x40000) PN5180DEBUG(" CollDet"); 221 | 222 | uint8_t coll_pos = (*value >> 19) & 0x7F; 223 | if (coll_pos) { 224 | PN5180DEBUG(F(" coll_pos=")); 225 | PN5180DEBUG(coll_pos); 226 | } 227 | 228 | uint8_t num_frames = (*value >> 9) & 0x0F; 229 | if (num_frames) { 230 | PN5180DEBUG(F(" frames=")); 231 | PN5180DEBUG(num_frames); 232 | } 233 | 234 | uint16_t num_bytes = *value & 0x1FF; 235 | if (num_bytes) { 236 | PN5180DEBUG(F(" bytes=")); 237 | PN5180DEBUG(num_bytes); 238 | } 239 | 240 | uint8_t last_bits = (*value >> 13) & 0x03; 241 | if (last_bits) { 242 | PN5180DEBUG(F(" last_bits=")); 243 | PN5180DEBUG(last_bits); 244 | } 245 | PN5180DEBUG("\n"); 246 | break; 247 | } 248 | } 249 | #endif 250 | 251 | return true; 252 | } 253 | 254 | /* 255 | * WRITE_EEPROM - 0x06 256 | */ 257 | bool PN5180::writeEEprom(uint8_t addr, uint8_t *buffer, uint8_t len) { 258 | uint8_t cmd[len + 2]; 259 | cmd[0] = PN5180_WRITE_EEPROM; 260 | cmd[1] = addr; 261 | for (int i = 0; i < len; i++) cmd[2 + i] = buffer[i]; 262 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 263 | transceiveCommand(cmd, len + 2); 264 | PN5180_SPI.endTransaction(); 265 | return true; 266 | } 267 | 268 | /* 269 | * READ_EEPROM - 0x07 270 | * This command is used to read data from EEPROM memory area. The field 'Address' 271 | * indicates the start address of the read operation. The field Length indicates the number 272 | * of bytes to read. The response contains the data read from EEPROM (content of the 273 | * EEPROM); The data is read in sequentially increasing order starting with the given 274 | * address. 275 | * EEPROM Address must be in the range from 0 to 254, inclusive. Read operation must 276 | * not go beyond EEPROM address 254. If the condition is not fulfilled, an exception is 277 | * raised. 278 | */ 279 | bool PN5180::readEEprom(uint8_t addr, uint8_t *buffer, int len) { 280 | if ((addr > 254) || ((addr+len) > 254)) { 281 | PN5180DEBUG(F("ERROR: Reading beyond addr 254!\n")); 282 | return false; 283 | } 284 | 285 | uint8_t cmd[3] = { PN5180_READ_EEPROM, addr, uint8_t(len) }; 286 | 287 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 288 | transceiveCommand(cmd, 3, buffer, len); 289 | PN5180_SPI.endTransaction(); 290 | 291 | return true; 292 | } 293 | 294 | 295 | /* 296 | * SEND_DATA - 0x09 297 | * This command writes data to the RF transmission buffer and starts the RF transmission. 298 | * The parameter ‘Number of valid bits in last Byte’ indicates the exact number of bits to be 299 | * transmitted for the last byte (for non-byte aligned frames). 300 | * Precondition: Host shall configure the Transceiver by setting the register 301 | * SYSTEM_CONFIG.COMMAND to 0x3 before using the SEND_DATA command, as 302 | * the command SEND_DATA is only writing data to the transmission buffer and starts the 303 | * transmission but does not perform any configuration. 304 | * The size of ‘Tx Data’ field must be in the range from 0 to 260, inclusive (the 0 byte length 305 | * allows a symbol only transmission when the TX_DATA_ENABLE is cleared).‘Number of 306 | * valid bits in last Byte’ field must be in the range from 0 to 7. The command must not be 307 | * called during an ongoing RF transmission. Transceiver must be in ‘WaitTransmit’ state 308 | * with ‘Transceive’ command set. If the condition is not fulfilled, an exception is raised. 309 | */ 310 | bool PN5180::sendData(uint8_t *data, int len, uint8_t validBits) { 311 | if (len > 260) { 312 | PN5180DEBUG(F("ERROR: sendData with more than 260 bytes is not supported!\n")); 313 | return false; 314 | } 315 | 316 | uint8_t buffer[len+2]; 317 | buffer[0] = PN5180_SEND_DATA; 318 | buffer[1] = validBits; // number of valid bits of last byte are transmitted (0 = all bits are transmitted) 319 | for (int i=0; i 508) { 359 | Serial.println(F("*** FATAL: Reading more than 508 bytes is not supported!")); 360 | return 0L; 361 | } 362 | 363 | uint8_t cmd[2] = { PN5180_READ_DATA, 0x00 }; 364 | 365 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 366 | transceiveCommand(cmd, 2, readBuffer, len); 367 | PN5180_SPI.endTransaction(); 368 | 369 | return readBuffer; 370 | } 371 | 372 | bool PN5180::readData(uint8_t len, uint8_t *buffer) { 373 | if (len > 508) { 374 | return false; 375 | } 376 | uint8_t cmd[2] = { PN5180_READ_DATA, 0x00 }; 377 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 378 | bool success = transceiveCommand(cmd, 2, buffer, len); 379 | PN5180_SPI.endTransaction(); 380 | return success; 381 | } 382 | 383 | /* prepare LPCD registers */ 384 | bool PN5180::prepareLPCD() { 385 | //=======================================LPCD CONFIG================================================================================ 386 | PN5180DEBUG(F("----------------------------------\n")); 387 | PN5180DEBUG(F("prepare LPCD...\n")); 388 | 389 | uint8_t data[255]; 390 | uint8_t response[256]; 391 | //1. Set Fieldon time LPCD_FIELD_ON_TIME (0x36) 392 | uint8_t fieldOn = 0xF0;//0x## -> ##(base 10) x 8μs + 62 μs 393 | data[0] = fieldOn; 394 | writeEEprom(0x36, data, 1); 395 | readEEprom(0x36, response, 1); 396 | fieldOn = response[0]; 397 | PN5180DEBUG("LPCD-fieldOn time: "); 398 | PN5180DEBUG(formatHex(fieldOn)); 399 | PN5180DEBUG("\n"); 400 | 401 | //2. Set threshold level AGC_LPCD_THRESHOLD @ EEPROM 0x37 402 | uint8_t threshold = 0x03; 403 | data[0] = threshold; 404 | writeEEprom(0x37, data, 1); 405 | readEEprom(0x37, response, 1); 406 | threshold = response[0]; 407 | PN5180DEBUG("LPCD-threshold: "); 408 | PN5180DEBUG(formatHex(threshold)); 409 | PN5180DEBUG("\n"); 410 | 411 | //3. Select LPCD mode LPCD_REFVAL_GPO_CONTROL (0x38) 412 | uint8_t lpcdMode = 0x01; // 1 = LPCD SELF CALIBRATION 413 | // 0 = LPCD AUTO CALIBRATION (this mode does not work, should look more into it, no reason why it shouldn't work) 414 | data[0] = lpcdMode; 415 | writeEEprom(0x38, data, 1); 416 | readEEprom(0x38, response, 1); 417 | lpcdMode = response[0]; 418 | PN5180DEBUG("lpcdMode: "); 419 | PN5180DEBUG(formatHex(lpcdMode)); 420 | PN5180DEBUG("\n"); 421 | 422 | // LPCD_GPO_TOGGLE_BEFORE_FIELD_ON (0x39) 423 | uint8_t beforeFieldOn = 0xF0; 424 | data[0] = beforeFieldOn; 425 | writeEEprom(0x39, data, 1); 426 | readEEprom(0x39, response, 1); 427 | beforeFieldOn = response[0]; 428 | PN5180DEBUG("beforeFieldOn: "); 429 | PN5180DEBUG(formatHex(beforeFieldOn)); 430 | PN5180DEBUG("\n"); 431 | 432 | // LPCD_GPO_TOGGLE_AFTER_FIELD_ON (0x3A) 433 | uint8_t afterFieldOn = 0xF0; 434 | data[0] = afterFieldOn; 435 | writeEEprom(0x3A, data, 1); 436 | readEEprom(0x3A, response, 1); 437 | afterFieldOn = response[0]; 438 | PN5180DEBUG("afterFieldOn: "); 439 | PN5180DEBUG(formatHex(afterFieldOn)); 440 | PN5180DEBUG("\n"); 441 | delay(100); 442 | return true; 443 | } 444 | 445 | /* switch the mode to LPCD (low power card detection) 446 | * Parameter 'wakeupCounterInMs' must be in the range from 0x0 - 0xA82 447 | * max. wake-up time is 2960 ms. 448 | */ 449 | bool PN5180::switchToLPCD(uint16_t wakeupCounterInMs) { 450 | // clear all IRQ flags 451 | clearIRQStatus(0xffffffff); 452 | // enable only LPCD and general error IRQ 453 | writeRegister(IRQ_ENABLE, LPCD_IRQ_STAT | GENERAL_ERROR_IRQ_STAT); 454 | // switch mode to LPCD 455 | uint8_t cmd[4] = { PN5180_SWITCH_MODE, 0x01, (uint8_t)(wakeupCounterInMs & 0xFF), (uint8_t)((wakeupCounterInMs >> 8U) & 0xFF) }; 456 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 457 | bool success = transceiveCommand(cmd, sizeof(cmd)); 458 | PN5180_SPI.endTransaction(); 459 | return success; 460 | } 461 | 462 | /* 463 | * LOAD_RF_CONFIG - 0x11 464 | * Parameter 'Transmitter Configuration' must be in the range from 0x0 - 0x1C, inclusive. If 465 | * the transmitter parameter is 0xFF, transmitter configuration is not changed. 466 | * Field 'Receiver Configuration' must be in the range from 0x80 - 0x9C, inclusive. If the 467 | * receiver parameter is 0xFF, the receiver configuration is not changed. If the condition is 468 | * not fulfilled, an exception is raised. 469 | * The transmitter and receiver configuration shall always be configured for the same 470 | * transmission/reception speed. No error is returned in case this condition is not taken into 471 | * account. 472 | * 473 | * Transmitter: RF Protocol Speed Receiver: RF Protocol Speed 474 | * configuration (kbit/s) configuration (kbit/s) 475 | * byte (hex) byte (hex) 476 | * ---------------------------------------------------------------------------------------------- 477 | * ->0D ISO 15693 ASK100 26 8D ISO 15693 26 478 | * 0E ISO 15693 ASK10 26 8E ISO 15693 53 479 | */ 480 | bool PN5180::loadRFConfig(uint8_t txConf, uint8_t rxConf) { 481 | uint8_t cmd[3] = { PN5180_LOAD_RF_CONFIG, txConf, rxConf }; 482 | 483 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 484 | transceiveCommand(cmd, 3); 485 | PN5180_SPI.endTransaction(); 486 | 487 | return true; 488 | } 489 | 490 | /* 491 | * RF_ON - 0x16 492 | * This command is used to switch on the internal RF field. If enabled the TX_RFON_IRQ is 493 | * set after the field is switched on. 494 | */ 495 | bool PN5180::setRF_on() { 496 | uint8_t cmd[2] = { PN5180_RF_ON, 0x00 }; 497 | 498 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 499 | transceiveCommand(cmd, 2); 500 | PN5180_SPI.endTransaction(); 501 | 502 | unsigned long startedWaiting = millis(); 503 | while (0 == (TX_RFON_IRQ_STAT & getIRQStatus())) { // wait for RF field to set up (max 500ms) 504 | if (millis() - startedWaiting > 500) { 505 | PN5180DEBUG(F("Timeout setting RF ON\n")); 506 | return false; 507 | } 508 | }; 509 | 510 | clearIRQStatus(TX_RFON_IRQ_STAT); 511 | return true; 512 | } 513 | 514 | /* 515 | * RF_OFF - 0x17 516 | * This command is used to switch off the internal RF field. If enabled, the TX_RFOFF_IRQ 517 | * is set after the field is switched off. 518 | */ 519 | bool PN5180::setRF_off() { 520 | uint8_t cmd[2] { PN5180_RF_OFF, 0x00 }; 521 | 522 | PN5180_SPI.beginTransaction(SPI_SETTINGS); 523 | transceiveCommand(cmd, 2); 524 | PN5180_SPI.endTransaction(); 525 | 526 | unsigned long startedWaiting = millis(); 527 | while (0 == (TX_RFOFF_IRQ_STAT & getIRQStatus())) { // wait for RF field to shut down 528 | if (millis() - startedWaiting > 500) { 529 | PN5180DEBUG(F("Timeout setting RF OFF\n")); 530 | return false; 531 | } 532 | }; 533 | clearIRQStatus(TX_RFOFF_IRQ_STAT); 534 | return true; 535 | } 536 | 537 | //--------------------------------------------------------------------------------------------- 538 | 539 | /* 540 | 11.4.3.1 A Host Interface Command consists of either 1 or 2 SPI frames depending whether the 541 | host wants to write or read data from the PN5180. An SPI Frame consists of multiple 542 | bytes. 543 | 544 | All commands are packed into one SPI Frame. An SPI Frame consists of multiple bytes. 545 | No NSS toggles allowed during sending of an SPI frame. 546 | 547 | For all 4 byte command parameter transfers (e.g. register values), the payload 548 | parameters passed follow the little endian approach (Least Significant Byte first). 549 | 550 | Direct Instructions are built of a command code (1 Byte) and the instruction parameters 551 | (max. 260 bytes). The actual payload size depends on the instruction used. 552 | Responses to direct instructions contain only a payload field (no header). 553 | All instructions are bound to conditions. If at least one of the conditions is not fulfilled, an exception is 554 | raised. In case of an exception, the IRQ line of PN5180 is asserted and corresponding interrupt 555 | status register contain information on the exception. 556 | */ 557 | 558 | /* 559 | * A Host Interface Command consists of either 1 or 2 SPI frames depending whether the 560 | * host wants to write or read data from the PN5180. An SPI Frame consists of multiple 561 | * bytes. 562 | * All commands are packed into one SPI Frame. An SPI Frame consists of multiple bytes. 563 | * No NSS toggles allowed during sending of an SPI frame. 564 | * For all 4 byte command parameter transfers (e.g. register values), the payload 565 | * parameters passed follow the little endian approach (Least Significant Byte first). 566 | * The BUSY line is used to indicate that the system is BUSY and cannot receive any data 567 | * from a host. Recommendation for the BUSY line handling by the host: 568 | * 1. Assert NSS to Low 569 | * 2. Perform Data Exchange 570 | * 3. Wait until BUSY is high 571 | * 4. Deassert NSS 572 | * 5. Wait until BUSY is low 573 | * If there is a parameter error, the IRQ is set to ACTIVE and a GENERAL_ERROR_IRQ is set. 574 | */ 575 | bool PN5180::transceiveCommand(uint8_t *sendBuffer, size_t sendBufferLen, uint8_t *recvBuffer, size_t recvBufferLen) { 576 | #ifdef DEBUG 577 | PN5180DEBUG(F("Sending SPI frame: [")); 578 | for (uint8_t i=0; i 0) PN5180DEBUG(" "); 580 | 581 | // Decode commands and register names for debugging 582 | if (i == 0) { 583 | switch (sendBuffer[i]) { 584 | #define C(s) case PN5180_ ## s: PN5180DEBUG(#s); break; 585 | C(WRITE_REGISTER) 586 | C(WRITE_REGISTER_OR_MASK) 587 | C(WRITE_REGISTER_AND_MASK) 588 | C(READ_REGISTER) 589 | C(WRITE_EEPROM) 590 | C(READ_EEPROM) 591 | C(SEND_DATA) 592 | C(READ_DATA) 593 | C(SWITCH_MODE) 594 | C(LOAD_RF_CONFIG) 595 | C(RF_ON) 596 | C(RF_OFF) 597 | #undef C 598 | default: PN5180DEBUG(formatHex(sendBuffer[i])); break; 599 | } 600 | } else if (i == 1 && sendBuffer[0] <= PN5180_READ_REGISTER) { 601 | switch (sendBuffer[i]) { 602 | #define R(s) case s: PN5180DEBUG(#s); break; 603 | R(SYSTEM_CONFIG); 604 | R(IRQ_ENABLE); 605 | R(IRQ_STATUS); 606 | R(IRQ_CLEAR); 607 | R(TRANSCEIVE_CONTROL); 608 | R(TIMER1_RELOAD); 609 | R(TIMER1_CONFIG); 610 | R(RX_WAIT_CONFIG); 611 | R(CRC_RX_CONFIG); 612 | R(RX_STATUS); 613 | R(TX_WAIT_CONFIG); 614 | R(TX_CONFIG); 615 | R(CRC_TX_CONFIG); 616 | R(RF_STATUS); 617 | R(SYSTEM_STATUS); 618 | R(TEMP_CONTROL); 619 | R(AGC_REF_CONFIG); 620 | #undef R 621 | default: PN5180DEBUG(formatHex(sendBuffer[i])); break; 622 | } 623 | } else if (i == 1 && sendBuffer[0] <= PN5180_READ_EEPROM) { 624 | switch (sendBuffer[i]) { 625 | #define E(s) case s: PN5180DEBUG(#s); break; 626 | E(DIE_IDENTIFIER); 627 | E(PRODUCT_VERSION); 628 | E(FIRMWARE_VERSION); 629 | E(EEPROM_VERSION); 630 | E(IRQ_PIN_CONFIG); 631 | #undef E 632 | default: PN5180DEBUG(formatHex(sendBuffer[i])); break; 633 | } 634 | } else { 635 | PN5180DEBUG(formatHex(sendBuffer[i])); 636 | } 637 | } 638 | PN5180DEBUG("]\n"); 639 | #endif 640 | 641 | // 0. 642 | unsigned long startedWaiting = millis(); 643 | while (LOW != digitalRead(PN5180_BUSY)) { 644 | if (millis() - startedWaiting > commandTimeout) { 645 | PN5180DEBUG("transceiveCommand timeout (send/0)\n"); 646 | return false; 647 | }; 648 | }; // wait until busy is low 649 | // 1. 650 | digitalWrite(PN5180_NSS, LOW); delay(1); 651 | // 2. 652 | PN5180_SPI.transfer((uint8_t*)sendBuffer, sendBufferLen); 653 | // 3. 654 | startedWaiting = millis(); 655 | while (HIGH != digitalRead(PN5180_BUSY)) { 656 | if (millis() - startedWaiting > commandTimeout) { 657 | PN5180DEBUG("transceiveCommand timeout (send/3)\n"); 658 | return false; 659 | } 660 | }; // wait until busy is high 661 | // 4. 662 | digitalWrite(PN5180_NSS, HIGH); delay(1); 663 | // 5. 664 | startedWaiting = millis(); 665 | while (LOW != digitalRead(PN5180_BUSY)) { 666 | if (millis() - startedWaiting > commandTimeout) { 667 | PN5180DEBUG("transceiveCommand timeout (send/5)\n"); 668 | return false; 669 | }; 670 | }; // wait until busy is low 671 | 672 | // check, if write-only 673 | if ((0 == recvBuffer) || (0 == recvBufferLen)) return true; 674 | PN5180DEBUG(F("Receive SPI frame: ")); 675 | 676 | // 1. 677 | digitalWrite(PN5180_NSS, LOW); 678 | // 2. 679 | memset(recvBuffer, 0xFF, recvBufferLen); 680 | PN5180_SPI.transfer(recvBuffer, recvBufferLen); 681 | // 3. 682 | startedWaiting = millis(); //delay(1); 683 | while (HIGH != digitalRead(PN5180_BUSY)) { 684 | if (millis() - startedWaiting > commandTimeout) { 685 | PN5180DEBUG("transceiveCommand timeout (receive/3)\n"); 686 | return false; 687 | }; 688 | }; // wait until busy is high 689 | // 4. 690 | digitalWrite(PN5180_NSS, HIGH); 691 | // 5. 692 | startedWaiting = millis(); 693 | while (LOW != digitalRead(PN5180_BUSY)) { 694 | if (millis() - startedWaiting > commandTimeout) { 695 | PN5180DEBUG("transceiveCommand timeout (receive/5)\n"); 696 | return false; 697 | }; 698 | }; // wait until busy is low 699 | 700 | #ifdef DEBUG 701 | PN5180DEBUG(F("[")); 702 | for (uint8_t i=0; i 0) PN5180DEBUG(" "); 704 | PN5180DEBUG(formatHex(recvBuffer[i])); 705 | } 706 | PN5180DEBUG("]\n"); 707 | #endif 708 | 709 | return true; 710 | } 711 | 712 | /* 713 | * Reset NFC device 714 | */ 715 | void PN5180::reset() { 716 | PN5180DEBUG(F("Resetting PN5180...\n")); 717 | digitalWrite(PN5180_RST, LOW); // at least 10us required 718 | delay(1); 719 | digitalWrite(PN5180_RST, HIGH); // 2ms to ramp up required 720 | delay(5); 721 | 722 | unsigned long startedWaiting = millis(); 723 | while (0 == (IDLE_IRQ_STAT & getIRQStatus())) { 724 | // wait for system to start up (with timeout) 725 | if (millis() - startedWaiting > commandTimeout) { 726 | PN5180DEBUG(F("reset failed (timeout)!!!\n")); 727 | // try again with larger time 728 | digitalWrite(PN5180_RST, LOW); 729 | delay(10); 730 | digitalWrite(PN5180_RST, HIGH); 731 | delay(50); 732 | return; 733 | } 734 | } 735 | } 736 | 737 | /** 738 | * @name getInterrupt 739 | * @desc read interrupt status register and clear interrupt status 740 | */ 741 | uint32_t PN5180::getIRQStatus() { 742 | uint32_t irqStatus; 743 | readRegister(IRQ_STATUS, &irqStatus); 744 | return irqStatus; 745 | } 746 | 747 | bool PN5180::clearIRQStatus(uint32_t irqMask) { 748 | return writeRegister(IRQ_CLEAR, irqMask); 749 | } 750 | 751 | /* 752 | * Get TRANSCEIVE_STATE from RF_STATUS register 753 | */ 754 | PN5180TransceiveStat PN5180::getTransceiveState() { 755 | uint32_t rfStatus; 756 | if (!readRegister(RF_STATUS, &rfStatus)) { 757 | PN5180DEBUG(F("ERROR reading RF_STATUS register.\n")); 758 | return PN5180TransceiveStat(0); 759 | } 760 | 761 | return PN5180TransceiveStat((rfStatus >> 24) & 0x07); 762 | } 763 | -------------------------------------------------------------------------------- /PN5180ISO15693.cpp: -------------------------------------------------------------------------------- 1 | // NAME: PN5180ISO15693.h 2 | // 3 | // DESC: ISO15693 protocol on NXP Semiconductors PN5180 module for Arduino. 4 | // 5 | // Copyright (c) 2018 by Andreas Trappmann. All rights reserved. 6 | // 7 | // This file is part of the PN5180 library for the Arduino environment. 8 | // 9 | // This library is free software; you can redistribute it and/or 10 | // modify it under the terms of the GNU Lesser General Public 11 | // License as published by the Free Software Foundation; either 12 | // version 2.1 of the License, or (at your option) any later version. 13 | // 14 | // This library is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | // Lesser General Public License for more details. 18 | // 19 | //#define DEBUG 1 20 | 21 | #include 22 | #include "PN5180ISO15693.h" 23 | #include "Debug.h" 24 | 25 | PN5180ISO15693::PN5180ISO15693(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin, SPIClass& spi) 26 | : PN5180(SSpin, BUSYpin, RSTpin, spi) { 27 | } 28 | 29 | /* 30 | * Inventory, code=01 31 | * 32 | * Request format: SOF, Req.Flags, Inventory, AFI (opt.), Mask len, Mask value, CRC16, EOF 33 | * Response format: SOF, Resp.Flags, DSFID, UID, CRC16, EOF 34 | * 35 | */ 36 | ISO15693ErrorCode PN5180ISO15693::getInventory(uint8_t *uid) { 37 | // Flags, CMD, maskLen 38 | uint8_t inventory[] = { 0x26, 0x01, 0x00 }; 39 | // |\- inventory flag + high data rate 40 | // \-- 1 slot: only one card, no AFI field present 41 | 42 | for (int i=0; i<8; i++) { 43 | uid[i] = 0; 44 | } 45 | 46 | uint8_t *readBuffer; 47 | ISO15693ErrorCode rc = issueISO15693Command(inventory, sizeof(inventory), &readBuffer); 48 | if (ISO15693_EC_OK != rc) { 49 | return rc; 50 | } 51 | 52 | PN5180DEBUG(F("Response flags: ")); 53 | PN5180DEBUG(formatHex(readBuffer[0])); 54 | PN5180DEBUG(F(", Data Storage Format ID: ")); 55 | PN5180DEBUG(formatHex(readBuffer[1])); 56 | PN5180DEBUG(F(", UID: ")); 57 | 58 | for (int i=0; i<8; i++) { 59 | uid[i] = readBuffer[2+i]; 60 | #ifdef DEBUG 61 | PN5180DEBUG(formatHex(uid[7-i])); // LSB comes first 62 | if (i<2) PN5180DEBUG(":"); 63 | #endif 64 | } 65 | 66 | PN5180DEBUG("\n"); 67 | 68 | return ISO15693_EC_OK; 69 | } 70 | 71 | /* 72 | * Inventory with flag set for 16 time slots, code=01 73 | * https://www.nxp.com.cn/docs/en/application-note/AN12650.pdf 74 | * Request format: SOF, Req.Flags, Inventory, AFI (opt.), Mask len, Mask value, CRC16, EOF 75 | * Response format: SOF, Resp.Flags, DSFID, UID, CRC16, EOF 76 | */ 77 | ISO15693ErrorCode PN5180ISO15693::getInventoryMultiple(uint8_t *uid, uint8_t maxTags, uint8_t *numCard) { 78 | PN5180DEBUG("PN5180ISO15693: Get Inventory..."); 79 | uint16_t collision[maxTags]; 80 | *numCard = 0; 81 | uint8_t numCollisions = 0; 82 | // Send an inventory command and listen for the response 83 | inventoryPoll(uid, maxTags, numCard, &numCollisions, collision); 84 | PN5180DEBUG("Number of collisions="); 85 | PN5180DEBUG(numCollisions); 86 | PN5180DEBUG("\n"); 87 | // Continue to call inventory until no further collisions detected 88 | // (numCard will be incremented automatically on each call) 89 | while(numCollisions > 0){ 90 | #ifdef DEBUG 91 | printf("inventoryPoll: Polling with mask=0x%X\n", collision[0]); 92 | #endif 93 | inventoryPoll(uid, maxTags, numCard, &numCollisions, collision); 94 | numCollisions--; 95 | for(int i=0; i 0){ 109 | uint32_t mask = collision[0]; 110 | do{ 111 | mask >>= 4L; 112 | maskLen++; 113 | }while(mask > 0); 114 | } 115 | uint8_t *p = (uint8_t*)&(collision[0]); 116 | // Flags, CMD, 117 | uint8_t inventory[7] = { 0x06, 0x01, maskLen*4, p[0], p[1], p[2], p[3] }; 118 | // |\- inventory flag + high data rate 119 | // \-- 16 slots: upto 16 cards, no AFI field present 120 | uint8_t cmdLen = 3 + (maskLen/2) + (maskLen%2); 121 | #ifdef DEBUG 122 | printf("inventoryPoll inputs: maxTags=%d, numCard=%d, numCol=%d\n", maxTags, *numCard, *numCol); 123 | printf("mask=%d, maskLen=%d, cmdLen=%d\n", p[0], maskLen, cmdLen); 124 | #endif 125 | clearIRQStatus(0x000FFFFF); // 3. Clear all IRQ_STATUS flags 126 | sendData(inventory, cmdLen, 0); // 4. 5. 6. Idle/StopCom Command, Transceive Command, Inventory command 127 | 128 | for(int slot=0; slot<16; slot++){ // 7. Loop to check 16 time slots for data 129 | uint32_t rxStatus; 130 | uint32_t irqStatus = getIRQStatus(); 131 | readRegister(RX_STATUS, &rxStatus); 132 | uint16_t len = (uint16_t)(rxStatus & 0x000001ff); 133 | if((rxStatus >> 18) & 0x01 && *numCol < maxTags){ // 7+ Determine if a collision occurred 134 | if(maskLen > 0) collision[*numCol] = collision[0] | (slot << (maskLen * 2)); 135 | else collision[*numCol] = slot << (maskLen * 2); // Yes, store position of collision 136 | *numCol = *numCol + 1; 137 | #ifdef DEBUG 138 | printf("Collision detected for UIDs matching %X starting at LSB", collision[*numCol-1]); 139 | #endif 140 | } 141 | else if(!(irqStatus & RX_IRQ_STAT) && !len){ // 8. Check if a card has responded 142 | PN5180DEBUG("getInventoryMultiple: No card in this time slot. State="); 143 | PN5180DEBUG(irqStatus); 144 | PN5180DEBUG("\n"); 145 | } 146 | else{ 147 | #ifdef DEBUG 148 | printf("slot=%d, irqStatus: %ld, RX_STATUS: %ld, Response length=%d\n", slot, irqStatus, rxStatus, len); 149 | #endif 150 | uint8_t *readBuffer; 151 | readBuffer = readData(len+1); // 9. Read reception buffer 152 | #ifdef DEBUG 153 | printf("readBuffer= "); 154 | for(int i=0; i numBlock-1){ // Attempted to start at a block greater than the num blocks on the VICC 346 | PN5180DEBUG("Starting block exceeds length of data"); 347 | return ISO15693_EC_BLOCK_NOT_AVAILABLE; 348 | } 349 | if( (blockNo + numBlock) > numBlock ){ // Will attempt to read a block greater than the num blocks on the VICC 350 | PN5180DEBUG("End of block exceeds length of data"); 351 | return ISO15693_EC_BLOCK_NOT_AVAILABLE; 352 | } 353 | 354 | // flags, cmd, uid, 1stBlock blocksToRead 355 | uint8_t readMultipleCmd[12] = { 0x22, 0x23, 1,2,3,4,5,6,7,8, blockNo, numBlock-1 }; // UID has LSB first! 356 | // |\- high data rate 357 | // \-- no options, addressed by UID 358 | 359 | for (int i=0; i<8; i++) { 360 | readMultipleCmd[2+i] = uid[i]; 361 | } 362 | 363 | uint8_t *resultPtr; 364 | ISO15693ErrorCode rc = issueISO15693Command(readMultipleCmd, sizeof(readMultipleCmd), &resultPtr); 365 | if (ISO15693_EC_OK != rc) return rc; 366 | 367 | PN5180DEBUG("readMultipleBlock: Value="); 368 | for (int i=0; i> 4) { 481 | case 0: PN5180DEBUG(F("All families")); break; 482 | case 1: PN5180DEBUG(F("Transport")); break; 483 | case 2: PN5180DEBUG(F("Financial")); break; 484 | case 3: PN5180DEBUG(F("Identification")); break; 485 | case 4: PN5180DEBUG(F("Telecommunication")); break; 486 | case 5: PN5180DEBUG(F("Medical")); break; 487 | case 6: PN5180DEBUG(F("Multimedia")); break; 488 | case 7: PN5180DEBUG(F("Gaming")); break; 489 | case 8: PN5180DEBUG(F("Data storage")); break; 490 | case 9: PN5180DEBUG(F("Item management")); break; 491 | case 10: PN5180DEBUG(F("Express parcels")); break; 492 | case 11: PN5180DEBUG(F("Postal services")); break; 493 | case 12: PN5180DEBUG(F("Airline bags")); break; 494 | default: PN5180DEBUG(F("Unknown")); break; 495 | } 496 | PN5180DEBUG("\n"); 497 | } 498 | #ifdef DEBUG 499 | else PN5180DEBUG(F("No AFI\n")); 500 | #endif 501 | 502 | if (infoFlags & 0x04) { // VICC Memory size 503 | *numBlocks = *p++; 504 | *blockSize = *p++; 505 | *blockSize = (*blockSize) & 0x1f; 506 | 507 | *blockSize = *blockSize + 1; // range: 1-32 508 | *numBlocks = *numBlocks + 1; // range: 1-256 509 | 510 | PN5180DEBUG("VICC MemSize="); 511 | PN5180DEBUG(uint16_t(*blockSize) * (*numBlocks)); 512 | PN5180DEBUG(" BlockSize="); 513 | PN5180DEBUG(*blockSize); 514 | PN5180DEBUG(" NumBlocks="); 515 | PN5180DEBUG(*numBlocks); 516 | PN5180DEBUG("\n"); 517 | } 518 | #ifdef DEBUG 519 | else PN5180DEBUG(F("No VICC memory size\n")); 520 | #endif 521 | 522 | if (infoFlags & 0x08) { // IC reference 523 | PN5180DEBUG("IC Ref="); 524 | PN5180DEBUG(formatHex(uint8_t(*p++))); 525 | PN5180DEBUG("\n"); 526 | } 527 | #ifdef DEBUG 528 | else PN5180DEBUG(F("No IC ref\n")); 529 | #endif 530 | 531 | return ISO15693_EC_OK; 532 | } 533 | 534 | 535 | // ICODE SLIX specific commands 536 | 537 | /* 538 | * The GET RANDOM NUMBER command is required to receive a random number from the label IC. 539 | * The passwords that will be transmitted with the SET PASSWORD,ENABLEPRIVACY and DESTROY commands 540 | * have to be calculated with the password and the random number (see Section 9.5.3.2 "SET PASSWORD") 541 | */ 542 | ISO15693ErrorCode PN5180ISO15693::getRandomNumber(uint8_t *randomData) { 543 | uint8_t getrandom[] = {0x02, 0xB2, 0x04}; 544 | uint8_t *readBuffer; 545 | ISO15693ErrorCode rc = issueISO15693Command(getrandom, sizeof(getrandom), &readBuffer); 546 | if (rc == ISO15693_EC_OK) { 547 | randomData[0] = readBuffer[1]; 548 | randomData[1] = readBuffer[2]; 549 | } 550 | return rc; 551 | } 552 | 553 | /* 554 | * The SET PASSWORD command enables the different passwords to be transmitted to the label 555 | * to access the different protected functionalities of the following commands. 556 | * The SET PASSWORD command has to be executed just once for the related passwords if the label is powered 557 | */ 558 | ISO15693ErrorCode PN5180ISO15693::setPassword(uint8_t identifier, uint8_t *password, uint8_t *random) { 559 | uint8_t setPassword[] = {0x02, 0xB3, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00}; 560 | uint8_t *readBuffer; 561 | setPassword[3] = identifier; 562 | setPassword[4] = password[0] ^ random[0]; 563 | setPassword[5] = password[1] ^ random[1]; 564 | setPassword[6] = password[2] ^ random[0]; 565 | setPassword[7] = password[3] ^ random[1]; 566 | ISO15693ErrorCode rc = issueISO15693Command(setPassword, sizeof(setPassword), &readBuffer); 567 | return rc; 568 | } 569 | 570 | /* 571 | * The ENABLE PRIVACY command enables the ICODE SLIX2 Label IC to be set to 572 | * Privacy mode if the Privacy password is correct. The ICODE SLIX2 will not respond to 573 | * any command except GET RANDOM NUMBER and SET PASSWORD 574 | */ 575 | ISO15693ErrorCode PN5180ISO15693::enablePrivacy(uint8_t *password, uint8_t *random) { 576 | uint8_t setPrivacy[] = {0x02, 0xBA, 0x04, 0x00, 0x00, 0x00, 0x00}; 577 | uint8_t *readBuffer; 578 | setPrivacy[3] = password[0] ^ random[0]; 579 | setPrivacy[4] = password[1] ^ random[1]; 580 | setPrivacy[5] = password[2] ^ random[0]; 581 | setPrivacy[6] = password[3] ^ random[1]; 582 | ISO15693ErrorCode rc = issueISO15693Command(setPrivacy, sizeof(setPrivacy), &readBuffer); 583 | return rc; 584 | } 585 | 586 | 587 | // disable privacy mode for ICODE SLIX2 tag with given password 588 | ISO15693ErrorCode PN5180ISO15693::disablePrivacyMode(uint8_t *password) { 589 | // get a random number from the tag 590 | uint8_t random[]= {0x00, 0x00}; 591 | ISO15693ErrorCode rc = getRandomNumber(random); 592 | if (rc != ISO15693_EC_OK) { 593 | return rc; 594 | } 595 | 596 | // set password to disable privacy mode 597 | rc = setPassword(0x04, password, random); 598 | return rc; 599 | } 600 | 601 | // enable privacy mode for ICODE SLIX2 tag with given password 602 | ISO15693ErrorCode PN5180ISO15693::enablePrivacyMode(uint8_t *password) { 603 | // get a random number from the tag 604 | uint8_t random[]= {0x00, 0x00}; 605 | ISO15693ErrorCode rc = getRandomNumber(random); 606 | if (rc != ISO15693_EC_OK) { 607 | return rc; 608 | } 609 | 610 | // enable privacy command to lock the tag 611 | rc = enablePrivacy(password, random); 612 | return rc; 613 | } 614 | 615 | 616 | /* 617 | * ISO 15693 - Protocol 618 | * 619 | * General Request Format: 620 | * SOF, Req.Flags, Command code, Parameters, Data, CRC16, EOF 621 | * 622 | * Request Flags: 623 | * xxxx.3210 624 | * |||\_ Subcarrier flag: 0=single sub-carrier, 1=two sub-carrier 625 | * ||\__ Datarate flag: 0=low data rate, 1=high data rate 626 | * |\___ Inventory flag: 0=no inventory, 1=inventory 627 | * \____ Protocol extension flag: 0=no extension, 1=protocol format is extended 628 | * 629 | * If Inventory flag is set: 630 | * 7654.xxxx 631 | * ||\_ AFI flag: 0=no AFI field present, 1=AFI field is present 632 | * |\__ Number of slots flag: 0=16 slots, 1=1 slot 633 | * \___ Option flag: 0=default, 1=meaning is defined by command description 634 | * 635 | * If Inventory flag is NOT set: 636 | * 7654.xxxx 637 | * ||\_ Select flag: 0=request shall be executed by any VICC according to Address_flag 638 | * || 1=request shall be executed only by VICC in selected state 639 | * |\__ Address flag: 0=request is not addressed. UID field is not present. 640 | * | 1=request is addressed. UID field is present. Only VICC with UID shall answer 641 | * \___ Option flag: 0=default, 1=meaning is defined by command description 642 | * 643 | * General Response Format: 644 | * SOF, Resp.Flags, Parameters, Data, CRC16, EOF 645 | * 646 | * Response Flags: 647 | * xxxx.3210 648 | * |||\_ Error flag: 0=no error, 1=error detected, see error field 649 | * ||\__ RFU: 0 650 | * |\___ RFU: 0 651 | * \____ Extension flag: 0=no extension, 1=protocol format is extended 652 | * 653 | * If Error flag is set, the following error codes are defined: 654 | * 01 = The command is not supported, i.e. the request code is not recognized. 655 | * 02 = The command is not recognized, i.e. a format error occurred. 656 | * 03 = The option is not supported. 657 | * 0F = Unknown error. 658 | * 10 = The specific block is not available. 659 | * 11 = The specific block is already locked and cannot be locked again. 660 | * 12 = The specific block is locked and cannot be changed. 661 | * 13 = The specific block was not successfully programmed. 662 | * 14 = The specific block was not successfully locked. 663 | * A0-DF = Custom command error codes 664 | * 665 | * Function return values: 666 | * 0 = OK 667 | * -1 = No card detected 668 | * >0 = Error code 669 | */ 670 | ISO15693ErrorCode PN5180ISO15693::issueISO15693Command(uint8_t *cmd, uint8_t cmdLen, uint8_t **resultPtr) { 671 | #ifdef DEBUG 672 | PN5180DEBUG(F("Sending ISO15693 [")); 673 | for (int b = 0; b < cmdLen; b++) { 674 | if (b > 0) PN5180DEBUG(" "); 675 | if (b == 0) { 676 | PN5180DEBUG(F("(")); 677 | PN5180DEBUG((cmd[b] & 0x1) ? F("2Sub") : F("1Sub")); 678 | PN5180DEBUG((cmd[b] & 0x2) ? F(" HiRate") : F(" LoRate")); 679 | if (cmd[b] & 0x8) PN5180DEBUG(F(" Ext")); 680 | if (cmd[b] & 0x4) { 681 | PN5180DEBUG(F(" Inv")); 682 | if (cmd[b] & 0x10) PN5180DEBUG(F(" AFI")); 683 | PN5180DEBUG((cmd[b] & 0x20) ? F(" 1Slot") : F(" 16Slot")); 684 | if (cmd[b] & 0x40) PN5180DEBUG(F(" Opt")); 685 | } else { 686 | if (cmd[b] & 0x10) PN5180DEBUG(F(" Sel")); 687 | if (cmd[b] & 0x20) PN5180DEBUG(F(" Addr")); 688 | if (cmd[b] & 0x40) PN5180DEBUG(F(" Opt")); 689 | } 690 | PN5180DEBUG(F(")")); 691 | } else if (b == 1) { 692 | switch (cmd[b]) { 693 | case 0x01: PN5180DEBUG(F("INVENTORY")); break; 694 | case 0x02: PN5180DEBUG(F("STAY-QUIET")); break; 695 | case 0x20: PN5180DEBUG(F("READ-BLOCK")); break; 696 | case 0x21: PN5180DEBUG(F("WRITE-BLOCK")); break; 697 | case 0x22: PN5180DEBUG(F("LOCK-BLOCK")); break; 698 | case 0x23: PN5180DEBUG(F("READ-BLOCKS")); break; 699 | case 0x24: PN5180DEBUG(F("WRITE-BLOCKS")); break; 700 | case 0x25: PN5180DEBUG(F("SELECT")); break; 701 | case 0x26: PN5180DEBUG(F("RESET-TO-READY")); break; 702 | case 0x27: PN5180DEBUG(F("WRITE-AFI")); break; 703 | case 0x28: PN5180DEBUG(F("LOCK-AFI")); break; 704 | case 0x29: PN5180DEBUG(F("WRITE-DSFID")); break; 705 | case 0x2A: PN5180DEBUG(F("LOCK-DSFID")); break; 706 | case 0x2B: PN5180DEBUG(F("GET-SYS-INFO")); break; 707 | // ICODE SLIX specific commands 708 | case 0xA0: PN5180DEBUG(F("GET-SECURITY")); break; 709 | case 0xB2: PN5180DEBUG(F("GET-RANDOM")); break; 710 | case 0xB3: PN5180DEBUG(F("SET-PASSWORD")); break; 711 | case 0xBA: PN5180DEBUG(F("ENABLE-PRIVACY")); break; 712 | default: 713 | PN5180DEBUG(""); 714 | PN5180DEBUG(formatHex(cmd[b])); 715 | break; 716 | } 717 | } else { 718 | PN5180DEBUG(formatHex(cmd[b])); 719 | } 720 | } 721 | PN5180DEBUG("]\n"); 722 | #endif 723 | 724 | sendData(cmd, cmdLen); 725 | delay(10); 726 | 727 | uint32_t irqR = getIRQStatus(); 728 | if (0 == (irqR & RX_SOF_DET_IRQ_STAT)) { 729 | PN5180DEBUG("Didnt detect RX_SOF_DET_IRQ_STAT after sendData"); 730 | return EC_NO_CARD; 731 | } 732 | 733 | // The following line prevents code from continuing until the RX_IRQ_STAT flag 734 | // is set in the IRQ_STATUS register, which signifies the end of RF reception 735 | // But, should the card be removed from the reader quickly before reception is complete, 736 | // this bit will never be received and the code will hang at this point. 737 | // TODO implement a reasonable timeout in which to expect the bit to be read, as in https://github.com/playfultechnology/PN5180-Library/blob/e64468d65906c207bdaf900a1645615c71f3d5ca/PN5180.cpp#L489-L494 738 | /* 739 | while(!(irqR & RX_IRQ_STAT)) { 740 | delay(1); 741 | irqR = getIRQStatus(); 742 | } 743 | */ 744 | unsigned long startedWaiting = millis(); 745 | while (!(irqR & RX_IRQ_STAT)) { 746 | irqR = getIRQStatus(); 747 | if (millis() - startedWaiting > commandTimeout) { 748 | PN5180DEBUG("Didnt detect RX_IRQ_STAT after sendData"); 749 | return EC_NO_CARD; 750 | } 751 | } 752 | 753 | uint32_t rxStatus; 754 | readRegister(RX_STATUS, &rxStatus); 755 | uint16_t len = (uint16_t)(rxStatus & 0x1ff); 756 | 757 | *resultPtr = readData(len); 758 | if (0L == *resultPtr) { 759 | PN5180DEBUG(F("*** ERROR in readData!\n")); 760 | return ISO15693_EC_UNKNOWN_ERROR; 761 | } 762 | 763 | uint32_t irqStatus = getIRQStatus(); 764 | if (0 == (RX_SOF_DET_IRQ_STAT & irqStatus)) { // no card detected 765 | PN5180DEBUG("Didnt detect RX_SOF_DET_IRQ_STAT after readData"); 766 | clearIRQStatus(TX_IRQ_STAT | IDLE_IRQ_STAT); 767 | return EC_NO_CARD; 768 | } 769 | 770 | uint8_t responseFlags = (*resultPtr)[0]; 771 | if (responseFlags & (1<<0)) { // error flag 772 | uint8_t errorCode = (*resultPtr)[1]; 773 | 774 | PN5180DEBUG("ERROR code="); 775 | PN5180DEBUG(formatHex(errorCode)); 776 | PN5180DEBUG(" - "); 777 | PN5180DEBUG(strerror((ISO15693ErrorCode)errorCode)); 778 | PN5180DEBUG("\n"); 779 | 780 | if (errorCode >= 0xA0) { // custom command error codes 781 | return ISO15693_EC_CUSTOM_CMD_ERROR; 782 | } 783 | else return (ISO15693ErrorCode)errorCode; 784 | } 785 | 786 | #ifdef DEBUG 787 | if (responseFlags & (1<<3)) { // extendsion flag 788 | PN5180DEBUG("Extension flag is set!\n"); 789 | } 790 | #endif 791 | 792 | clearIRQStatus(RX_SOF_DET_IRQ_STAT | IDLE_IRQ_STAT | TX_IRQ_STAT | RX_IRQ_STAT); 793 | return ISO15693_EC_OK; 794 | } 795 | 796 | bool PN5180ISO15693::setupRF() { 797 | if (!loadRFConfig(0x0d, 0x8d)) return false; // ISO15693 parameters 798 | if (!setRF_on()) return false; 799 | 800 | writeRegisterWithAndMask(SYSTEM_CONFIG, 0xfffffff8); // Idle/StopCom Command 801 | writeRegisterWithOrMask(SYSTEM_CONFIG, 0x00000003); // Transceive Command 802 | 803 | return true; 804 | } 805 | 806 | const __FlashStringHelper *PN5180ISO15693::strerror(ISO15693ErrorCode err) { 807 | PN5180DEBUG(F("ISO15693ErrorCode=")); 808 | PN5180DEBUG(err); 809 | PN5180DEBUG("\n"); 810 | 811 | switch (err) { 812 | case EC_NO_CARD: return F("No card detected!"); 813 | case ISO15693_EC_OK: return F("OK!"); 814 | case ISO15693_EC_NOT_SUPPORTED: return F("Command is not supported!"); 815 | case ISO15693_EC_NOT_RECOGNIZED: return F("Command is not recognized!"); 816 | case ISO15693_EC_OPTION_NOT_SUPPORTED: return F("Option is not supported!"); 817 | case ISO15693_EC_UNKNOWN_ERROR: return F("Unknown error!"); 818 | case ISO15693_EC_BLOCK_NOT_AVAILABLE: return F("Specified block is not available!"); 819 | case ISO15693_EC_BLOCK_ALREADY_LOCKED: return F("Specified block is already locked!"); 820 | case ISO15693_EC_BLOCK_IS_LOCKED: return F("Specified block is locked and cannot be changed!"); 821 | case ISO15693_EC_BLOCK_NOT_PROGRAMMED: return F("Specified block was not successfully programmed!"); 822 | case ISO15693_EC_BLOCK_NOT_LOCKED: return F("Specified block was not successfully locked!"); 823 | default: 824 | if ((err >= 0xA0) && (err <= 0xDF)) { 825 | return F("Custom command error code!"); 826 | } 827 | else return F("Undefined error code in ISO15693!"); 828 | } 829 | } 830 | --------------------------------------------------------------------------------