diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index b5279991..9976d25d 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -126,6 +126,8 @@ add_executable (m24lr64e-example m24lr64e.cxx) add_executable (grovecircularled-example grovecircularled.cxx) add_executable (rgbringcoder-example rgbringcoder.cxx) add_executable (hp20x-example hp20x.cxx) +add_executable (pn532-example pn532.cxx) +add_executable (pn532-writeurl-example pn532-writeurl.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -230,6 +232,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/m24lr64e) include_directories (${PROJECT_SOURCE_DIR}/src/grovecircularled) include_directories (${PROJECT_SOURCE_DIR}/src/rgbringcoder) include_directories (${PROJECT_SOURCE_DIR}/src/hp20x) +include_directories (${PROJECT_SOURCE_DIR}/src/pn532) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -357,3 +360,5 @@ target_link_libraries (m24lr64e-example m24lr64e ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (grovecircularled-example grovecircularled ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (rgbringcoder-example rgbringcoder ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (hp20x-example hp20x ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (pn532-example pn532 ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (pn532-writeurl-example pn532 ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/c++/pn532-writeurl.cxx b/examples/c++/pn532-writeurl.cxx new file mode 100644 index 00000000..f7fc956f --- /dev/null +++ b/examples/c++/pn532-writeurl.cxx @@ -0,0 +1,115 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "pn532.h" + +using namespace std; + +// the URL we want to add as an NDEF record +// NOTE: this cannot exceed 34 characters. +static char url[] = "iotdk.intel.com"; + + +int main(int argc, char **argv) +{ +//! [Interesting] + // Instantiate an PN532 on I2C bus 0 (default) using gpio 3 for the + // IRQ, and gpio 2 for the reset pin. + + upm::PN532 *nfc = new upm::PN532(3, 2); + + if (!nfc->init()) + cerr << "init() failed" << endl; + + uint32_t vers = nfc->getFirmwareVersion(); + + if (vers) + printf("Got firmware version: 0x%08x\n", vers); + else + { + printf("Could not identify PN532\n"); + return 1; + } + + // Now scan and identify any cards that come in range (1 for now) + + // Retry forever + nfc->setPassiveActivationRetries(0xff); + + nfc->SAMConfig(); + + uint8_t uidSize; + uint8_t uid[7]; + + bool foundCard = false; + while (!foundCard) + { + memset(uid, 0, 7); + if (nfc->readPassiveTargetID(nfc->BAUD_MIFARE_ISO14443A, + uid, &uidSize, 2000)) + { + // found a card + printf("Found a card: UID len %d\n", uidSize); + printf("UID: "); + for (int i = 0; i < uidSize; i++) + printf("%02x ", uid[i]); + printf("\n"); + printf("SAK: 0x%02x\n", nfc->getSAK()); + printf("ATQA: 0x%04x\n\n", nfc->getATQA()); + foundCard = true; + } + else + { + printf("Waiting for a card...\n"); + } + } + + if (uidSize != 7) + { + printf("This example will only write an NDEF URI to preformatted\n"); + printf("Mifare Ultralight or NTAG2XX tags\n"); + + return 1; + } + + // 48 bytes is maximum data area on ultralight cards, so we use that + // as the maximum datasize here. Obviously if you have a bigger + // card, you can write more data. + if (!nfc->ntag2xx_WriteNDEFURI(nfc->NDEF_URIPREFIX_HTTP, url, 48)) + { + // failure + printf("Failed to write NDEF record tag.\n"); + return 1; + } + + printf("Success, URL record written to tag.\n"); + + +//! [Interesting] + + delete nfc; + return 0; +} diff --git a/examples/c++/pn532.cxx b/examples/c++/pn532.cxx new file mode 100644 index 00000000..d1b7ba0f --- /dev/null +++ b/examples/c++/pn532.cxx @@ -0,0 +1,101 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "pn532.h" + +using namespace std; + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); + +//! [Interesting] + // Instantiate an PN532 on I2C bus 0 (default) using gpio 3 for the + // IRQ, and gpio 2 for the reset pin. + + upm::PN532 *nfc = new upm::PN532(3, 2); + + if (!nfc->init()) + cerr << "init() failed" << endl; + + uint32_t vers = nfc->getFirmwareVersion(); + + if (vers) + printf("Got firmware version: 0x%08x\n", vers); + else + { + printf("Could not identify PN532\n"); + return 1; + } + + // Now scan and identify any cards that come in range (1 for now) + + // Retry forever + nfc->setPassiveActivationRetries(0xff); + + nfc->SAMConfig(); + + uint8_t uidSize; + uint8_t uid[7]; + + while (shouldRun) + { + memset(uid, 0, 7); + if (nfc->readPassiveTargetID(nfc->BAUD_MIFARE_ISO14443A, + uid, &uidSize, 2000)) + { + // found a card + printf("Found a card: UID len %d\n", uidSize); + printf("UID: "); + for (int i = 0; i < uidSize; i++) + printf("%02x ", uid[i]); + printf("\n"); + printf("SAK: 0x%02x\n", nfc->getSAK()); + printf("ATQA: 0x%04x\n\n", nfc->getATQA()); + sleep(1); + } + else + { + printf("Waiting for a card...\n"); + } + } + + +//! [Interesting] + + delete nfc; + return 0; +} diff --git a/examples/javascript/pn532-writeurl.js b/examples/javascript/pn532-writeurl.js new file mode 100644 index 00000000..2a037ad0 --- /dev/null +++ b/examples/javascript/pn532-writeurl.js @@ -0,0 +1,136 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* +* Author: Jon Trulson +* Copyright (c) 2015 Intel Corporation. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Load PN532 module +var pn532 = require('jsupm_pn532'); + +// Instantiate an PN532 on I2C bus 0 (default) using gpio 3 for the +// IRQ, and gpio 2 for the reset pin. +var myNFCObj = new pn532.PN532(3, 2); + +function writeUrl() +{ + if (uidSize.getitem(0) != 7) + { + console.log("This example will only write an NDEF URI to preformatted"); + console.log("Mifare Ultralight or NTAG2XX tags"); + exit(); + } + + // 48 bytes is maximum data area on ultralight cards, so we use that + // as the maximum datasize here. Obviously if you have a bigger + // card, you can write more data. + if (!myNFCObj.ntag2xx_WriteNDEFURI(pn532.PN532.NDEF_URIPREFIX_HTTP, + url, 48)) + { + // failure + console.log("Failed to write NDEF record tag."); + exit(1); + } + + console.log("Success, URL record written to tag."); +} + +function toHex(d, pad) +{ + // pad should be between 1 and 8 + return ("00000000"+(Number(d).toString(16))).slice(-pad) +} + +function exit() +{ + clearInterval(myInterval); + myNFCObj = null; + pn532.cleanUp(); + pn532 = null; + console.log("Exiting"); + process.exit(0); +} + +// When exiting: clear interval, and print message +process.on('SIGINT', function() +{ + exit(); +}); + +// "main" +if (!myNFCObj.init()) + console.log("init() failed"); + +var vers = myNFCObj.getFirmwareVersion(); + +if (vers) + console.log("Got firmware version: " + toHex(vers, 8)); +else +{ + console.log("Could not identify PN532"); + exit(); +} + +// Now scan and identify any cards that come in range (1 for now) + +// Retry forever +myNFCObj.setPassiveActivationRetries(0xff); + +myNFCObj.SAMConfig(); + +var uidSize = new pn532.uint8Array(0); +var uid = new pn532.uint8Array(7); + +// the URL we want to add as an NDEF record +// NOTE: this cannot exceed 34 characters. +url = "iotdk.intel.com"; + +var myInterval = setInterval(function() +{ + for (var x = 0; x < 7; x++) + uid.setitem(x, 0); + if (myNFCObj.readPassiveTargetID(pn532.PN532.BAUD_MIFARE_ISO14443A, + uid, uidSize, 2000)) + { + // found a card + console.log("Found a card: UID len " + uidSize.getitem(0)); + process.stdout.write("UID: "); + for (var i = 0; i < uidSize.getitem(0); i++) + { + var byteVal = uid.getitem(i); + process.stdout.write(toHex(byteVal, 2) + " "); + } + process.stdout.write("\n"); + console.log("SAK: " + toHex(myNFCObj.getSAK(), 2)); + console.log("ATQA: " + toHex(myNFCObj.getATQA(), 4)); + console.log(" "); + + // write the URL + writeUrl(); + clearInterval(myInterval); + return; + } + else + console.log("Waiting for a card..."); +}, 1000); + diff --git a/examples/javascript/pn532.js b/examples/javascript/pn532.js new file mode 100644 index 00000000..e740a73a --- /dev/null +++ b/examples/javascript/pn532.js @@ -0,0 +1,102 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* +* Author: Zion Orent +* Copyright (c) 2015 Intel Corporation. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Load PN532 module +var pn532 = require('jsupm_pn532'); + +// Instantiate an PN532 on I2C bus 0 (default) using gpio 3 for the +// IRQ, and gpio 2 for the reset pin. +var myNFCObj = new pn532.PN532(3, 2); + +if (!myNFCObj.init()) + console.log("init() failed"); + +var vers = myNFCObj.getFirmwareVersion(); + +if (vers) + console.log("Got firmware version: " + toHex(vers, 8)); +else +{ + console.log("Could not identify PN532"); + exit(); +} + +// Now scan and identify any cards that come in range (1 for now) + +// Retry forever +myNFCObj.setPassiveActivationRetries(0xff); + +myNFCObj.SAMConfig(); + +var uidSize = new pn532.uint8Array(0); +var uid = new pn532.uint8Array(7); + +var myInterval = setInterval(function() +{ + for (var x = 0; x < 7; x++) + uid.setitem(x, 0); + if (myNFCObj.readPassiveTargetID(pn532.PN532.BAUD_MIFARE_ISO14443A, + uid, uidSize, 2000)) + { + // found a card + console.log("Found a card: UID len " + uidSize.getitem(0)); + process.stdout.write("UID: "); + for (var i = 0; i < uidSize.getitem(0); i++) + { + var byteVal = uid.getitem(i); + process.stdout.write(toHex(byteVal, 2) + " "); + } + process.stdout.write("\n"); + console.log("SAK: " + toHex(myNFCObj.getSAK(), 2)); + console.log("ATQA: " + toHex(myNFCObj.getATQA(), 4)); + console.log(" "); + } + else + console.log("Waiting for a card..."); +}, 1000); + +function toHex(d, pad) +{ + // pad should be between 1 and 8 + return ("00000000"+(Number(d).toString(16))).slice(-pad) +} + +function exit() +{ + clearInterval(myInterval); + myNFCObj = null; + pn532.cleanUp(); + pn532 = null; + console.log("Exiting"); + process.exit(0); +} + +// When exiting: clear interval, and print message +process.on('SIGINT', function() +{ + exit(); +}); diff --git a/examples/python/pn532-writeurl.py b/examples/python/pn532-writeurl.py new file mode 100644 index 00000000..b3abc410 --- /dev/null +++ b/examples/python/pn532-writeurl.py @@ -0,0 +1,105 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2015 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import time, sys, signal, atexit +import pyupm_pn532 as upmPn532 + +# Instantiate an PN532 on I2C bus 0 (default) using gpio 3 for the +# IRQ, and gpio 2 for the reset pin. +myNFC = upmPn532.PN532(3, 2) + +## Exit handlers ## +# This stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This lets you run code on exit +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + + +if (not myNFC.init()): + print "init() failed" + sys.exit(0) + +vers = myNFC.getFirmwareVersion() + +if (vers): + print "Got firmware version: %08x" % vers +else: + print "Could not identify PN532" + sys.exit(0) + +# Now scan and identify any cards that come in range (1 for now) + +# Retry forever +myNFC.setPassiveActivationRetries(0xff) + +myNFC.SAMConfig() + +uidSize = upmPn532.uint8Array(0) +uid = upmPn532.uint8Array(7) + +# the URL we want to add as an NDEF record +# NOTE: this cannot exceed 34 characters. +url = "iotdk.intel.com" + +foundCard = False + +while (not foundCard): + for i in range(7): + uid.__setitem__(i, 0) + if (myNFC.readPassiveTargetID(upmPn532.PN532.BAUD_MIFARE_ISO14443A, + uid, uidSize, 2000)): + # found a card + print "Found a card: UID len", uidSize.__getitem__(0) + print "UID: ", + for i in range(uidSize.__getitem__(0)): + print "%02x" % uid.__getitem__(i), + print + print "SAK: %02x" % myNFC.getSAK() + print "ATQA: %04x" % myNFC.getATQA() + print + foundCard = True + else: + print "Waiting for a card...\n" + +if (uidSize.__getitem__(0) != 7): + print "This example will only write an NDEF URI to preformatted" + print "Mifare Ultralight or NTAG2XX tags" + sys.exit(1) + +# 48 bytes is maximum data area on ultralight cards, so we use that +# as the maximum datasize here. Obviously if you have a bigger +# card, you can write more data. +if (not myNFC.ntag2xx_WriteNDEFURI(upmPn532.PN532.NDEF_URIPREFIX_HTTP, url, 48)): + # failure + print "Failed to write NDEF record tag." + sys.exit(1) + +print "Success, URL record written to tag." diff --git a/examples/python/pn532.py b/examples/python/pn532.py new file mode 100644 index 00000000..bb000d4b --- /dev/null +++ b/examples/python/pn532.py @@ -0,0 +1,85 @@ +#!/usr/bin/python +# Author: Zion Orent +# Copyright (c) 2015 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import time, sys, signal, atexit +import pyupm_pn532 as upmPn532 + +# Instantiate an PN532 on I2C bus 0 (default) using gpio 3 for the +# IRQ, and gpio 2 for the reset pin. +myNFC = upmPn532.PN532(3, 2) + +## Exit handlers ## +# This stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This lets you run code on exit +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + + +if (not myNFC.init()): + print "init() failed" + sys.exit(0) + +vers = myNFC.getFirmwareVersion() + +if (vers): + print "Got firmware version: %08x" % vers +else: + print "Could not identify PN532" + sys.exit(0) + +# Now scan and identify any cards that come in range (1 for now) + +# Retry forever +myNFC.setPassiveActivationRetries(0xff) + +myNFC.SAMConfig() + +uidSize = upmPn532.uint8Array(0) +uid = upmPn532.uint8Array(7) + + +while (1): + for i in range(7): + uid.__setitem__(i, 0) + if (myNFC.readPassiveTargetID(upmPn532.PN532.BAUD_MIFARE_ISO14443A, + uid, uidSize, 2000)): + # found a card + print "Found a card: UID len", uidSize.__getitem__(0) + print "UID: ", + for i in range(uidSize.__getitem__(0)): + print "%02x" % uid.__getitem__(i), + print + print "SAK: %02x" % myNFC.getSAK() + print "ATQA: %04x" % myNFC.getATQA() + print + time.sleep(1) + else: + print "Waiting for a card...\n" diff --git a/src/pn532/CMakeLists.txt b/src/pn532/CMakeLists.txt new file mode 100644 index 00000000..a03aeb48 --- /dev/null +++ b/src/pn532/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "pn532") +set (libdescription "upm pn532 NFC/RFID reader/writer") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/pn532/jsupm_pn532.i b/src/pn532/jsupm_pn532.i new file mode 100644 index 00000000..c0b1eb4b --- /dev/null +++ b/src/pn532/jsupm_pn532.i @@ -0,0 +1,17 @@ +%module jsupm_pn532 +%include "../upm.i" +%include "../carrays_uint8_t.i" + +// Adding this typemap because SWIG is converting uint8 into a short by default +// This forces SWIG to convert it correctly +%typemap(in) uint8_t * { + void *argp = 0 ; + int res = SWIG_ConvertPtr($input, &argp, SWIGTYPE_p_uint8Array, 0 | 0 ); + $1 = (uint8_t *)(argp); +} + +%{ + #include "pn532.h" +%} + +%include "pn532.h" diff --git a/src/pn532/license.txt b/src/pn532/license.txt new file mode 100644 index 00000000..f6a0f22b --- /dev/null +++ b/src/pn532/license.txt @@ -0,0 +1,26 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012, Adafruit Industries +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holders nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/pn532/pn532.cxx b/src/pn532/pn532.cxx new file mode 100644 index 00000000..4c874922 --- /dev/null +++ b/src/pn532/pn532.cxx @@ -0,0 +1,1569 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * This code is heavily based on the Adafruit-PN532 library at + * https://github.com/adafruit/Adafruit-PN532, which is licensed under + * the BSD license. See upm/src/pn532/license.txt + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "pn532.h" + +using namespace upm; +using namespace std; + + +#define PN532_PACKBUFFSIZ 64 +static uint8_t pn532_packetbuffer[PN532_PACKBUFFSIZ]; + +static uint8_t pn532ack[] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}; +static uint32_t pn532_firmwarerev = 0x00320106; + +PN532::PN532(int irq, int reset, int bus, uint8_t address): + m_gpioIRQ(irq), m_gpioReset(reset), m_i2c(bus) +{ + m_addr = address; + m_uidLen = 0; + m_inListedTag = 0; + m_SAK = 0; + m_ATQA = 0; + m_isrInstalled = false; + m_irqRcvd = false; + + memset(m_uid, 0, 7); + memset(m_key, 0, 6); + + // turn off debugging by default + pn532Debug(false); + mifareDebug(false); + + mraa_result_t rv; + if ( (rv = m_i2c.address(m_addr)) != MRAA_SUCCESS) + { + cerr << "PN532: Could not initialize i2c address. " << endl; + mraa_result_print(rv); + return; + } + + m_gpioIRQ.dir(mraa::DIR_IN); + m_gpioReset.dir(mraa::DIR_OUT); +} + +PN532::~PN532() +{ + if (m_isrInstalled) + m_gpioIRQ.isrExit(); +} + +bool PN532::init() +{ + m_gpioReset.write(1); + m_gpioReset.write(0); + usleep(400000); + + // install an interrupt handler + m_gpioIRQ.isr(mraa::EDGE_FALLING, dataReadyISR, this); + m_isrInstalled = true; + + m_gpioReset.write(1); + + return true; +} + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters + + @param data Pointer to the byte data + @param numBytes Data length in bytes +*/ +/**************************************************************************/ +static void PrintHex(const uint8_t * data, const uint32_t numBytes) +{ + uint32_t szPos; + for (szPos=0; szPos < numBytes; szPos++) + { + fprintf(stderr, "0x%02x ", data[szPos] & 0xff); + } + fprintf(stderr, "\n"); +} + +/**************************************************************************/ +/*! + @brief Prints a hexadecimal value in plain characters, along with + the char equivalents in the following format + + 00 00 00 00 00 00 ...... + + @param data Pointer to the byte data + @param numBytes Data length in bytes +*/ +/**************************************************************************/ +static void PrintHexChar(const uint8_t * data, const uint32_t numBytes) +{ + uint32_t szPos; + for (szPos=0; szPos < numBytes; szPos++) + { + fprintf(stderr, "0x%02x ", data[szPos] & 0xff); + } + fprintf(stderr, " "); + for (szPos=0; szPos < numBytes; szPos++) + { + if (data[szPos] <= 0x1F) + fprintf(stderr, "."); + else + fprintf(stderr, "%c ", (char)data[szPos]); + } + fprintf(stderr, "\n"); +} + + +/**************************************************************************/ +/*! + @brief Checks the firmware version of the PN5xx chip + + @returns The chip's firmware version and ID +*/ +/**************************************************************************/ +uint32_t PN532::getFirmwareVersion() +{ + uint32_t response; + + pn532_packetbuffer[0] = CMD_GETFIRMWAREVERSION; + + if (! sendCommandCheckAck(pn532_packetbuffer, 1)) + return 0; + + // read data packet + readData(pn532_packetbuffer, 12); + + int offset = 7; // Skip the ready byte when using I2C + + response <<= 8; + response |= pn532_packetbuffer[offset++]; + response <<= 8; + response |= pn532_packetbuffer[offset++]; + response <<= 8; + response |= pn532_packetbuffer[offset++]; + + if (response != pn532_firmwarerev) + fprintf(stderr, + "Warning: firmware revision 0x%08x does not match expected rev 0x%08x\n", + response, pn532_firmwarerev); + + return response; +} + + +/**************************************************************************/ +/*! + @brief Sends a command and waits a specified period for the ACK + + @param cmd Pointer to the command buffer + @param cmdlen The size of the command in bytes + @param timeout timeout before giving up + + @returns 1 if everything is OK, 0 if timeout occured before an + ACK was recieved +*/ +/**************************************************************************/ +// default timeout of one second +bool PN532::sendCommandCheckAck(uint8_t *cmd, uint8_t cmdlen, + uint16_t timeout) +{ + uint16_t timer = 0; + + // clear any outstanding irq's + isReady(); + + // write the command + writeCommand(cmd, cmdlen); + + // Wait for chip to say its ready! + if (!waitForReady(timeout)) { + cerr << __FUNCTION__ << ": Not ready, timeout" << endl; + return false; + } + + if (m_pn532Debug) + cerr << __FUNCTION__ << ": IRQ received" << endl; + + // read acknowledgement + if (!readAck()) { + if (m_pn532Debug) + cerr << __FUNCTION__ << ": No ACK frame received!" << endl; + + return false; + } + + return true; // ack'd command +} + +/**************************************************************************/ +/*! + @brief Configures the SAM (Secure Access Module) +*/ +/**************************************************************************/ +bool PN532::SAMConfig(void) +{ + pn532_packetbuffer[0] = CMD_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; // normal mode; + pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second + pn532_packetbuffer[3] = 0x01; // use IRQ pin! + + if (! sendCommandCheckAck(pn532_packetbuffer, 4)) + return false; + + // read data packet + readData(pn532_packetbuffer, 8); + + int offset = 6; + return (pn532_packetbuffer[offset] == 0x15); +} + +/**************************************************************************/ +/*! + Sets the MxRtyPassiveActivation byte of the RFConfiguration register + + @param maxRetries 0xFF to wait forever, 0x00..0xFE to timeout + after mxRetries + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::setPassiveActivationRetries(uint8_t maxRetries) +{ + pn532_packetbuffer[0] = CMD_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; // Config item 5 (MaxRetries) + pn532_packetbuffer[2] = 0xFF; // MxRtyATR (default = 0xFF) + pn532_packetbuffer[3] = 0x01; // MxRtyPSL (default = 0x01) + pn532_packetbuffer[4] = maxRetries; + + if (m_mifareDebug) + cerr << __FUNCTION__ << ": Setting MxRtyPassiveActivation to " + << (int)maxRetries << endl; + + if (! sendCommandCheckAck(pn532_packetbuffer, 5)) + return false; // no ACK + + return true; +} + +/***** ISO14443A Commands ******/ + +/**************************************************************************/ +/*! + Waits for an ISO14443A target to enter the field + + @param cardBaudRate Baud rate of the card + @param uid Pointer to the array that will be populated + with the card's UID (up to 7 bytes) + @param uidLength Pointer to the variable that will hold the + length of the card's UID. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::readPassiveTargetID(BAUD_T cardbaudrate, uint8_t * uid, + uint8_t * uidLength, uint16_t timeout) +{ + pn532_packetbuffer[0] = CMD_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this + // to 2 later) + pn532_packetbuffer[2] = cardbaudrate; + + if (!sendCommandCheckAck(pn532_packetbuffer, 3, timeout)) + { + if (m_pn532Debug) + cerr << __FUNCTION__ << ": No card(s) read" << endl; + + return false; // no cards read + } + + // wait for a card to enter the field (only possible with I2C) + if (m_pn532Debug) + cerr << __FUNCTION__ << ": Waiting for IRQ (indicates card presence)" << endl; + + if (!waitForReady(timeout)) { + if (m_pn532Debug) + cerr << __FUNCTION__ << ": IRQ Timeout" << endl; + + return false; + } + + // read data packet + readData(pn532_packetbuffer, 20); + + // check some basic stuff + + /* ISO14443A card response should be in the following format: + + byte Description + ------------- ------------------------------------------ + b0..6 Frame header and preamble + b7 Tags Found + b8 Tag Number (only one used in this example) + b9..10 SENS_RES + b11 SEL_RES + b12 NFCID Length + b13..NFCIDLen NFCID */ + + // SENS_RES SEL_RES Manufacturer/Card Type NFCID Len + // -------- ------- ----------------------- --------- + // 00 04 08 NXP Mifare Classic 1K 4 bytes + // 00 02 18 NXP Mifare Classic 4K 4 bytes + + if (m_mifareDebug) + cerr << __FUNCTION__ << ": Found " << (int)pn532_packetbuffer[7] << " tags" + << endl; + + // only one card can be handled currently + if (pn532_packetbuffer[7] != 1) + return false; + + uint16_t sens_res = pn532_packetbuffer[9]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[10]; + + // store these for later retrieval, they can be used to more accurately + // ID the type of card. + + m_ATQA = sens_res; + m_SAK = pn532_packetbuffer[11]; // SEL_RES + + if (m_mifareDebug) + { + fprintf(stderr, "ATQA: 0x%04x\n", m_ATQA); + fprintf(stderr, "SAK: 0x%02x\n", m_SAK); + } + + /* Card appears to be Mifare Classic */ + // JET: How so? + + *uidLength = pn532_packetbuffer[12]; + if (m_mifareDebug) + fprintf(stderr, "UID: "); + + for (uint8_t i=0; i < pn532_packetbuffer[12]; i++) + { + uid[i] = pn532_packetbuffer[13+i]; + if (m_mifareDebug) + fprintf(stderr, "0x%02x ", uid[i]); + } + if (m_mifareDebug) + fprintf(stderr, "\n"); + + return true; +} + +/**************************************************************************/ +/*! + @brief Exchanges an APDU with the currently inlisted peer + + @param send Pointer to data to send + @param sendLength Length of the data to send + @param response Pointer to response data + @param responseLength Pointer to the response data length +*/ +/**************************************************************************/ +bool PN532::inDataExchange(uint8_t * send, uint8_t sendLength, + uint8_t * response, uint8_t * responseLength) +{ + if (sendLength > PN532_PACKBUFFSIZ-2) { + if (m_pn532Debug) + cerr << __FUNCTION__ << ": APDU length too long for packet buffer" + << endl; + + return false; + } + uint8_t i; + + pn532_packetbuffer[0] = CMD_INDATAEXCHANGE; // 0x40 + pn532_packetbuffer[1] = m_inListedTag; + for (i=0; i *responseLength) { + length = *responseLength; // silent truncation... + } + + for (i=0; i 15)) + return false; + + // Make sure the URI payload is between 1 and 38 chars + if ((len < 1) || (len > 38)) + return false; + + // Note 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 must be used for key A + // in NDEF records + + // Setup the sector buffer (w/pre-formatted TLV wrapper and NDEF message) + uint8_t sectorbuffer1[16] = {0x00, 0x00, 0x03, len+5, 0xD1, 0x01, len+1, + 0x55, uriIdentifier, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + uint8_t sectorbuffer2[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}; + uint8_t sectorbuffer3[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}; + uint8_t sectorbuffer4[16] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, + 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF}; + if (len <= 6) + { + // Unlikely we'll get a url this short, but why not ... + memcpy (sectorbuffer1+9, url, len); + sectorbuffer1[len+9] = 0xFE; + } + else if (len == 7) + { + // 0xFE needs to be wrapped around to next block + memcpy (sectorbuffer1+9, url, len); + sectorbuffer2[0] = 0xFE; + } + else if ((len > 7) && (len <= 22)) + { + // Url fits in two blocks + memcpy (sectorbuffer1+9, url, 7); + memcpy (sectorbuffer2, url+7, len-7); + sectorbuffer2[len-7] = 0xFE; + } + else if (len == 23) + { + // 0xFE needs to be wrapped around to final block + memcpy (sectorbuffer1+9, url, 7); + memcpy (sectorbuffer2, url+7, len-7); + sectorbuffer3[0] = 0xFE; + } + else + { + // Url fits in three blocks + memcpy (sectorbuffer1+9, url, 7); + memcpy (sectorbuffer2, url+7, 16); + memcpy (sectorbuffer3, url+23, len-24); + sectorbuffer3[len-22] = 0xFE; + } + + // Now write all three blocks back to the card + if (!(mifareclassic_WriteDataBlock (sectorNumber*4, sectorbuffer1))) + return false; + if (!(mifareclassic_WriteDataBlock ((sectorNumber*4)+1, sectorbuffer2))) + return false; + if (!(mifareclassic_WriteDataBlock ((sectorNumber*4)+2, sectorbuffer3))) + return false; + if (!(mifareclassic_WriteDataBlock ((sectorNumber*4)+3, sectorbuffer4))) + return false; + + // Seems that everything was OK (?!) + return true; +} + + +/***** NTAG2xx/ultralight Functions ******/ + +// Ultralight tags are limited to 64 pages max, with ntag2XX tags can +// have up to 231 pages. + +/* MIFARE ULTRALIGHT DESCRIPTION + ============================= + + Taken from: https://www.kismetwireless.net/code-old/svn/hardware/kisbee-02/firmware/drivers/rf/pn532/helpers/pn532_mifare_ultralight.c + + MIFARE Ultralight cards typically contain 512 bits (64 bytes) of + memory, including 4 bytes (32-bits) of OTP (One Time Programmable) + memory where the individual bits can be written but not erased. + + MF0ICU1 Mifare Ultralight Functional Specification: + http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + + + Mifare Ultralight cards have a 7-byte UID + + EEPROM MEMORY + ============= + Mifare Ultralight cards have 512 bits (64 bytes) of EEPROM memory, + including 4 byte (32 bits) of OTP memory. Unlike Mifare Classic cards, + there is no authentication on a per block level, although the blocks + can be set to "read-only" mode using Lock Bytes (described below). + + EEPROM memory is organised into 16 pages of four bytes eachs, in + the following order + + Page Description + ---- ------------ + 0 Serial Number (4 bytes) + 1 Serial Number (4 bytes) + 2 Byte 0: Serial Number + Byte 1: Internal Memory + Byte 2..3: lock bytes + 3 One-time programmable memory (4 bytes) + 4..15 User memory (4 bytes) + + Lock Bytes (Page 2) + ------------------- + Bytes 2 and 3 of page 2 are referred to as "Lock Bytes". Each + page from 0x03 and higher can individually locked by setting the + corresponding locking bit to "1" to prevent further write access, + effectively making the memory read only. + + For information on the lock byte mechanism, refer to section 8.5.2 of + the datasheet (referenced above). + + OTP Bytes (Page 3) + ------------------ + Page 3 is the OTP memory, and by default all bits on this page are + set to 0. These bits can be bitwise modified using the Mifare WRITE + command, and individual bits can be set to 1, but can not be changed + back to 0. + + Data Pages (Pages 4..15) + ------------------------ + Pages 4 to 15 are can be freely read from and written to, + provided there is no conflict with the Lock Bytes described above. + + After production, the bytes have the following default values: + + Page Byte Values + ---- ---------------------- + 0 1 2 3 + 4 0xFF 0xFF 0xFF 0xFF + 5..15 0x00 0x00 0x00 0x00 + + ACCESSING DATA BLOCKS + ===================== + + Before you can access the cards, you must following two steps: + + 1.) 'Connect' to a Mifare Ultralight card and retrieve the 7 byte + UID of the card. + + 2.) Memory can be read and written directly once a passive mode + connection has been made. No authentication is required for + Mifare Ultralight cards. + +*/ + + +/**************************************************************************/ +/*! + Tries to read an entire 4-byte page at the specified address. + + @param page The page number (0..63 in most cases) + @param buffer Pointer to the byte array that will hold the + retrieved data (if any) +*/ +/**************************************************************************/ +bool PN532::ntag2xx_ReadPage (uint8_t page, uint8_t * buffer) +{ + // TAG Type PAGES USER START USER STOP + // -------- ----- ---------- --------- + // NTAG 203 42 4 39 + // NTAG 213 45 4 39 + // NTAG 215 135 4 129 + // NTAG 216 231 4 225 + + if (page >= 231) + { + cerr << __FUNCTION__ << ": Page value out of range" << endl; + return false; + } + + if (m_mifareDebug) + fprintf(stderr, "Reading page %d\n", page); + + /* Prepare the command */ + pn532_packetbuffer[0] = CMD_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 + in most cases) */ + + /* Send the command */ + if (! sendCommandCheckAck(pn532_packetbuffer, 4)) + { + if (m_mifareDebug) + cerr << __FUNCTION__ << ": Failed to receive ACK for write command" + << endl; + + return false; + } + + /* Read the response packet */ + readData(pn532_packetbuffer, 26); + + if (m_mifareDebug) + { + fprintf(stderr, "Received: \n"); + PrintHexChar(pn532_packetbuffer, 26); + } + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[7] == 0x00) + { + /* Copy the 4 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + /* Note that the command actually reads 16 byte or 4 */ + /* pages at a time ... we simply discard the last 12 */ + /* bytes */ + memcpy (buffer, pn532_packetbuffer+8, 4); + } + else + { + if (m_mifareDebug) + { + fprintf(stderr, "Unexpected response reading block: \n"); + PrintHexChar(pn532_packetbuffer, 26); + } + + return false; + } + + /* Display data for debug if requested */ + if (m_mifareDebug) + { + fprintf(stderr, "Page %d:\n", page); + PrintHexChar(buffer, 4); + } + + // Return OK signal + return true; +} + +/**************************************************************************/ +/*! + Tries to write an entire 4-byte page at the specified block + address. + + @param page The page number to write. (0..63 for most cases) + @param data The byte array that contains the data to write. + Should be exactly 4 bytes long. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::ntag2xx_WritePage (uint8_t page, uint8_t * data) +{ + // TAG Type PAGES USER START USER STOP + // -------- ----- ---------- --------- + // NTAG 203 42 4 39 + // NTAG 213 45 4 39 + // NTAG 215 135 4 129 + // NTAG 216 231 4 225 + + if ((page < 4) || (page > 225)) + { + cerr << __FUNCTION__ << ": Page value out of range" << endl; + return false; + } + + if (m_mifareDebug) + fprintf(stderr, "Trying to write 4 byte page %d\n", page); + + /* Prepare the first command */ + pn532_packetbuffer[0] = CMD_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_ULTRALIGHT_CMD_WRITE; /* Mifare + Ultralight + Write + command = + 0xA2 */ + pn532_packetbuffer[3] = page; /* Page Number (0..63 for most cases) */ + memcpy (pn532_packetbuffer+4, data, 4); /* Data Payload */ + + /* Send the command */ + if (! sendCommandCheckAck(pn532_packetbuffer, 8)) + { + if (m_mifareDebug) + cerr << __FUNCTION__ << ": Failed to receive ACK for write command" + << endl; + + // Return Failed Signal + return false; + } + usleep(10000); + + /* Read the response packet */ + readData(pn532_packetbuffer, 26); + + // Return OK Signal + return true; +} + +/**************************************************************************/ +/*! + Writes an NDEF URI Record starting at the specified page (4..nn) + + Note that this function assumes that the NTAG2xx card is + already formatted to work as an "NFC Forum Tag". + + @param uriIdentifier The uri identifier code (0 = none, 0x01 = + "http://www.", etc.) + @param url The uri text to write (null-terminated string). + @param dataLen The size of the data area for overflow checks. + + @returns 1 if everything executed properly, 0 for an error +*/ +/**************************************************************************/ +bool PN532::ntag2xx_WriteNDEFURI (NDEF_URI_T uriIdentifier, char * url, + uint8_t dataLen) +{ + uint8_t pageBuffer[4] = { 0, 0, 0, 0 }; + + // Remove NDEF record overhead from the URI data (pageHeader below) + uint8_t wrapperSize = 12; + + // Figure out how long the string is + uint8_t len = strlen(url); + + // Make sure the URI payload will fit in dataLen (include 0xFE trailer) + if ((len < 1) || (len+1 > (dataLen-wrapperSize))) + return false; + + // Setup the record header + // See NFCForum-TS-Type-2-Tag_1.1.pdf for details + uint8_t pageHeader[12] = + { + /* NDEF Lock Control TLV (must be first and always present) */ + 0x01, /* Tag Field (0x01 = Lock Control TLV) */ + 0x03, /* Payload Length (always 3) */ + 0xA0, /* The position inside the tag of the lock bytes + (upper 4 = page address, lower 4 = byte + offset) */ + 0x10, /* Size in bits of the lock area */ + 0x44, /* Size in bytes of a page and the number of bytes + each lock bit can lock (4 bit + 4 bits) */ + /* NDEF Message TLV - URI Record */ + 0x03, /* Tag Field (0x03 = NDEF Message) */ + len+5, /* Payload Length (not including 0xFE trailer) */ + 0xD1, /* NDEF Record Header (TNF=0x1:Well known record + + SR + ME + MB) */ + 0x01, /* Type Length for the record type indicator */ + len+1, /* Payload len */ + 0x55, /* Record Type Indicator (0x55 or 'U' = URI Record) */ + uriIdentifier /* URI Prefix (ex. 0x01 = "http://www.") */ + }; + + // Write 12 byte header (three pages of data starting at page 4) + memcpy (pageBuffer, pageHeader, 4); + if (!(ntag2xx_WritePage (4, pageBuffer))) + return false; + memcpy (pageBuffer, pageHeader+4, 4); + if (!(ntag2xx_WritePage (5, pageBuffer))) + return false; + memcpy (pageBuffer, pageHeader+8, 4); + if (!(ntag2xx_WritePage (6, pageBuffer))) + return false; + + // Write URI (starting at page 7) + uint8_t currentPage = 7; + char * urlcopy = url; + while (len) + { + if (len < 4) + { + memset(pageBuffer, 0, 4); + memcpy(pageBuffer, urlcopy, len); + pageBuffer[len] = 0xFE; // NDEF record footer + if (!(ntag2xx_WritePage (currentPage, pageBuffer))) + return false; + // DONE! + return true; + } + else if (len == 4) + { + memcpy(pageBuffer, urlcopy, len); + if (!(ntag2xx_WritePage (currentPage, pageBuffer))) + return false; + memset(pageBuffer, 0, 4); + pageBuffer[0] = 0xFE; // NDEF record footer + currentPage++; + if (!(ntag2xx_WritePage (currentPage, pageBuffer))) + return false; + // DONE! + return true; + } + else + { + // More than one page of data left + memcpy(pageBuffer, urlcopy, 4); + if (!(ntag2xx_WritePage (currentPage, pageBuffer))) + return false; + currentPage++; + urlcopy+=4; + len-=4; + } + } + + // Seems that everything was OK (?!) + return true; +} + + +/**************************************************************************/ +/*! + @brief Tries to read/verify the ACK packet +*/ +/**************************************************************************/ +bool PN532::readAck() +{ + uint8_t ackbuff[6]; + + readData(ackbuff, 6); + + return (0 == memcmp((char *)ackbuff, (char *)pn532ack, 6)); +} + + +/**************************************************************************/ +/*! + @brief Return true if the PN532 is ready with a response. +*/ +/**************************************************************************/ +bool PN532::isReady() +{ + // ALWAYS clear the m_irqRcvd flag if set. + if (m_irqRcvd) + { + m_irqRcvd = false; + return true; + } + + return false; +} + +/**************************************************************************/ +/*! + @brief Waits until the PN532 is ready. + + @param timeout Timeout before giving up +*/ +/**************************************************************************/ +bool PN532::waitForReady(uint16_t timeout) +{ + uint16_t timer = 0; + while(!isReady()) + { + if (timeout != 0) + { + timer += 10; + if (timer > timeout) + { + return false; + } + } + usleep(10000); + } + return true; +} + +/**************************************************************************/ +/*! + @brief Reads n bytes of data from the PN532 via SPI or I2C. + + @param buff Pointer to the buffer where data will be written + @param n Number of bytes to be read +*/ +/**************************************************************************/ +void PN532::readData(uint8_t* buff, uint8_t n) +{ + uint8_t buf[n + 2]; + int rv; + + memset(buf, 0, n+2); + usleep(2000); + m_i2c.address(m_addr); + rv = m_i2c.read(buf, n + 2); + + if (m_pn532Debug) + { + cerr << __FUNCTION__ << ": read returned " << rv << "bytes" << endl; + + fprintf(stderr, "(raw) buf (%d) = ", rv); + PrintHex(buf, rv); + fprintf(stderr, "\n"); + } + + for (int i=0; im_irqRcvd = true; +} + +PN532::TAG_TYPE_T PN532::tagType() +{ + // This is really just a guess, ATQA and SAK could in theory be used + // to refine the detection. + + if (m_uidLen == 4) + return TAG_TYPE_MIFARE_CLASSIC; + else if (m_uidLen == 7) + return TAG_TYPE_NFC2; + else + return TAG_TYPE_UNKNOWN; +} diff --git a/src/pn532/pn532.h b/src/pn532/pn532.h new file mode 100644 index 00000000..3e6f7f7c --- /dev/null +++ b/src/pn532/pn532.h @@ -0,0 +1,502 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * This code is heavily based on the Adafruit-PN532 library at + * https://github.com/adafruit/Adafruit-PN532, which is licensed under + * the BSD license. See upm/src/pn532/license.txt + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#pragma once + +#include +#include +#include +#include + +#define PN532_I2C_BUS 0 +#define PN532_DEFAULT_I2C_ADDR (0x48 >> 1) + +#define PN532_PREAMBLE (0x00) +#define PN532_STARTCODE1 (0x00) +#define PN532_STARTCODE2 (0xFF) +#define PN532_POSTAMBLE (0x00) + +#define PN532_HOSTTOPN532 (0xD4) +#define PN532_PN532TOHOST (0xD5) + +namespace upm { + + /** + * @brief PN532 NFC/RFID reader/writer + * @defgroup pn532 libupm-pn532 + * @ingroup adafruit i2c rfid + */ + + /** + * @library pn532 + * @sensor pn532 + * @comname PN532 NFC/RFID reader/writer + * @type rfid + * @man adafruit + * @web http://www.adafruit.com/products/364 + * @con i2c + * + * @brief API for the PN532 based NFC/RFID reader/writer + * + * Identify a card and print out basic info + * @snippet pn532.cxx Interesting + * Add a URI to an already NDEF formatted ultralight or NTAG2XX tag + * @snippet pn532-writeurl.cxx Interesting + */ + class PN532 { + public: + + /** + * PN532 commands + */ + typedef enum { + CMD_DIAGNOSE = 0x00, + CMD_GETFIRMWAREVERSION = 0x02, + CMD_GETGENERALSTATUS = 0x04, + CMD_READREGISTER = 0x06, + CMD_WRITEREGISTER = 0x08, + CMD_READGPIO = 0x0C, + CMD_WRITEGPIO = 0x0E, + CMD_SETSERIALBAUDRATE = 0x10, + CMD_SETPARAMETERS = 0x12, + CMD_SAMCONFIGURATION = 0x14, + CMD_POWERDOWN = 0x16, + CMD_RFCONFIGURATION = 0x32, + CMD_RFREGULATIONTEST = 0x58, + CMD_INJUMPFORDEP = 0x56, + CMD_INJUMPFORPSL = 0x46, + CMD_INLISTPASSIVETARGET = 0x4A, + CMD_INATR = 0x50, + CMD_INPSL = 0x4E, + CMD_INDATAEXCHANGE = 0x40, + CMD_INCOMMUNICATETHRU = 0x42, + CMD_INDESELECT = 0x44, + CMD_INRELEASE = 0x52, + CMD_INSELECT = 0x54, + CMD_INAUTOPOLL = 0x60, + CMD_TGINITASTARGET = 0x8C, + CMD_TGSETGENERALBYTES = 0x92, + CMD_TGGETDATA = 0x86, + CMD_TGSETDATA = 0x8E, + CMD_TGSETMETADATA = 0x94, + CMD_TGGETINITIATORCOMMAND = 0x88, + CMD_TGRESPONSETOINITIATOR = 0x90, + CMD_TGGETTARGETSTATUS = 0x8A + } PN532_CMD_T; + + /** + * Response bytes + */ + typedef enum { + RSP_INDATAEXCHANGE = 0x41, + RSP_INLISTPASSIVETARGET = 0x4B + } PN532_RSP_T; + + /** + * MIFARE commands + */ + typedef enum { + MIFARE_CMD_AUTH_A = 0x60, + MIFARE_CMD_AUTH_B = 0x61, + MIFARE_CMD_READ = 0x30, + MIFARE_CMD_WRITE = 0xA0, + MIFARE_CMD_TRANSFER = 0xB0, + MIFARE_CMD_DECREMENT = 0xC0, + MIFARE_CMD_INCREMENT = 0xC1, + MIFARE_CMD_STORE = 0xC2, + MIFARE_ULTRALIGHT_CMD_WRITE = 0xA2 + } MIFARE_CMD_T; + + /** + * NDEF prefixes + */ + typedef enum { + NDEF_URIPREFIX_NONE = 0x00, + NDEF_URIPREFIX_HTTP_WWWDOT = 0x01, + NDEF_URIPREFIX_HTTPS_WWWDOT = 0x02, + NDEF_URIPREFIX_HTTP = 0x03, + NDEF_URIPREFIX_HTTPS = 0x04, + NDEF_URIPREFIX_TEL = 0x05, + NDEF_URIPREFIX_MAILTO = 0x06, + NDEF_URIPREFIX_FTP_ANONAT = 0x07, + NDEF_URIPREFIX_FTP_FTPDOT = 0x08, + NDEF_URIPREFIX_FTPS = 0x09, + NDEF_URIPREFIX_SFTP = 0x0A, + NDEF_URIPREFIX_SMB = 0x0B, + NDEF_URIPREFIX_NFS = 0x0C, + NDEF_URIPREFIX_FTP = 0x0D, + NDEF_URIPREFIX_DAV = 0x0E, + NDEF_URIPREFIX_NEWS = 0x0F, + NDEF_URIPREFIX_TELNET = 0x10, + NDEF_URIPREFIX_IMAP = 0x11, + NDEF_URIPREFIX_RTSP = 0x12, + NDEF_URIPREFIX_URN = 0x13, + NDEF_URIPREFIX_POP = 0x14, + NDEF_URIPREFIX_SIP = 0x15, + NDEF_URIPREFIX_SIPS = 0x16, + NDEF_URIPREFIX_TFTP = 0x17, + NDEF_URIPREFIX_BTSPP = 0x18, + NDEF_URIPREFIX_BTL2CAP = 0x19, + NDEF_URIPREFIX_BTGOEP = 0x1A, + NDEF_URIPREFIX_TCPOBEX = 0x1B, + NDEF_URIPREFIX_IRDAOBEX = 0x1C, + NDEF_URIPREFIX_FILE = 0x1D, + NDEF_URIPREFIX_URN_EPC_ID = 0x1E, + NDEF_URIPREFIX_URN_EPC_TAG = 0x1F, + NDEF_URIPREFIX_URN_EPC_PAT = 0x20, + NDEF_URIPREFIX_URN_EPC_RAW = 0x21, + NDEF_URIPREFIX_URN_EPC = 0x22, + NDEF_URIPREFIX_URN_NFC = 0x23 + } NDEF_URI_T; + + /** + * Card baud rates + */ + typedef enum { + BAUD_MIFARE_ISO14443A = 0x00 // 106 Kbit/s + } BAUD_T; + + + /** + * Tag types + */ + typedef enum { + TAG_TYPE_UNKNOWN = 0, + TAG_TYPE_MIFARE_CLASSIC = 1, + TAG_TYPE_NFC2 = 2 /* ultralight or NTAG2XX */ + } TAG_TYPE_T; + + /** + * pn532 constructor + * + * @param irq pin to use for IRQ + * @param reset reset pin + * @param bus i2c bus to use + * @param address the address for this device + */ + PN532(int irq, int reset, int bus=PN532_I2C_BUS, + uint8_t address=PN532_DEFAULT_I2C_ADDR); + + /** + * PN532 Destructor + */ + ~PN532(); + + /** + * set up initial values and start operation + * + * @return true if successful + */ + bool init(); + + /** + * Checks the firmware version of the PN5xx chip + * + * @return the chip's firmware version and ID + */ + uint32_t getFirmwareVersion(); + + /** + * sends a command and waits a specified period for the ACK + * + * @param cmd Pointer to the command buffer + * @param cmdlen the size of the command in bytes + * @param timeout timeout before giving up (in ms) + * + * @return true if everything is OK, false if timeout occured + * before an ACK was recieved + */ + bool sendCommandCheckAck(uint8_t *cmd, uint8_t cmdlen, + uint16_t timeout=1000); + + /** + * configures the SAM (Secure Access Module) + * + * @return true if successfully configured + */ + bool SAMConfig(void); + + /** + * sets the MxRtyPassiveActivation byte of the RFConfiguration + * register. By default the pn532 will retry indefinitely. + * + * @param maxRetries 0xFF to wait forever, 0x00..0xFE to timeout + * after maxRetries. 0x00 means try once, with no retries on failure. + * + * @return true if everything executed properly, false for an error + */ + bool setPassiveActivationRetries(uint8_t maxRetries); + + /** + * waits for an ISO14443A target to enter the field + * + * @param cardbaudbate baud rate of the card, one of the BAUD_T values + * @param uid Pointer to the array that will be populated with the + * cards UID, up to 7 bytes + * @param uidLength Pointer to the variable that will hold the + * length of the card's UID. + * @param timeout the number of milliseconds to wait + * + * @return true if everything executed properly, false for an error + */ + bool readPassiveTargetID(BAUD_T cardbaudrate, uint8_t * uid, + uint8_t * uidLength, uint16_t timeout); + + /** + * exchanges an APDU (Application Protocol Data Unit) with the + * currently inlisted peer + * + * @param send Pointer to data to send + * @param sendLength Length of the data to send + * @param response Pointer to response data + * @param responseLength Pointer to the response data length + * + * @return true if everything executed properly, false for an error + */ + bool inDataExchange(uint8_t * send, uint8_t sendLength, + uint8_t * response, uint8_t * responseLength); + + /** + * 'InLists' a passive target. PN532 acting as reader/initiator, + * peer acting as card/responder. + * + * @return true if everything executed properly, false for an error + */ + bool inListPassiveTarget(); + + /** + * Indicates whether the specified block number is the first block + * in the sector (block 0 relative to the current sector) + * + * @return true if it's the first block, false otherwise + */ + bool mifareclassic_IsFirstBlock (uint32_t uiBlock); + + /** + * indicates whether the specified block number is the sector trailer + * + * @return true if it's the trailer block, false otherwise + */ + bool mifareclassic_IsTrailerBlock (uint32_t uiBlock); + + /** + * tries to authenticate a block of memory on a MIFARE card using the + * INDATAEXCHANGE command. See section 7.3.8 of the PN532 User Manual + * for more information on sending MIFARE and other commands. + * + * @param uid Pointer to a byte array containing the card UID + * @param uidLen The length (in bytes) of the card's UID (Should + * be 4 for MIFARE Classic) + * @param blockNumber The block number to authenticate. (0..63 for + * 1KB cards, and 0..255 for 4KB cards). + * @param keyNumber Which key type to use during authentication + * (0 = MIFARE_CMD_AUTH_A, 1 = MIFARE_CMD_AUTH_B) + * @param keyData Pointer to a byte array containing the 6 byte + * key value + * + * @return true if everything executed properly, false for an error + */ + bool mifareclassic_AuthenticateBlock (uint8_t * uid, uint8_t uidLen, + uint32_t blockNumber, + uint8_t keyNumber, + uint8_t * keyData); + + /** + * tries to read an entire 16-byte data block at the specified block + * address. + * + * @param blockNumber The block number to read (0..63 for + * 1KB cards, and 0..255 for 4KB cards). + * @param data Pointer to the byte array that will hold the + * retrieved data (if any) + * + * @return true if everything executed properly, false for an error + */ + bool mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t * data); + + /** + * tries to write an entire 16-byte data block at the specified block + * address. + * + * @param blockNumber The block number to write. (0..63 for + * 1KB cards, and 0..255 for 4KB cards). + * @param data The byte array that contains the data to write. + * + * @returns true if everything executed properly, false for an error + */ + bool mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t * data); + + /** + * formats a Mifare Classic card to store NDEF Records + * + * @return true if everything executed properly, false for an error + */ + bool mifareclassic_FormatNDEF (void); + + /** + * writes an NDEF URI Record to the specified sector (1..15) + * + * Note that this function assumes that the Mifare Classic card is + * already formatted to work as an "NFC Forum Tag" and uses a MAD1 + * file system. You can use the NXP TagWriter app on Android to + * properly format cards for this. + * + * @param sectorNumber The sector that the URI record should be written + * to (can be 1..15 for a 1K card) + * @param uriIdentifier The uri identifier code (one of the NDEF_URI_T + * values + * @param url the uri text to write (max 38 characters). + * + * @return true if everything executed properly, false for an error + */ + bool mifareclassic_WriteNDEFURI (uint8_t sectorNumber, + NDEF_URI_T uriIdentifier, + const char * url); + + /** + * read an entire 4-byte page at the specified address + * + * @param page The page number (0..63 in most cases) + * @param buffer Pointer to the byte array that will hold the + * retrieved data (if any) + * + * @return true if everything executed properly, false for an error + */ + bool ntag2xx_ReadPage (uint8_t page, uint8_t * buffer); + + /** + * write an entire 4-byte page at the specified block address + * + * @param page The page number to write. (0..63 for most cases) + * @param data The byte array that contains the data to write. + * Should be exactly 4 bytes long. + * + * @return true if everything executed properly, false for an error + */ + bool ntag2xx_WritePage (uint8_t page, uint8_t * data); + + /** + * writes an NDEF URI Record starting at the specified page (4..nn) + * + * Note that this function assumes that the NTAG2xx card is + * already formatted to work as an "NFC Forum Tag". + * + * @param uriIdentifier The uri identifier code (one of the NDEF_URI_T + * values + * @param url The uri text to write (null-terminated string). + * @param dataLen The size of the data area for overflow checks. + * + * @return true if everything executed properly, false for an error + */ + bool ntag2xx_WriteNDEFURI (NDEF_URI_T uriIdentifier, char * url, + uint8_t dataLen); + + /** + * return the ATQA (Answer to Request Acknowlege) value. This + * value is only valid after a successfull call to + * readPassiveTargetID() + * + * @return ATQA value + */ + uint16_t getATQA() { return m_ATQA; }; + + /** + * return the SAK (Select Acknowlege) value. This + * value is only valid after a successfull call to + * readPassiveTargetID() + * + * @return SAK value + */ + uint8_t getSAK() { return m_SAK; }; + + /** + * provide public access to the class's MRAA i2C context for + * direct user access + * + * @return a reference to the class i2c context + */ + mraa::I2c& i2cContext() { return m_i2c; }; + + /** + * enable or disable debugging output for pn532 related operations + * + * @param enable true to enabloe debug output, false to disable + */ + void pn532Debug(bool enable) { m_pn532Debug = enable; }; + + /** + * enable or disable debugging output for mifare related operations + * + * @param enable true to enabloe debug output, false to disable + */ + + void mifareDebug(bool enable) { m_mifareDebug = enable; }; + + /** + * try to determine the tag type + * + * @return one of the TAG_TYPE_T values + */ + TAG_TYPE_T tagType(); + + protected: + mraa::I2c m_i2c; + mraa::Gpio m_gpioIRQ; + mraa::Gpio m_gpioReset; + + bool readAck(); + bool isReady(); + bool waitForReady(uint16_t timeout); + void readData(uint8_t* buff, uint8_t n); + void writeCommand(uint8_t* cmd, uint8_t cmdlen); + + private: + static void dataReadyISR(void *ctx); + bool m_isrInstalled; + volatile bool m_irqRcvd; + + uint8_t m_addr; + + uint8_t m_uid[7]; // ISO14443A uid + uint8_t m_uidLen; // uid len + uint8_t m_key[6]; // Mifare Classic key + uint8_t m_inListedTag; // Tg number of inlisted tag. + + uint16_t m_ATQA; // ATQA (Answer to Request Acknowlege - ISO14443) + // for currently inlisted card + uint8_t m_SAK; // SAK (Select Acknowlege) + // for currently inlisted card + + // debugables + bool m_pn532Debug; + bool m_mifareDebug; + }; +} + + diff --git a/src/pn532/pyupm_pn532.i b/src/pn532/pyupm_pn532.i new file mode 100644 index 00000000..20b44a15 --- /dev/null +++ b/src/pn532/pyupm_pn532.i @@ -0,0 +1,22 @@ +%module pyupm_pn532 +%include "../upm.i" +%include "../carrays_uint8_t.i" + +// Adding this typemap because SWIG is converting uint8, uint16, and uint32 into a short by default +// This forces SWIG to convert it correctly +%typemap(in) uint8_t * { + void *argp = 0 ; + int res = SWIG_ConvertPtr($input, &argp, SWIGTYPE_p_uint8Array, 0 | 0 ); + $1 = reinterpret_cast< uint8_t * >(argp); +} + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "pn532_doc.i" +#endif + +%include "pn532.h" +%{ + #include "pn532.h" +%}