├── .gitattributes ├── .github └── workflows │ ├── run-cl-arduino.yml │ ├── stale.yml │ └── sync_issues.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── examples ├── FeliCa_card_detection │ └── FeliCa_card_detection.ino ├── FeliCa_card_read │ └── FeliCa_card_read.ino ├── android_hce │ └── android_hce.ino ├── emulate_tag_ndef │ └── emulate_tag_ndef.ino ├── iso14443a_uid │ └── iso14443a_uid.ino ├── mifareclassic_formatndef │ └── mifareclassic_formatndef.ino ├── mifareclassic_memdump │ └── mifareclassic_memdump.ino ├── mifareclassic_ndeftoclassic │ └── mifareclassic_ndeftoclassic.ino ├── mifareclassic_updatendef │ └── mifareclassic_updatendef.ino ├── ntag21x_protect │ └── ntag21x_protect.ino ├── ntag21x_rw │ └── ntag21x_rw.ino ├── p2p_raw │ └── p2p_raw.ino ├── p2p_with_ndef_library │ └── p2p_with_ndef_library.ino └── readMifare │ └── readMifare.ino ├── library.json ├── library.properties └── src ├── PN532.cpp ├── PN532.h ├── PN532Interface.h ├── PN532_HSU.cpp ├── PN532_HSU.h ├── PN532_I2C.cpp ├── PN532_I2C.h ├── PN532_SPI.cpp ├── PN532_SPI.h ├── PN532_SWHSU.cpp ├── PN532_SWHSU.h ├── PN532_debug.h ├── emulatetag.cpp ├── emulatetag.h ├── llcp.cpp ├── llcp.h ├── mac_link.cpp ├── mac_link.h ├── snep.cpp └── snep.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # From https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings 2 | # Set the default behavior, in case people don't have core.autocrlf set. 3 | * text=auto 4 | 5 | # Explicitly declare text files you want to always be normalized and converted 6 | # to native line endings on checkout. 7 | *.cpp text 8 | *.h text -------------------------------------------------------------------------------- /.github/workflows/run-cl-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Run Ci Arduino 2 | 3 | on: 4 | push: 5 | pull_request: 6 | repository_dispatch: 7 | types: [trigger-workflow] 8 | 9 | jobs: 10 | ci-arduino: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Checkout script repository 18 | uses: actions/checkout@v4 19 | with: 20 | repository: Seeed-Studio/ci-arduino 21 | path: ci 22 | 23 | 24 | - name: Setup arduino cli 25 | uses: arduino/setup-arduino-cli@v2.0.0 26 | 27 | - name: Create a depend.list file 28 | run: | 29 | # eg: echo "" >> depend.list 30 | echo "OSSLibraries/Arduino_MFRC522v2" >> depend.list 31 | echo "don/NDEF" >> depend.list 32 | 33 | 34 | - name: Create a ignore.list file 35 | run: | 36 | # eg: echo "," >> ignore.list 37 | 38 | 39 | 40 | - name: Build sketch 41 | run: ./ci/tools/compile.sh 42 | 43 | - name: Build result 44 | run: | 45 | cat build.log 46 | if [ ${{ github.event_name }} == 'pull_request' ] && [ -f compile.failed ]; then 47 | exit 1 48 | fi 49 | 50 | - name: Generate issue 51 | if: ${{ github.event_name != 'pull_request' }} 52 | run: ./ci/tools/issue.sh 53 | env: 54 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 4 * * *' 7 | 8 | jobs: 9 | stale: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Checkout script repository 17 | uses: actions/checkout@v4 18 | with: 19 | repository: Seeed-Studio/sync-github-all-issues 20 | path: ci 21 | 22 | - name: Run script 23 | run: ./ci/tools/stale.sh 24 | env: 25 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/sync_issues.yml: -------------------------------------------------------------------------------- 1 | name: Automate Issue Management 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - edited 8 | - assigned 9 | - unassigned 10 | - labeled 11 | - unlabeled 12 | - reopened 13 | 14 | jobs: 15 | add_issue_to_project: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Add issue to GitHub Project 19 | uses: actions/add-to-project@v1.0.2 20 | with: 21 | project-url: https://github.com/orgs/Seeed-Studio/projects/17 22 | github-token: ${{ secrets.ISSUE_ASSEMBLE }} 23 | labeled: bug 24 | label-operator: NOT 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/c++,platformio 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=c++,platformio 3 | 4 | ### C++ ### 5 | # Prerequisites 6 | *.d 7 | 8 | # Compiled Object files 9 | *.slo 10 | *.lo 11 | *.o 12 | *.obj 13 | 14 | # Precompiled Headers 15 | *.gch 16 | *.pch 17 | 18 | # Compiled Dynamic libraries 19 | *.so 20 | *.dylib 21 | *.dll 22 | 23 | # Fortran module files 24 | *.mod 25 | *.smod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | 38 | ### PlatformIO ### 39 | .pioenvs 40 | .piolibdeps 41 | .clang_complete 42 | .gcc-flags.json 43 | .pio 44 | 45 | # End of https://www.toptal.com/developers/gitignore/api/c++,platformio -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2012, Adafruit Industries 4 | Copyright (c) 2013, Seeed Technology Inc. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFC library 2 | 3 | This is an library for PN532 to use NFC technology. 4 | It is for [NFC Shield](https://www.seeedstudio.com/NFC-Shield-V2-0.html) and [Grove - NFC](https://www.seeedstudio.com/Grove-NFC.html). 5 | 6 | [![NFC Shield](https://statics3.seeedstudio.com/images/113030001%201.jpg)](https://www.seeedstudio.com/NFC-Shield-V2-0.html) 7 | [![Grove - NFC](https://statics3.seeedstudio.com/images/product/grove%20nfc.jpg)](https://www.seeedstudio.com/Grove-NFC.html) 8 | 9 | ## Features 10 | 11 | - Support all interfaces of PN532 (I2C, SPI, HSU) 12 | - Read/write Mifare Classic Card 13 | - Works with [Don's NDEF Library](https://github.com/don/NDEF) 14 | - Communicate with android 4.0+([Lists of devices supported](https://github.com/Seeed-Studio/PN532/wiki/List-of-devices-supported)) 15 | - Support [mbed platform](https://os.mbed.com/teams/Seeed/code/PN532/) 16 | - Card emulation (NFC Type 4 tag) 17 | 18 | ## To Do 19 | 20 | - To support more than one INFO PDU of P2P communication 21 | - To read/write NFC Type 4 tag 22 | 23 | ## Getting Started 24 | 25 | ### Using Arduino IDE 26 | 27 | 1. Download [zip file](https://github.com/Seeed-Studio/PN532/archive/refs/heads/arduino.zip), extract it into Arduino's libraries and rename it to PN532-Arduino. 28 | 2. Download [Don's NDEF library](https://github.com/don/NDEF/archive/refs/heads/master.zip), extract it into Arduino's libraries and rename it to NDEF. 29 | 3. Add the `NFC_INTERFACE_` build flag to your build system or define it in your code using `#define NFC_INTERFACE_` like 30 | 31 | ```cpp 32 | #define NFC_INTERFACE_I2C 33 | ``` 34 | 35 | 4. Follow the examples of the two libraries. 36 | 37 | ### PlatformIO library 38 | 39 | Add `https://github.com/Seeed-Studio/PN532.git` to your `lib_deps` variable in `platformio.ini` like so. This library will automatically include Don's NDEF library as well. 40 | 41 | ``` 42 | lib_deps = 43 | https://github.com/Seeed-Studio/PN532.git 44 | ``` 45 | 46 | > ⚠️ Besides using the correct `PN532_.h` include file, you have to add `-DNFC_INTERFACE_` to `build_flags` to select what interface you want to use. This is done to prevent requiring unnecessary dependencies on e.g. `SoftwareSerial` or `SPI` when you are not using those interfaces. 47 | 48 | ``` 49 | build_flags = 50 | -DNFC_INTERFACE_HSU 51 | ``` 52 | 53 | ### Git way for Linux/Mac (recommended) 54 | 55 | 1. Get PN532 library and NDEF library 56 | 57 | cd {Arduino}\libraries 58 | git clone --recursive https://github.com/Seeed-Studio/PN532.git NFC 59 | git clone --recursive https://github.com/don/NDEF.git NDEF 60 | ln -s NFC/PN532 ./ 61 | ln -s NDEF/NDEF ./ 62 | 63 | 1. Add the `NFC_INTERFACE_` build flag to your build system or define it in your code using `#define NFC_INTERFACE_` like 64 | 65 | ```cpp 66 | #define NFC_INTERFACE_I2C 67 | ``` 68 | 69 | 1. Follow the examples of the two libraries 70 | 71 | ## Interfaces 72 | 73 | This library offers four ways to interface with the PN532 board: 74 | 75 | - `HSU` (High Speed Uart) 76 | - `I2C` 77 | - `SPI` 78 | - `SWHSU` (Software-based High Speed Uart) 79 | 80 | Read the section for the interface you want to use. 81 | 82 | > Make sure to add the `PN532_.h` include file and the `NFC_INTERFACE_` define to your code like the example below: 83 | 84 | ```cpp 85 | #define NFC_INTERFACE_HSU 86 | 87 | #include 88 | #include 89 | ``` 90 | 91 | ## HSU Interface 92 | 93 | HSU is short for High Speed Uart. HSU interface needs only 4 wires to connect PN532 with Arduino, [Sensor Shield](http://goo.gl/i0EQgd) can make it more easier. For some Arduino boards like [Leonardo][leonardo], [DUE][due], [Mega][mega] ect, there are more than one `Serial` on these boards, so we can use this additional Serial to control PN532, HSU uses 115200 baud rate. 94 | 95 | To use the `Serial1` control PN532, refer to the code below. 96 | 97 | ```c++ 98 | /* If you need to specify the pin: For ESP32, you can use PN532_HSU pn532hsu(Serial1, 36, 4) to specify the pin; 99 | For other series that support soft serial port, you can use the soft serial port to specify the pin: 100 | #define USE_SOFT_SERIAL_PIN 101 | #define NFC_INTERFACE_HSU 102 | #include 103 | #include 104 | 105 | SoftwareSerial mysoft_serial(D7,D6); 106 | PN532_HSU pn532hsu(mysoft_serial); 107 | PN532 nfc(pn532hsu); 108 | */ 109 | 110 | #define NFC_INTERFACE_HSU 111 | 112 | #include 113 | #include 114 | 115 | PN532_HSU pn532hsu(Serial1); 116 | PN532 nfc(pn532hsu); 117 | 118 | void setup(void) 119 | { 120 | nfc.begin(); 121 | //... 122 | } 123 | ``` 124 | 125 | If your Arduino has only one serial interface and you want to keep it for control or debugging with the Serial Monitor, you can use the [`SoftwareSerial`][softwareserial] library to control the PN532 by emulating a serial interface. Include `PN532_SWHSU.h` instead of `PN532_HSU.h`: 126 | 127 | ```c++ 128 | #define NFC_INTERFACE_SWHSU 129 | 130 | #include 131 | #include 132 | #include 133 | 134 | SoftwareSerial SWSerial( 10, 11 ); // RX, TX 135 | 136 | PN532_SWHSU pn532swhsu( SWSerial ); 137 | PN532 nfc( pn532swhsu ); 138 | 139 | void setup(void) 140 | { 141 | nfc.begin(); 142 | //... 143 | } 144 | ``` 145 | 146 | ## Attribution 147 | 148 | This library is based on [Adafruit_NFCShield_I2C](https://github.com/adafruit/Adafruit_NFCShield_I2C). 149 | [Seeed Studio](hhttps://www.seeedstudio.com/) rewrite the library to make it easy to support different interfaces and platforms. 150 | [@Don](https://github.com/don) writes the [NDEF library](https://github.com/don/NDEF) to make it more easy to use. 151 | [@JiapengLi](https://github.com/JiapengLi) adds HSU interface. 152 | [@awieser](https://github.com/awieser) adds card emulation function. 153 | 154 | [mega]: http://arduino.cc/en/Main/arduinoBoardMega 155 | [due]: http://arduino.cc/en/Main/arduinoBoardDue 156 | [leonardo]: http://arduino.cc/en/Main/arduinoBoardLeonardo 157 | [softwareserial]: https://www.arduino.cc/en/Reference/softwareSerial 158 | -------------------------------------------------------------------------------- /examples/FeliCa_card_detection/FeliCa_card_detection.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will attempt to connect to an FeliCa 4 | card or tag and retrieve some basic information about it 5 | that can be used to determine what type of card it is. 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | 12 | */ 13 | /**************************************************************************/ 14 | #include 15 | 16 | #if 1 17 | #define NFC_INTERFACE_SPI 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | PN532_SPI pn532spi(SPI, 10); 24 | PN532 nfc(pn532spi); 25 | #elif 0 26 | #define NFC_INTERFACE_HSU 27 | #include 28 | #include 29 | #include 30 | 31 | PN532_HSU pn532hsu(Serial1); 32 | PN532 nfc(pn532hsu); 33 | #else 34 | #define NFC_INTERFACE_I2C 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | PN532_I2C pn532i2c(Wire); 41 | PN532 nfc(pn532i2c); 42 | #endif 43 | 44 | #include 45 | 46 | uint8_t _prevIDm[8]; 47 | unsigned long _prevTime; 48 | 49 | void setup(void) 50 | { 51 | Serial.begin(115200); 52 | Serial.println("Hello!"); 53 | 54 | nfc.begin(); 55 | 56 | uint32_t versiondata = nfc.getFirmwareVersion(); 57 | if (!versiondata) 58 | { 59 | Serial.print("Didn't find PN53x board"); 60 | while (1) {delay(10);}; // halt 61 | } 62 | 63 | // Got ok data, print it out! 64 | Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX); 65 | Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); 66 | Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); 67 | 68 | // Set the max number of retry attempts to read from a card 69 | // This prevents us from waiting forever for a card, which is 70 | // the default behaviour of the PN532. 71 | nfc.setPassiveActivationRetries(0xFF); 72 | nfc.SAMConfig(); 73 | 74 | memset(_prevIDm, 0, 8); 75 | } 76 | 77 | void loop(void) 78 | { 79 | uint8_t ret; 80 | uint16_t systemCode = 0xFFFF; 81 | uint8_t requestCode = 0x01; // System Code request 82 | uint8_t idm[8]; 83 | uint8_t pmm[8]; 84 | uint16_t systemCodeResponse; 85 | 86 | // Wait for an FeliCa type cards. 87 | // When one is found, some basic information such as IDm, PMm, and System Code are retrieved. 88 | Serial.print("Waiting for an FeliCa card... "); 89 | ret = nfc.felica_Polling(systemCode, requestCode, idm, pmm, &systemCodeResponse, 5000); 90 | 91 | if (ret != 1) 92 | { 93 | Serial.println("Could not find a card"); 94 | delay(500); 95 | return; 96 | } 97 | 98 | if ( memcmp(idm, _prevIDm, 8) == 0 ) { 99 | if ( (millis() - _prevTime) < 3000 ) { 100 | Serial.println("Same card"); 101 | delay(500); 102 | return; 103 | } 104 | } 105 | 106 | Serial.println("Found a card!"); 107 | Serial.print(" IDm: "); 108 | nfc.PrintHex(idm, 8); 109 | Serial.print(" PMm: "); 110 | nfc.PrintHex(pmm, 8); 111 | Serial.print(" System Code: "); 112 | Serial.print(systemCodeResponse, HEX); 113 | Serial.print("\n"); 114 | 115 | memcpy(_prevIDm, idm, 8); 116 | _prevTime = millis(); 117 | 118 | // Wait 1 second before continuing 119 | Serial.println("Card access completed!\n"); 120 | delay(1000); 121 | } 122 | -------------------------------------------------------------------------------- /examples/FeliCa_card_read/FeliCa_card_read.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will attempt to connect to an FeliCa 4 | card or tag and retrieve some basic information about it 5 | that can be used to determine what type of card it is. 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | 12 | */ 13 | /**************************************************************************/ 14 | #include 15 | 16 | #if 1 17 | #define NFC_INTERFACE_SPI 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | PN532_SPI pn532spi(SPI, 10); 24 | PN532 nfc(pn532spi); 25 | #elif 0 26 | #define NFC_INTERFACE_HSU 27 | #include 28 | #include 29 | #include 30 | 31 | PN532_HSU pn532hsu(Serial1); 32 | PN532 nfc(pn532hsu); 33 | #else 34 | #define NFC_INTERFACE_I2C 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | PN532_I2C pn532i2c(Wire); 41 | PN532 nfc(pn532i2c); 42 | #endif 43 | 44 | #include 45 | 46 | uint8_t _prevIDm[8]; 47 | unsigned long _prevTime; 48 | 49 | void PrintHex8(const uint8_t d) { 50 | Serial.print(" "); 51 | Serial.print( (d >> 4) & 0x0F, HEX); 52 | Serial.print( d & 0x0F, HEX); 53 | } 54 | 55 | void setup(void) 56 | { 57 | Serial.begin(115200); 58 | Serial.println("Hello!"); 59 | 60 | nfc.begin(); 61 | 62 | uint32_t versiondata = nfc.getFirmwareVersion(); 63 | if (!versiondata) 64 | { 65 | Serial.print("Didn't find PN53x board"); 66 | while (1) {delay(10);}; // halt 67 | } 68 | 69 | // Got ok data, print it out! 70 | Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX); 71 | Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); 72 | Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); 73 | 74 | // Set the max number of retry attempts to read from a card 75 | // This prevents us from waiting forever for a card, which is 76 | // the default behaviour of the PN532. 77 | nfc.setPassiveActivationRetries(0xFF); 78 | nfc.SAMConfig(); 79 | 80 | memset(_prevIDm, 0, 8); 81 | } 82 | 83 | void loop(void) 84 | { 85 | uint8_t ret; 86 | uint16_t systemCode = 0xFFFF; 87 | uint8_t requestCode = 0x01; // System Code request 88 | uint8_t idm[8]; 89 | uint8_t pmm[8]; 90 | uint16_t systemCodeResponse; 91 | 92 | // Wait for an FeliCa type cards. 93 | // When one is found, some basic information such as IDm, PMm, and System Code are retrieved. 94 | Serial.print("Waiting for an FeliCa card... "); 95 | ret = nfc.felica_Polling(systemCode, requestCode, idm, pmm, &systemCodeResponse, 5000); 96 | 97 | if (ret != 1) 98 | { 99 | Serial.println("Could not find a card"); 100 | delay(500); 101 | return; 102 | } 103 | 104 | if ( memcmp(idm, _prevIDm, 8) == 0 ) { 105 | if ( (millis() - _prevTime) < 3000 ) { 106 | Serial.println("Same card"); 107 | delay(500); 108 | return; 109 | } 110 | } 111 | 112 | Serial.println("Found a card!"); 113 | Serial.print(" IDm: "); 114 | nfc.PrintHex(idm, 8); 115 | Serial.print(" PMm: "); 116 | nfc.PrintHex(pmm, 8); 117 | Serial.print(" System Code: "); 118 | Serial.print(systemCodeResponse, HEX); 119 | Serial.print("\n"); 120 | 121 | memcpy(_prevIDm, idm, 8); 122 | _prevTime = millis(); 123 | 124 | uint8_t blockData[3][16]; 125 | uint16_t serviceCodeList[1]; 126 | uint16_t blockList[3]; 127 | 128 | Serial.println("Write Without Encryption command "); 129 | serviceCodeList[0] = 0x0009; 130 | blockList[0] = 0x8000; 131 | unsigned long now = millis(); 132 | blockData[0][3] = now & 0xFF; 133 | blockData[0][2] = (now >>= 8) & 0xFF; 134 | blockData[0][1] = (now >>= 8) & 0xFF; 135 | blockData[0][0] = (now >>= 8) & 0xFF; 136 | Serial.print(" Writing current millis ("); 137 | PrintHex8(blockData[0][0]); 138 | PrintHex8(blockData[0][1]); 139 | PrintHex8(blockData[0][2]); 140 | PrintHex8(blockData[0][3]); 141 | Serial.print(" ) to Block 0 -> "); 142 | ret = nfc.felica_WriteWithoutEncryption(1, serviceCodeList, 1, blockList, blockData); 143 | if (ret != 1) 144 | { 145 | Serial.println("error"); 146 | } else { 147 | Serial.println("OK!"); 148 | } 149 | memset(blockData[0], 0, 16); 150 | 151 | Serial.print("Read Without Encryption command -> "); 152 | serviceCodeList[0] = 0x000B; 153 | blockList[0] = 0x8000; 154 | blockList[1] = 0x8001; 155 | blockList[2] = 0x8002; 156 | ret = nfc.felica_ReadWithoutEncryption(1, serviceCodeList, 3, blockList, blockData); 157 | if (ret != 1) 158 | { 159 | Serial.println("error"); 160 | } else { 161 | Serial.println("OK!"); 162 | for(int i=0; i<3; i++ ) { 163 | Serial.print(" Block no. "); Serial.print(i, DEC); Serial.print(": "); 164 | nfc.PrintHex(blockData[i], 16); 165 | } 166 | } 167 | 168 | // Wait 1 second before continuing 169 | Serial.println("Card access completed!\n"); 170 | delay(1000); 171 | } 172 | -------------------------------------------------------------------------------- /examples/android_hce/android_hce.ino: -------------------------------------------------------------------------------- 1 | #if 0 2 | #define NFC_INTERFACE_SPI 3 | #include 4 | #include 5 | #include 6 | #include "PN532.h" 7 | 8 | PN532_SPI pn532spi(SPI, 10); 9 | PN532 nfc(pn532spi); 10 | #elif 1 11 | #define NFC_INTERFACE_HSU 12 | #include 13 | #include 14 | #include 15 | 16 | PN532_HSU pn532hsu(Serial1); 17 | PN532 nfc(pn532hsu); 18 | #else 19 | #define NFC_INTERFACE_I2C 20 | #include 21 | #include 22 | #include 23 | #include 24 | #endif 25 | 26 | #ifdef USE_TINYUSB 27 | #include 28 | #endif 29 | 30 | 31 | void setup() 32 | { 33 | Serial.begin(115200); 34 | Serial.println("-------Peer to Peer HCE--------"); 35 | 36 | nfc.begin(); 37 | 38 | uint32_t versiondata = nfc.getFirmwareVersion(); 39 | if (!versiondata) 40 | { 41 | Serial.print("Didn't find PN53x board"); 42 | while (1) 43 | ; // halt 44 | } 45 | 46 | // Got ok data, print it out! 47 | Serial.print("Found chip PN5"); 48 | Serial.println((versiondata >> 24) & 0xFF, HEX); 49 | Serial.print("Firmware ver. "); 50 | Serial.print((versiondata >> 16) & 0xFF, DEC); 51 | Serial.print('.'); 52 | Serial.println((versiondata >> 8) & 0xFF, DEC); 53 | 54 | // Set the max number of retry attempts to read from a card 55 | // This prevents us from waiting forever for a card, which is 56 | // the default behaviour of the PN532. 57 | //nfc.setPassiveActivationRetries(0xFF); 58 | 59 | // configure board to read RFID tags 60 | nfc.SAMConfig(); 61 | } 62 | 63 | void loop() 64 | { 65 | bool success; 66 | 67 | uint8_t responseLength = 32; 68 | 69 | Serial.println("Waiting for an ISO14443A card"); 70 | 71 | // set shield to inListPassiveTarget 72 | success = nfc.inListPassiveTarget(); 73 | 74 | if (success) 75 | { 76 | 77 | Serial.println("Found something!"); 78 | 79 | uint8_t selectApdu[] = {0x00, /* CLA */ 80 | 0xA4, /* INS */ 81 | 0x04, /* P1 */ 82 | 0x00, /* P2 */ 83 | 0x07, /* Length of AID */ 84 | 0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* AID defined on Android App */ 85 | 0x00 /* Le */}; 86 | 87 | uint8_t response[32]; 88 | 89 | success = nfc.inDataExchange(selectApdu, sizeof(selectApdu), response, &responseLength); 90 | 91 | if (success) 92 | { 93 | 94 | Serial.print("responseLength: "); 95 | Serial.println(responseLength); 96 | 97 | nfc.PrintHexChar(response, responseLength); 98 | 99 | do 100 | { 101 | uint8_t apdu[] = "Hello from Arduino"; 102 | uint8_t back[32]; 103 | uint8_t length = 32; 104 | 105 | success = nfc.inDataExchange(apdu, sizeof(apdu), back, &length); 106 | 107 | if (success) 108 | { 109 | 110 | Serial.print("responseLength: "); 111 | Serial.println(length); 112 | 113 | nfc.PrintHexChar(back, length); 114 | } 115 | else 116 | { 117 | 118 | Serial.println("Broken connection?"); 119 | } 120 | } while (success); 121 | } 122 | else 123 | { 124 | 125 | Serial.println("Failed sending SELECT AID"); 126 | } 127 | } 128 | else 129 | { 130 | 131 | Serial.println("Didn't find anything!"); 132 | } 133 | 134 | delay(1000); 135 | } 136 | 137 | void printResponse(uint8_t *response, uint8_t responseLength) 138 | { 139 | 140 | String respBuffer; 141 | 142 | for (int i = 0; i < responseLength; i++) 143 | { 144 | 145 | if (response[i] < 0x10) 146 | respBuffer = respBuffer + "0"; //Adds leading zeros if hex value is smaller than 0x10 147 | 148 | respBuffer = respBuffer + String(response[i], HEX) + " "; 149 | } 150 | 151 | Serial.print("response: "); 152 | Serial.println(respBuffer); 153 | } 154 | 155 | void setupNFC() 156 | { 157 | 158 | nfc.begin(); 159 | 160 | uint32_t versiondata = nfc.getFirmwareVersion(); 161 | if (!versiondata) 162 | { 163 | Serial.print("Didn't find PN53x board"); 164 | while (1) 165 | ; // halt 166 | } 167 | 168 | // Got ok data, print it out! 169 | Serial.print("Found chip PN5"); 170 | Serial.println((versiondata >> 24) & 0xFF, HEX); 171 | Serial.print("Firmware ver. "); 172 | Serial.print((versiondata >> 16) & 0xFF, DEC); 173 | Serial.print('.'); 174 | Serial.println((versiondata >> 8) & 0xFF, DEC); 175 | 176 | // configure board to read RFID tags 177 | nfc.SAMConfig(); 178 | } 179 | -------------------------------------------------------------------------------- /examples/emulate_tag_ndef/emulate_tag_ndef.ino: -------------------------------------------------------------------------------- 1 | #include "emulatetag.h" 2 | #include "NdefMessage.h" 3 | 4 | #ifdef NRF52840_XXAA 5 | #ifdef USE_TINYUSB 6 | #include 7 | #endif 8 | #endif 9 | 10 | #if 0 11 | #define NFC_INTERFACE_SPI 12 | #include 13 | #include 14 | #include 15 | #include "PN532.h" 16 | 17 | PN532_SPI pn532spi(SPI, 10); 18 | EmulateTag nfc(pn532spi); 19 | #elif 1 20 | #define NFC_INTERFACE_HSU 21 | #include 22 | #include 23 | #include 24 | 25 | PN532_HSU pn532hsu(Serial1); 26 | EmulateTag nfc(pn532hsu); 27 | #endif 28 | 29 | uint8_t ndefBuf[120]; 30 | NdefMessage message; 31 | int messageSize; 32 | 33 | uint8_t uid[3] = {0x12, 0x34, 0x56}; 34 | 35 | void setup() 36 | { 37 | Serial.begin(115200); 38 | Serial.println("------- Emulate Tag --------"); 39 | 40 | message = NdefMessage(); 41 | message.addUriRecord("http://www.seeedstudio.com"); 42 | messageSize = message.getEncodedSize(); 43 | if (messageSize > sizeof(ndefBuf)) 44 | { 45 | Serial.println("ndefBuf is too small"); 46 | while (1) 47 | { 48 | } 49 | } 50 | 51 | Serial.print("Ndef encoded message size: "); 52 | Serial.println(messageSize); 53 | 54 | message.encode(ndefBuf); 55 | 56 | // comment out this command for no ndef message 57 | nfc.setNdefFile(ndefBuf, messageSize); 58 | 59 | // uid must be 3 bytes! 60 | nfc.setUid(uid); 61 | 62 | nfc.init(); 63 | } 64 | 65 | void loop() 66 | { 67 | // uncomment for overriding ndef in case a write to this tag occured 68 | //nfc.setNdefFile(ndefBuf, messageSize); 69 | 70 | // start emulation (blocks) 71 | nfc.emulate(); 72 | 73 | // or start emulation with timeout 74 | /*if(!nfc.emulate(1000)){ // timeout 1 second 75 | Serial.println("timed out"); 76 | }*/ 77 | 78 | // deny writing to the tag 79 | // nfc.setTagWriteable(false); 80 | 81 | if (nfc.writeOccured()) 82 | { 83 | Serial.println("\nWrite occured !"); 84 | uint8_t *tag_buf; 85 | uint16_t length; 86 | 87 | nfc.getContent(&tag_buf, &length); 88 | NdefMessage msg = NdefMessage(tag_buf, length); 89 | // msg.print(); 90 | } 91 | 92 | delay(1000); 93 | } 94 | -------------------------------------------------------------------------------- /examples/iso14443a_uid/iso14443a_uid.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will attempt to connect to an ISO14443A 4 | card or tag and retrieve some basic information about it 5 | that can be used to determine what type of card it is. 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | 12 | */ 13 | /**************************************************************************/ 14 | 15 | 16 | /* When the number after #if set as 1, it will be switch to SPI Mode*/ 17 | #if 0 18 | #define NFC_INTERFACE_SPI 19 | #include 20 | #include 21 | #include 22 | #include "PN532.h" 23 | 24 | PN532_SPI pn532spi(SPI, 10); 25 | PN532 nfc(pn532spi); 26 | 27 | /* When the number after #elif set as 1, it will be switch to HSU Mode*/ 28 | #elif 0 29 | #define NFC_INTERFACE_HSU 30 | #include 31 | #include 32 | #include 33 | 34 | PN532_HSU pn532hsu(Serial1); 35 | PN532 nfc(pn532hsu); 36 | 37 | /* When the number after #if & #elif set as 0, it will be switch to I2C Mode*/ 38 | #else 39 | #define NFC_INTERFACE_I2C 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | PN532_I2C pn532i2c(Wire); 46 | PN532 nfc(pn532i2c); 47 | #endif 48 | 49 | void setup(void) { 50 | Serial.begin(115200); 51 | Serial.println("Hello!"); 52 | 53 | nfc.begin(); 54 | 55 | uint32_t versiondata = nfc.getFirmwareVersion(); 56 | if (! versiondata) { 57 | Serial.print("Didn't find PN53x board"); 58 | while (1); // halt 59 | } 60 | 61 | // Got ok data, print it out! 62 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 63 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 64 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 65 | 66 | // Set the max number of retry attempts to read from a card 67 | // This prevents us from waiting forever for a card, which is 68 | // the default behaviour of the PN532. 69 | nfc.setPassiveActivationRetries(0xFF); 70 | 71 | // configure board to read RFID tags 72 | nfc.SAMConfig(); 73 | 74 | Serial.println("Waiting for an ISO14443A card"); 75 | } 76 | 77 | void loop(void) { 78 | boolean success; 79 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 80 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 81 | 82 | // Wait for an ISO14443A type cards (Mifare, etc.). When one is found 83 | // 'uid' will be populated with the UID, and uidLength will indicate 84 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 85 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength); 86 | 87 | if (success) { 88 | Serial.println("Found a card!"); 89 | Serial.print("UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 90 | Serial.print("UID Value: "); 91 | for (uint8_t i=0; i < uidLength; i++) 92 | { 93 | Serial.print(" 0x");Serial.print(uid[i], HEX); 94 | } 95 | Serial.println(""); 96 | // Wait 1 second before continuing 97 | delay(1000); 98 | } 99 | else 100 | { 101 | // PN532 probably timed out waiting for a card 102 | Serial.println("Timed out waiting for a card"); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /examples/mifareclassic_formatndef/mifareclassic_formatndef.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example attempts to format a clean Mifare Classic 1K card as 4 | an NFC Forum tag (to store NDEF messages that can be read by any 5 | NFC enabled Android phone, etc.) 6 | 7 | Note that you need the baud rate to be 115200 because we need to print 8 | out the data and read from the card at the same time! 9 | 10 | To enable debug message, define DEBUG in PN532/PN532_debug.h 11 | */ 12 | /**************************************************************************/ 13 | 14 | #if 0 15 | #define NFC_INTERFACE_SPI 16 | #include 17 | #include 18 | #include 19 | #include "PN532.h" 20 | 21 | PN532_SPI pn532spi(SPI, 10); 22 | PN532 nfc(pn532spi); 23 | #elif 1 24 | #define NFC_INTERFACE_HSU 25 | #include 26 | #include 27 | #include 28 | 29 | PN532_HSU pn532hsu(Serial1); 30 | PN532 nfc(pn532hsu); 31 | #else 32 | #define NFC_INTERFACE_I2C 33 | #include 34 | #include 35 | #include 36 | #include 37 | #endif 38 | 39 | #ifdef USE_TINYUSB 40 | #include 41 | #endif 42 | 43 | /* 44 | We can encode many different kinds of pointers to the card, 45 | from a URL, to an Email address, to a phone number, and many more 46 | check the library header .h file to see the large # of supported 47 | prefixes! 48 | */ 49 | // For a http://www. url: 50 | const char * url = "elechouse.com"; 51 | uint8_t ndefprefix = NDEF_URIPREFIX_HTTP_WWWDOT; 52 | 53 | // for an email address 54 | //const char * url = "mail@example.com"; 55 | //uint8_t ndefprefix = NDEF_URIPREFIX_MAILTO; 56 | 57 | // for a phone number 58 | //const char * url = "+1 212 555 1212"; 59 | //uint8_t ndefprefix = NDEF_URIPREFIX_TEL; 60 | 61 | 62 | void setup(void) { 63 | Serial.begin(115200); 64 | Serial.println("Looking for PN532..."); 65 | 66 | nfc.begin(); 67 | 68 | uint32_t versiondata = nfc.getFirmwareVersion(); 69 | if (! versiondata) { 70 | Serial.print("Didn't find PN53x board"); 71 | while (1); // halt 72 | } 73 | 74 | // Got ok data, print it out! 75 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 76 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 77 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 78 | 79 | // configure board to read RFID tags 80 | nfc.SAMConfig(); 81 | } 82 | 83 | void loop(void) { 84 | uint8_t success; // Flag to check if there was an error with the PN532 85 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 86 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 87 | bool authenticated = false; // Flag to indicate if the sector is authenticated 88 | 89 | // Use the default key 90 | uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 91 | 92 | Serial.println(""); 93 | Serial.println("PLEASE NOTE: Formatting your card for NDEF records will change the"); 94 | Serial.println("authentication keys. To reformat your NDEF tag as a clean Mifare"); 95 | Serial.println("Classic tag, use the mifareclassic_ndeftoclassic example!"); 96 | Serial.println(""); 97 | Serial.println("Place your Mifare Classic card on the reader to format with NDEF"); 98 | Serial.println("and press any key to continue ..."); 99 | // Wait for user input before proceeding 100 | while (!Serial.available()); 101 | // a key was pressed1 102 | while (Serial.available()) Serial.read(); 103 | 104 | // Wait for an ISO14443A type card (Mifare, etc.). When one is found 105 | // 'uid' will be populated with the UID, and uidLength will indicate 106 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 107 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 108 | 109 | if (success) 110 | { 111 | // Display some basic information about the card 112 | Serial.println("Found an ISO14443A card"); 113 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 114 | Serial.print(" UID Value: "); 115 | nfc.PrintHex(uid, uidLength); 116 | for (uint8_t i = 0; i < uidLength; i++) { 117 | Serial.print(uid[i], HEX); 118 | Serial.print(' '); 119 | } 120 | Serial.println(""); 121 | 122 | // Make sure this is a Mifare Classic card 123 | if (uidLength != 4) 124 | { 125 | Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!"); 126 | return; 127 | } 128 | 129 | // We probably have a Mifare Classic card ... 130 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 131 | 132 | // Try to format the card for NDEF data 133 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 0, 0, keya); 134 | if (!success) 135 | { 136 | Serial.println("Unable to authenticate block 0 to enable card formatting!"); 137 | return; 138 | } 139 | success = nfc.mifareclassic_FormatNDEF(); 140 | if (!success) 141 | { 142 | Serial.println("Unable to format the card for NDEF"); 143 | return; 144 | } 145 | 146 | Serial.println("Card has been formatted for NDEF data using MAD1"); 147 | 148 | // Try to authenticate block 4 (first block of sector 1) using our key 149 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 4, 0, keya); 150 | 151 | // Make sure the authentification process didn't fail 152 | if (!success) 153 | { 154 | Serial.println("Authentication failed."); 155 | return; 156 | } 157 | 158 | // Try to write a URL 159 | Serial.println("Writing URI to sector 1 as an NDEF Message"); 160 | 161 | // Authenticated seems to have worked 162 | // Try to write an NDEF record to sector 1 163 | // Use 0x01 for the URI Identifier Code to prepend "http://www." 164 | // to the url (and save some space). For information on URI ID Codes 165 | // see http://www.ladyada.net/wiki/private/articlestaging/nfc/ndef 166 | if (strlen(url) > 38) 167 | { 168 | // The length is also checked in the WriteNDEFURI function, but lets 169 | // warn users here just in case they change the value and it's bigger 170 | // than it should be 171 | Serial.println("URI is too long ... must be less than 38 characters long"); 172 | return; 173 | } 174 | 175 | // URI is within size limits ... write it to the card and report success/failure 176 | success = nfc.mifareclassic_WriteNDEFURI(1, ndefprefix, url); 177 | if (success) 178 | { 179 | Serial.println("NDEF URI Record written to sector 1"); 180 | } 181 | else 182 | { 183 | Serial.println("NDEF Record creation failed! :("); 184 | } 185 | } 186 | 187 | // Wait a bit before trying again 188 | Serial.println("\n\nDone!"); 189 | delay(1000); 190 | Serial.flush(); 191 | while(Serial.available()) Serial.read(); 192 | } -------------------------------------------------------------------------------- /examples/mifareclassic_memdump/mifareclassic_memdump.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example attempts to dump the contents of a Mifare Classic 1K card 4 | 5 | Note that you need the baud rate to be 115200 because we need to print 6 | out the data and read from the card at the same time! 7 | 8 | To enable debug message, define DEBUG in PN532/PN532_debug.h 9 | */ 10 | /**************************************************************************/ 11 | 12 | #if 0 13 | #define NFC_INTERFACE_SPI 14 | #include 15 | #include 16 | #include 17 | #include "PN532.h" 18 | 19 | PN532_SPI pn532spi(SPI, 10); 20 | PN532 nfc(pn532spi); 21 | #elif 1 22 | #define NFC_INTERFACE_HSU 23 | #include 24 | #include 25 | #include 26 | 27 | PN532_HSU pn532hsu(Serial1); 28 | PN532 nfc(pn532hsu); 29 | #else 30 | #define NFC_INTERFACE_I2C 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | PN532_I2C pn532i2c(Wire); 37 | PN532 nfc(pn532i2c); 38 | #endif 39 | 40 | #ifdef USE_TINYUSB 41 | #include 42 | #endif 43 | 44 | 45 | void setup(void) { 46 | // has to be fast to dump the entire memory contents! 47 | Serial.begin(115200); 48 | Serial.println("Looking for PN532..."); 49 | 50 | nfc.begin(); 51 | 52 | uint32_t versiondata = nfc.getFirmwareVersion(); 53 | if (! versiondata) { 54 | Serial.print("Didn't find PN53x board"); 55 | while (1); // halt 56 | } 57 | // Got ok data, print it out! 58 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 59 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 60 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 61 | 62 | // configure board to read RFID tags 63 | nfc.SAMConfig(); 64 | 65 | Serial.println("Waiting for an ISO14443A Card ..."); 66 | } 67 | 68 | 69 | void loop(void) { 70 | uint8_t success; // Flag to check if there was an error with the PN532 71 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 72 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 73 | uint8_t currentblock; // Counter to keep track of which block we're on 74 | bool authenticated = false; // Flag to indicate if the sector is authenticated 75 | uint8_t data[16]; // Array to store block data during reads 76 | 77 | // Keyb on NDEF and Mifare Classic should be the same 78 | uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 79 | 80 | // Wait for an ISO14443A type cards (Mifare, etc.). When one is found 81 | // 'uid' will be populated with the UID, and uidLength will indicate 82 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 83 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 84 | 85 | if (success) { 86 | // Display some basic information about the card 87 | Serial.println("Found an ISO14443A card"); 88 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 89 | Serial.print(" UID Value: "); 90 | for (uint8_t i = 0; i < uidLength; i++) { 91 | Serial.print(uid[i], HEX); 92 | Serial.print(' '); 93 | } 94 | Serial.println(""); 95 | 96 | if (uidLength == 4) 97 | { 98 | // We probably have a Mifare Classic card ... 99 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 100 | 101 | // Now we try to go through all 16 sectors (each having 4 blocks) 102 | // authenticating each sector, and then dumping the blocks 103 | for (currentblock = 0; currentblock < 64; currentblock++) 104 | { 105 | // Check if this is a new block so that we can reauthenticate 106 | if (nfc.mifareclassic_IsFirstBlock(currentblock)) authenticated = false; 107 | 108 | // If the sector hasn't been authenticated, do so first 109 | if (!authenticated) 110 | { 111 | // Starting of a new sector ... try to to authenticate 112 | Serial.print("------------------------Sector ");Serial.print(currentblock/4, DEC);Serial.println("-------------------------"); 113 | if (currentblock == 0) 114 | { 115 | // This will be 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF for Mifare Classic (non-NDEF!) 116 | // or 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 for NDEF formatted cards using key a, 117 | // but keyb should be the same for both (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) 118 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentblock, 1, keyuniversal); 119 | } 120 | else 121 | { 122 | // This will be 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF for Mifare Classic (non-NDEF!) 123 | // or 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 for NDEF formatted cards using key a, 124 | // but keyb should be the same for both (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) 125 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentblock, 1, keyuniversal); 126 | } 127 | if (success) 128 | { 129 | authenticated = true; 130 | } 131 | else 132 | { 133 | Serial.println("Authentication error"); 134 | } 135 | } 136 | // If we're still not authenticated just skip the block 137 | if (!authenticated) 138 | { 139 | Serial.print("Block ");Serial.print(currentblock, DEC);Serial.println(" unable to authenticate"); 140 | } 141 | else 142 | { 143 | // Authenticated ... we should be able to read the block now 144 | // Dump the data into the 'data' array 145 | success = nfc.mifareclassic_ReadDataBlock(currentblock, data); 146 | if (success) 147 | { 148 | // Read successful 149 | Serial.print("Block ");Serial.print(currentblock, DEC); 150 | if (currentblock < 10) 151 | { 152 | Serial.print(" "); 153 | } 154 | else 155 | { 156 | Serial.print(" "); 157 | } 158 | // Dump the raw data 159 | nfc.PrintHexChar(data, 16); 160 | } 161 | else 162 | { 163 | // Oops ... something happened 164 | Serial.print("Block ");Serial.print(currentblock, DEC); 165 | Serial.println(" unable to read this block"); 166 | } 167 | } 168 | } 169 | } 170 | else 171 | { 172 | Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!"); 173 | } 174 | } 175 | // Wait a bit before trying again 176 | Serial.println("\n\nSend a character to run the mem dumper again!"); 177 | Serial.flush(); 178 | while (!Serial.available()); 179 | while (Serial.available()) { 180 | Serial.read(); 181 | } 182 | Serial.flush(); 183 | } -------------------------------------------------------------------------------- /examples/mifareclassic_ndeftoclassic/mifareclassic_ndeftoclassic.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This examples attempts to take a Mifare Classic 1K card that has been 4 | formatted for NDEF messages using mifareclassic_formatndef, and resets 5 | the authentication keys back to the Mifare Classic defaults 6 | 7 | To enable debug message, define DEBUG in PN532/PN532_debug.h 8 | */ 9 | /**************************************************************************/ 10 | 11 | #if 0 12 | #define NFC_INTERFACE_SPI 13 | #include 14 | #include 15 | #include 16 | #include "PN532.h" 17 | 18 | PN532_SPI pn532spi(SPI, 10); 19 | PN532 nfc(pn532spi); 20 | #elif 1 21 | #define NFC_INTERFACE_HSU 22 | #include 23 | #include 24 | #include 25 | 26 | PN532_HSU pn532hsu(Serial1); 27 | PN532 nfc(pn532hsu); 28 | #else 29 | #define NFC_INTERFACE_I2C 30 | #include 31 | #include 32 | #include 33 | #include 34 | #endif 35 | 36 | #ifdef USE_TINYUSB 37 | #include 38 | #endif 39 | 40 | #define NR_SHORTSECTOR (32) // Number of short sectors on Mifare 1K/4K 41 | #define NR_LONGSECTOR (8) // Number of long sectors on Mifare 4K 42 | #define NR_BLOCK_OF_SHORTSECTOR (4) // Number of blocks in a short sector 43 | #define NR_BLOCK_OF_LONGSECTOR (16) // Number of blocks in a long sector 44 | 45 | // Determine the sector trailer block based on sector number 46 | #define BLOCK_NUMBER_OF_SECTOR_TRAILER(sector) (((sector)>24) & 0xFF, HEX); 72 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 73 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 74 | 75 | // configure board to read RFID tags 76 | nfc.SAMConfig(); 77 | } 78 | 79 | void loop(void) { 80 | uint8_t success; // Flag to check if there was an error with the PN532 81 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 82 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 83 | bool authenticated = false; // Flag to indicate if the sector is authenticated 84 | uint8_t blockBuffer[16]; // Buffer to store block contents 85 | uint8_t blankAccessBits[3] = { 0xff, 0x07, 0x80 }; 86 | uint8_t idx = 0; 87 | uint8_t numOfSector = 16; // Assume Mifare Classic 1K for now (16 4-block sectors) 88 | 89 | Serial.println("Place your NDEF formatted Mifare Classic 1K card on the reader"); 90 | Serial.println("and press any key to continue ..."); 91 | 92 | // Wait for user input before proceeding 93 | while (!Serial.available()); 94 | while (Serial.available()) Serial.read(); 95 | 96 | // Wait for an ISO14443A type card (Mifare, etc.). When one is found 97 | // 'uid' will be populated with the UID, and uidLength will indicate 98 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 99 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 100 | 101 | if (success) 102 | { 103 | // We seem to have a tag ... 104 | // Display some basic information about it 105 | Serial.println("Found an ISO14443A card/tag"); 106 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 107 | Serial.print(" UID Value: "); 108 | nfc.PrintHex(uid, uidLength); 109 | Serial.println(""); 110 | 111 | // Make sure this is a Mifare Classic card 112 | if (uidLength != 4) 113 | { 114 | Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!"); 115 | return; 116 | } 117 | 118 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 119 | Serial.println(""); 120 | Serial.println("Reformatting card for Mifare Classic (please don't touch it!) ... "); 121 | 122 | // Now run through the card sector by sector 123 | for (idx = 0; idx < numOfSector; idx++) 124 | { 125 | // Step 1: Authenticate the current sector using key B 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 126 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, BLOCK_NUMBER_OF_SECTOR_TRAILER(idx), 1, (uint8_t *)KEY_DEFAULT_KEYAB); 127 | if (!success) 128 | { 129 | Serial.print("Authentication failed for sector "); Serial.println(numOfSector); 130 | return; 131 | } 132 | 133 | // Step 2: Write to the other blocks 134 | if (idx == 16) 135 | { 136 | memset(blockBuffer, 0, sizeof(blockBuffer)); 137 | if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 3, blockBuffer))) 138 | { 139 | Serial.print("Unable to write to sector "); Serial.println(numOfSector); 140 | return; 141 | } 142 | } 143 | if ((idx == 0) || (idx == 16)) 144 | { 145 | memset(blockBuffer, 0, sizeof(blockBuffer)); 146 | if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 2, blockBuffer))) 147 | { 148 | Serial.print("Unable to write to sector "); Serial.println(numOfSector); 149 | return; 150 | } 151 | } 152 | else 153 | { 154 | memset(blockBuffer, 0, sizeof(blockBuffer)); 155 | if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 3, blockBuffer))) 156 | { 157 | Serial.print("Unable to write to sector "); Serial.println(numOfSector); 158 | return; 159 | } 160 | if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 2, blockBuffer))) 161 | { 162 | Serial.print("Unable to write to sector "); Serial.println(numOfSector); 163 | return; 164 | } 165 | } 166 | memset(blockBuffer, 0, sizeof(blockBuffer)); 167 | if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 1, blockBuffer))) 168 | { 169 | Serial.print("Unable to write to sector "); Serial.println(numOfSector); 170 | return; 171 | } 172 | 173 | // Step 3: Reset both keys to 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 174 | memcpy(blockBuffer, KEY_DEFAULT_KEYAB, sizeof(KEY_DEFAULT_KEYAB)); 175 | memcpy(blockBuffer + 6, blankAccessBits, sizeof(blankAccessBits)); 176 | blockBuffer[9] = 0x69; 177 | memcpy(blockBuffer + 10, KEY_DEFAULT_KEYAB, sizeof(KEY_DEFAULT_KEYAB)); 178 | 179 | // Step 4: Write the trailer block 180 | if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)), blockBuffer))) 181 | { 182 | Serial.print("Unable to write trailer block of sector "); Serial.println(numOfSector); 183 | return; 184 | } 185 | } 186 | } 187 | 188 | // Wait a bit before trying again 189 | Serial.println("\n\nDone!"); 190 | delay(1000); 191 | Serial.flush(); 192 | while(Serial.available()) Serial.read(); 193 | } -------------------------------------------------------------------------------- /examples/mifareclassic_updatendef/mifareclassic_updatendef.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | Updates a sector that is already formatted for NDEF (using 4 | mifareclassic_formatndef.pde for example), inserting a new url 5 | 6 | To enable debug message, define DEBUG in PN532/PN532_debug.h 7 | */ 8 | /**************************************************************************/ 9 | 10 | #if 0 11 | #define NFC_INTERFACE_SPI 12 | #include 13 | #include 14 | #include 15 | #include "PN532.h" 16 | 17 | PN532_SPI pn532spi(SPI, 10); 18 | PN532 nfc(pn532spi); 19 | #elif 1 20 | #define NFC_INTERFACE_HSU 21 | #include 22 | #include 23 | #include 24 | 25 | PN532_HSU pn532hsu(Serial1); 26 | PN532 nfc(pn532hsu); 27 | #else 28 | #define NFC_INTERFACE_I2C 29 | #include 30 | #include 31 | #include 32 | #include 33 | #endif 34 | 35 | #ifdef USE_TINYUSB 36 | #include 37 | #endif 38 | 39 | 40 | /* 41 | We can encode many different kinds of pointers to the card, 42 | from a URL, to an Email address, to a phone number, and many more 43 | check the library header .h file to see the large # of supported 44 | prefixes! 45 | */ 46 | // For a http://www. url: 47 | const char * url = "elechouse.com"; 48 | uint8_t ndefprefix = NDEF_URIPREFIX_HTTP_WWWDOT; 49 | 50 | // for an email address 51 | //const char * url = "mail@example.com"; 52 | //uint8_t ndefprefix = NDEF_URIPREFIX_MAILTO; 53 | 54 | // for a phone number 55 | //const char * url = "+1 212 555 1212"; 56 | //uint8_t ndefprefix = NDEF_URIPREFIX_TEL; 57 | 58 | 59 | void setup(void) { 60 | Serial.begin(115200); 61 | Serial.println("Looking for PN532..."); 62 | 63 | nfc.begin(); 64 | 65 | uint32_t versiondata = nfc.getFirmwareVersion(); 66 | if (! versiondata) { 67 | Serial.print("Didn't find PN53x board"); 68 | while (1); // halt 69 | } 70 | 71 | // Got ok data, print it out! 72 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 73 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 74 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 75 | 76 | // configure board to read RFID tags 77 | nfc.SAMConfig(); 78 | } 79 | 80 | void loop(void) { 81 | uint8_t success; // Flag to check if there was an error with the PN532 82 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 83 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 84 | bool authenticated = false; // Flag to indicate if the sector is authenticated 85 | 86 | // Use the default NDEF keys (these would have have set by mifareclassic_formatndef.pde!) 87 | uint8_t keya[6] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 }; 88 | uint8_t keyb[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 }; 89 | 90 | Serial.println("Place your NDEF formatted Mifare Classic card on the reader to update the"); 91 | Serial.println("NDEF record and press any key to continue ..."); 92 | // Wait for user input before proceeding 93 | while (!Serial.available()); 94 | // a key was pressed1 95 | while (Serial.available()) Serial.read(); 96 | 97 | // Wait for an ISO14443A type card (Mifare, etc.). When one is found 98 | // 'uid' will be populated with the UID, and uidLength will indicate 99 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 100 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 101 | 102 | if (success) 103 | { 104 | // Display some basic information about the card 105 | Serial.println("Found an ISO14443A card"); 106 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 107 | Serial.print(" UID Value: "); 108 | nfc.PrintHex(uid, uidLength); 109 | Serial.println(""); 110 | 111 | // Make sure this is a Mifare Classic card 112 | if (uidLength != 4) 113 | { 114 | Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!"); 115 | return; 116 | } 117 | 118 | // We probably have a Mifare Classic card ... 119 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 120 | 121 | // Check if this is an NDEF card (using first block of sector 1 from mifareclassic_formatndef.pde) 122 | // Must authenticate on the first key using 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 123 | success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 4, 0, keyb); 124 | if (!success) 125 | { 126 | Serial.println("Unable to authenticate block 4 ... is this card NDEF formatted?"); 127 | return; 128 | } 129 | 130 | Serial.println("Authentication succeeded (seems to be an NDEF/NFC Forum tag) ..."); 131 | 132 | // Authenticated seems to have worked 133 | // Try to write an NDEF record to sector 1 134 | // Use 0x01 for the URI Identifier Code to prepend "http://www." 135 | // to the url (and save some space). For information on URI ID Codes 136 | // see http://www.ladyada.net/wiki/private/articlestaging/nfc/ndef 137 | if (strlen(url) > 38) 138 | { 139 | // The length is also checked in the WriteNDEFURI function, but lets 140 | // warn users here just in case they change the value and it's bigger 141 | // than it should be 142 | Serial.println("URI is too long ... must be less than 38 characters!"); 143 | return; 144 | } 145 | 146 | Serial.println("Updating sector 1 with URI as NDEF Message"); 147 | 148 | // URI is within size limits ... write it to the card and report success/failure 149 | success = nfc.mifareclassic_WriteNDEFURI(1, ndefprefix, url); 150 | if (success) 151 | { 152 | Serial.println("NDEF URI Record written to sector 1"); 153 | Serial.println(""); 154 | } 155 | else 156 | { 157 | Serial.println("NDEF Record creation failed! :("); 158 | } 159 | } 160 | 161 | // Wait a bit before trying again 162 | Serial.println("\n\nDone!"); 163 | delay(1000); 164 | Serial.flush(); 165 | while(Serial.available()) Serial.read(); 166 | } 167 | -------------------------------------------------------------------------------- /examples/ntag21x_protect/ntag21x_protect.ino: -------------------------------------------------------------------------------- 1 | // NTAG21x supports 4 bytes password to protect pages started from AUTH0 2 | // AUTH0 defines the page address from which the password verification is required. 3 | // Valid address range for byte AUTH0 is from 00h to FFh. 4 | // If AUTH0 is set to a page address which is higher than the last page from the user configuration, 5 | // the password protection is effectively disabled 6 | #include 7 | #include 8 | #if 0 // Using PN532's SPI (Seeed NFC shield) 9 | #define NFC_INTERFACE_SPI 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | PN532_SPI intf(SPI, 10); 16 | PN532 nfc = PN532(intf); 17 | #else // Using PN532's I2C 18 | #define NFC_INTERFACE_I2C 19 | #include 20 | #include 21 | #include 22 | 23 | PN532_I2C intf(Wire); 24 | PN532 nfc = PN532(intf); 25 | #endif 26 | 27 | // Using PN532's UART (Grove NFC) 28 | 29 | // #include 30 | // #include 31 | // #include 32 | // PN532_HSU intf(Serial1); 33 | // PN532 nfc = PN532(intf); 34 | 35 | uint8_t password[4] = {0x12, 0x34, 0x56, 0x78}; 36 | uint8_t buf[4]; 37 | uint8_t uid[7]; 38 | uint8_t uidLength; 39 | 40 | void setup(void) 41 | { 42 | Serial.begin(9600); 43 | Serial.println("NTAG21x R/W"); 44 | 45 | nfc.begin(); 46 | nfc.SAMConfig(); 47 | } 48 | 49 | void loop(void) 50 | { 51 | Serial.println("wait for a tag"); 52 | // wait until a tag is present 53 | while (!nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) 54 | { 55 | } 56 | 57 | // if NTAG21x enables r/w protection, uncomment the following line 58 | // nfc.ntag21x_auth(password); 59 | 60 | nfc.mifareultralight_ReadPage(3, buf); 61 | int capacity = buf[2] * 8; 62 | Serial.print(F("Tag capacity ")); 63 | Serial.print(capacity); 64 | Serial.println(F(" bytes")); 65 | 66 | uint8_t cfg_page_base = 0x29; // NTAG213 67 | if (capacity == 0x3E) 68 | { 69 | cfg_page_base = 0x83; // NTAG215 70 | } 71 | else if (capacity == 0x6D) 72 | { 73 | cfg_page_base = 0xE3; // NTAG216 74 | } 75 | 76 | // PWD page, set new password 77 | nfc.mifareultralight_WritePage(cfg_page_base + 2, password); 78 | 79 | // disable r/w 80 | // | PROT | CFG_LCK | RFUI | NFC_CNT_EN | NFC_CNT_PWD_PROT | AUTHLIM (2:0) | 81 | buf[0] = (1 << 7) | 0x0; 82 | nfc.mifareultralight_WritePage(cfg_page_base + 1, buf); 83 | 84 | // protect pages started from AUTH0 85 | uint8_t auth0 = 0x10; 86 | buf[0] = 0; 87 | buf[1] = 0; 88 | buf[2] = 0; 89 | buf[3] = auth0; 90 | nfc.mifareultralight_WritePage(cfg_page_base, buf); 91 | 92 | // wait until the tag is removed 93 | while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) 94 | { 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/ntag21x_rw/ntag21x_rw.ino: -------------------------------------------------------------------------------- 1 | 2 | // Clean resets a tag back to factory-like state 3 | // For Mifare Classic, tag is zero'd and reformatted as Mifare Classic 4 | // For Mifare Ultralight, tags is zero'd and left empty 5 | #include 6 | #include 7 | 8 | #if 0 // Using PN532's SPI (Seeed NFC shield) 9 | #define NFC_INTERFACE_SPI 10 | #include 11 | #include 12 | #include 13 | 14 | PN532_SPI intf(SPI, 10); 15 | PN532 nfc = PN532(intf); 16 | #else // Using PN532's I2C 17 | #define NFC_INTERFACE_I2C 18 | #include 19 | #include 20 | #include 21 | PN532_I2C intf(Wire); 22 | PN532 nfc = PN532(intf); 23 | #endif 24 | 25 | // Using PN532's UART (Grove NFC) 26 | 27 | // #include 28 | // #include 29 | // #include 30 | // PN532_HSU intf(Serial1); 31 | // PN532 nfc = PN532(intf); 32 | 33 | uint8_t password[4] = {0x12, 0x34, 0x56, 0x78}; 34 | uint8_t buf[4]; 35 | uint8_t uid[7]; 36 | uint8_t uidLength; 37 | 38 | void setup(void) 39 | { 40 | Serial.begin(9600); 41 | Serial.println("NTAG21x R/W"); 42 | 43 | nfc.begin(); 44 | nfc.SAMConfig(); 45 | } 46 | 47 | void loop(void) 48 | { 49 | Serial.println("wait for a tag"); 50 | // wait until a tag is present 51 | while (!nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) 52 | { 53 | } 54 | 55 | // if NTAG21x enables r/w protection, uncomment the following line 56 | // nfc.ntag21x_auth(password); 57 | 58 | nfc.mifareultralight_ReadPage(3, buf); 59 | int capacity = buf[2] * 8; 60 | Serial.print(F("Tag capacity ")); 61 | Serial.print(capacity); 62 | Serial.println(F(" bytes")); 63 | 64 | for (int i = 4; i < capacity / 4; i++) 65 | { 66 | nfc.mifareultralight_ReadPage(i, buf); 67 | nfc.PrintHexChar(buf, 4); 68 | } 69 | 70 | // wait until the tag is removed 71 | while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) 72 | { 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/p2p_raw/p2p_raw.ino: -------------------------------------------------------------------------------- 1 | // snep_test.ino 2 | // send a SNEP message to adnroid and get a message from android 3 | #define NFC_INTERFACE_SPI 4 | #include "SPI.h" 5 | #include "PN532_SPI.h" 6 | #include "PN532_SPI.cpp" 7 | #include "llcp.h" 8 | #include "snep.h" 9 | 10 | PN532_SPI pn532spi(SPI, 10); 11 | SNEP nfc(pn532spi); 12 | 13 | void setup() 14 | { 15 | Serial.begin(115200); 16 | Serial.println("-------Peer to Peer--------"); 17 | } 18 | 19 | uint8_t message[] = { 20 | 0xD2, 0xA, 0xB, 0x74, 0x65, 0x78, 0x74, 0x2F, 0x70, 0x6C, 21 | 0x61, 0x69, 0x6E, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 22 | 0x6F, 0x72, 0x6C, 0x64}; 23 | 24 | uint8_t buf[128]; 25 | 26 | void loop() 27 | { 28 | 29 | nfc.write(message, sizeof(message)); 30 | delay(3000); 31 | 32 | int16_t len = nfc.read(buf, sizeof(buf)); 33 | if (len > 0) 34 | { 35 | Serial.println("get a SNEP message:"); 36 | for (uint8_t i = 0; i < len; i++) 37 | { 38 | Serial.print(buf[i], HEX); 39 | Serial.print(' '); 40 | } 41 | Serial.print('\n'); 42 | for (uint8_t i = 0; i < len; i++) 43 | { 44 | char c = buf[i]; 45 | if (c <= 0x1f || c > 0x7f) 46 | { 47 | Serial.print('.'); 48 | } 49 | else 50 | { 51 | Serial.print(c); 52 | } 53 | } 54 | Serial.print('\n'); 55 | } 56 | delay(3000); 57 | } -------------------------------------------------------------------------------- /examples/p2p_with_ndef_library/p2p_with_ndef_library.ino: -------------------------------------------------------------------------------- 1 | // send a NDEF message to adnroid or get a NDEF message 2 | // 3 | // note: [NDEF library](https://github.com/Don/NDEF) is needed. 4 | #define NFC_INTERFACE_SPI 5 | #include "SPI.h" 6 | #include "PN532_SPI.h" 7 | #include "PN532_SPI.cpp" 8 | #include "snep.h" 9 | #include "NdefMessage.h" 10 | 11 | PN532_SPI pn532spi(SPI, 10); 12 | SNEP nfc(pn532spi); 13 | uint8_t ndefBuf[128]; 14 | 15 | void setup() 16 | { 17 | Serial.begin(115200); 18 | Serial.println("-------Peer to Peer--------"); 19 | } 20 | 21 | void loop() 22 | { 23 | #if 1 24 | Serial.println("Send a message to Android"); 25 | NdefMessage message = NdefMessage(); 26 | message.addUriRecord("http://www.seeedstudio.com"); 27 | int messageSize = message.getEncodedSize(); 28 | if (messageSize > sizeof(ndefBuf)) 29 | { 30 | Serial.println("ndefBuf is too small"); 31 | while (1) 32 | { 33 | } 34 | } 35 | 36 | message.encode(ndefBuf); 37 | if (0 >= nfc.write(ndefBuf, messageSize)) 38 | { 39 | Serial.println("Failed"); 40 | } 41 | else 42 | { 43 | Serial.println("Success"); 44 | } 45 | 46 | delay(3000); 47 | #else 48 | Serial.println("Get a message from Android"); 49 | int msgSize = nfc.read(ndefBuf, sizeof(ndefBuf)); 50 | if (msgSize > 0) 51 | { 52 | NdefMessage msg = NdefMessage(ndefBuf, msgSize); 53 | // msg.print(); 54 | Serial.println("\nSuccess"); 55 | } 56 | else 57 | { 58 | Serial.println("failed"); 59 | } 60 | delay(3000); 61 | #endif 62 | } 63 | -------------------------------------------------------------------------------- /examples/readMifare/readMifare.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | This example will wait for any ISO14443A card or tag, and 4 | depending on the size of the UID will attempt to read from it. 5 | 6 | If the card has a 4-byte UID it is probably a Mifare 7 | Classic card, and the following steps are taken: 8 | 9 | - Authenticate block 4 (the first block of Sector 1) using 10 | the default KEYA of 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF 11 | - If authentication succeeds, we can then read any of the 12 | 4 blocks in that sector (though only block 4 is read here) 13 | 14 | If the card has a 7-byte UID it is probably a Mifare 15 | Ultralight card, and the 4 byte pages can be read directly. 16 | Page 4 is read by default since this is the first 'general- 17 | purpose' page on the tags. 18 | 19 | To enable debug message, define DEBUG in PN532/PN532_debug.h 20 | */ 21 | /**************************************************************************/ 22 | 23 | #if 0 24 | #define NFC_INTERFACE_SPI 25 | #include 26 | #include 27 | #include 28 | #include "PN532.h" 29 | 30 | PN532_SPI pn532spi(SPI, 10); 31 | PN532 nfc(pn532spi); 32 | #elif 1 33 | #define NFC_INTERFACE_HSU 34 | #include 35 | #include 36 | #include 37 | 38 | PN532_HSU pn532hsu(Serial1); 39 | PN532 nfc(pn532hsu); 40 | #else 41 | #define NFC_INTERFACE_I2C 42 | #include 43 | #include 44 | #include 45 | #include 46 | PN532_I2C pn532i2c(Wire); 47 | PN532 nfc(pn532i2c); 48 | #endif 49 | 50 | #ifdef USE_TINYUSB 51 | #include 52 | #endif 53 | 54 | void setup(void) { 55 | Serial.begin(115200); 56 | Serial.println("Hello!"); 57 | 58 | nfc.begin(); 59 | 60 | uint32_t versiondata = nfc.getFirmwareVersion(); 61 | if (! versiondata) { 62 | Serial.print("Didn't find PN53x board"); 63 | while (1); // halt 64 | } 65 | // Got ok data, print it out! 66 | Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 67 | Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 68 | Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC); 69 | 70 | // configure board to read RFID tags 71 | nfc.SAMConfig(); 72 | 73 | Serial.println("Waiting for an ISO14443A Card ..."); 74 | } 75 | 76 | 77 | void loop(void) { 78 | uint8_t success; 79 | uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID 80 | uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type) 81 | 82 | // Wait for an ISO14443A type cards (Mifare, etc.). When one is found 83 | // 'uid' will be populated with the UID, and uidLength will indicate 84 | // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight) 85 | success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength); 86 | 87 | if (success) { 88 | // Display some basic information about the card 89 | Serial.println("Found an ISO14443A card"); 90 | Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes"); 91 | Serial.print(" UID Value: "); 92 | nfc.PrintHex(uid, uidLength); 93 | Serial.println(""); 94 | 95 | if (uidLength == 4) 96 | { 97 | // We probably have a Mifare Classic card ... 98 | Serial.println("Seems to be a Mifare Classic card (4 byte UID)"); 99 | 100 | // Now we need to try to authenticate it for read/write access 101 | // Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 102 | Serial.println("Trying to authenticate block 4 with default KEYA value"); 103 | uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 104 | 105 | // Start with block 4 (the first block of sector 1) since sector 0 106 | // contains the manufacturer data and it's probably better just 107 | // to leave it alone unless you know what you're doing 108 | success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya); 109 | 110 | if (success) 111 | { 112 | Serial.println("Sector 1 (Blocks 4..7) has been authenticated"); 113 | uint8_t data[16]; 114 | 115 | // If you want to write something to block 4 to test with, uncomment 116 | // the following line and this text should be read back in a minute 117 | // data = { 'a', 'd', 'a', 'f', 'r', 'u', 'i', 't', '.', 'c', 'o', 'm', 0, 0, 0, 0}; 118 | // success = nfc.mifareclassic_WriteDataBlock (4, data); 119 | 120 | // Try to read the contents of block 4 121 | success = nfc.mifareclassic_ReadDataBlock(4, data); 122 | 123 | if (success) 124 | { 125 | // Data seems to have been read ... spit it out 126 | Serial.println("Reading Block 4:"); 127 | nfc.PrintHexChar(data, 16); 128 | Serial.println(""); 129 | 130 | // Wait a bit before reading the card again 131 | delay(1000); 132 | } 133 | else 134 | { 135 | Serial.println("Ooops ... unable to read the requested block. Try another key?"); 136 | } 137 | } 138 | else 139 | { 140 | Serial.println("Ooops ... authentication failed: Try another key?"); 141 | } 142 | } 143 | 144 | if (uidLength == 7) 145 | { 146 | // We probably have a Mifare Ultralight card ... 147 | Serial.println("Seems to be a Mifare Ultralight tag (7 byte UID)"); 148 | 149 | // Try to read the first general-purpose user page (#4) 150 | Serial.println("Reading page 4"); 151 | uint8_t data[32]; 152 | success = nfc.mifareultralight_ReadPage (4, data); 153 | if (success) 154 | { 155 | // Data seems to have been read ... spit it out 156 | nfc.PrintHexChar(data, 4); 157 | Serial.println(""); 158 | 159 | // Wait a bit before reading the card again 160 | delay(1000); 161 | } 162 | else 163 | { 164 | Serial.println("Ooops ... unable to read the requested page!?"); 165 | } 166 | } 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", 3 | "name": "PN532-Arduino", 4 | "description": "This is Seeed-Studio's library for PN532 to use NFC technology.", 5 | "authors": [ 6 | { 7 | "maintainer": true, 8 | "name": "Baozhu Zuo", 9 | "email": "zuobaozhu@gmail.com", 10 | "url": "https://github.com/Pillar1989" 11 | }, 12 | { 13 | "name": "Yihui Xiong", 14 | "url": "https://github.com/xiongyihui" 15 | }, 16 | { 17 | "url": "https://github.com/awieser" 18 | }, 19 | { 20 | "name": "Jiapeng Li", 21 | "email": "mail@jiapeng.me", 22 | "url": "https://github.com/JiapengLi" 23 | }, 24 | { 25 | "url": "https://github.com/wilson-elechouse" 26 | } 27 | ], 28 | "frameworks": ["arduino"], 29 | "platforms": ["*"], 30 | "keywords": ["PN532", "NFC"], 31 | "headers": [ 32 | "PN532.h", 33 | "PN532_HSU.h", 34 | "PN532_I2C.h", 35 | "PN532_SPI.h", 36 | "PN532_SWHSU.h" 37 | ], 38 | "repository": { 39 | "url": "https://github.com/Seeed-Studio/PN532.git", 40 | "branch": "arduino", 41 | "type": "git" 42 | }, 43 | "dependencies": [ 44 | { 45 | "name": "NDEF", 46 | "version": "https://github.com/don/NDEF" 47 | } 48 | ], 49 | "build": { 50 | "includeDir": "src" 51 | }, 52 | "examples": [ 53 | { 54 | "name": "FeliCa card detection", 55 | "base": "examples/FeliCa_card_detection", 56 | "files": ["FeliCa_card_detection.pde"] 57 | }, 58 | { 59 | "name": "FeliCa card read", 60 | "base": "examples/FeliCa_card_read", 61 | "files": ["FeliCa_card_read.pde"] 62 | }, 63 | { 64 | "name": "Android Host-based card emulation", 65 | "base": "examples/android_hce", 66 | "files": ["android_hce.ino"] 67 | }, 68 | { 69 | "name": "Emulate tag ndef", 70 | "base": "examples/emulate_tag_ndef", 71 | "files": ["emulate_tag_ndef.ino"] 72 | }, 73 | { 74 | "name": "Read ISO14443A UID", 75 | "base": "examples/iso14443a_uid", 76 | "files": ["iso14443a_uid.pde"] 77 | }, 78 | { 79 | "name": "Format a clean Mifare Classic 1K card as an NFC Forum tag", 80 | "base": "examples/mifareclassic_formatndef", 81 | "files": ["mifareclassic_formatndef.pde"] 82 | }, 83 | { 84 | "name": "Dump the contents of a Mifare Classic 1K card", 85 | "base": "examples/mifareclassic_memdump", 86 | "files": ["mifareclassic_memdump.pde"] 87 | }, 88 | { 89 | "name": "Resets the authentication keys back to the Mifare Classic defaults", 90 | "base": "examples/mifareclassic_ndeftoclassic", 91 | "files": ["mifareclassic_ndeftoclassic.pde"] 92 | }, 93 | { 94 | "name": "Updates a Mifare Classic that is already formatted for NDEF", 95 | "base": "examples/mifareclassic_updatendef", 96 | "files": ["mifareclassic_updatendef.pde"] 97 | }, 98 | { 99 | "name": "NTAG21x password protection", 100 | "base": "examples/ntag21x_protect", 101 | "files": ["ntag21x_protect.ino"] 102 | }, 103 | { 104 | "name": "Factory reset a tag", 105 | "base": "examples/ntag21x_rw", 106 | "files": ["ntag21x_rw.ino"] 107 | }, 108 | { 109 | "name": "Send a SNEP message to Android and get a message from Android", 110 | "base": "examples/p2p_raw", 111 | "files": ["p2p_raw.ino"] 112 | }, 113 | { 114 | "name": "Send a NDEF message to Android or get a NDEF message", 115 | "base": "examples/p2p_with_ndef_library", 116 | "files": ["p2p_with_ndef_library.ino"] 117 | }, 118 | { 119 | "name": "Read Mifare card", 120 | "base": "examples/readMifare", 121 | "files": ["readMifare.pde"] 122 | } 123 | ] 124 | } 125 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PN532-Arduino 2 | version=1.0.0 3 | author=Baozhu Zuo , Yihui Xiong, awieser, Jiapeng Li , wilson-elechouse 4 | maintainer=Baozhu Zuo 5 | sentence=This is the Seeed-Studio library for PN532 to use NFC technology. 6 | paragraph= 7 | category=Sensors 8 | url=https://github.com/Seeed-Studio/PN532 9 | architectures=* 10 | includes=PN532.h 11 | depends=NDEF -------------------------------------------------------------------------------- /src/PN532.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file PN532.cpp 4 | @author Adafruit Industries & Seeed Studio 5 | @license BSD 6 | */ 7 | /**************************************************************************/ 8 | 9 | #include "Arduino.h" 10 | #include "PN532.h" 11 | #include "PN532_debug.h" 12 | #include 13 | 14 | #define HAL(func) (_interface->func) 15 | 16 | PN532::PN532(PN532Interface &interface) 17 | { 18 | _interface = &interface; 19 | } 20 | 21 | /**************************************************************************/ 22 | /*! 23 | @brief Setups the HW 24 | */ 25 | /**************************************************************************/ 26 | void PN532::begin() 27 | { 28 | HAL(begin) 29 | (); 30 | HAL(wakeup) 31 | (); 32 | } 33 | 34 | /**************************************************************************/ 35 | /*! 36 | @brief Prints a hexadecimal value in plain characters 37 | 38 | @param data Pointer to the uint8_t data 39 | @param numBytes Data length in bytes 40 | */ 41 | /**************************************************************************/ 42 | void PN532::PrintHex(const uint8_t *data, const uint32_t numBytes) 43 | { 44 | #ifdef ARDUINO 45 | for (uint8_t i = 0; i < numBytes; i++) 46 | { 47 | if (data[i] < 0x10) 48 | { 49 | PN532_DEBUG_SERIAL.print(" 0"); 50 | } 51 | else 52 | { 53 | PN532_DEBUG_SERIAL.print(' '); 54 | } 55 | PN532_DEBUG_SERIAL.print(data[i], HEX); 56 | } 57 | PN532_DEBUG_SERIAL.println(""); 58 | #else 59 | for (uint8_t i = 0; i < numBytes; i++) 60 | { 61 | printf(" %2X", data[i]); 62 | } 63 | printf("\n"); 64 | #endif 65 | } 66 | 67 | /**************************************************************************/ 68 | /*! 69 | @brief Prints a hexadecimal value in plain characters, along with 70 | the char equivalents in the following format 71 | 72 | 00 00 00 00 00 00 ...... 73 | 74 | @param data Pointer to the data 75 | @param numBytes Data length in bytes 76 | */ 77 | /**************************************************************************/ 78 | void PN532::PrintHexChar(const uint8_t *data, const uint32_t numBytes) 79 | { 80 | #ifdef ARDUINO 81 | for (uint8_t i = 0; i < numBytes; i++) 82 | { 83 | if (data[i] < 0x10) 84 | { 85 | PN532_DEBUG_SERIAL.print(" 0"); 86 | } 87 | else 88 | { 89 | PN532_DEBUG_SERIAL.print(' '); 90 | } 91 | PN532_DEBUG_SERIAL.print(data[i], HEX); 92 | } 93 | PN532_DEBUG_SERIAL.print(" "); 94 | for (uint8_t i = 0; i < numBytes; i++) 95 | { 96 | char c = data[i]; 97 | if (c <= 0x1f || c > 0x7f) 98 | { 99 | PN532_DEBUG_SERIAL.print('.'); 100 | } 101 | else 102 | { 103 | PN532_DEBUG_SERIAL.print(c); 104 | } 105 | } 106 | PN532_DEBUG_SERIAL.println(""); 107 | #else 108 | for (uint8_t i = 0; i < numBytes; i++) 109 | { 110 | printf(" %2X", data[i]); 111 | } 112 | printf(" "); 113 | for (uint8_t i = 0; i < numBytes; i++) 114 | { 115 | char c = data[i]; 116 | if (c <= 0x1f || c > 0x7f) 117 | { 118 | printf("."); 119 | } 120 | else 121 | { 122 | printf("%c", c); 123 | } 124 | printf("\n"); 125 | } 126 | #endif 127 | } 128 | 129 | /**************************************************************************/ 130 | /*! 131 | @brief Checks the firmware version of the PN5xx chip 132 | 133 | @returns The chip's firmware version and ID 134 | */ 135 | /**************************************************************************/ 136 | uint32_t PN532::getFirmwareVersion(void) 137 | { 138 | uint32_t response; 139 | 140 | pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION; 141 | 142 | if (HAL(writeCommand)(pn532_packetbuffer, 1)) 143 | { 144 | return 0; 145 | } 146 | 147 | // read data packet 148 | int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); 149 | if (0 > status) 150 | { 151 | return 0; 152 | } 153 | 154 | response = pn532_packetbuffer[0]; 155 | response <<= 8; 156 | response |= pn532_packetbuffer[1]; 157 | response <<= 8; 158 | response |= pn532_packetbuffer[2]; 159 | response <<= 8; 160 | response |= pn532_packetbuffer[3]; 161 | 162 | return response; 163 | } 164 | 165 | /**************************************************************************/ 166 | /*! 167 | @brief Read a PN532 register. 168 | 169 | @param reg the 16-bit register address. 170 | 171 | @returns The register value. 172 | */ 173 | /**************************************************************************/ 174 | uint32_t PN532::readRegister(uint16_t reg) 175 | { 176 | uint32_t response; 177 | 178 | pn532_packetbuffer[0] = PN532_COMMAND_READREGISTER; 179 | pn532_packetbuffer[1] = (reg >> 8) & 0xFF; 180 | pn532_packetbuffer[2] = reg & 0xFF; 181 | 182 | if (HAL(writeCommand)(pn532_packetbuffer, 3)) 183 | { 184 | return 0; 185 | } 186 | 187 | // read data packet 188 | int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); 189 | if (0 > status) 190 | { 191 | return 0; 192 | } 193 | 194 | response = pn532_packetbuffer[0]; 195 | 196 | return response; 197 | } 198 | 199 | /**************************************************************************/ 200 | /*! 201 | @brief Write to a PN532 register. 202 | 203 | @param reg the 16-bit register address. 204 | @param val the 8-bit value to write. 205 | 206 | @returns 0 for failure, 1 for success. 207 | */ 208 | /**************************************************************************/ 209 | uint32_t PN532::writeRegister(uint16_t reg, uint8_t val) 210 | { 211 | uint32_t response; 212 | 213 | pn532_packetbuffer[0] = PN532_COMMAND_WRITEREGISTER; 214 | pn532_packetbuffer[1] = (reg >> 8) & 0xFF; 215 | pn532_packetbuffer[2] = reg & 0xFF; 216 | pn532_packetbuffer[3] = val; 217 | 218 | if (HAL(writeCommand)(pn532_packetbuffer, 4)) 219 | { 220 | return 0; 221 | } 222 | 223 | // read data packet 224 | int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); 225 | if (0 > status) 226 | { 227 | return 0; 228 | } 229 | 230 | return 1; 231 | } 232 | 233 | /**************************************************************************/ 234 | /*! 235 | Writes an 8-bit value that sets the state of the PN532's GPIO pins 236 | 237 | @warning This function is provided exclusively for board testing and 238 | is dangerous since it will throw an error if any pin other 239 | than the ones marked "Can be used as GPIO" are modified! All 240 | pins that can not be used as GPIO should ALWAYS be left high 241 | (value = 1) or the system will become unstable and a HW reset 242 | will be required to recover the PN532. 243 | 244 | pinState[0] = P30 Can be used as GPIO 245 | pinState[1] = P31 Can be used as GPIO 246 | pinState[2] = P32 *** RESERVED (Must be 1!) *** 247 | pinState[3] = P33 Can be used as GPIO 248 | pinState[4] = P34 *** RESERVED (Must be 1!) *** 249 | pinState[5] = P35 Can be used as GPIO 250 | 251 | @returns 1 if everything executed properly, 0 for an error 252 | */ 253 | /**************************************************************************/ 254 | bool PN532::writeGPIO(uint8_t pinstate) 255 | { 256 | // Make sure pinstate does not try to toggle P32 or P34 257 | pinstate |= (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34); 258 | 259 | // Fill command buffer 260 | pn532_packetbuffer[0] = PN532_COMMAND_WRITEGPIO; 261 | pn532_packetbuffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate; // P3 Pins 262 | pn532_packetbuffer[2] = 0x00; // P7 GPIO Pins (not used ... taken by I2C) 263 | 264 | DMSG("Writing P3 GPIO: "); 265 | DMSG_HEX(pn532_packetbuffer[1]); 266 | DMSG("\n"); 267 | 268 | // Send the WRITEGPIO command (0x0E) 269 | if (HAL(writeCommand)(pn532_packetbuffer, 3)) 270 | return 0; 271 | 272 | return (0 <= HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); 273 | } 274 | 275 | /**************************************************************************/ 276 | /*! 277 | Reads the state of the PN532's GPIO pins 278 | 279 | @returns An 8-bit value containing the pin state where: 280 | 281 | pinState[0] = P30 282 | pinState[1] = P31 283 | pinState[2] = P32 284 | pinState[3] = P33 285 | pinState[4] = P34 286 | pinState[5] = P35 287 | */ 288 | /**************************************************************************/ 289 | uint8_t PN532::readGPIO(void) 290 | { 291 | pn532_packetbuffer[0] = PN532_COMMAND_READGPIO; 292 | 293 | // Send the READGPIO command (0x0C) 294 | if (HAL(writeCommand)(pn532_packetbuffer, 1)) 295 | return 0x0; 296 | 297 | HAL(readResponse) 298 | (pn532_packetbuffer, sizeof(pn532_packetbuffer)); 299 | 300 | /* READGPIO response without prefix and suffix should be in the following format: 301 | 302 | byte Description 303 | ------------- ------------------------------------------ 304 | b0 P3 GPIO Pins 305 | b1 P7 GPIO Pins (not used ... taken by I2C) 306 | b2 Interface Mode Pins (not used ... bus select pins) 307 | */ 308 | 309 | DMSG("P3 GPIO: "); 310 | DMSG_HEX(pn532_packetbuffer[7]); 311 | DMSG("P7 GPIO: "); 312 | DMSG_HEX(pn532_packetbuffer[8]); 313 | DMSG("I0I1 GPIO: "); 314 | DMSG_HEX(pn532_packetbuffer[9]); 315 | DMSG("\n"); 316 | 317 | return pn532_packetbuffer[0]; 318 | } 319 | 320 | /**************************************************************************/ 321 | /*! 322 | @brief Configures the SAM (Secure Access Module) 323 | */ 324 | /**************************************************************************/ 325 | bool PN532::SAMConfig(void) 326 | { 327 | pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; 328 | pn532_packetbuffer[1] = 0x01; // normal mode; 329 | pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second 330 | pn532_packetbuffer[3] = 0x01; // use IRQ pin! 331 | 332 | DMSG("SAMConfig\n"); 333 | 334 | if (HAL(writeCommand)(pn532_packetbuffer, 4)) 335 | return false; 336 | 337 | return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); 338 | } 339 | 340 | /**************************************************************************/ 341 | /*! 342 | @brief Turn the module into power mode will wake up on I2C or SPI request 343 | */ 344 | /**************************************************************************/ 345 | bool PN532::powerDownMode() 346 | { 347 | pn532_packetbuffer[0] = PN532_COMMAND_POWERDOWN; 348 | pn532_packetbuffer[1] = 0xC0; // I2C or SPI Wakeup 349 | pn532_packetbuffer[2] = 0x00; // no IRQ 350 | 351 | DMSG("POWERDOWN\n"); 352 | 353 | if (HAL(writeCommand)(pn532_packetbuffer, 4)) 354 | return false; 355 | 356 | return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); 357 | } 358 | 359 | /**************************************************************************/ 360 | /*! 361 | Sets the MxRtyPassiveActivation uint8_t of the RFConfiguration register 362 | 363 | @param maxRetries 0xFF to wait forever, 0x00..0xFE to timeout 364 | after mxRetries 365 | 366 | @returns 1 if everything executed properly, 0 for an error 367 | */ 368 | /**************************************************************************/ 369 | bool PN532::setPassiveActivationRetries(uint8_t maxRetries) 370 | { 371 | pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; 372 | pn532_packetbuffer[1] = 5; // Config item 5 (MaxRetries) 373 | pn532_packetbuffer[2] = 0xFF; // MxRtyATR (default = 0xFF) 374 | pn532_packetbuffer[3] = 0x01; // MxRtyPSL (default = 0x01) 375 | pn532_packetbuffer[4] = maxRetries; 376 | 377 | if (HAL(writeCommand)(pn532_packetbuffer, 5)) 378 | return 0x0; // no ACK 379 | 380 | return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); 381 | } 382 | 383 | /**************************************************************************/ 384 | /*! 385 | Sets the RFon/off uint8_t of the RFConfiguration register 386 | 387 | @param autoRFCA 0x00 No check of the external field before 388 | activation 389 | 390 | 0x02 Check the external field before 391 | activation 392 | 393 | @param rFOnOff 0x00 Switch the RF field off, 0x01 switch the RF 394 | field on 395 | 396 | @returns 1 if everything executed properly, 0 for an error 397 | */ 398 | /**************************************************************************/ 399 | 400 | bool PN532::setRFField(uint8_t autoRFCA, uint8_t rFOnOff) 401 | { 402 | pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; 403 | pn532_packetbuffer[1] = 1; 404 | pn532_packetbuffer[2] = 0x00 | autoRFCA | rFOnOff; 405 | 406 | if (HAL(writeCommand)(pn532_packetbuffer, 3)) 407 | { 408 | return 0x0; // command failed 409 | } 410 | 411 | return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); 412 | } 413 | 414 | /***** ISO14443A Commands ******/ 415 | 416 | /**************************************************************************/ 417 | /*! 418 | Puts PN532 into passive detection state with IRQ while waiting for an ISO14443A target 419 | 420 | @param cardBaudRate Baud rate of the card 421 | 422 | @returns 1 if everything executed properly, 0 for an error 423 | */ 424 | bool PN532::startPassiveTargetIDDetection(uint8_t cardbaudrate) 425 | { 426 | pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; 427 | pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) 428 | pn532_packetbuffer[2] = cardbaudrate; 429 | 430 | if (HAL(writeCommand)(pn532_packetbuffer, 3)) 431 | { 432 | return 0x0; // command failed 433 | } 434 | return 1; 435 | } 436 | 437 | /**************************************************************************/ 438 | /*! 439 | Waits for an ISO14443A target to enter the field 440 | 441 | @param cardBaudRate Baud rate of the card 442 | @param uid Pointer to the array that will be populated 443 | with the card's UID (up to 7 bytes) 444 | @param uidLength Pointer to the variable that will hold the 445 | length of the card's UID. 446 | @param timeout The number of tries before timing out 447 | @param inlist If set to true, the card will be inlisted 448 | 449 | @returns 1 if everything executed properly, 0 for an error 450 | */ 451 | /**************************************************************************/ 452 | bool PN532::readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout, bool inlist) 453 | { 454 | pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; 455 | pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) 456 | pn532_packetbuffer[2] = cardbaudrate; 457 | 458 | if (HAL(writeCommand)(pn532_packetbuffer, 3)) 459 | { 460 | return 0x0; // command failed 461 | } 462 | 463 | // read data packet 464 | if (HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) 465 | { 466 | return 0x0; 467 | } 468 | 469 | // check some basic stuff 470 | /* ISO14443A card response should be in the following format: 471 | 472 | byte Description 473 | ------------- ------------------------------------------ 474 | b0 Tags Found 475 | b1 Tag Number (only one used in this example) 476 | b2..3 SENS_RES 477 | b4 SEL_RES 478 | b5 NFCID Length 479 | b6..NFCIDLen NFCID 480 | */ 481 | 482 | if (pn532_packetbuffer[0] != 1) 483 | return 0; 484 | 485 | uint16_t sens_res = pn532_packetbuffer[2]; 486 | sens_res <<= 8; 487 | sens_res |= pn532_packetbuffer[3]; 488 | 489 | DMSG("ATQA: 0x"); 490 | DMSG_HEX(sens_res); 491 | DMSG("SAK: 0x"); 492 | DMSG_HEX(pn532_packetbuffer[4]); 493 | DMSG("\n"); 494 | 495 | /* Card appears to be Mifare Classic */ 496 | *uidLength = pn532_packetbuffer[5]; 497 | 498 | for (uint8_t i = 0; i < pn532_packetbuffer[5]; i++) 499 | { 500 | uid[i] = pn532_packetbuffer[6 + i]; 501 | } 502 | 503 | if (inlist) 504 | { 505 | inListedTag = pn532_packetbuffer[1]; 506 | } 507 | 508 | return 1; 509 | } 510 | 511 | /***** Mifare Classic Functions ******/ 512 | 513 | /**************************************************************************/ 514 | /*! 515 | Indicates whether the specified block number is the first block 516 | in the sector (block 0 relative to the current sector) 517 | */ 518 | /**************************************************************************/ 519 | bool PN532::mifareclassic_IsFirstBlock(uint32_t uiBlock) 520 | { 521 | // Test if we are in the small or big sectors 522 | if (uiBlock < 128) 523 | return ((uiBlock) % 4 == 0); 524 | else 525 | return ((uiBlock) % 16 == 0); 526 | } 527 | 528 | /**************************************************************************/ 529 | /*! 530 | Indicates whether the specified block number is the sector trailer 531 | */ 532 | /**************************************************************************/ 533 | bool PN532::mifareclassic_IsTrailerBlock(uint32_t uiBlock) 534 | { 535 | // Test if we are in the small or big sectors 536 | if (uiBlock < 128) 537 | return ((uiBlock + 1) % 4 == 0); 538 | else 539 | return ((uiBlock + 1) % 16 == 0); 540 | } 541 | 542 | /**************************************************************************/ 543 | /*! 544 | Tries to authenticate a block of memory on a MIFARE card using the 545 | INDATAEXCHANGE command. See section 7.3.8 of the PN532 User Manual 546 | for more information on sending MIFARE and other commands. 547 | 548 | @param uid Pointer to a byte array containing the card UID 549 | @param uidLen The length (in bytes) of the card's UID (Should 550 | be 4 for MIFARE Classic) 551 | @param blockNumber The block number to authenticate. (0..63 for 552 | 1KB cards, and 0..255 for 4KB cards). 553 | @param keyNumber Which key type to use during authentication 554 | (0 = MIFARE_CMD_AUTH_A, 1 = MIFARE_CMD_AUTH_B) 555 | @param keyData Pointer to a byte array containing the 6 bytes 556 | key value 557 | 558 | @returns 1 if everything executed properly, 0 for an error 559 | */ 560 | /**************************************************************************/ 561 | uint8_t PN532::mifareclassic_AuthenticateBlock(uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) 562 | { 563 | uint8_t i; 564 | 565 | // Hang on to the key and uid data 566 | memcpy(_key, keyData, 6); 567 | memcpy(_uid, uid, uidLen); 568 | _uidLen = uidLen; 569 | 570 | // Prepare the authentication command // 571 | pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ 572 | pn532_packetbuffer[1] = 1; /* Max card numbers */ 573 | pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; 574 | pn532_packetbuffer[3] = blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ 575 | memcpy(pn532_packetbuffer + 4, _key, 6); 576 | for (i = 0; i < _uidLen; i++) 577 | { 578 | pn532_packetbuffer[10 + i] = _uid[i]; /* 4 bytes card ID */ 579 | } 580 | 581 | if (HAL(writeCommand)(pn532_packetbuffer, 10 + _uidLen)) 582 | return 0; 583 | 584 | // Read the response packet 585 | HAL(readResponse) 586 | (pn532_packetbuffer, sizeof(pn532_packetbuffer)); 587 | 588 | // Check if the response is valid and we are authenticated??? 589 | // for an auth success it should be bytes 5-7: 0xD5 0x41 0x00 590 | // Mifare auth error is technically byte 7: 0x14 but anything other and 0x00 is not good 591 | if (pn532_packetbuffer[0] != 0x00) 592 | { 593 | DMSG("Authentification failed\n"); 594 | return 0; 595 | } 596 | 597 | return 1; 598 | } 599 | 600 | /**************************************************************************/ 601 | /*! 602 | Tries to read an entire 16-bytes data block at the specified block 603 | address. 604 | 605 | @param blockNumber The block number to authenticate. (0..63 for 606 | 1KB cards, and 0..255 for 4KB cards). 607 | @param data Pointer to the byte array that will hold the 608 | retrieved data (if any) 609 | 610 | @returns 1 if everything executed properly, 0 for an error 611 | */ 612 | /**************************************************************************/ 613 | uint8_t PN532::mifareclassic_ReadDataBlock(uint8_t blockNumber, uint8_t *data) 614 | { 615 | DMSG("Trying to read 16 bytes from block "); 616 | DMSG_INT(blockNumber); 617 | 618 | /* Prepare the command */ 619 | pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; 620 | pn532_packetbuffer[1] = 1; /* Card number */ 621 | pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ 622 | pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ 623 | 624 | /* Send the command */ 625 | if (HAL(writeCommand)(pn532_packetbuffer, 4)) 626 | { 627 | return 0; 628 | } 629 | 630 | /* Read the response packet */ 631 | HAL(readResponse) 632 | (pn532_packetbuffer, sizeof(pn532_packetbuffer)); 633 | 634 | /* If byte 8 isn't 0x00 we probably have an error */ 635 | if (pn532_packetbuffer[0] != 0x00) 636 | { 637 | return 0; 638 | } 639 | 640 | /* Copy the 16 data bytes to the output buffer */ 641 | /* Block content starts at byte 9 of a valid response */ 642 | memcpy(data, pn532_packetbuffer + 1, 16); 643 | 644 | return 1; 645 | } 646 | 647 | /**************************************************************************/ 648 | /*! 649 | Tries to write an entire 16-bytes data block at the specified block 650 | address. 651 | 652 | @param blockNumber The block number to authenticate. (0..63 for 653 | 1KB cards, and 0..255 for 4KB cards). 654 | @param data The byte array that contains the data to write. 655 | 656 | @returns 1 if everything executed properly, 0 for an error 657 | */ 658 | /**************************************************************************/ 659 | uint8_t PN532::mifareclassic_WriteDataBlock(uint8_t blockNumber, uint8_t *data) 660 | { 661 | /* Prepare the first command */ 662 | pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; 663 | pn532_packetbuffer[1] = 1; /* Card number */ 664 | pn532_packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */ 665 | pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ 666 | memcpy(pn532_packetbuffer + 4, data, 16); /* Data Payload */ 667 | 668 | /* Send the command */ 669 | if (HAL(writeCommand)(pn532_packetbuffer, 20)) 670 | { 671 | return 0; 672 | } 673 | 674 | /* Read the response packet */ 675 | if (0 > HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))) 676 | { 677 | return 0; 678 | } 679 | 680 | /* Check status */ 681 | if (pn532_packetbuffer[0] != 0x00) 682 | { 683 | DMSG("Status code indicates an error: "); 684 | DMSG_HEX(pn532_packetbuffer[0]); 685 | DMSG("\n"); 686 | return 0; 687 | } 688 | 689 | return 1; 690 | } 691 | 692 | /**************************************************************************/ 693 | /*! 694 | Formats a Mifare Classic card to store NDEF Records 695 | 696 | @returns 1 if everything executed properly, 0 for an error 697 | */ 698 | /**************************************************************************/ 699 | uint8_t PN532::mifareclassic_FormatNDEF(void) 700 | { 701 | uint8_t sectorbuffer1[16] = {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; 702 | uint8_t sectorbuffer2[16] = {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}; 703 | uint8_t sectorbuffer3[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 704 | 705 | // Note 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 must be used for key A 706 | // for the MAD sector in NDEF records (sector 0) 707 | 708 | // Write block 1 and 2 to the card 709 | if (!(mifareclassic_WriteDataBlock(1, sectorbuffer1))) 710 | return 0; 711 | if (!(mifareclassic_WriteDataBlock(2, sectorbuffer2))) 712 | return 0; 713 | // Write key A and access rights card 714 | if (!(mifareclassic_WriteDataBlock(3, sectorbuffer3))) 715 | return 0; 716 | 717 | // Seems that everything was OK (?!) 718 | return 1; 719 | } 720 | 721 | /**************************************************************************/ 722 | /*! 723 | Writes an NDEF URI Record to the specified sector (1..15) 724 | 725 | Note that this function assumes that the Mifare Classic card is 726 | already formatted to work as an "NFC Forum Tag" and uses a MAD1 727 | file system. You can use the NXP TagWriter app on Android to 728 | properly format cards for this. 729 | 730 | @param sectorNumber The sector that the URI record should be written 731 | to (can be 1..15 for a 1K card) 732 | @param uriIdentifier The uri identifier code (0 = none, 0x01 = 733 | "http://www.", etc.) 734 | @param url The uri text to write (max 38 characters). 735 | 736 | @returns 1 if everything executed properly, 0 for an error 737 | */ 738 | /**************************************************************************/ 739 | uint8_t PN532::mifareclassic_WriteNDEFURI(uint8_t sectorNumber, uint8_t uriIdentifier, const char *url) 740 | { 741 | // Figure out how long the string is 742 | uint8_t len = strlen(url); 743 | 744 | // Make sure we're within a 1K limit for the sector number 745 | if ((sectorNumber < 1) || (sectorNumber > 15)) 746 | return 0; 747 | 748 | // Make sure the URI payload is between 1 and 38 chars 749 | if ((len < 1) || (len > 38)) 750 | return 0; 751 | 752 | // Note 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 must be used for key A 753 | // in NDEF records 754 | 755 | // Setup the sector buffer (w/pre-formatted TLV wrapper and NDEF message) 756 | uint8_t sectorbuffer1[16] = {0x00, 0x00, 0x03, (uint8_t)(len + 5), 0xD1, 0x01, (uint8_t)(len + 1), 0x55, uriIdentifier, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 757 | uint8_t sectorbuffer2[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 758 | uint8_t sectorbuffer3[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 759 | uint8_t sectorbuffer4[16] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 760 | if (len <= 6) 761 | { 762 | // Unlikely we'll get a url this short, but why not ... 763 | memcpy(sectorbuffer1 + 9, url, len); 764 | sectorbuffer1[len + 9] = 0xFE; 765 | } 766 | else if (len == 7) 767 | { 768 | // 0xFE needs to be wrapped around to next block 769 | memcpy(sectorbuffer1 + 9, url, len); 770 | sectorbuffer2[0] = 0xFE; 771 | } 772 | else if ((len > 7) && (len <= 22)) 773 | { 774 | // Url fits in two blocks 775 | memcpy(sectorbuffer1 + 9, url, 7); 776 | memcpy(sectorbuffer2, url + 7, len - 7); 777 | sectorbuffer2[len - 7] = 0xFE; 778 | } 779 | else if (len == 23) 780 | { 781 | // 0xFE needs to be wrapped around to final block 782 | memcpy(sectorbuffer1 + 9, url, 7); 783 | memcpy(sectorbuffer2, url + 7, len - 7); 784 | sectorbuffer3[0] = 0xFE; 785 | } 786 | else 787 | { 788 | // Url fits in three blocks 789 | memcpy(sectorbuffer1 + 9, url, 7); 790 | memcpy(sectorbuffer2, url + 7, 16); 791 | memcpy(sectorbuffer3, url + 23, len - 23); 792 | sectorbuffer3[len - 23] = 0xFE; 793 | } 794 | 795 | // Now write all three blocks back to the card 796 | if (!(mifareclassic_WriteDataBlock(sectorNumber * 4, sectorbuffer1))) 797 | return 0; 798 | if (!(mifareclassic_WriteDataBlock((sectorNumber * 4) + 1, sectorbuffer2))) 799 | return 0; 800 | if (!(mifareclassic_WriteDataBlock((sectorNumber * 4) + 2, sectorbuffer3))) 801 | return 0; 802 | if (!(mifareclassic_WriteDataBlock((sectorNumber * 4) + 3, sectorbuffer4))) 803 | return 0; 804 | 805 | // Seems that everything was OK (?!) 806 | return 1; 807 | } 808 | 809 | /***** Mifare Ultralight Functions ******/ 810 | 811 | /**************************************************************************/ 812 | /*! 813 | Tries to read an entire 4-bytes page at the specified address. 814 | 815 | @param page The page number (0..63 in most cases) 816 | @param buffer Pointer to the byte array that will hold the 817 | retrieved data (if any) 818 | */ 819 | /**************************************************************************/ 820 | uint8_t PN532::mifareultralight_ReadPage(uint8_t page, uint8_t *buffer) 821 | { 822 | /* Prepare the command */ 823 | pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; 824 | pn532_packetbuffer[1] = 1; /* Card number */ 825 | pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ 826 | pn532_packetbuffer[3] = page; /* Page Number (0..63 in most cases) */ 827 | 828 | /* Send the command */ 829 | if (HAL(writeCommand)(pn532_packetbuffer, 4)) 830 | { 831 | return 0; 832 | } 833 | 834 | /* Read the response packet */ 835 | HAL(readResponse) 836 | (pn532_packetbuffer, sizeof(pn532_packetbuffer)); 837 | 838 | /* If byte 8 isn't 0x00 we probably have an error */ 839 | if (pn532_packetbuffer[0] == 0x00) 840 | { 841 | /* Copy the 4 data bytes to the output buffer */ 842 | /* Block content starts at byte 9 of a valid response */ 843 | /* Note that the command actually reads 16 bytes or 4 */ 844 | /* pages at a time ... we simply discard the last 12 */ 845 | /* bytes */ 846 | memcpy(buffer, pn532_packetbuffer + 1, 4); 847 | } 848 | else 849 | { 850 | return 0; 851 | } 852 | 853 | // Return OK signal 854 | return 1; 855 | } 856 | 857 | /**************************************************************************/ 858 | /*! 859 | Tries to write an entire 4-bytes data buffer at the specified page 860 | address. 861 | 862 | @param page The page number to write into. (0..63). 863 | @param buffer The byte array that contains the data to write. 864 | 865 | @returns 1 if everything executed properly, 0 for an error 866 | */ 867 | /**************************************************************************/ 868 | uint8_t PN532::mifareultralight_WritePage(uint8_t page, uint8_t *buffer) 869 | { 870 | /* Prepare the first command */ 871 | pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; 872 | pn532_packetbuffer[1] = 1; /* Card number */ 873 | pn532_packetbuffer[2] = MIFARE_CMD_WRITE_ULTRALIGHT; /* Mifare UL Write cmd = 0xA2 */ 874 | pn532_packetbuffer[3] = page; /* page Number (0..63) */ 875 | memcpy(pn532_packetbuffer + 4, buffer, 4); /* Data Payload */ 876 | 877 | /* Send the command */ 878 | if (HAL(writeCommand)(pn532_packetbuffer, 8)) 879 | { 880 | return 0; 881 | } 882 | 883 | /* Read the response packet */ 884 | return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer))); 885 | } 886 | 887 | /**************************************************************************/ 888 | /*! 889 | @brief Exchanges an APDU with the currently inlisted peer 890 | 891 | @param send Pointer to data to send 892 | @param sendLength Length of the data to send 893 | @param response Pointer to response data 894 | @param responseLength Pointer to the response data length 895 | */ 896 | /**************************************************************************/ 897 | bool PN532::inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength) 898 | { 899 | uint8_t i; 900 | 901 | pn532_packetbuffer[0] = 0x40; // PN532_COMMAND_INDATAEXCHANGE; 902 | pn532_packetbuffer[1] = inListedTag; 903 | 904 | if (HAL(writeCommand)(pn532_packetbuffer, 2, send, sendLength)) 905 | { 906 | return false; 907 | } 908 | 909 | int16_t status = HAL(readResponse)(response, *responseLength, 1000); 910 | if (status < 0) 911 | { 912 | return false; 913 | } 914 | 915 | if ((response[0] & 0x3f) != 0) 916 | { 917 | DMSG("Status code indicates an error\n"); 918 | return false; 919 | } 920 | 921 | uint8_t length = status; 922 | length -= 1; 923 | 924 | if (length > *responseLength) 925 | { 926 | length = *responseLength; // silent truncation... 927 | } 928 | 929 | for (uint8_t i = 0; i < length; i++) 930 | { 931 | response[i] = response[i + 1]; 932 | } 933 | *responseLength = length; 934 | 935 | return true; 936 | } 937 | 938 | /**************************************************************************/ 939 | /*! 940 | This command is used to support basic data exchanges 941 | between the PN532 and a target. 942 | 943 | @param send Pointer to the command buffer 944 | @param sendLength Command length in bytes 945 | @param response Pointer to response data 946 | @param responseLength Pointer to the response data length 947 | */ 948 | /**************************************************************************/ 949 | bool PN532::inCommunicateThru(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength) 950 | { 951 | pn532_packetbuffer[0] = PN532_COMMAND_INCOMMUNICATETHRU; 952 | 953 | if (HAL(writeCommand)(pn532_packetbuffer, 1, send, sendLength)) 954 | { 955 | return false; 956 | } 957 | 958 | int16_t status = HAL(readResponse)(response, *responseLength, 1000); 959 | if (status < 0) 960 | { 961 | return false; 962 | } 963 | 964 | // check status code 965 | if (response[0] != 0x0) 966 | { 967 | DMSG("Status code indicates an error : 0x"); 968 | DMSG_HEX(pn532_packetbuffer[0]); 969 | DMSG("\n"); 970 | return false; 971 | } 972 | 973 | uint8_t length = status; 974 | length -= 1; 975 | 976 | if (length > *responseLength) 977 | { 978 | length = *responseLength; // silent truncation... 979 | } 980 | 981 | for (uint8_t i = 0; i < length; i++) 982 | { 983 | response[i] = response[i + 1]; 984 | } 985 | *responseLength = length; 986 | 987 | return true; 988 | } 989 | 990 | /**************************************************************************/ 991 | /*! 992 | @brief 'InLists' a passive target. PN532 acting as reader/initiator, 993 | peer acting as card/responder. 994 | */ 995 | /**************************************************************************/ 996 | bool PN532::inListPassiveTarget() 997 | { 998 | pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; 999 | pn532_packetbuffer[1] = 1; 1000 | pn532_packetbuffer[2] = 0; 1001 | 1002 | DMSG("inList passive target\n"); 1003 | 1004 | if (HAL(writeCommand)(pn532_packetbuffer, 3)) 1005 | { 1006 | return false; 1007 | } 1008 | 1009 | int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 30000); 1010 | if (status < 0) 1011 | { 1012 | return false; 1013 | } 1014 | 1015 | if (pn532_packetbuffer[0] != 1) 1016 | { 1017 | return false; 1018 | } 1019 | 1020 | inListedTag = pn532_packetbuffer[1]; 1021 | 1022 | return true; 1023 | } 1024 | 1025 | int8_t PN532::tgInitAsTarget(const uint8_t *command, const uint8_t len, const uint16_t timeout) 1026 | { 1027 | 1028 | int8_t status = HAL(writeCommand)(command, len); 1029 | if (status < 0) 1030 | { 1031 | return -1; 1032 | } 1033 | 1034 | status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout); 1035 | if (status > 0) 1036 | { 1037 | return 1; 1038 | } 1039 | else if (PN532_TIMEOUT == status) 1040 | { 1041 | return 0; 1042 | } 1043 | else 1044 | { 1045 | return -2; 1046 | } 1047 | } 1048 | 1049 | /** 1050 | * Peer to Peer 1051 | */ 1052 | int8_t PN532::tgInitAsTarget(uint16_t timeout) 1053 | { 1054 | const uint8_t command[] = { 1055 | PN532_COMMAND_TGINITASTARGET, 1056 | 0, 1057 | 0x00, 0x00, // SENS_RES 1058 | 0x00, 0x00, 0x00, // NFCID1 1059 | 0x40, // SEL_RES 1060 | 1061 | 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, // POL_RES 1062 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1063 | 0xFF, 0xFF, 1064 | 1065 | 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, 0x00, 0x00, // NFCID3t: Change this to desired value 1066 | 1067 | 0x0a, 0x46, 0x66, 0x6D, 0x01, 0x01, 0x10, 0x02, 0x02, 0x00, 0x80, // LLCP magic number, version parameter and MIUX 1068 | 0x00}; 1069 | return tgInitAsTarget(command, sizeof(command), timeout); 1070 | } 1071 | 1072 | int16_t PN532::tgGetData(uint8_t *buf, uint8_t len) 1073 | { 1074 | buf[0] = PN532_COMMAND_TGGETDATA; 1075 | 1076 | if (HAL(writeCommand)(buf, 1)) 1077 | { 1078 | return -1; 1079 | } 1080 | 1081 | int16_t status = HAL(readResponse)(buf, len, 3000); 1082 | if (0 >= status) 1083 | { 1084 | return status; 1085 | } 1086 | 1087 | uint16_t length = status - 1; 1088 | 1089 | if (buf[0] != 0) 1090 | { 1091 | DMSG("status is not ok\n"); 1092 | return -5; 1093 | } 1094 | 1095 | for (uint8_t i = 0; i < length; i++) 1096 | { 1097 | buf[i] = buf[i + 1]; 1098 | } 1099 | 1100 | return length; 1101 | } 1102 | 1103 | bool PN532::tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 1104 | { 1105 | if (hlen > (sizeof(pn532_packetbuffer) - 1)) 1106 | { 1107 | if ((body != 0) || (header == pn532_packetbuffer)) 1108 | { 1109 | DMSG("tgSetData:buffer too small\n"); 1110 | return false; 1111 | } 1112 | 1113 | pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA; 1114 | if (HAL(writeCommand)(pn532_packetbuffer, 1, header, hlen)) 1115 | { 1116 | return false; 1117 | } 1118 | } 1119 | else 1120 | { 1121 | for (int8_t i = hlen - 1; i >= 0; i--) 1122 | { 1123 | pn532_packetbuffer[i + 1] = header[i]; 1124 | } 1125 | pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA; 1126 | 1127 | if (HAL(writeCommand)(pn532_packetbuffer, hlen + 1, body, blen)) 1128 | { 1129 | return false; 1130 | } 1131 | } 1132 | 1133 | if (0 > HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 3000)) 1134 | { 1135 | return false; 1136 | } 1137 | 1138 | if (0 != pn532_packetbuffer[0]) 1139 | { 1140 | return false; 1141 | } 1142 | 1143 | return true; 1144 | } 1145 | 1146 | int16_t PN532::inRelease(const uint8_t relevantTarget) 1147 | { 1148 | 1149 | pn532_packetbuffer[0] = PN532_COMMAND_INRELEASE; 1150 | pn532_packetbuffer[1] = relevantTarget; 1151 | 1152 | if (HAL(writeCommand)(pn532_packetbuffer, 2)) 1153 | { 1154 | return 0; 1155 | } 1156 | 1157 | // read data packet 1158 | return HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)); 1159 | } 1160 | 1161 | /***** FeliCa Functions ******/ 1162 | /**************************************************************************/ 1163 | /*! 1164 | @brief Poll FeliCa card. PN532 acting as reader/initiator, 1165 | peer acting as card/responder. 1166 | @param[in] systemCode Designation of System Code. When sending FFFFh as System Code, 1167 | all FeliCa cards can return response. 1168 | @param[in] requestCode Designation of Request Data as follows: 1169 | 00h: No Request 1170 | 01h: System Code request (to acquire System Code of the card) 1171 | 02h: Communication perfomance request 1172 | @param[out] idm IDm of the card (8 bytes) 1173 | @param[out] pmm PMm of the card (8 bytes) 1174 | @param[out] systemCodeResponse System Code of the card (Optional, 2bytes) 1175 | @return = 1: A FeliCa card has detected 1176 | = 0: No card has detected 1177 | < 0: error 1178 | */ 1179 | /**************************************************************************/ 1180 | int8_t PN532::felica_Polling(uint16_t systemCode, uint8_t requestCode, uint8_t *idm, uint8_t *pmm, uint16_t *systemCodeResponse, uint16_t timeout) 1181 | { 1182 | pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; 1183 | pn532_packetbuffer[1] = 1; 1184 | pn532_packetbuffer[2] = 1; 1185 | pn532_packetbuffer[3] = FELICA_CMD_POLLING; 1186 | pn532_packetbuffer[4] = (systemCode >> 8) & 0xFF; 1187 | pn532_packetbuffer[5] = systemCode & 0xFF; 1188 | pn532_packetbuffer[6] = requestCode; 1189 | pn532_packetbuffer[7] = 0; 1190 | 1191 | if (HAL(writeCommand)(pn532_packetbuffer, 8)) 1192 | { 1193 | DMSG("Could not send Polling command\n"); 1194 | return -1; 1195 | } 1196 | 1197 | int16_t status = HAL(readResponse)(pn532_packetbuffer, 22, timeout); 1198 | if (status < 0) 1199 | { 1200 | DMSG("Could not receive response\n"); 1201 | return -2; 1202 | } 1203 | 1204 | // Check NbTg (pn532_packetbuffer[7]) 1205 | if (pn532_packetbuffer[0] == 0) 1206 | { 1207 | DMSG("No card had detected\n"); 1208 | return 0; 1209 | } 1210 | else if (pn532_packetbuffer[0] != 1) 1211 | { 1212 | DMSG("Unhandled number of targets inlisted. NbTg: "); 1213 | DMSG_HEX(pn532_packetbuffer[7]); 1214 | DMSG("\n"); 1215 | return -3; 1216 | } 1217 | 1218 | inListedTag = pn532_packetbuffer[1]; 1219 | DMSG("Tag number: "); 1220 | DMSG_HEX(pn532_packetbuffer[1]); 1221 | DMSG("\n"); 1222 | 1223 | // length check 1224 | uint8_t responseLength = pn532_packetbuffer[2]; 1225 | if (responseLength != 18 && responseLength != 20) 1226 | { 1227 | DMSG("Wrong response length\n"); 1228 | return -4; 1229 | } 1230 | 1231 | uint8_t i; 1232 | for (i = 0; i < 8; ++i) 1233 | { 1234 | idm[i] = pn532_packetbuffer[4 + i]; 1235 | _felicaIDm[i] = pn532_packetbuffer[4 + i]; 1236 | pmm[i] = pn532_packetbuffer[12 + i]; 1237 | _felicaPMm[i] = pn532_packetbuffer[12 + i]; 1238 | } 1239 | 1240 | if (responseLength == 20) 1241 | { 1242 | *systemCodeResponse = (uint16_t)((pn532_packetbuffer[20] << 8) + pn532_packetbuffer[21]); 1243 | } 1244 | 1245 | return 1; 1246 | } 1247 | 1248 | /**************************************************************************/ 1249 | /*! 1250 | @brief Sends FeliCa command to the currently inlisted peer 1251 | 1252 | @param[in] command FeliCa command packet. (e.g. 00 FF FF 00 00 for Polling command) 1253 | @param[in] commandlength Length of the FeliCa command packet. (e.g. 0x05 for above Polling command ) 1254 | @param[out] response FeliCa response packet. (e.g. 01 NFCID2(8 bytes) PAD(8 bytes) for Polling response) 1255 | @param[out] responselength Length of the FeliCa response packet. (e.g. 0x11 for above Polling command ) 1256 | @return = 1: Success 1257 | < 0: error 1258 | */ 1259 | /**************************************************************************/ 1260 | int8_t PN532::felica_SendCommand(const uint8_t *command, uint8_t commandlength, uint8_t *response, uint8_t *responseLength) 1261 | { 1262 | if (commandlength > 0xFE) 1263 | { 1264 | DMSG("Command length too long\n"); 1265 | return -1; 1266 | } 1267 | 1268 | pn532_packetbuffer[0] = 0x40; // PN532_COMMAND_INDATAEXCHANGE; 1269 | pn532_packetbuffer[1] = inListedTag; 1270 | pn532_packetbuffer[2] = commandlength + 1; 1271 | 1272 | if (HAL(writeCommand)(pn532_packetbuffer, 3, command, commandlength)) 1273 | { 1274 | DMSG("Could not send FeliCa command\n"); 1275 | return -2; 1276 | } 1277 | 1278 | // Wait card response 1279 | int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 200); 1280 | if (status < 0) 1281 | { 1282 | DMSG("Could not receive response\n"); 1283 | return -3; 1284 | } 1285 | 1286 | // Check status (pn532_packetbuffer[0]) 1287 | if ((pn532_packetbuffer[0] & 0x3F) != 0) 1288 | { 1289 | DMSG("Status code indicates an error: "); 1290 | DMSG_HEX(pn532_packetbuffer[0]); 1291 | DMSG("\n"); 1292 | return -4; 1293 | } 1294 | 1295 | // length check 1296 | *responseLength = pn532_packetbuffer[1] - 1; 1297 | if ((status - 2) != *responseLength) 1298 | { 1299 | DMSG("Wrong response length\n"); 1300 | return -5; 1301 | } 1302 | 1303 | memcpy(response, &pn532_packetbuffer[2], *responseLength); 1304 | 1305 | return 1; 1306 | } 1307 | 1308 | /**************************************************************************/ 1309 | /*! 1310 | @brief Sends FeliCa Request Service command 1311 | 1312 | @param[in] numNode length of the nodeCodeList 1313 | @param[in] nodeCodeList Node codes(Big Endian) 1314 | @param[out] keyVersions Key Version of each Node (Big Endian) 1315 | @return = 1: Success 1316 | < 0: error 1317 | */ 1318 | /**************************************************************************/ 1319 | int8_t PN532::felica_RequestService(uint8_t numNode, uint16_t *nodeCodeList, uint16_t *keyVersions) 1320 | { 1321 | if (numNode > FELICA_REQ_SERVICE_MAX_NODE_NUM) 1322 | { 1323 | DMSG("numNode is too large\n"); 1324 | return -1; 1325 | } 1326 | 1327 | uint8_t i, j = 0; 1328 | uint8_t cmdLen = 1 + 8 + 1 + 2 * numNode; 1329 | uint8_t cmd[cmdLen]; 1330 | cmd[j++] = FELICA_CMD_REQUEST_SERVICE; 1331 | for (i = 0; i < 8; ++i) 1332 | { 1333 | cmd[j++] = _felicaIDm[i]; 1334 | } 1335 | cmd[j++] = numNode; 1336 | for (i = 0; i < numNode; ++i) 1337 | { 1338 | cmd[j++] = nodeCodeList[i] & 0xFF; 1339 | cmd[j++] = (nodeCodeList[i] >> 8) & 0xff; 1340 | } 1341 | 1342 | uint8_t response[10 + 2 * numNode]; 1343 | uint8_t responseLength; 1344 | 1345 | if (felica_SendCommand(cmd, cmdLen, response, &responseLength) != 1) 1346 | { 1347 | DMSG("Request Service command failed\n"); 1348 | return -2; 1349 | } 1350 | 1351 | // length check 1352 | if (responseLength != 10 + 2 * numNode) 1353 | { 1354 | DMSG("Request Service command failed (wrong response length)\n"); 1355 | return -3; 1356 | } 1357 | 1358 | for (i = 0; i < numNode; i++) 1359 | { 1360 | keyVersions[i] = (uint16_t)(response[10 + i * 2] + (response[10 + i * 2 + 1] << 8)); 1361 | } 1362 | return 1; 1363 | } 1364 | 1365 | /**************************************************************************/ 1366 | /*! 1367 | @brief Sends FeliCa Request Service command 1368 | 1369 | @param[out] mode Current Mode of the card 1370 | @return = 1: Success 1371 | < 0: error 1372 | */ 1373 | /**************************************************************************/ 1374 | int8_t PN532::felica_RequestResponse(uint8_t *mode) 1375 | { 1376 | uint8_t cmd[9]; 1377 | cmd[0] = FELICA_CMD_REQUEST_RESPONSE; 1378 | memcpy(&cmd[1], _felicaIDm, 8); 1379 | 1380 | uint8_t response[10]; 1381 | uint8_t responseLength; 1382 | if (felica_SendCommand(cmd, 9, response, &responseLength) != 1) 1383 | { 1384 | DMSG("Request Response command failed\n"); 1385 | return -1; 1386 | } 1387 | 1388 | // length check 1389 | if (responseLength != 10) 1390 | { 1391 | DMSG("Request Response command failed (wrong response length)\n"); 1392 | return -2; 1393 | } 1394 | 1395 | *mode = response[9]; 1396 | return 1; 1397 | } 1398 | 1399 | /**************************************************************************/ 1400 | /*! 1401 | @brief Sends FeliCa Read Without Encryption command 1402 | 1403 | @param[in] numService Length of the serviceCodeList 1404 | @param[in] serviceCodeList Service Code List (Big Endian) 1405 | @param[in] numBlock Length of the blockList 1406 | @param[in] blockList Block List (Big Endian, This API only accepts 2-byte block list element) 1407 | @param[out] blockData Block Data 1408 | @return = 1: Success 1409 | < 0: error 1410 | */ 1411 | /**************************************************************************/ 1412 | int8_t PN532::felica_ReadWithoutEncryption(uint8_t numService, const uint16_t *serviceCodeList, uint8_t numBlock, const uint16_t *blockList, uint8_t blockData[][16]) 1413 | { 1414 | if (numService > FELICA_READ_MAX_SERVICE_NUM) 1415 | { 1416 | DMSG("numService is too large\n"); 1417 | return -1; 1418 | } 1419 | if (numBlock > FELICA_READ_MAX_BLOCK_NUM) 1420 | { 1421 | DMSG("numBlock is too large\n"); 1422 | return -2; 1423 | } 1424 | 1425 | uint8_t i, j = 0, k; 1426 | uint8_t cmdLen = 1 + 8 + 1 + 2 * numService + 1 + 2 * numBlock; 1427 | uint8_t cmd[cmdLen]; 1428 | cmd[j++] = FELICA_CMD_READ_WITHOUT_ENCRYPTION; 1429 | for (i = 0; i < 8; ++i) 1430 | { 1431 | cmd[j++] = _felicaIDm[i]; 1432 | } 1433 | cmd[j++] = numService; 1434 | for (i = 0; i < numService; ++i) 1435 | { 1436 | cmd[j++] = serviceCodeList[i] & 0xFF; 1437 | cmd[j++] = (serviceCodeList[i] >> 8) & 0xff; 1438 | } 1439 | cmd[j++] = numBlock; 1440 | for (i = 0; i < numBlock; ++i) 1441 | { 1442 | cmd[j++] = (blockList[i] >> 8) & 0xFF; 1443 | cmd[j++] = blockList[i] & 0xff; 1444 | } 1445 | 1446 | uint8_t response[12 + 16 * numBlock]; 1447 | uint8_t responseLength; 1448 | if (felica_SendCommand(cmd, cmdLen, response, &responseLength) != 1) 1449 | { 1450 | DMSG("Read Without Encryption command failed\n"); 1451 | return -3; 1452 | } 1453 | 1454 | // length check 1455 | if (responseLength != 12 + 16 * numBlock) 1456 | { 1457 | DMSG("Read Without Encryption command failed (wrong response length)\n"); 1458 | return -4; 1459 | } 1460 | 1461 | // status flag check 1462 | if (response[9] != 0 || response[10] != 0) 1463 | { 1464 | DMSG("Read Without Encryption command failed (Status Flag: "); 1465 | DMSG_HEX(pn532_packetbuffer[9]); 1466 | DMSG_HEX(pn532_packetbuffer[10]); 1467 | DMSG(")\n"); 1468 | return -5; 1469 | } 1470 | 1471 | k = 12; 1472 | for (i = 0; i < numBlock; i++) 1473 | { 1474 | for (j = 0; j < 16; j++) 1475 | { 1476 | blockData[i][j] = response[k++]; 1477 | } 1478 | } 1479 | 1480 | return 1; 1481 | } 1482 | 1483 | /**************************************************************************/ 1484 | /*! 1485 | @brief Sends FeliCa Write Without Encryption command 1486 | 1487 | @param[in] numService Length of the serviceCodeList 1488 | @param[in] serviceCodeList Service Code List (Big Endian) 1489 | @param[in] numBlock Length of the blockList 1490 | @param[in] blockList Block List (Big Endian, This API only accepts 2-byte block list element) 1491 | @param[in] blockData Block Data (each Block has 16 bytes) 1492 | @return = 1: Success 1493 | < 0: error 1494 | */ 1495 | /**************************************************************************/ 1496 | int8_t PN532::felica_WriteWithoutEncryption(uint8_t numService, const uint16_t *serviceCodeList, uint8_t numBlock, const uint16_t *blockList, uint8_t blockData[][16]) 1497 | { 1498 | if (numService > FELICA_WRITE_MAX_SERVICE_NUM) 1499 | { 1500 | DMSG("numService is too large\n"); 1501 | return -1; 1502 | } 1503 | if (numBlock > FELICA_WRITE_MAX_BLOCK_NUM) 1504 | { 1505 | DMSG("numBlock is too large\n"); 1506 | return -2; 1507 | } 1508 | 1509 | uint8_t i, j = 0, k; 1510 | uint8_t cmdLen = 1 + 8 + 1 + 2 * numService + 1 + 2 * numBlock + 16 * numBlock; 1511 | uint8_t cmd[cmdLen]; 1512 | cmd[j++] = FELICA_CMD_WRITE_WITHOUT_ENCRYPTION; 1513 | for (i = 0; i < 8; ++i) 1514 | { 1515 | cmd[j++] = _felicaIDm[i]; 1516 | } 1517 | cmd[j++] = numService; 1518 | for (i = 0; i < numService; ++i) 1519 | { 1520 | cmd[j++] = serviceCodeList[i] & 0xFF; 1521 | cmd[j++] = (serviceCodeList[i] >> 8) & 0xff; 1522 | } 1523 | cmd[j++] = numBlock; 1524 | for (i = 0; i < numBlock; ++i) 1525 | { 1526 | cmd[j++] = (blockList[i] >> 8) & 0xFF; 1527 | cmd[j++] = blockList[i] & 0xff; 1528 | } 1529 | for (i = 0; i < numBlock; ++i) 1530 | { 1531 | for (k = 0; k < 16; k++) 1532 | { 1533 | cmd[j++] = blockData[i][k]; 1534 | } 1535 | } 1536 | 1537 | uint8_t response[11]; 1538 | uint8_t responseLength; 1539 | if (felica_SendCommand(cmd, cmdLen, response, &responseLength) != 1) 1540 | { 1541 | DMSG("Write Without Encryption command failed\n"); 1542 | return -3; 1543 | } 1544 | 1545 | // length check 1546 | if (responseLength != 11) 1547 | { 1548 | DMSG("Write Without Encryption command failed (wrong response length)\n"); 1549 | return -4; 1550 | } 1551 | 1552 | // status flag check 1553 | if (response[9] != 0 || response[10] != 0) 1554 | { 1555 | DMSG("Write Without Encryption command failed (Status Flag: "); 1556 | DMSG_HEX(pn532_packetbuffer[9]); 1557 | DMSG_HEX(pn532_packetbuffer[10]); 1558 | DMSG(")\n"); 1559 | return -5; 1560 | } 1561 | 1562 | return 1; 1563 | } 1564 | 1565 | /**************************************************************************/ 1566 | /*! 1567 | @brief Sends FeliCa Request System Code command 1568 | 1569 | @param[out] numSystemCode Length of the systemCodeList 1570 | @param[out] systemCodeList System Code list (Array length should longer than 16) 1571 | @return = 1: Success 1572 | < 0: error 1573 | */ 1574 | /**************************************************************************/ 1575 | int8_t PN532::felica_RequestSystemCode(uint8_t *numSystemCode, uint16_t *systemCodeList) 1576 | { 1577 | uint8_t cmd[9]; 1578 | cmd[0] = FELICA_CMD_REQUEST_SYSTEM_CODE; 1579 | memcpy(&cmd[1], _felicaIDm, 8); 1580 | 1581 | uint8_t response[10 + 2 * 16]; 1582 | uint8_t responseLength; 1583 | if (felica_SendCommand(cmd, 9, response, &responseLength) != 1) 1584 | { 1585 | DMSG("Request System Code command failed\n"); 1586 | return -1; 1587 | } 1588 | *numSystemCode = response[9]; 1589 | 1590 | // length check 1591 | if (responseLength < 10 + 2 * *numSystemCode) 1592 | { 1593 | DMSG("Request System Code command failed (wrong response length)\n"); 1594 | return -2; 1595 | } 1596 | 1597 | uint8_t i; 1598 | for (i = 0; i < *numSystemCode; i++) 1599 | { 1600 | systemCodeList[i] = (uint16_t)((response[10 + i * 2] << 8) + response[10 + i * 2 + 1]); 1601 | } 1602 | 1603 | return 1; 1604 | } 1605 | 1606 | /**************************************************************************/ 1607 | /*! 1608 | @brief Release FeliCa card 1609 | @return = 1: Success 1610 | < 0: error 1611 | */ 1612 | /**************************************************************************/ 1613 | int8_t PN532::felica_Release() 1614 | { 1615 | // InRelease 1616 | pn532_packetbuffer[0] = PN532_COMMAND_INRELEASE; 1617 | pn532_packetbuffer[1] = 0x00; // All target 1618 | DMSG("Release all FeliCa target\n"); 1619 | 1620 | if (HAL(writeCommand)(pn532_packetbuffer, 2)) 1621 | { 1622 | DMSG("No ACK\n"); 1623 | return -1; // no ACK 1624 | } 1625 | 1626 | // Wait card response 1627 | int16_t frameLength = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 1000); 1628 | if (frameLength < 0) 1629 | { 1630 | DMSG("Could not receive response\n"); 1631 | return -2; 1632 | } 1633 | 1634 | // Check status (pn532_packetbuffer[0]) 1635 | if ((pn532_packetbuffer[0] & 0x3F) != 0) 1636 | { 1637 | DMSG("Status code indicates an error: "); 1638 | DMSG_HEX(pn532_packetbuffer[7]); 1639 | DMSG("\n"); 1640 | return -3; 1641 | } 1642 | 1643 | return 1; 1644 | } 1645 | -------------------------------------------------------------------------------- /src/PN532.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file PN532.h 4 | @author Adafruit Industries & Seeed Studio 5 | @license BSD 6 | */ 7 | /**************************************************************************/ 8 | 9 | #ifndef __PN532_H__ 10 | #define __PN532_H__ 11 | 12 | #include 13 | #include "PN532Interface.h" 14 | 15 | // PN532 Commands 16 | #define PN532_COMMAND_DIAGNOSE (0x00) 17 | #define PN532_COMMAND_GETFIRMWAREVERSION (0x02) 18 | #define PN532_COMMAND_GETGENERALSTATUS (0x04) 19 | #define PN532_COMMAND_READREGISTER (0x06) 20 | #define PN532_COMMAND_WRITEREGISTER (0x08) 21 | #define PN532_COMMAND_READGPIO (0x0C) 22 | #define PN532_COMMAND_WRITEGPIO (0x0E) 23 | #define PN532_COMMAND_SETSERIALBAUDRATE (0x10) 24 | #define PN532_COMMAND_SETPARAMETERS (0x12) 25 | #define PN532_COMMAND_SAMCONFIGURATION (0x14) 26 | #define PN532_COMMAND_POWERDOWN (0x16) 27 | #define PN532_COMMAND_RFCONFIGURATION (0x32) 28 | #define PN532_COMMAND_RFREGULATIONTEST (0x58) 29 | #define PN532_COMMAND_INJUMPFORDEP (0x56) 30 | #define PN532_COMMAND_INJUMPFORPSL (0x46) 31 | #define PN532_COMMAND_INLISTPASSIVETARGET (0x4A) 32 | #define PN532_COMMAND_INATR (0x50) 33 | #define PN532_COMMAND_INPSL (0x4E) 34 | #define PN532_COMMAND_INDATAEXCHANGE (0x40) 35 | #define PN532_COMMAND_INCOMMUNICATETHRU (0x42) 36 | #define PN532_COMMAND_INDESELECT (0x44) 37 | #define PN532_COMMAND_INRELEASE (0x52) 38 | #define PN532_COMMAND_INSELECT (0x54) 39 | #define PN532_COMMAND_INAUTOPOLL (0x60) 40 | #define PN532_COMMAND_TGINITASTARGET (0x8C) 41 | #define PN532_COMMAND_TGSETGENERALBYTES (0x92) 42 | #define PN532_COMMAND_TGGETDATA (0x86) 43 | #define PN532_COMMAND_TGSETDATA (0x8E) 44 | #define PN532_COMMAND_TGSETMETADATA (0x94) 45 | #define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88) 46 | #define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90) 47 | #define PN532_COMMAND_TGGETTARGETSTATUS (0x8A) 48 | 49 | #define PN532_RESPONSE_INDATAEXCHANGE (0x41) 50 | #define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B) 51 | 52 | #define PN532_MIFARE_ISO14443A (0x00) 53 | 54 | // Mifare Commands 55 | #define MIFARE_CMD_AUTH_A (0x60) 56 | #define MIFARE_CMD_AUTH_B (0x61) 57 | #define MIFARE_CMD_READ (0x30) 58 | #define MIFARE_CMD_WRITE (0xA0) 59 | #define MIFARE_CMD_WRITE_ULTRALIGHT (0xA2) 60 | #define MIFARE_CMD_TRANSFER (0xB0) 61 | #define MIFARE_CMD_DECREMENT (0xC0) 62 | #define MIFARE_CMD_INCREMENT (0xC1) 63 | #define MIFARE_CMD_STORE (0xC2) 64 | 65 | // FeliCa Commands 66 | #define FELICA_CMD_POLLING (0x00) 67 | #define FELICA_CMD_REQUEST_SERVICE (0x02) 68 | #define FELICA_CMD_REQUEST_RESPONSE (0x04) 69 | #define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06) 70 | #define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08) 71 | #define FELICA_CMD_REQUEST_SYSTEM_CODE (0x0C) 72 | 73 | // Prefixes for NDEF Records (to identify record type) 74 | #define NDEF_URIPREFIX_NONE (0x00) 75 | #define NDEF_URIPREFIX_HTTP_WWWDOT (0x01) 76 | #define NDEF_URIPREFIX_HTTPS_WWWDOT (0x02) 77 | #define NDEF_URIPREFIX_HTTP (0x03) 78 | #define NDEF_URIPREFIX_HTTPS (0x04) 79 | #define NDEF_URIPREFIX_TEL (0x05) 80 | #define NDEF_URIPREFIX_MAILTO (0x06) 81 | #define NDEF_URIPREFIX_FTP_ANONAT (0x07) 82 | #define NDEF_URIPREFIX_FTP_FTPDOT (0x08) 83 | #define NDEF_URIPREFIX_FTPS (0x09) 84 | #define NDEF_URIPREFIX_SFTP (0x0A) 85 | #define NDEF_URIPREFIX_SMB (0x0B) 86 | #define NDEF_URIPREFIX_NFS (0x0C) 87 | #define NDEF_URIPREFIX_FTP (0x0D) 88 | #define NDEF_URIPREFIX_DAV (0x0E) 89 | #define NDEF_URIPREFIX_NEWS (0x0F) 90 | #define NDEF_URIPREFIX_TELNET (0x10) 91 | #define NDEF_URIPREFIX_IMAP (0x11) 92 | #define NDEF_URIPREFIX_RTSP (0x12) 93 | #define NDEF_URIPREFIX_URN (0x13) 94 | #define NDEF_URIPREFIX_POP (0x14) 95 | #define NDEF_URIPREFIX_SIP (0x15) 96 | #define NDEF_URIPREFIX_SIPS (0x16) 97 | #define NDEF_URIPREFIX_TFTP (0x17) 98 | #define NDEF_URIPREFIX_BTSPP (0x18) 99 | #define NDEF_URIPREFIX_BTL2CAP (0x19) 100 | #define NDEF_URIPREFIX_BTGOEP (0x1A) 101 | #define NDEF_URIPREFIX_TCPOBEX (0x1B) 102 | #define NDEF_URIPREFIX_IRDAOBEX (0x1C) 103 | #define NDEF_URIPREFIX_FILE (0x1D) 104 | #define NDEF_URIPREFIX_URN_EPC_ID (0x1E) 105 | #define NDEF_URIPREFIX_URN_EPC_TAG (0x1F) 106 | #define NDEF_URIPREFIX_URN_EPC_PAT (0x20) 107 | #define NDEF_URIPREFIX_URN_EPC_RAW (0x21) 108 | #define NDEF_URIPREFIX_URN_EPC (0x22) 109 | #define NDEF_URIPREFIX_URN_NFC (0x23) 110 | 111 | #define PN532_GPIO_VALIDATIONBIT (0x80) 112 | #define PN532_GPIO_P30 (0) 113 | #define PN532_GPIO_P31 (1) 114 | #define PN532_GPIO_P32 (2) 115 | #define PN532_GPIO_P33 (3) 116 | #define PN532_GPIO_P34 (4) 117 | #define PN532_GPIO_P35 (5) 118 | 119 | // FeliCa consts 120 | #define FELICA_READ_MAX_SERVICE_NUM 16 121 | #define FELICA_READ_MAX_BLOCK_NUM 12 // for typical FeliCa card 122 | #define FELICA_WRITE_MAX_SERVICE_NUM 16 123 | #define FELICA_WRITE_MAX_BLOCK_NUM 10 // for typical FeliCa card 124 | #define FELICA_REQ_SERVICE_MAX_NODE_NUM 32 125 | 126 | class PN532 127 | { 128 | public: 129 | PN532(PN532Interface &interface); 130 | 131 | void begin(void); 132 | 133 | // Generic PN532 functions 134 | bool SAMConfig(void); 135 | uint32_t getFirmwareVersion(void); 136 | uint32_t readRegister(uint16_t reg); 137 | uint32_t writeRegister(uint16_t reg, uint8_t val); 138 | bool writeGPIO(uint8_t pinstate); 139 | uint8_t readGPIO(void); 140 | bool setPassiveActivationRetries(uint8_t maxRetries); 141 | bool setRFField(uint8_t autoRFCA, uint8_t rFOnOff); 142 | bool powerDownMode(); 143 | 144 | /** 145 | * @brief Init PN532 as a target 146 | * @param timeout max time to wait, 0 means no timeout 147 | * @return > 0 success 148 | * = 0 timeout 149 | * < 0 failed 150 | */ 151 | int8_t tgInitAsTarget(uint16_t timeout = 0); 152 | int8_t tgInitAsTarget(const uint8_t *command, const uint8_t len, const uint16_t timeout = 0); 153 | 154 | int16_t tgGetData(uint8_t *buf, uint8_t len); 155 | bool tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 156 | 157 | int16_t inRelease(const uint8_t relevantTarget = 0); 158 | 159 | // ISO14443A functions 160 | bool inListPassiveTarget(); 161 | bool startPassiveTargetIDDetection(uint8_t cardbaudrate); 162 | bool readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 1000, bool inlist = false); 163 | bool inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength); 164 | bool inCommunicateThru(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength); 165 | 166 | // Mifare Classic functions 167 | bool mifareclassic_IsFirstBlock(uint32_t uiBlock); 168 | bool mifareclassic_IsTrailerBlock(uint32_t uiBlock); 169 | uint8_t mifareclassic_AuthenticateBlock(uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData); 170 | uint8_t mifareclassic_ReadDataBlock(uint8_t blockNumber, uint8_t *data); 171 | uint8_t mifareclassic_WriteDataBlock(uint8_t blockNumber, uint8_t *data); 172 | uint8_t mifareclassic_FormatNDEF(void); 173 | uint8_t mifareclassic_WriteNDEFURI(uint8_t sectorNumber, uint8_t uriIdentifier, const char *url); 174 | 175 | // Mifare Ultralight functions 176 | uint8_t mifareultralight_ReadPage(uint8_t page, uint8_t *buffer); 177 | uint8_t mifareultralight_WritePage(uint8_t page, uint8_t *buffer); 178 | 179 | // FeliCa Functions 180 | int8_t felica_Polling(uint16_t systemCode, uint8_t requestCode, uint8_t *idm, uint8_t *pmm, uint16_t *systemCodeResponse, uint16_t timeout = 1000); 181 | int8_t felica_SendCommand(const uint8_t *command, uint8_t commandlength, uint8_t *response, uint8_t *responseLength); 182 | int8_t felica_RequestService(uint8_t numNode, uint16_t *nodeCodeList, uint16_t *keyVersions); 183 | int8_t felica_RequestResponse(uint8_t *mode); 184 | int8_t felica_ReadWithoutEncryption(uint8_t numService, const uint16_t *serviceCodeList, uint8_t numBlock, const uint16_t *blockList, uint8_t blockData[][16]); 185 | int8_t felica_WriteWithoutEncryption(uint8_t numService, const uint16_t *serviceCodeList, uint8_t numBlock, const uint16_t *blockList, uint8_t blockData[][16]); 186 | int8_t felica_RequestSystemCode(uint8_t *numSystemCode, uint16_t *systemCodeList); 187 | int8_t felica_Release(); 188 | 189 | // Help functions to display formatted text 190 | static void PrintHex(const uint8_t *data, const uint32_t numBytes); 191 | static void PrintHexChar(const uint8_t *pbtData, const uint32_t numBytes); 192 | 193 | uint8_t *getBuffer(uint8_t *len) 194 | { 195 | *len = sizeof(pn532_packetbuffer) - 4; 196 | return pn532_packetbuffer; 197 | }; 198 | 199 | private: 200 | uint8_t _uid[7]; // ISO14443A uid 201 | uint8_t _uidLen; // uid len 202 | uint8_t _key[6]; // Mifare Classic key 203 | uint8_t inListedTag; // Tg number of inlisted tag. 204 | uint8_t _felicaIDm[8]; // FeliCa IDm (NFCID2) 205 | uint8_t _felicaPMm[8]; // FeliCa PMm (PAD) 206 | 207 | uint8_t pn532_packetbuffer[64]; 208 | 209 | PN532Interface *_interface; 210 | }; 211 | 212 | #endif 213 | -------------------------------------------------------------------------------- /src/PN532Interface.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __PN532_INTERFACE_H__ 4 | #define __PN532_INTERFACE_H__ 5 | 6 | #include 7 | 8 | #define PN532_PREAMBLE (0x00) 9 | #define PN532_STARTCODE1 (0x00) 10 | #define PN532_STARTCODE2 (0xFF) 11 | #define PN532_POSTAMBLE (0x00) 12 | 13 | #define PN532_HOSTTOPN532 (0xD4) 14 | #define PN532_PN532TOHOST (0xD5) 15 | 16 | #define PN532_ACK_WAIT_TIME (10) // ms, timeout of waiting for ACK 17 | 18 | #define PN532_INVALID_ACK (-1) 19 | #define PN532_TIMEOUT (-2) 20 | #define PN532_INVALID_FRAME (-3) 21 | #define PN532_NO_SPACE (-4) 22 | 23 | #define REVERSE_BITS_ORDER(b) b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; \ 24 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2; \ 25 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1 26 | 27 | class PN532Interface 28 | { 29 | public: 30 | virtual void begin() = 0; 31 | virtual void wakeup() = 0; 32 | 33 | /** 34 | * @brief write a command and check ack 35 | * @param header packet header 36 | * @param hlen length of header 37 | * @param body packet body 38 | * @param blen length of body 39 | * @return 0 success 40 | * not 0 failed 41 | */ 42 | virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) = 0; 43 | 44 | /** 45 | * @brief read the response of a command, strip prefix and suffix 46 | * @param buf to contain the response data 47 | * @param len lenght to read 48 | * @param timeout max time to wait, 0 means no timeout 49 | * @return >=0 length of response without prefix and suffix 50 | * <0 failed to read response 51 | */ 52 | virtual int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 1000) = 0; 53 | }; 54 | 55 | #endif 56 | 57 | -------------------------------------------------------------------------------- /src/PN532_HSU.cpp: -------------------------------------------------------------------------------- 1 | #ifdef NFC_INTERFACE_HSU 2 | 3 | #include "PN532_HSU.h" 4 | #include "PN532_debug.h" 5 | 6 | 7 | 8 | #ifdef ESP32 9 | 10 | PN532_HSU::PN532_HSU(HardwareSerial &serial, int8_t rxPin, int8_t txPin) 11 | { 12 | hsu_serial = &serial; 13 | command = 0; 14 | _rxPin = rxPin; 15 | _txPin = txPin; 16 | } 17 | #else 18 | #ifdef USE_SOFT_SERIAL_PIN 19 | PN532_HSU::PN532_HSU(SoftwareSerial &serial){ 20 | hsu_serial = &serial; 21 | command = 0; 22 | } 23 | #else 24 | PN532_HSU::PN532_HSU(HardwareSerial &serial) 25 | { 26 | hsu_serial = &serial; 27 | command = 0; 28 | } 29 | #endif 30 | #endif 31 | 32 | void PN532_HSU::begin() 33 | { 34 | 35 | #ifdef ESP32 36 | get_serial(hsu_serial)->begin(115200, SERIAL_8N1, _rxPin, _txPin); 37 | #else 38 | #ifdef USE_SOFT_SERIAL_PIN 39 | get_serial(hsu_serial)->begin(115200); 40 | #else 41 | get_serial(hsu_serial)->begin(115200); 42 | #endif 43 | #endif 44 | } 45 | 46 | void PN532_HSU::wakeup() 47 | { 48 | get_serial(hsu_serial)->write(0x55); 49 | get_serial(hsu_serial)->write(0x55); 50 | get_serial(hsu_serial)->write(uint8_t(0x00)); 51 | get_serial(hsu_serial)->write(uint8_t(0x00)); 52 | get_serial(hsu_serial)->write(uint8_t(0x00)); 53 | 54 | /** dump serial buffer */ 55 | if (get_serial(hsu_serial)->available()) 56 | { 57 | DMSG("Dump serial buffer: "); 58 | } 59 | while (get_serial(hsu_serial)->available()) 60 | { 61 | uint8_t ret = get_serial(hsu_serial)->read(); 62 | DMSG_HEX(ret); 63 | } 64 | } 65 | 66 | int8_t PN532_HSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 67 | { 68 | 69 | /** dump serial buffer */ 70 | if (get_serial(hsu_serial)->available()) 71 | { 72 | DMSG("Dump serial buffer: "); 73 | } 74 | while (get_serial(hsu_serial)->available()) 75 | { 76 | uint8_t ret = get_serial(hsu_serial)->read(); 77 | DMSG_HEX(ret); 78 | } 79 | 80 | command = header[0]; 81 | 82 | get_serial(hsu_serial)->write(uint8_t(PN532_PREAMBLE)); 83 | get_serial(hsu_serial)->write(uint8_t(PN532_STARTCODE1)); 84 | get_serial(hsu_serial)->write(uint8_t(PN532_STARTCODE2)); 85 | 86 | uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA 87 | get_serial(hsu_serial)->write(length); 88 | get_serial(hsu_serial)->write(~length + 1); // checksum of length 89 | 90 | get_serial(hsu_serial)->write(PN532_HOSTTOPN532); 91 | uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA 92 | 93 | DMSG("\nWrite: "); 94 | 95 | get_serial(hsu_serial)->write(header, hlen); 96 | for (uint8_t i = 0; i < hlen; i++) 97 | { 98 | sum += header[i]; 99 | 100 | DMSG_HEX(header[i]); 101 | } 102 | 103 | get_serial(hsu_serial)->write(body, blen); 104 | for (uint8_t i = 0; i < blen; i++) 105 | { 106 | sum += body[i]; 107 | 108 | DMSG_HEX(body[i]); 109 | } 110 | 111 | uint8_t checksum = ~sum + 1; // checksum of TFI + DATA 112 | get_serial(hsu_serial)->write(checksum); 113 | get_serial(hsu_serial)->write(uint8_t(PN532_POSTAMBLE)); 114 | 115 | return readAckFrame(); 116 | } 117 | 118 | int16_t PN532_HSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) 119 | { 120 | uint8_t tmp[3]; 121 | 122 | DMSG("\nRead: "); 123 | 124 | /** Frame Preamble and Start Code */ 125 | if (receive(tmp, 3, timeout) <= 0) 126 | { 127 | return PN532_TIMEOUT; 128 | } 129 | if (0 != tmp[0] || 0 != tmp[1] || 0xFF != tmp[2]) 130 | { 131 | DMSG("Preamble error"); 132 | return PN532_INVALID_FRAME; 133 | } 134 | 135 | /** receive length and check */ 136 | uint8_t length[2]; 137 | if (receive(length, 2, timeout) <= 0) 138 | { 139 | return PN532_TIMEOUT; 140 | } 141 | if (0 != (uint8_t)(length[0] + length[1])) 142 | { 143 | DMSG("Length error"); 144 | return PN532_INVALID_FRAME; 145 | } 146 | length[0] -= 2; 147 | if (length[0] > len) 148 | { 149 | return PN532_NO_SPACE; 150 | } 151 | 152 | /** receive command byte */ 153 | uint8_t cmd = command + 1; // response command 154 | if (receive(tmp, 2, timeout) <= 0) 155 | { 156 | return PN532_TIMEOUT; 157 | } 158 | if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) 159 | { 160 | DMSG("Command error"); 161 | return PN532_INVALID_FRAME; 162 | } 163 | 164 | if (receive(buf, length[0], timeout) != length[0]) 165 | { 166 | return PN532_TIMEOUT; 167 | } 168 | uint8_t sum = PN532_PN532TOHOST + cmd; 169 | for (uint8_t i = 0; i < length[0]; i++) 170 | { 171 | sum += buf[i]; 172 | } 173 | 174 | /** checksum and postamble */ 175 | if (receive(tmp, 2, timeout) <= 0) 176 | { 177 | return PN532_TIMEOUT; 178 | } 179 | if (0 != (uint8_t)(sum + tmp[0]) || 0 != tmp[1]) 180 | { 181 | DMSG("Checksum error"); 182 | return PN532_INVALID_FRAME; 183 | } 184 | 185 | return length[0]; 186 | } 187 | 188 | int8_t PN532_HSU::readAckFrame() 189 | { 190 | const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; 191 | uint8_t ackBuf[sizeof(PN532_ACK)]; 192 | 193 | DMSG("\nAck: "); 194 | 195 | if (receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) 196 | { 197 | DMSG("Timeout\n"); 198 | return PN532_TIMEOUT; 199 | } 200 | 201 | if (memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK))) 202 | { 203 | DMSG("Invalid\n"); 204 | return PN532_INVALID_ACK; 205 | } 206 | return 0; 207 | } 208 | 209 | /** 210 | @brief receive data . 211 | @param buf --> return value buffer. 212 | len --> length expect to receive. 213 | timeout --> time of reveiving 214 | @retval number of received bytes, 0 means no data received. 215 | */ 216 | int8_t PN532_HSU::receive(uint8_t *buf, int len, uint16_t timeout) 217 | { 218 | int read_bytes = 0; 219 | int ret; 220 | unsigned long start_millis; 221 | 222 | while (read_bytes < len) 223 | { 224 | start_millis = millis(); 225 | do 226 | { 227 | ret = get_serial(hsu_serial)->read(); 228 | if (ret >= 0) 229 | { 230 | break; 231 | } 232 | } while ((timeout == 0) || ((millis() - start_millis) < timeout)); 233 | 234 | if (ret < 0) 235 | { 236 | if (read_bytes) 237 | { 238 | return read_bytes; 239 | } 240 | else 241 | { 242 | return PN532_TIMEOUT; 243 | } 244 | buf[read_bytes] = (uint8_t)ret; 245 | DMSG_HEX(ret); 246 | read_bytes++; 247 | } 248 | buf[read_bytes] = (uint8_t)ret; 249 | DMSG_HEX(ret); 250 | read_bytes++; 251 | } 252 | return read_bytes; 253 | } 254 | 255 | #endif // NFC_INTERFACE_HSU -------------------------------------------------------------------------------- /src/PN532_HSU.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __PN532_HSU_H__ 3 | #define __PN532_HSU_H__ 4 | 5 | 6 | #ifdef USE_SOFT_SERIAL_PIN 7 | #include 8 | #define get_serial(x) (static_cast(x)) 9 | #else 10 | #define get_serial(x) (static_cast(x)) 11 | #endif 12 | 13 | 14 | #include "PN532Interface.h" 15 | #include "Arduino.h" 16 | 17 | #define PN532_HSU_DEBUG 18 | 19 | #define PN532_HSU_READ_TIMEOUT (1000) 20 | 21 | class PN532_HSU : public PN532Interface 22 | { 23 | public: 24 | #ifdef ESP32 25 | PN532_HSU(HardwareSerial &serial, int8_t rxPin = -1, int8_t txPin = -1); 26 | #else 27 | #ifdef USE_SOFT_SERIAL_PIN 28 | PN532_HSU(SoftwareSerial &serial); 29 | #else 30 | PN532_HSU(HardwareSerial &serial); 31 | #endif 32 | #endif 33 | 34 | void begin(); 35 | void wakeup(); 36 | virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 37 | int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); 38 | 39 | private: 40 | // HardwareSerial *_serial; 41 | void *hsu_serial; 42 | uint8_t command; 43 | #ifdef ESP32 44 | int8_t _rxPin; 45 | int8_t _txPin; 46 | #endif 47 | int8_t readAckFrame(); 48 | 49 | int8_t receive(uint8_t *buf, int len, uint16_t timeout = PN532_HSU_READ_TIMEOUT); 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/PN532_I2C.cpp: -------------------------------------------------------------------------------- 1 | #ifdef NFC_INTERFACE_I2C 2 | 3 | /** 4 | * @modified picospuch 5 | */ 6 | 7 | #include "PN532_I2C.h" 8 | #include "PN532_debug.h" 9 | #include "Arduino.h" 10 | 11 | #define PN532_I2C_ADDRESS (0x48 >> 1) 12 | 13 | PN532_I2C::PN532_I2C(TwoWire &wire) 14 | { 15 | _wire = &wire; 16 | command = 0; 17 | } 18 | 19 | void PN532_I2C::begin() 20 | { 21 | _wire->begin(); 22 | } 23 | 24 | void PN532_I2C::wakeup() 25 | { 26 | delay(500); // wait for all ready to manipulate pn532 27 | } 28 | 29 | int8_t PN532_I2C::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 30 | { 31 | command = header[0]; 32 | _wire->beginTransmission(PN532_I2C_ADDRESS); 33 | 34 | write(PN532_PREAMBLE); 35 | write(PN532_STARTCODE1); 36 | write(PN532_STARTCODE2); 37 | 38 | uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA 39 | write(length); 40 | write(~length + 1); // checksum of length 41 | 42 | write(PN532_HOSTTOPN532); 43 | uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA 44 | 45 | DMSG("write: "); 46 | 47 | for (uint8_t i = 0; i < hlen; i++) 48 | { 49 | if (write(header[i])) 50 | { 51 | sum += header[i]; 52 | 53 | DMSG_HEX(header[i]); 54 | } 55 | else 56 | { 57 | DMSG("\nToo many data to send, I2C doesn't support such a big packet\n"); // I2C max packet: 32 bytes 58 | return PN532_INVALID_FRAME; 59 | } 60 | } 61 | 62 | for (uint8_t i = 0; i < blen; i++) 63 | { 64 | if (write(body[i])) 65 | { 66 | sum += body[i]; 67 | 68 | DMSG_HEX(body[i]); 69 | } 70 | else 71 | { 72 | DMSG("\nToo many data to send, I2C doesn't support such a big packet\n"); // I2C max packet: 32 bytes 73 | return PN532_INVALID_FRAME; 74 | } 75 | } 76 | 77 | uint8_t checksum = ~sum + 1; // checksum of TFI + DATA 78 | write(checksum); 79 | write(PN532_POSTAMBLE); 80 | 81 | _wire->endTransmission(); 82 | 83 | DMSG('\n'); 84 | 85 | return readAckFrame(); 86 | } 87 | 88 | int16_t PN532_I2C::getResponseLength(uint8_t buf[], uint8_t len, uint16_t timeout) 89 | { 90 | const uint8_t PN532_NACK[] = {0, 0, 0xFF, 0xFF, 0, 0}; 91 | uint16_t time = 0; 92 | 93 | do 94 | { 95 | if (_wire->requestFrom(PN532_I2C_ADDRESS, 6)) 96 | { 97 | if (read() & 1) 98 | { // check first byte --- status 99 | break; // PN532 is ready 100 | } 101 | } 102 | 103 | delay(1); 104 | time++; 105 | if ((0 != timeout) && (time > timeout)) 106 | { 107 | return -1; 108 | } 109 | } while (1); 110 | 111 | if (0x00 != read() || // PREAMBLE 112 | 0x00 != read() || // STARTCODE1 113 | 0xFF != read() // STARTCODE2 114 | ) 115 | { 116 | 117 | return PN532_INVALID_FRAME; 118 | } 119 | 120 | uint8_t length = read(); 121 | 122 | // request for last respond msg again 123 | _wire->beginTransmission(PN532_I2C_ADDRESS); 124 | for (uint16_t i = 0; i < sizeof(PN532_NACK); ++i) 125 | { 126 | write(PN532_NACK[i]); 127 | } 128 | _wire->endTransmission(); 129 | 130 | return length; 131 | } 132 | 133 | int16_t PN532_I2C::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) 134 | { 135 | uint16_t time = 0; 136 | uint8_t length; 137 | 138 | length = getResponseLength(buf, len, timeout); 139 | 140 | // [RDY] 00 00 FF LEN LCS (TFI PD0 ... PDn) DCS 00 141 | do 142 | { 143 | if (_wire->requestFrom(PN532_I2C_ADDRESS, 6 + length + 2)) 144 | { 145 | if (read() & 1) 146 | { // check first byte --- status 147 | break; // PN532 is ready 148 | } 149 | } 150 | 151 | delay(1); 152 | time++; 153 | if ((0 != timeout) && (time > timeout)) 154 | { 155 | return -1; 156 | } 157 | } while (1); 158 | 159 | if (0x00 != read() || // PREAMBLE 160 | 0x00 != read() || // STARTCODE1 161 | 0xFF != read() // STARTCODE2 162 | ) 163 | { 164 | 165 | return PN532_INVALID_FRAME; 166 | } 167 | 168 | length = read(); 169 | 170 | if (0 != (uint8_t)(length + read())) 171 | { // checksum of length 172 | return PN532_INVALID_FRAME; 173 | } 174 | 175 | uint8_t cmd = command + 1; // response command 176 | if (PN532_PN532TOHOST != read() || (cmd) != read()) 177 | { 178 | return PN532_INVALID_FRAME; 179 | } 180 | 181 | length -= 2; 182 | if (length > len) 183 | { 184 | return PN532_NO_SPACE; // not enough space 185 | } 186 | 187 | DMSG("read: "); 188 | DMSG_HEX(cmd); 189 | 190 | uint8_t sum = PN532_PN532TOHOST + cmd; 191 | for (uint8_t i = 0; i < length; i++) 192 | { 193 | buf[i] = read(); 194 | sum += buf[i]; 195 | 196 | DMSG_HEX(buf[i]); 197 | } 198 | DMSG('\n'); 199 | 200 | uint8_t checksum = read(); 201 | if (0 != (uint8_t)(sum + checksum)) 202 | { 203 | DMSG("checksum is not ok\n"); 204 | return PN532_INVALID_FRAME; 205 | } 206 | read(); // POSTAMBLE 207 | 208 | return length; 209 | } 210 | 211 | int8_t PN532_I2C::readAckFrame() 212 | { 213 | const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; 214 | uint8_t ackBuf[sizeof(PN532_ACK)]; 215 | 216 | DMSG("wait for ack at : "); 217 | DMSG(millis()); 218 | DMSG('\n'); 219 | 220 | uint16_t time = 0; 221 | do 222 | { 223 | if (_wire->requestFrom(PN532_I2C_ADDRESS, (int)sizeof(PN532_ACK) + 1)) 224 | { 225 | if (read() & 1) 226 | { // check first byte --- status 227 | break; // PN532 is ready 228 | } 229 | } 230 | 231 | delay(1); 232 | time++; 233 | if (time > PN532_ACK_WAIT_TIME) 234 | { 235 | DMSG("Time out when waiting for ACK\n"); 236 | return PN532_TIMEOUT; 237 | } 238 | } while (1); 239 | 240 | DMSG("ready at : "); 241 | DMSG(millis()); 242 | DMSG('\n'); 243 | 244 | for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) 245 | { 246 | ackBuf[i] = read(); 247 | } 248 | 249 | if (memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK))) 250 | { 251 | DMSG("Invalid ACK\n"); 252 | return PN532_INVALID_ACK; 253 | } 254 | 255 | return 0; 256 | } 257 | 258 | #endif // NFC_INTERFACE_I2C -------------------------------------------------------------------------------- /src/PN532_I2C.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @modified picospuch 3 | */ 4 | 5 | #ifndef __PN532_I2C_H__ 6 | #define __PN532_I2C_H__ 7 | 8 | #include 9 | #include "PN532Interface.h" 10 | 11 | class PN532_I2C : public PN532Interface 12 | { 13 | public: 14 | PN532_I2C(TwoWire &wire); 15 | 16 | void begin(); 17 | void wakeup(); 18 | virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 19 | int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); 20 | 21 | private: 22 | TwoWire *_wire; 23 | uint8_t command; 24 | 25 | int8_t readAckFrame(); 26 | int16_t getResponseLength(uint8_t buf[], uint8_t len, uint16_t timeout); 27 | 28 | inline uint8_t write(uint8_t data) 29 | { 30 | #if ARDUINO >= 100 31 | return _wire->write(data); 32 | #else 33 | return _wire->send(data); 34 | #endif 35 | } 36 | 37 | inline uint8_t read() 38 | { 39 | #if ARDUINO >= 100 40 | return _wire->read(); 41 | #else 42 | return _wire->receive(); 43 | #endif 44 | } 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/PN532_SPI.cpp: -------------------------------------------------------------------------------- 1 | #ifdef NFC_INTERFACE_SPI 2 | 3 | #include "PN532_SPI.h" 4 | #include "PN532_debug.h" 5 | #include "Arduino.h" 6 | 7 | #define STATUS_READ 2 8 | #define DATA_WRITE 1 9 | #define DATA_READ 3 10 | 11 | PN532_SPI::PN532_SPI(SPIClass &spi, uint8_t ss) 12 | { 13 | command = 0; 14 | _spi = &spi; 15 | _ss = ss; 16 | } 17 | 18 | void PN532_SPI::begin() 19 | { 20 | pinMode(_ss, OUTPUT); 21 | 22 | _spi->begin(); 23 | _spi->beginTransaction(SPISettings(1000000, LSBFIRST, SPI_MODE0)); 24 | #if defined(ARDUINO_XIAO_RA4M1) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) 25 | #else 26 | // _spi->setDataMode(SPI_MODE0); // PN532 only supports mode0 27 | // _spi->setBitOrder(LSBFIRST); 28 | #if defined __SAM3X8E__ 29 | /** DUE spi library does not support SPI_CLOCK_DIV8 macro */ 30 | _spi->setClockDivider(42); // set clock 2MHz(max: 5MHz) 31 | #elif defined __SAMD21G18A__ 32 | /** M0 spi library does not support SPI_CLOCK_DIV8 macro */ 33 | _spi->setClockDivider(24); // set clock 2MHz(max: 5MHz) 34 | #else 35 | _spi->setClockDivider(SPI_CLOCK_DIV8); // set clock 2MHz(max: 5MHz) 36 | #endif 37 | #endif 38 | } 39 | 40 | void PN532_SPI::wakeup() 41 | { 42 | digitalWrite(_ss, LOW); 43 | delay(2); 44 | digitalWrite(_ss, HIGH); 45 | } 46 | 47 | int8_t PN532_SPI::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 48 | { 49 | command = header[0]; 50 | writeFrame(header, hlen, body, blen); 51 | 52 | uint8_t timeout = PN532_ACK_WAIT_TIME; 53 | while (!isReady()) 54 | { 55 | delay(1); 56 | timeout--; 57 | if (0 == timeout) 58 | { 59 | DMSG("Time out when waiting for ACK\n"); 60 | return -2; 61 | } 62 | } 63 | if (readAckFrame()) 64 | { 65 | DMSG("Invalid ACK\n"); 66 | return PN532_INVALID_ACK; 67 | } 68 | return 0; 69 | } 70 | 71 | int16_t PN532_SPI::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) 72 | { 73 | uint16_t time = 0; 74 | while (!isReady()) 75 | { 76 | delay(1); 77 | time++; 78 | if (time > timeout) 79 | { 80 | return PN532_TIMEOUT; 81 | } 82 | } 83 | 84 | digitalWrite(_ss, LOW); 85 | delay(1); 86 | 87 | int16_t result; 88 | do 89 | { 90 | write(DATA_READ); 91 | 92 | if (0x00 != read() || // PREAMBLE 93 | 0x00 != read() || // STARTCODE1 94 | 0xFF != read() // STARTCODE2 95 | ) 96 | { 97 | 98 | result = PN532_INVALID_FRAME; 99 | break; 100 | } 101 | 102 | uint8_t length = read(); 103 | if (0 != (uint8_t)(length + read())) 104 | { // checksum of length 105 | result = PN532_INVALID_FRAME; 106 | break; 107 | } 108 | 109 | uint8_t cmd = command + 1; // response command 110 | if (PN532_PN532TOHOST != read() || (cmd) != read()) 111 | { 112 | result = PN532_INVALID_FRAME; 113 | break; 114 | } 115 | 116 | DMSG("read: "); 117 | DMSG_HEX(cmd); 118 | 119 | length -= 2; 120 | if (length > len) 121 | { 122 | for (uint8_t i = 0; i < length; i++) 123 | { 124 | DMSG_HEX(read()); // dump message 125 | } 126 | DMSG("\nNot enough space\n"); 127 | read(); 128 | read(); 129 | result = PN532_NO_SPACE; // not enough space 130 | break; 131 | } 132 | 133 | uint8_t sum = PN532_PN532TOHOST + cmd; 134 | for (uint8_t i = 0; i < length; i++) 135 | { 136 | buf[i] = read(); 137 | sum += buf[i]; 138 | 139 | DMSG_HEX(buf[i]); 140 | } 141 | DMSG('\n'); 142 | 143 | uint8_t checksum = read(); 144 | if (0 != (uint8_t)(sum + checksum)) 145 | { 146 | DMSG("checksum is not ok\n"); 147 | result = PN532_INVALID_FRAME; 148 | break; 149 | } 150 | read(); // POSTAMBLE 151 | 152 | result = length; 153 | } while (0); 154 | 155 | digitalWrite(_ss, HIGH); 156 | 157 | return result; 158 | } 159 | 160 | bool PN532_SPI::isReady() 161 | { 162 | digitalWrite(_ss, LOW); 163 | 164 | write(STATUS_READ); 165 | uint8_t status = read() & 1; 166 | digitalWrite(_ss, HIGH); 167 | return status; 168 | } 169 | 170 | void PN532_SPI::writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 171 | { 172 | digitalWrite(_ss, LOW); 173 | delay(2); // wake up PN532 174 | 175 | write(DATA_WRITE); 176 | write(PN532_PREAMBLE); 177 | write(PN532_STARTCODE1); 178 | write(PN532_STARTCODE2); 179 | 180 | uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA 181 | write(length); 182 | write(~length + 1); // checksum of length 183 | 184 | write(PN532_HOSTTOPN532); 185 | uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA 186 | 187 | DMSG("write: "); 188 | 189 | for (uint8_t i = 0; i < hlen; i++) 190 | { 191 | write(header[i]); 192 | sum += header[i]; 193 | 194 | DMSG_HEX(header[i]); 195 | } 196 | for (uint8_t i = 0; i < blen; i++) 197 | { 198 | write(body[i]); 199 | sum += body[i]; 200 | 201 | DMSG_HEX(body[i]); 202 | } 203 | 204 | uint8_t checksum = ~sum + 1; // checksum of TFI + DATA 205 | write(checksum); 206 | write(PN532_POSTAMBLE); 207 | 208 | digitalWrite(_ss, HIGH); 209 | 210 | DMSG('\n'); 211 | } 212 | 213 | int8_t PN532_SPI::readAckFrame() 214 | { 215 | const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; 216 | 217 | uint8_t ackBuf[sizeof(PN532_ACK)]; 218 | 219 | digitalWrite(_ss, LOW); 220 | delay(1); 221 | write(DATA_READ); 222 | 223 | for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) 224 | { 225 | ackBuf[i] = read(); 226 | } 227 | 228 | digitalWrite(_ss, HIGH); 229 | 230 | return memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)); 231 | } 232 | 233 | #endif // NFC_INTERFACE_SPI -------------------------------------------------------------------------------- /src/PN532_SPI.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __PN532_SPI_H__ 3 | #define __PN532_SPI_H__ 4 | 5 | #include 6 | #include "PN532Interface.h" 7 | 8 | class PN532_SPI : public PN532Interface 9 | { 10 | public: 11 | PN532_SPI(SPIClass &spi, uint8_t ss); 12 | 13 | void begin(); 14 | void wakeup(); 15 | int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 16 | 17 | int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); 18 | 19 | private: 20 | SPIClass *_spi; 21 | uint8_t _ss; 22 | uint8_t command; 23 | 24 | bool isReady(); 25 | void writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 26 | int8_t readAckFrame(); 27 | 28 | inline void write(uint8_t data) 29 | { 30 | _spi->transfer(data); 31 | }; 32 | 33 | inline uint8_t read() 34 | { 35 | return _spi->transfer(0); 36 | }; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/PN532_SWHSU.cpp: -------------------------------------------------------------------------------- 1 | #ifdef NFC_INTERFACE_SWHSU 2 | 3 | #include "PN532_SWHSU.h" 4 | #include "PN532_debug.h" 5 | 6 | PN532_SWHSU::PN532_SWHSU(SoftwareSerial &serial) 7 | { 8 | _serial = &serial; 9 | command = 0; 10 | } 11 | 12 | void PN532_SWHSU::begin() 13 | { 14 | _serial->begin(115200); 15 | } 16 | 17 | void PN532_SWHSU::wakeup() 18 | { 19 | _serial->write(0x55); 20 | _serial->write(0x55); 21 | _serial->write((uint8_t)0); 22 | _serial->write((uint8_t)0); 23 | _serial->write((uint8_t)0); 24 | 25 | /** dump serial buffer */ 26 | if (_serial->available()) 27 | { 28 | DMSG("Dump serial buffer: "); 29 | } 30 | while (_serial->available()) 31 | { 32 | uint8_t ret = _serial->read(); 33 | DMSG_HEX(ret); 34 | } 35 | } 36 | 37 | int8_t PN532_SWHSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 38 | { 39 | 40 | /** dump serial buffer */ 41 | if (_serial->available()) 42 | { 43 | DMSG("Dump serial buffer: "); 44 | } 45 | while (_serial->available()) 46 | { 47 | uint8_t ret = _serial->read(); 48 | DMSG_HEX(ret); 49 | } 50 | 51 | command = header[0]; 52 | 53 | _serial->write((uint8_t)PN532_PREAMBLE); 54 | _serial->write((uint8_t)PN532_STARTCODE1); 55 | _serial->write((uint8_t)PN532_STARTCODE2); 56 | 57 | uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA 58 | _serial->write(length); 59 | _serial->write(~length + 1); // checksum of length 60 | 61 | _serial->write((uint8_t)PN532_HOSTTOPN532); 62 | uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA 63 | 64 | DMSG("\nWrite: "); 65 | 66 | _serial->write(header, hlen); 67 | for (uint8_t i = 0; i < hlen; i++) 68 | { 69 | sum += header[i]; 70 | 71 | DMSG_HEX(header[i]); 72 | } 73 | 74 | _serial->write(body, blen); 75 | for (uint8_t i = 0; i < blen; i++) 76 | { 77 | sum += body[i]; 78 | 79 | DMSG_HEX(body[i]); 80 | } 81 | 82 | uint8_t checksum = ~sum + 1; // checksum of TFI + DATA 83 | _serial->write(checksum); 84 | _serial->write((uint8_t)PN532_POSTAMBLE); 85 | 86 | return readAckFrame(); 87 | } 88 | 89 | int16_t PN532_SWHSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout) 90 | { 91 | uint8_t tmp[3]; 92 | 93 | DMSG("\nRead: "); 94 | 95 | /** Frame Preamble and Start Code */ 96 | if (receive(tmp, 3, timeout) <= 0) 97 | { 98 | return PN532_TIMEOUT; 99 | } 100 | if (0 != tmp[0] || 0 != tmp[1] || 0xFF != tmp[2]) 101 | { 102 | DMSG("Preamble error"); 103 | return PN532_INVALID_FRAME; 104 | } 105 | 106 | /** receive length and check */ 107 | uint8_t length[2]; 108 | if (receive(length, 2, timeout) <= 0) 109 | { 110 | return PN532_TIMEOUT; 111 | } 112 | if (0 != (uint8_t)(length[0] + length[1])) 113 | { 114 | DMSG("Length error"); 115 | return PN532_INVALID_FRAME; 116 | } 117 | length[0] -= 2; 118 | if (length[0] > len) 119 | { 120 | return PN532_NO_SPACE; 121 | } 122 | 123 | /** receive command byte */ 124 | uint8_t cmd = command + 1; // response command 125 | if (receive(tmp, 2, timeout) <= 0) 126 | { 127 | return PN532_TIMEOUT; 128 | } 129 | if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) 130 | { 131 | DMSG("Command error"); 132 | return PN532_INVALID_FRAME; 133 | } 134 | 135 | if (receive(buf, length[0], timeout) != length[0]) 136 | { 137 | return PN532_TIMEOUT; 138 | } 139 | uint8_t sum = PN532_PN532TOHOST + cmd; 140 | for (uint8_t i = 0; i < length[0]; i++) 141 | { 142 | sum += buf[i]; 143 | } 144 | 145 | /** checksum and postamble */ 146 | if (receive(tmp, 2, timeout) <= 0) 147 | { 148 | return PN532_TIMEOUT; 149 | } 150 | if (0 != (uint8_t)(sum + tmp[0]) || 0 != tmp[1]) 151 | { 152 | DMSG("Checksum error"); 153 | return PN532_INVALID_FRAME; 154 | } 155 | 156 | return length[0]; 157 | } 158 | 159 | int8_t PN532_SWHSU::readAckFrame() 160 | { 161 | const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; 162 | uint8_t ackBuf[sizeof(PN532_ACK)]; 163 | 164 | DMSG("\nAck: "); 165 | 166 | if (receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) 167 | { 168 | DMSG("Timeout\n"); 169 | return PN532_TIMEOUT; 170 | } 171 | 172 | if (memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK))) 173 | { 174 | DMSG("Invalid\n"); 175 | return PN532_INVALID_ACK; 176 | } 177 | return 0; 178 | } 179 | 180 | /** 181 | @brief receive data . 182 | @param buf --> return value buffer. 183 | len --> length expect to receive. 184 | timeout --> time of reveiving 185 | @retval number of received bytes, 0 means no data received. 186 | */ 187 | int8_t PN532_SWHSU::receive(uint8_t *buf, int len, uint16_t timeout) 188 | { 189 | int read_bytes = 0; 190 | int ret; 191 | unsigned long start_millis; 192 | 193 | while (read_bytes < len) 194 | { 195 | start_millis = millis(); 196 | do 197 | { 198 | ret = _serial->read(); 199 | if (ret >= 0) 200 | { 201 | break; 202 | } 203 | } while ((timeout == 0) || ((millis() - start_millis) < timeout)); 204 | 205 | if (ret < 0) 206 | { 207 | if (read_bytes) 208 | { 209 | return read_bytes; 210 | } 211 | else 212 | { 213 | return PN532_TIMEOUT; 214 | } 215 | } 216 | buf[read_bytes] = (uint8_t)ret; 217 | DMSG_HEX(ret); 218 | read_bytes++; 219 | } 220 | return read_bytes; 221 | } 222 | 223 | #endif // NFC_INTERFACE_SWHSU -------------------------------------------------------------------------------- /src/PN532_SWHSU.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __PN532_SWHSU_H__ 3 | #define __PN532_SWHSU_H__ 4 | 5 | #include 6 | 7 | #include "PN532Interface.h" 8 | #include "Arduino.h" 9 | 10 | #define PN532_SWHSU_DEBUG 11 | 12 | #define PN532_SWHSU_READ_TIMEOUT (1000) 13 | 14 | class PN532_SWHSU : public PN532Interface 15 | { 16 | public: 17 | PN532_SWHSU(SoftwareSerial &serial); 18 | 19 | void begin(); 20 | void wakeup(); 21 | virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 22 | int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout); 23 | 24 | private: 25 | SoftwareSerial *_serial; 26 | uint8_t command; 27 | 28 | int8_t readAckFrame(); 29 | 30 | int8_t receive(uint8_t *buf, int len, uint16_t timeout = PN532_SWHSU_READ_TIMEOUT); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/PN532_debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEBUG_H__ 2 | #define __DEBUG_H__ 3 | 4 | // #define DEBUG 5 | 6 | #include "Arduino.h" 7 | 8 | #ifdef SEEED_XIAO_M0 9 | #define PN532_DEBUG_SERIAL Serial 10 | #elif defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) 11 | #define PN532_DEBUG_SERIAL SerialUSB 12 | #else 13 | #define PN532_DEBUG_SERIAL Serial 14 | #endif 15 | 16 | 17 | #ifdef DEBUG 18 | #define DMSG(args...) PN532_DEBUG_SERIAL.print(args) 19 | #define DMSG_STR(str) PN532_DEBUG_SERIAL.println(str) 20 | #define DMSG_HEX(num) \ 21 | PN532_DEBUG_SERIAL.print(' '); \ 22 | PN532_DEBUG_SERIAL.print((num >> 4) & 0x0F, HEX); \ 23 | PN532_DEBUG_SERIAL.print(num & 0x0F, HEX) 24 | #define DMSG_INT(num) \ 25 | PN532_DEBUG_SERIAL.print(' '); \ 26 | PN532_DEBUG_SERIAL.print(num) 27 | #else 28 | #define DMSG(args...) 29 | #define DMSG_STR(str) 30 | #define DMSG_HEX(num) 31 | #define DMSG_INT(num) 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/emulatetag.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file emulatetag.cpp 4 | @author Armin Wieser 5 | @license BSD 6 | */ 7 | /**************************************************************************/ 8 | 9 | #include "emulatetag.h" 10 | #include "PN532_debug.h" 11 | 12 | #include 13 | 14 | #define MAX_TGREAD 15 | 16 | // Command APDU 17 | #define C_APDU_CLA 0 18 | #define C_APDU_INS 1 // instruction 19 | #define C_APDU_P1 2 // parameter 1 20 | #define C_APDU_P2 3 // parameter 2 21 | #define C_APDU_LC 4 // length command 22 | #define C_APDU_DATA 5 // data 23 | 24 | #define C_APDU_P1_SELECT_BY_ID 0x00 25 | #define C_APDU_P1_SELECT_BY_NAME 0x04 26 | 27 | // Response APDU 28 | #define R_APDU_SW1_COMMAND_COMPLETE 0x90 29 | #define R_APDU_SW2_COMMAND_COMPLETE 0x00 30 | 31 | #define R_APDU_SW1_NDEF_TAG_NOT_FOUND 0x6a 32 | #define R_APDU_SW2_NDEF_TAG_NOT_FOUND 0x82 33 | 34 | #define R_APDU_SW1_FUNCTION_NOT_SUPPORTED 0x6A 35 | #define R_APDU_SW2_FUNCTION_NOT_SUPPORTED 0x81 36 | 37 | #define R_APDU_SW1_MEMORY_FAILURE 0x65 38 | #define R_APDU_SW2_MEMORY_FAILURE 0x81 39 | 40 | #define R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x62 41 | #define R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x82 42 | 43 | // ISO7816-4 commands 44 | #define ISO7816_SELECT_FILE 0xA4 45 | #define ISO7816_READ_BINARY 0xB0 46 | #define ISO7816_UPDATE_BINARY 0xD6 47 | 48 | typedef enum 49 | { 50 | NONE, 51 | CC, 52 | NDEF 53 | } tag_file; // CC ... Compatibility Container 54 | 55 | bool EmulateTag::init() 56 | { 57 | pn532.begin(); 58 | return pn532.SAMConfig(); 59 | } 60 | 61 | void EmulateTag::setNdefFile(const uint8_t *ndef, const int16_t ndefLength) 62 | { 63 | if (ndefLength > (NDEF_MAX_LENGTH - 2)) 64 | { 65 | DMSG("ndef file too large (> NDEF_MAX_LENGHT -2) - aborting"); 66 | return; 67 | } 68 | 69 | ndef_file[0] = ndefLength >> 8; 70 | ndef_file[1] = ndefLength & 0xFF; 71 | memcpy(ndef_file + 2, ndef, ndefLength); 72 | } 73 | 74 | void EmulateTag::setUid(uint8_t *uid) 75 | { 76 | uidPtr = uid; 77 | } 78 | 79 | bool EmulateTag::emulate(const uint16_t tgInitAsTargetTimeout) 80 | { 81 | 82 | uint8_t command[] = { 83 | PN532_COMMAND_TGINITASTARGET, 84 | 5, // MODE: PICC only, Passive only 85 | 86 | 0x04, 0x00, // SENS_RES 87 | 0x00, 0x00, 0x00, // NFCID1 88 | 0x20, // SEL_RES 89 | 90 | 0, 0, 0, 0, 0, 0, 0, 0, 91 | 0, 0, 0, 0, 0, 0, 0, 0, // FeliCaParams 92 | 0, 0, 93 | 94 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // NFCID3t 95 | 96 | 0, // length of general bytes 97 | 0 // length of historical bytes 98 | }; 99 | 100 | if (uidPtr != 0) 101 | { // if uid is set copy 3 bytes to nfcid1 102 | memcpy(command + 4, uidPtr, 3); 103 | } 104 | 105 | if (1 != pn532.tgInitAsTarget(command, sizeof(command), tgInitAsTargetTimeout)) 106 | { 107 | DMSG("tgInitAsTarget failed or timed out!"); 108 | return false; 109 | } 110 | 111 | uint8_t compatibility_container[] = { 112 | 0, 0x0F, 113 | 0x20, 114 | 0, 0x54, 115 | 0, 0xFF, 116 | 0x04, // T 117 | 0x06, // L 118 | 0xE1, 0x04, // File identifier 119 | ((NDEF_MAX_LENGTH & 0xFF00) >> 8), (NDEF_MAX_LENGTH & 0xFF), // maximum NDEF file size 120 | 0x00, // read access 0x0 = granted 121 | 0x00 // write access 0x0 = granted | 0xFF = deny 122 | }; 123 | 124 | if (tagWriteable == false) 125 | { 126 | compatibility_container[14] = 0xFF; 127 | } 128 | 129 | tagWrittenByInitiator = false; 130 | 131 | uint8_t rwbuf[128]; 132 | uint8_t sendlen; 133 | int16_t status; 134 | tag_file currentFile = NONE; 135 | uint16_t cc_size = sizeof(compatibility_container); 136 | bool runLoop = true; 137 | 138 | while (runLoop) 139 | { 140 | status = pn532.tgGetData(rwbuf, sizeof(rwbuf)); 141 | if (status < 0) 142 | { 143 | DMSG("tgGetData failed!\n"); 144 | pn532.inRelease(); 145 | return true; 146 | } 147 | 148 | uint8_t p1 = rwbuf[C_APDU_P1]; 149 | uint8_t p2 = rwbuf[C_APDU_P2]; 150 | uint8_t lc = rwbuf[C_APDU_LC]; 151 | uint16_t p1p2_length = ((int16_t)p1 << 8) + p2; 152 | 153 | switch (rwbuf[C_APDU_INS]) 154 | { 155 | case ISO7816_SELECT_FILE: 156 | switch (p1) 157 | { 158 | case C_APDU_P1_SELECT_BY_ID: 159 | if (p2 != 0x0c) 160 | { 161 | DMSG("C_APDU_P2 != 0x0c\n"); 162 | setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); 163 | } 164 | else if (lc == 2 && rwbuf[C_APDU_DATA] == 0xE1 && (rwbuf[C_APDU_DATA + 1] == 0x03 || rwbuf[C_APDU_DATA + 1] == 0x04)) 165 | { 166 | setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); 167 | if (rwbuf[C_APDU_DATA + 1] == 0x03) 168 | { 169 | currentFile = CC; 170 | } 171 | else if (rwbuf[C_APDU_DATA + 1] == 0x04) 172 | { 173 | currentFile = NDEF; 174 | } 175 | } 176 | else 177 | { 178 | setResponse(TAG_NOT_FOUND, rwbuf, &sendlen); 179 | } 180 | break; 181 | case C_APDU_P1_SELECT_BY_NAME: 182 | const uint8_t ndef_tag_application_name_v2[] = {0, 0x7, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}; 183 | if (0 == memcmp(ndef_tag_application_name_v2, rwbuf + C_APDU_P2, sizeof(ndef_tag_application_name_v2))) 184 | { 185 | setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); 186 | } 187 | else 188 | { 189 | DMSG("function not supported\n"); 190 | setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); 191 | } 192 | break; 193 | } 194 | break; 195 | case ISO7816_READ_BINARY: 196 | switch (currentFile) 197 | { 198 | case NONE: 199 | setResponse(TAG_NOT_FOUND, rwbuf, &sendlen); 200 | break; 201 | case CC: 202 | if (p1p2_length > NDEF_MAX_LENGTH) 203 | { 204 | setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen); 205 | } 206 | else 207 | { 208 | memcpy(rwbuf, compatibility_container + p1p2_length, lc); 209 | setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc); 210 | } 211 | break; 212 | case NDEF: 213 | if (p1p2_length > NDEF_MAX_LENGTH) 214 | { 215 | setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen); 216 | } 217 | else 218 | { 219 | memcpy(rwbuf, ndef_file + p1p2_length, lc); 220 | setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc); 221 | } 222 | break; 223 | } 224 | break; 225 | case ISO7816_UPDATE_BINARY: 226 | if (!tagWriteable) 227 | { 228 | setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); 229 | } 230 | else 231 | { 232 | if (p1p2_length > NDEF_MAX_LENGTH) 233 | { 234 | setResponse(MEMORY_FAILURE, rwbuf, &sendlen); 235 | } 236 | else 237 | { 238 | memcpy(ndef_file + p1p2_length, rwbuf + C_APDU_DATA, lc); 239 | setResponse(COMMAND_COMPLETE, rwbuf, &sendlen); 240 | tagWrittenByInitiator = true; 241 | 242 | uint16_t ndef_length = (ndef_file[0] << 8) + ndef_file[1]; 243 | if ((ndef_length > 0) && (updateNdefCallback != 0)) 244 | { 245 | updateNdefCallback(ndef_file + 2, ndef_length); 246 | } 247 | } 248 | } 249 | break; 250 | default: 251 | DMSG("Command not supported!"); 252 | DMSG_HEX(rwbuf[C_APDU_INS]); 253 | DMSG("\n"); 254 | setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen); 255 | } 256 | status = pn532.tgSetData(rwbuf, sendlen); 257 | if (status < 0) 258 | { 259 | DMSG("tgSetData failed\n!"); 260 | pn532.inRelease(); 261 | return true; 262 | } 263 | } 264 | pn532.inRelease(); 265 | return true; 266 | } 267 | 268 | void EmulateTag::setResponse(responseCommand cmd, uint8_t *buf, uint8_t *sendlen, uint8_t sendlenOffset) 269 | { 270 | switch (cmd) 271 | { 272 | case COMMAND_COMPLETE: 273 | buf[0] = R_APDU_SW1_COMMAND_COMPLETE; 274 | buf[1] = R_APDU_SW2_COMMAND_COMPLETE; 275 | *sendlen = 2 + sendlenOffset; 276 | break; 277 | case TAG_NOT_FOUND: 278 | buf[0] = R_APDU_SW1_NDEF_TAG_NOT_FOUND; 279 | buf[1] = R_APDU_SW2_NDEF_TAG_NOT_FOUND; 280 | *sendlen = 2; 281 | break; 282 | case FUNCTION_NOT_SUPPORTED: 283 | buf[0] = R_APDU_SW1_FUNCTION_NOT_SUPPORTED; 284 | buf[1] = R_APDU_SW2_FUNCTION_NOT_SUPPORTED; 285 | *sendlen = 2; 286 | break; 287 | case MEMORY_FAILURE: 288 | buf[0] = R_APDU_SW1_MEMORY_FAILURE; 289 | buf[1] = R_APDU_SW2_MEMORY_FAILURE; 290 | *sendlen = 2; 291 | break; 292 | case END_OF_FILE_BEFORE_REACHED_LE_BYTES: 293 | buf[0] = R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES; 294 | buf[1] = R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES; 295 | *sendlen = 2; 296 | break; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/emulatetag.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file emulatetag.h 4 | @author Armin Wieser 5 | @license BSD 6 | 7 | Implemented using NFC forum documents & library of libnfc 8 | */ 9 | /**************************************************************************/ 10 | 11 | #ifndef __EMULATETAG_H__ 12 | #define __EMULATETAG_H__ 13 | 14 | #include "PN532.h" 15 | 16 | #define NDEF_MAX_LENGTH 128 // altough ndef can handle up to 0xfffe in size, arduino cannot. 17 | typedef enum 18 | { 19 | COMMAND_COMPLETE, 20 | TAG_NOT_FOUND, 21 | FUNCTION_NOT_SUPPORTED, 22 | MEMORY_FAILURE, 23 | END_OF_FILE_BEFORE_REACHED_LE_BYTES 24 | } responseCommand; 25 | 26 | class EmulateTag 27 | { 28 | 29 | public: 30 | EmulateTag(PN532Interface &interface) : pn532(interface), uidPtr(0), tagWrittenByInitiator(false), tagWriteable(true), updateNdefCallback(0) {} 31 | 32 | bool init(); 33 | 34 | bool emulate(const uint16_t tgInitAsTargetTimeout = 0); 35 | 36 | /* 37 | * @param uid pointer to byte array of length 3 (uid is 4 bytes - first byte is fixed) or zero for uid 38 | */ 39 | void setUid(uint8_t *uid = 0); 40 | 41 | void setNdefFile(const uint8_t *ndef, const int16_t ndefLength); 42 | 43 | void getContent(uint8_t **buf, uint16_t *length) 44 | { 45 | *buf = ndef_file + 2; // first 2 bytes = length 46 | *length = (ndef_file[0] << 8) + ndef_file[1]; 47 | } 48 | 49 | bool writeOccured() 50 | { 51 | return tagWrittenByInitiator; 52 | } 53 | 54 | void setTagWriteable(bool setWriteable) 55 | { 56 | tagWriteable = setWriteable; 57 | } 58 | 59 | uint8_t *getNdefFilePtr() 60 | { 61 | return ndef_file; 62 | } 63 | 64 | uint8_t getNdefMaxLength() 65 | { 66 | return NDEF_MAX_LENGTH; 67 | } 68 | 69 | void attach(void (*func)(uint8_t *buf, uint16_t length)) 70 | { 71 | updateNdefCallback = func; 72 | }; 73 | 74 | private: 75 | PN532 pn532; 76 | uint8_t ndef_file[NDEF_MAX_LENGTH]; 77 | uint8_t *uidPtr; 78 | bool tagWrittenByInitiator; 79 | bool tagWriteable; 80 | void (*updateNdefCallback)(uint8_t *ndef, uint16_t length); 81 | 82 | void setResponse(responseCommand cmd, uint8_t *buf, uint8_t *sendlen, uint8_t sendlenOffset = 0); 83 | }; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/llcp.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "llcp.h" 3 | #include "PN532_debug.h" 4 | 5 | // LLCP PDU Type Values 6 | #define PDU_SYMM 0x00 7 | #define PDU_PAX 0x01 8 | #define PDU_CONNECT 0x04 9 | #define PDU_DISC 0x05 10 | #define PDU_CC 0x06 11 | #define PDU_DM 0x07 12 | #define PDU_I 0x0c 13 | #define PDU_RR 0x0d 14 | 15 | uint8_t LLCP::SYMM_PDU[2] = {0, 0}; 16 | 17 | inline uint8_t getPType(const uint8_t *buf) 18 | { 19 | return ((buf[0] & 0x3) << 2) + (buf[1] >> 6); 20 | } 21 | 22 | inline uint8_t getSSAP(const uint8_t *buf) 23 | { 24 | return buf[1] & 0x3f; 25 | } 26 | 27 | inline uint8_t getDSAP(const uint8_t *buf) 28 | { 29 | return buf[0] >> 2; 30 | } 31 | 32 | int8_t LLCP::activate(uint16_t timeout) 33 | { 34 | return link.activateAsTarget(timeout); 35 | } 36 | 37 | int8_t LLCP::waitForConnection(uint16_t timeout) 38 | { 39 | uint8_t type; 40 | 41 | mode = 1; 42 | ns = 0; 43 | nr = 0; 44 | 45 | // Get CONNECT PDU 46 | DMSG("wait for a CONNECT PDU\n"); 47 | do 48 | { 49 | if (2 > link.read(headerBuf, headerBufLen)) 50 | { 51 | return -1; 52 | } 53 | 54 | type = getPType(headerBuf); 55 | if (PDU_CONNECT == type) 56 | { 57 | break; 58 | } 59 | else if (PDU_SYMM == type) 60 | { 61 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 62 | { 63 | return -2; 64 | } 65 | } 66 | else 67 | { 68 | return -3; 69 | } 70 | 71 | } while (1); 72 | 73 | // Put CC PDU 74 | DMSG("put a CC(Connection Complete) PDU to response the CONNECT PDU\n"); 75 | ssap = getDSAP(headerBuf); 76 | dsap = getSSAP(headerBuf); 77 | headerBuf[0] = (dsap << 2) + ((PDU_CC >> 2) & 0x3); 78 | headerBuf[1] = ((PDU_CC & 0x3) << 6) + ssap; 79 | if (!link.write(headerBuf, 2)) 80 | { 81 | return -2; 82 | } 83 | 84 | return 1; 85 | } 86 | 87 | int8_t LLCP::waitForDisconnection(uint16_t timeout) 88 | { 89 | uint8_t type; 90 | 91 | // Get DISC PDU 92 | DMSG("wait for a DISC PDU\n"); 93 | do 94 | { 95 | if (2 > link.read(headerBuf, headerBufLen)) 96 | { 97 | return -1; 98 | } 99 | 100 | type = getPType(headerBuf); 101 | if (PDU_DISC == type) 102 | { 103 | break; 104 | } 105 | else if (PDU_SYMM == type) 106 | { 107 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 108 | { 109 | return -2; 110 | } 111 | } 112 | else 113 | { 114 | return -3; 115 | } 116 | 117 | } while (1); 118 | 119 | // Put DM PDU 120 | DMSG("put a DM(Disconnect Mode) PDU to response the DISC PDU\n"); 121 | // ssap = getDSAP(headerBuf); 122 | // dsap = getSSAP(headerBuf); 123 | headerBuf[0] = (dsap << 2) + (PDU_DM >> 2); 124 | headerBuf[1] = ((PDU_DM & 0x3) << 6) + ssap; 125 | if (!link.write(headerBuf, 2)) 126 | { 127 | return -2; 128 | } 129 | 130 | return 1; 131 | } 132 | 133 | int8_t LLCP::connect(uint16_t timeout) 134 | { 135 | uint8_t type; 136 | 137 | mode = 0; 138 | dsap = LLCP_DEFAULT_DSAP; 139 | ssap = LLCP_DEFAULT_SSAP; 140 | ns = 0; 141 | nr = 0; 142 | 143 | // try to get a SYMM PDU 144 | if (2 > link.read(headerBuf, headerBufLen)) 145 | { 146 | return -1; 147 | } 148 | type = getPType(headerBuf); 149 | if (PDU_SYMM != type) 150 | { 151 | return -1; 152 | } 153 | 154 | // put a CONNECT PDU 155 | headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_CONNECT >> 2); 156 | headerBuf[1] = ((PDU_CONNECT & 0x03) << 6) + LLCP_DEFAULT_SSAP; 157 | uint8_t body[] = " urn:nfc:sn:snep"; 158 | body[0] = 0x06; 159 | body[1] = sizeof(body) - 2 - 1; 160 | if (!link.write(headerBuf, 2, body, sizeof(body) - 1)) 161 | { 162 | return -2; 163 | } 164 | 165 | // wait for a CC PDU 166 | DMSG("wait for a CC PDU\n"); 167 | do 168 | { 169 | if (2 > link.read(headerBuf, headerBufLen)) 170 | { 171 | return -1; 172 | } 173 | 174 | type = getPType(headerBuf); 175 | if (PDU_CC == type) 176 | { 177 | break; 178 | } 179 | else if (PDU_SYMM == type) 180 | { 181 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 182 | { 183 | return -2; 184 | } 185 | } 186 | else 187 | { 188 | return -3; 189 | } 190 | 191 | } while (1); 192 | 193 | return 1; 194 | } 195 | 196 | int8_t LLCP::disconnect(uint16_t timeout) 197 | { 198 | uint8_t type; 199 | 200 | // try to get a SYMM PDU 201 | if (2 > link.read(headerBuf, headerBufLen)) 202 | { 203 | return -1; 204 | } 205 | type = getPType(headerBuf); 206 | if (PDU_SYMM != type) 207 | { 208 | return -1; 209 | } 210 | 211 | // put a DISC PDU 212 | headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_DISC >> 2); 213 | headerBuf[1] = ((PDU_DISC & 0x03) << 6) + LLCP_DEFAULT_SSAP; 214 | if (!link.write(headerBuf, 2)) 215 | { 216 | return -2; 217 | } 218 | 219 | // wait for a DM PDU 220 | DMSG("wait for a DM PDU\n"); 221 | do 222 | { 223 | if (2 > link.read(headerBuf, headerBufLen)) 224 | { 225 | return -1; 226 | } 227 | 228 | type = getPType(headerBuf); 229 | if (PDU_CC == type) 230 | { 231 | break; 232 | } 233 | else if (PDU_DM == type) 234 | { 235 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 236 | { 237 | return -2; 238 | } 239 | } 240 | else 241 | { 242 | return -3; 243 | } 244 | 245 | } while (1); 246 | 247 | return 1; 248 | } 249 | 250 | bool LLCP::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 251 | { 252 | uint8_t type; 253 | uint8_t buf[3]; 254 | 255 | if (mode) 256 | { 257 | // Get a SYMM PDU 258 | if (2 != link.read(buf, sizeof(buf))) 259 | { 260 | return false; 261 | } 262 | } 263 | 264 | if (headerBufLen < (hlen + 3)) 265 | { 266 | return false; 267 | } 268 | 269 | for (int8_t i = hlen - 1; i >= 0; i--) 270 | { 271 | headerBuf[i + 3] = header[i]; 272 | } 273 | 274 | headerBuf[0] = (dsap << 2) + (PDU_I >> 2); 275 | headerBuf[1] = ((PDU_I & 0x3) << 6) + ssap; 276 | headerBuf[2] = (ns << 4) + nr; 277 | if (!link.write(headerBuf, 3 + hlen, body, blen)) 278 | { 279 | return false; 280 | } 281 | 282 | ns++; 283 | 284 | // Get a RR PDU 285 | int16_t status; 286 | do 287 | { 288 | status = link.read(headerBuf, headerBufLen); 289 | if (2 > status) 290 | { 291 | return false; 292 | } 293 | 294 | type = getPType(headerBuf); 295 | if (PDU_RR == type) 296 | { 297 | break; 298 | } 299 | else if (PDU_SYMM == type) 300 | { 301 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 302 | { 303 | return false; 304 | } 305 | } 306 | else 307 | { 308 | return false; 309 | } 310 | } while (1); 311 | 312 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 313 | { 314 | return false; 315 | } 316 | 317 | return true; 318 | } 319 | 320 | int16_t LLCP::read(uint8_t *buf, uint8_t length) 321 | { 322 | uint8_t type; 323 | uint16_t status; 324 | 325 | // Get INFO PDU 326 | do 327 | { 328 | status = link.read(buf, length); 329 | if (2 > status) 330 | { 331 | return -1; 332 | } 333 | 334 | type = getPType(buf); 335 | if (PDU_I == type) 336 | { 337 | break; 338 | } 339 | else if (PDU_SYMM == type) 340 | { 341 | if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) 342 | { 343 | return -2; 344 | } 345 | } 346 | else 347 | { 348 | return -3; 349 | } 350 | 351 | } while (1); 352 | 353 | uint8_t len = status - 3; 354 | ssap = getDSAP(buf); 355 | dsap = getSSAP(buf); 356 | 357 | headerBuf[0] = (dsap << 2) + (PDU_RR >> 2); 358 | headerBuf[1] = ((PDU_RR & 0x3) << 6) + ssap; 359 | headerBuf[2] = (buf[2] >> 4) + 1; 360 | if (!link.write(headerBuf, 3)) 361 | { 362 | return -2; 363 | } 364 | 365 | for (uint8_t i = 0; i < len; i++) 366 | { 367 | buf[i] = buf[i + 3]; 368 | } 369 | 370 | nr++; 371 | 372 | return len; 373 | } 374 | -------------------------------------------------------------------------------- /src/llcp.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __LLCP_H__ 3 | #define __LLCP_H__ 4 | 5 | #include "mac_link.h" 6 | 7 | #define LLCP_DEFAULT_TIMEOUT 20000 8 | #define LLCP_DEFAULT_DSAP 0x04 9 | #define LLCP_DEFAULT_SSAP 0x20 10 | 11 | class LLCP { 12 | public: 13 | LLCP(PN532Interface &interface) : link(interface) { 14 | headerBuf = link.getHeaderBuffer(&headerBufLen); 15 | ns = 0; 16 | nr = 0; 17 | }; 18 | 19 | /** 20 | * @brief Actiave PN532 as a target 21 | * @param timeout max time to wait, 0 means no timeout 22 | * @return > 0 success 23 | * = 0 timeout 24 | * < 0 failed 25 | */ 26 | int8_t activate(uint16_t timeout = 0); 27 | 28 | int8_t waitForConnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 29 | 30 | int8_t waitForDisconnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 31 | 32 | int8_t connect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 33 | 34 | int8_t disconnect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT); 35 | 36 | /** 37 | * @brief write a packet, the packet should be less than (255 - 2) bytes 38 | * @param header packet header 39 | * @param hlen length of header 40 | * @param body packet body 41 | * @param blen length of body 42 | * @return true success 43 | * false failed 44 | */ 45 | bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 46 | 47 | /** 48 | * @brief read a packet, the packet will be less than (255 - 2) bytes 49 | * @param buf the buffer to contain the packet 50 | * @param len lenght of the buffer 51 | * @return >=0 length of the packet 52 | * <0 failed 53 | */ 54 | int16_t read(uint8_t *buf, uint8_t len); 55 | 56 | uint8_t *getHeaderBuffer(uint8_t *len) { 57 | uint8_t *buf = link.getHeaderBuffer(len); 58 | len -= 3; // I PDU header has 3 bytes 59 | return buf; 60 | }; 61 | 62 | private: 63 | MACLink link; 64 | uint8_t mode; 65 | uint8_t ssap; 66 | uint8_t dsap; 67 | uint8_t *headerBuf; 68 | uint8_t headerBufLen; 69 | uint8_t ns; // Number of I PDU Sent 70 | uint8_t nr; // Number of I PDU Received 71 | 72 | static uint8_t SYMM_PDU[2]; 73 | }; 74 | 75 | #endif // __LLCP_H__ 76 | -------------------------------------------------------------------------------- /src/mac_link.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mac_link.h" 3 | #include "PN532_debug.h" 4 | 5 | int8_t MACLink::activateAsTarget(uint16_t timeout) 6 | { 7 | pn532.begin(); 8 | pn532.SAMConfig(); 9 | return pn532.tgInitAsTarget(timeout); 10 | } 11 | 12 | bool MACLink::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen) 13 | { 14 | return pn532.tgSetData(header, hlen, body, blen); 15 | } 16 | 17 | int16_t MACLink::read(uint8_t *buf, uint8_t len) 18 | { 19 | return pn532.tgGetData(buf, len); 20 | } 21 | -------------------------------------------------------------------------------- /src/mac_link.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __MAC_LINK_H__ 4 | #define __MAC_LINK_H__ 5 | 6 | #include "PN532.h" 7 | 8 | class MACLink { 9 | public: 10 | MACLink(PN532Interface &interface) : pn532(interface) { 11 | 12 | }; 13 | 14 | /** 15 | * @brief Activate PN532 as a target 16 | * @param timeout max time to wait, 0 means no timeout 17 | * @return > 0 success 18 | * = 0 timeout 19 | * < 0 failed 20 | */ 21 | int8_t activateAsTarget(uint16_t timeout = 0); 22 | 23 | /** 24 | * @brief write a PDU packet, the packet should be less than (255 - 2) bytes 25 | * @param header packet header 26 | * @param hlen length of header 27 | * @param body packet body 28 | * @param blen length of body 29 | * @return true success 30 | * false failed 31 | */ 32 | bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0); 33 | 34 | /** 35 | * @brief read a PDU packet, the packet will be less than (255 - 2) bytes 36 | * @param buf the buffer to contain the PDU packet 37 | * @param len lenght of the buffer 38 | * @return >=0 length of the PDU packet 39 | * <0 failed 40 | */ 41 | int16_t read(uint8_t *buf, uint8_t len); 42 | 43 | uint8_t *getHeaderBuffer(uint8_t *len) { 44 | return pn532.getBuffer(len); 45 | }; 46 | 47 | private: 48 | PN532 pn532; 49 | }; 50 | 51 | #endif // __MAC_LINK_H__ 52 | -------------------------------------------------------------------------------- /src/snep.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "snep.h" 3 | #include "PN532_debug.h" 4 | 5 | int8_t SNEP::write(const uint8_t *buf, uint8_t len, uint16_t timeout) 6 | { 7 | if (0 >= llcp.activate(timeout)) 8 | { 9 | DMSG("failed to activate PN532 as a target\n"); 10 | return -1; 11 | } 12 | 13 | if (0 >= llcp.connect(timeout)) 14 | { 15 | DMSG("failed to set up a connection\n"); 16 | return -2; 17 | } 18 | 19 | // response a success SNEP message 20 | headerBuf[0] = SNEP_DEFAULT_VERSION; 21 | headerBuf[1] = SNEP_REQUEST_PUT; 22 | headerBuf[2] = 0; 23 | headerBuf[3] = 0; 24 | headerBuf[4] = 0; 25 | headerBuf[5] = len; 26 | if (0 >= llcp.write(headerBuf, 6, buf, len)) 27 | { 28 | return -3; 29 | } 30 | 31 | uint8_t rbuf[16]; 32 | if (6 > llcp.read(rbuf, sizeof(rbuf))) 33 | { 34 | return -4; 35 | } 36 | 37 | // check SNEP version 38 | if (SNEP_DEFAULT_VERSION != rbuf[0]) 39 | { 40 | DMSG("The received SNEP message's major version is different\n"); 41 | // To-do: send Unsupported Version response 42 | return -4; 43 | } 44 | 45 | // expect a put request 46 | if (SNEP_RESPONSE_SUCCESS != rbuf[1]) 47 | { 48 | DMSG("Expect a success response\n"); 49 | return -4; 50 | } 51 | 52 | llcp.disconnect(timeout); 53 | 54 | return 1; 55 | } 56 | 57 | int16_t SNEP::read(uint8_t *buf, uint8_t len, uint16_t timeout) 58 | { 59 | if (0 >= llcp.activate(timeout)) 60 | { 61 | DMSG("failed to activate PN532 as a target\n"); 62 | return -1; 63 | } 64 | 65 | if (0 >= llcp.waitForConnection(timeout)) 66 | { 67 | DMSG("failed to set up a connection\n"); 68 | return -2; 69 | } 70 | 71 | uint16_t status = llcp.read(buf, len); 72 | if (6 > status) 73 | { 74 | return -3; 75 | } 76 | 77 | // check SNEP version 78 | 79 | // in case of platform specific bug, shift SNEP message for 4 bytes. 80 | // tested on Nexus 5, Android 5.1 81 | if (SNEP_DEFAULT_VERSION != buf[0] && SNEP_DEFAULT_VERSION == buf[4]) 82 | { 83 | for (uint8_t i = 0; i < len - 4; i++) 84 | { 85 | buf[i] = buf[i + 4]; 86 | } 87 | } 88 | 89 | if (SNEP_DEFAULT_VERSION != buf[0]) 90 | { 91 | DMSG(F("SNEP->read: The received SNEP message's major version is different, me: ")); 92 | DMSG(SNEP_DEFAULT_VERSION); 93 | DMSG(", their: "); 94 | DMSG(buf[0]); 95 | DMSG("\n"); 96 | // To-do: send Unsupported Version response 97 | return -4; 98 | } 99 | 100 | // expect a put request 101 | if (SNEP_REQUEST_PUT != buf[1]) 102 | { 103 | DMSG("Expect a put request\n"); 104 | return -4; 105 | } 106 | 107 | // check message's length 108 | uint32_t length = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8) + buf[5]; 109 | // length should not be more than 244 (header + body < 255, header = 6 + 3 + 2) 110 | if (length > (status - 6)) 111 | { 112 | DMSG("The SNEP message is too large: "); 113 | DMSG_INT(length); 114 | DMSG_INT(status - 6); 115 | DMSG("\n"); 116 | return -4; 117 | } 118 | for (uint8_t i = 0; i < length; i++) 119 | { 120 | buf[i] = buf[i + 6]; 121 | } 122 | 123 | // response a success SNEP message 124 | headerBuf[0] = SNEP_DEFAULT_VERSION; 125 | headerBuf[1] = SNEP_RESPONSE_SUCCESS; 126 | headerBuf[2] = 0; 127 | headerBuf[3] = 0; 128 | headerBuf[4] = 0; 129 | headerBuf[5] = 0; 130 | llcp.write(headerBuf, 6); 131 | 132 | return length; 133 | } 134 | -------------------------------------------------------------------------------- /src/snep.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __SNEP_H__ 4 | #define __SNEP_H__ 5 | 6 | #include "llcp.h" 7 | 8 | #define SNEP_DEFAULT_VERSION 0x10 // Major: 1, Minor: 0 9 | 10 | #define SNEP_REQUEST_PUT 0x02 11 | #define SNEP_REQUEST_GET 0x01 12 | 13 | #define SNEP_RESPONSE_SUCCESS 0x81 14 | #define SNEP_RESPONSE_REJECT 0xFF 15 | 16 | class SNEP { 17 | public: 18 | SNEP(PN532Interface &interface) : llcp(interface) { 19 | headerBuf = llcp.getHeaderBuffer(&headerBufLen); 20 | }; 21 | 22 | /** 23 | * @brief write a SNEP packet, the packet should be less than (255 - 2 - 3) bytes 24 | * @param buf the buffer to contain the packet 25 | * @param len lenght of the buffer 26 | * @param timeout max time to wait, 0 means no timeout 27 | * @return >0 success 28 | * =0 timeout 29 | * <0 failed 30 | */ 31 | int8_t write(const uint8_t *buf, uint8_t len, uint16_t timeout = 0); 32 | 33 | /** 34 | * @brief read a SNEP packet, the packet will be less than (255 - 2 - 3) bytes 35 | * @param buf the buffer to contain the packet 36 | * @param len lenght of the buffer 37 | * @param timeout max time to wait, 0 means no timeout 38 | * @return >=0 length of the packet 39 | * <0 failed 40 | */ 41 | int16_t read(uint8_t *buf, uint8_t len, uint16_t timeout = 0); 42 | 43 | private: 44 | LLCP llcp; 45 | uint8_t *headerBuf; 46 | uint8_t headerBufLen; 47 | }; 48 | 49 | #endif // __SNEP_H__ 50 | --------------------------------------------------------------------------------