├── .gitignore ├── CHANGES.txt ├── LICENSE.txt ├── README.md ├── examples ├── format.js ├── read.js ├── write.js ├── writeAAR.js ├── writeContact.js ├── writeEmpty.js ├── writeMimeMediaRecord.js ├── writeSmartPoster.js ├── writeTextRecord.js └── writeUriRecord.js ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | examples/ndef.bin 2 | node_modules 3 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | == 0.1.0 == 2 | Add message when no tag found. #6 Thanks Tom Igoe (tigoe) 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2013, Don Coleman 4 | All rights reserved. 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 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holders nor the 17 | names of its contributors may be used to endorse or promote products 18 | derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 21 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NDEF Mifare Classic 2 | 3 | This module reads and writes NDEF bytes to Mifare Classic NFC tags. The module is a wrapper around [libfreefare](https://code.google.com/p/libfreefare/) command line tools. 4 | 5 | ## API 6 | 7 | ### mifare.read(callback) 8 | 9 | * callback - function 10 | 11 | Example 12 | 13 | mifare.read(function (err, data, uid) { 14 | if (err) throw err; 15 | console.log('The NFC tag UID is', uid); 16 | var message = ndef.decodeMessage(data.toJSON()); 17 | console.log(ndef.stringify(message)); 18 | }); 19 | 20 | The callback is passed two arguments (err, data), where data is the contents of the file 21 | 22 | ### mifare.write(data, callback) 23 | 24 | * bytes - byte array of ndef data 25 | * callback - function 26 | 27 | Example 28 | 29 | mifare.write(bytes, function (err) { 30 | if (err) throw err; 31 | console.log("OK"); 32 | }); 33 | 34 | ### mifare.format(callback) 35 | 36 | * callback - function 37 | 38 | Example 39 | 40 | mifare.format(function (err) { 41 | if (err) throw err; 42 | console.log("OK"); 43 | }); 44 | 45 | ## Requires 46 | 47 | * [libfreefare](https://code.google.com/p/libfreefare/) 14219ab1451f7c4e4da51acb6f8524924ec039e6 or newer 48 | * [libnfc](https://code.google.com/p/libnfc/) 1.7.0-rc7 or greater 49 | 50 | ### Linux (Debian) 51 | 52 | sudo apt-get install libfreefare* libnfc* -y 53 | 54 | ### macOS 55 | 56 | Use [Homebrew](https://brew.sh) 57 | 58 | brew install libfreefare libnfc 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/format.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var mifare = require('..'); 4 | 5 | mifare.format(function(err) { 6 | if (err) { 7 | console.log("Format failed "); 8 | console.log(err); 9 | } else { 10 | console.log("OK"); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /examples/read.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'); 5 | 6 | mifare.read(function(err, buffer, uid) { 7 | if (err) { 8 | console.log("Read failed "); 9 | console.log(err); 10 | } else { 11 | var message = ndef.decodeMessage(buffer); 12 | 13 | console.log('\nTag UID is', uid); 14 | console.log("Found NDEF message with " + message.length + 15 | (message.length === 1 ? " record" : " records" )); 16 | console.log(ndef.stringify(message)); 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /examples/write.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes; 7 | 8 | message = [ 9 | ndef.uriRecord("http://nfc-tools.org"), 10 | ndef.textRecord("Hello from nodejs"), 11 | ndef.emptyRecord() 12 | ]; 13 | 14 | bytes = ndef.encodeMessage(message); 15 | 16 | mifare.write(bytes, function(err) { 17 | if (err) { 18 | console.log("Write failed "); 19 | console.log(err); 20 | } else { 21 | console.log("OK"); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /examples/writeAAR.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes, 7 | vcard; 8 | 9 | message = [ 10 | ndef.record(ndef.TNF_EXTERNAL_TYPE, 'android.com:pkg' , [], 'com.joelapenna.foursquared') 11 | ]; 12 | 13 | bytes = ndef.encodeMessage(message); 14 | 15 | mifare.write(bytes, function(err) { 16 | if (err) { 17 | console.log("Write failed "); 18 | console.log(err); 19 | } else { 20 | console.log("OK"); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /examples/writeContact.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes, 7 | vcard; 8 | 9 | vcard = 'BEGIN:VCARD\n' + 10 | 'VERSION:2.1\n' + 11 | 'N:Coleman;Don;;;\n' + 12 | 'FN:Don Coleman\n' + 13 | 'ORG:Chariot Solutions;\n' + 14 | 'URL:http://chariotsolutions.com\n' + 15 | 'TEL;WORK:215-358-1780\n' + 16 | 'EMAIL;WORK:don@example.com\n' + 17 | 'END:VCARD' 18 | 19 | message = [ 20 | ndef.mimeMediaRecord('text/x-vCard', vcard) 21 | ]; 22 | 23 | bytes = ndef.encodeMessage(message); 24 | 25 | mifare.write(bytes, function(err) { 26 | if (err) { 27 | console.log("Write failed "); 28 | console.log(err); 29 | } else { 30 | console.log("OK"); 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /examples/writeEmpty.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes; 7 | 8 | message = [ 9 | ndef.emptyRecord() 10 | ]; 11 | 12 | bytes = ndef.encodeMessage(message); 13 | 14 | mifare.write(bytes, function(err) { 15 | if (err) { 16 | console.log("Write failed "); 17 | console.log(err); 18 | } else { 19 | console.log("OK"); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /examples/writeMimeMediaRecord.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes, 7 | hueSettings; 8 | 9 | // setting object for app the controls hue lights 10 | hueSettings = { 11 | "1": { 12 | "state": { 13 | "on": true, 14 | "bri": 65, 15 | "hue": 44591, 16 | "sat": 254 17 | } 18 | }, 19 | "2": { 20 | "state": { 21 | "on": true, 22 | "bri": 254, 23 | "hue": 13122, 24 | "sat": 211 25 | } 26 | }, 27 | "3": { 28 | "state": { 29 | "on": true, 30 | "bri": 255, 31 | "hue": 14922, 32 | "sat": 144 33 | } 34 | } 35 | } 36 | 37 | message = [ 38 | ndef.mimeMediaRecord("text/hue", JSON.stringify(hueSettings)) 39 | ]; 40 | 41 | bytes = ndef.encodeMessage(message); 42 | 43 | mifare.write(bytes, function(err) { 44 | if (err) { 45 | console.log("Write failed "); 46 | console.log(err); 47 | } else { 48 | console.log("OK"); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /examples/writeSmartPoster.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes; 7 | 8 | // a smart poster's payload is an NDEF message 9 | message = [ 10 | ndef.smartPoster( 11 | [ 12 | ndef.uriRecord("http://shop.oreilly.com/product/0636920021193.do"), 13 | ndef.textRecord("Beginning NFC") 14 | ] 15 | ), 16 | ]; 17 | 18 | bytes = ndef.encodeMessage(message); 19 | 20 | mifare.write(bytes, function(err) { 21 | if (err) { 22 | console.log("Write failed "); 23 | console.log(err); 24 | } else { 25 | console.log("OK"); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /examples/writeTextRecord.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes; 7 | 8 | message = [ 9 | ndef.textRecord("Hello from nodejs") 10 | ]; 11 | 12 | bytes = ndef.encodeMessage(message); 13 | 14 | mifare.write(bytes, function(err) { 15 | if (err) { 16 | console.log("Write failed "); 17 | console.log(err); 18 | } else { 19 | console.log("OK"); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /examples/writeUriRecord.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ndef = require('ndef'), 4 | mifare = require('..'), 5 | message, 6 | bytes; 7 | 8 | message = [ 9 | ndef.uriRecord("http://nfc-tools.org") 10 | ]; 11 | 12 | bytes = ndef.encodeMessage(message); 13 | 14 | mifare.write(bytes, function(err) { 15 | if (err) { 16 | console.log("Write failed "); 17 | console.log(err); 18 | } else { 19 | console.log("OK"); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var spawn = require('child_process').spawn, 2 | fs = require('fs'), 3 | fileName = 'ndef.bin'; // TODO use temp files 4 | 5 | function defaultCallback(err) { 6 | if (err) { throw err; } 7 | } 8 | 9 | function defaultReadCallback(err, data) { 10 | if (err) { throw err; } 11 | console.log(data); 12 | } 13 | 14 | // callback(err, data) 15 | // data is stream of ndef bytes from the tag 16 | function read(callback) { 17 | 18 | var errorMessage = "", 19 | result = "", // string for stdout from process 20 | uid = null, 21 | readMifareClassic = spawn('mifare-classic-read-ndef', [ '-y', '-o', fileName]); 22 | 23 | if (!callback) { callback = defaultReadCallback; } 24 | 25 | readMifareClassic.stdout.on('data', function (data) { 26 | process.stdout.write(data + ""); 27 | result += data; 28 | }); 29 | 30 | readMifareClassic.stderr.on('data', function (data) { 31 | errorMessage += data; 32 | // console.log('stderr: ' + data); 33 | }); 34 | 35 | readMifareClassic.on('close', function (code) { 36 | if (!result.includes('Found')) { // If stdout does not contain "Found" 37 | errorMessage = "No tag found"; // then there should be an error 38 | } else { 39 | // parse the UID from the mifare-classic-read-ndef output 40 | uid = result.match(/with UID ([A-F0-9]*)/i)[1]; 41 | } 42 | if (code === 0 && errorMessage.length === 0) { 43 | fs.readFile(fileName, function (err, data) { 44 | callback(err, data, uid); 45 | fs.unlinkSync(fileName); 46 | }); 47 | } else { 48 | callback(errorMessage); 49 | } 50 | }); 51 | } 52 | 53 | // callback(err) 54 | function write(data, callback) { 55 | 56 | var buffer = Buffer.from(data), 57 | errorMessage = "", 58 | result = ""; 59 | 60 | if (!callback) { callback = defaultCallback; } 61 | 62 | fs.writeFile(fileName, buffer, function(err) { 63 | if (err) callback(err); 64 | writeMifareClassic = spawn('mifare-classic-write-ndef', [ '-y', '-i', fileName]); 65 | 66 | writeMifareClassic.stdout.on('data', function (data) { 67 | process.stdout.write(data + ""); 68 | result += data; 69 | }); 70 | 71 | writeMifareClassic.stderr.on('data', function (data) { 72 | errorMessage += data; 73 | //console.log('stderr: ' + data); 74 | }); 75 | 76 | writeMifareClassic.on('close', function (code) { 77 | if (!result.includes('Found')) { // If stdout does not contain 78 | errorMessage = "No tag found"; // "Found" 79 | } // then there should be an error 80 | if (code === 0 && errorMessage.length === 0) { 81 | callback(null); 82 | fs.unlinkSync(fileName); 83 | } else { 84 | callback(errorMessage); 85 | } 86 | }); 87 | }); 88 | } 89 | 90 | function format(callback) { 91 | 92 | var errorMessage, 93 | result; 94 | 95 | if (!callback) { callback = defaultCallback; } 96 | 97 | formatMifareClassic = spawn('mifare-classic-format', [ '-y']); 98 | 99 | formatMifareClassic.stdout.on('data', function (data) { 100 | process.stdout.write(data + ""); 101 | result += data; 102 | }); 103 | 104 | formatMifareClassic.stderr.on('data', function (data) { 105 | errorMessage += data; 106 | // console.log('stderr: ' + data); 107 | }); 108 | 109 | formatMifareClassic.on('close', function (code) { 110 | if (!result.includes('Found')) { // If stdout does not contain 111 | errorMessage = "No tag found"; // "Found" 112 | } // then there should be an error 113 | if (code === 0 && errorMessage.length === 0) { 114 | callback(null); 115 | } else { 116 | callback(errorMessage); 117 | } 118 | }); 119 | } 120 | 121 | module.exports = { 122 | read: read, 123 | write: write, 124 | format: format 125 | }; 126 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mifare-classic", 3 | "version": "0.0.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ndef": { 8 | "version": "0.2.0", 9 | "resolved": "https://registry.npmjs.org/ndef/-/ndef-0.2.0.tgz", 10 | "integrity": "sha512-yl0t0oexuIjNLs8MAF5xZ6rT9P635+ZnCt4sng/aJT8/8gqJ0niBpZoKsioLi+NUA7ydb20MqXo+rIdAQcihVg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mifare-classic", 3 | "author": "Don Coleman ", 4 | "version": "0.1.0", 5 | "description": "Read and write NDEF bytes to Mifare Classic NFC Tags", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/don/mifare-classic-js" 10 | }, 11 | "keywords": [ 12 | "NDEF", 13 | "NFC" 14 | ], 15 | "license": "BSD-3-Clause", 16 | "readmeFilename": "README.md", 17 | "bugs": { 18 | "url": "https://github.com/don/mifare-classic-js/issues" 19 | }, 20 | "dependencies": { 21 | "ndef": "^0.2.0" 22 | }, 23 | "engines": { 24 | "node": ">=4.0.0" 25 | } 26 | } 27 | --------------------------------------------------------------------------------