I am working on a project which uses changeable UID cards and I would need to have a library on Arduino which could do that, but not using a different hardware like the RC522. The library from miguel balboa is very good in this respect, but it does not apply to the PN532.
I know that you could change the UID of the card using libnfc, but that could not work on Arduino because it needs a full OS to be installed.
I would therefore like to ask your help on writing such an example as we would find for the RC522.
I have inspected the PN532 code for mf-setuid and I have found that it would be possible to adapt the code for Arduino, so please see what I have managed to write so far:
/*-
Based on Free/Libre Near Field Communication (NFC) library
*
* Libnfc historical contributors:
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2009-2013 Romuald Conty
* Copyright (C) 2010-2012 Romain Tartière
* Copyright (C) 2010-2013 Philippe Teuwen
* Copyright (C) 2012-2013 Ludovic Rousseau
* See AUTHORS file for a more comprehensive list of contributors.
* Additional contributors of this file:
* Copyright (C) 2011 Adam Laurie
* Copyright (C) 2014 Dario Carluccio
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 OR CONTRIBUTORS 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.
*
* Note that this license only applies on the examples, NFC library itself is under LGPL
*
*/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>
// If using the breakout with SPI, define the pins for SPI communication.
#define PN532_SCK (13)
#define PN532_MOSI (11)
#define PN532_SS (10)
#define PN532_MISO (12)
// If using the breakout or shield with I2C, define just the pins connected
// to the IRQ and reset lines. Use the values below (2, 3) for the shield!
#define PN532_IRQ (2)
#define PN532_RESET (3) // Not connected by default on the NFC Shield
// Use this line for a breakout with a SPI connection:
Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
// Use this line for a breakout with a hardware SPI connection. Note that
// the PN532 SCK, MOSI, and MISO pins need to be connected to the Arduino's
// hardware SPI SCK, MOSI, and MISO pins. On an Arduino Uno these are
// SCK = 13, MOSI = 11, MISO = 12. The SS line can be any digital IO pin.
//Adafruit_PN532 nfc(PN532_SS);
// Or use this line for a breakout or shield with an I2C connection:
//Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
void setup() {
Serial.begin(115200);
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.println("Did not find the shield...");
while (true) {
}
}
Serial.print("The classic: Found chip PN5");
Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. ");
Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.');
Serial.println((versiondata>>8) & 0xFF, DEC);
// configure board to read RFID tags
nfc.SAMConfig();
nfc.begin();
}
// Defining the same variables as the libnfc-sample
#define SAK_FLAG_ATS_SUPPORTED 0x20
#define MAX_FRAME_LEN 264
static uint8_t abtRx[MAX_FRAME_LEN];
static int szRxBits;
static uint8_t abtRawUid[12];
static uint8_t abtAtqa[2];
static uint8_t abtSak;
static uint8_t abtAts[MAX_FRAME_LEN];
static uint8_t szAts = 0;
static size_t szCL = 1;//Always start with Cascade Level 1 (CL1)
bool quiet_output = false;
bool iso_ats_supported = false;
// ISO14443A Anti-Collision Commands
uint8_t abtReqa[1] = { 0x26 };
uint8_t abtSelectAll[2] = { 0x93, 0x20 };
uint8_t abtSelectTag[9] = { 0x93, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t abtRats[4] = { 0xe0, 0x50, 0x00, 0x00 };
uint8_t abtHalt[4] = { 0x50, 0x00, 0x00, 0x00 };
#define CASCADE_BIT 0x04
// special unlock command
uint8_t abtUnlock1[1] = { 0x40 };
uint8_t abtUnlock2[1] = { 0x43 };
uint8_t abtWipe[1] = { 0x41 };
uint8_t abtWrite[4] = { 0xa0, 0x00, 0x5f, 0xb1 };
uint8_t abtData[18] = { 0x01, 0x23, 0x45, 0x67, 0x00, 0x08, 0x04, 0x00, 0x46, 0x59, 0x25, 0x58, 0x49, 0x10, 0x23, 0x02, 0x23, 0xeb };
uint8_t abtBlank[18] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x36, 0xCC };
// Created my own transmit_bits method.
// I have replaced transmit_bits with transmit_bytes, but I think that's wrong.
static bool
transmit_bits(const uint8_t *pbtTx, const size_t szTxBits)
{
// Show transmitted command
printf("Sent bits: ");
nfc.PrintHex(pbtTx, szTxBits);
uint8_t responseLength = sizeof(abtRx);
if (nfc.inDataExchange(pbtTx,szTxBits,abtRx,&responseLength)){
printf("\nSuccess sending/receiving...");
}
else{
printf("\nCommunication failed...Try again...");
}
// Show received answer
printf("Received bits: ");
nfc.PrintHex(abtRx,&responseLength);
// Succesful transfer
return true;
}
void
iso14443a_crc(uint8_t *pbtData, size_t szLen, uint8_t *pbtCrc)
{
uint8_t bt;
uint32_t wCrc = 0x6363;
do {
bt = *pbtData++;
bt = (bt ^ (uint8_t)(wCrc & 0x00FF));
bt = (bt ^ (bt << 4));
wCrc = (wCrc >> 8) ^ ((uint32_t) bt << 8) ^ ((uint32_t) bt << 3) ^ ((uint32_t) bt >> 4);
} while (--szLen);
*pbtCrc++ = (uint8_t)(wCrc & 0xFF);
*pbtCrc = (uint8_t)((wCrc >> 8) & 0xFF);
}
/**
* @brief Append CRC_A
*
*/
void
iso14443a_crc_append(uint8_t *pbtData, size_t szLen)
{
iso14443a_crc(pbtData, szLen, pbtData + szLen);
}
void loop(void) {
uint8_t i;
abtData[4] = abtData[0] ^ abtData[1] ^ abtData[2] ^ abtData[3];
iso14443a_crc_append(abtData, 16);
// Serial.println("Listening...");
if (nfc.inListPassiveTarget()) {
Serial.println("Something's there...");
memcpy(abtAtqa, abtRx, 2);
// Anti-collision
transmit_bits(abtSelectAll, 2);
// Check answer
if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) {
printf("WARNING: BCC check failed!\n");
}
// Save the UID CL1
memcpy(abtRawUid, abtRx, 4);
//Prepare and send CL1 Select-Command
memcpy(abtSelectTag + 2, abtRx, 5);
iso14443a_crc_append(abtSelectTag, 7);
transmit_bits(abtSelectTag, 9);
abtSak = abtRx[0];
// Test if we are dealing with a CL2
if (abtSak & CASCADE_BIT) {
szCL = 2;//or more
// Check answer
if (abtRawUid[0] != 0x88) {
printf("WARNING: Cascade bit set but CT != 0x88!\n");
}
}
if (szCL == 2) {
// We have to do the anti-collision for cascade level 2
// Prepare CL2 commands
abtSelectAll[0] = 0x95;
// Anti-collision
transmit_bits(abtSelectAll, 2);
// Check answer
if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) {
printf("WARNING: BCC check failed!\n");
}
// Save UID CL2
memcpy(abtRawUid + 4, abtRx, 4);
// Selection
abtSelectTag[0] = 0x95;
memcpy(abtSelectTag + 2, abtRx, 5);
iso14443a_crc_append(abtSelectTag, 7);
transmit_bits(abtSelectTag, 9);
abtSak = abtRx[0];
// Test if we are dealing with a CL3
if (abtSak & CASCADE_BIT) {
szCL = 3;
// Check answer
if (abtRawUid[0] != 0x88) {
printf("WARNING: Cascade bit set but CT != 0x88!\n");
}
}
if (szCL == 3) {
// We have to do the anti-collision for cascade level 3
// Prepare and send CL3 AC-Command
abtSelectAll[0] = 0x97;
transmit_bits(abtSelectAll, 2);
// Check answer
if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) {
printf("WARNING: BCC check failed!\n");
}
// Save UID CL3
memcpy(abtRawUid + 8, abtRx, 4);
// Prepare and send final Select-Command
abtSelectTag[0] = 0x97;
memcpy(abtSelectTag + 2, abtRx, 5);
iso14443a_crc_append(abtSelectTag, 7);
transmit_bits(abtSelectTag, 9);
abtSak = abtRx[0];
}
}
// Request ATS, this only applies to tags that support ISO 14443A-4
if (abtRx[0] & SAK_FLAG_ATS_SUPPORTED) {
iso_ats_supported = true;
}
printf("\nFound tag with\n UID: ");
switch (szCL) {
case 1:
printf("%02x%02x%02x%02x", abtRawUid[0], abtRawUid[1], abtRawUid[2], abtRawUid[3]);
break;
case 2:
printf("%02x%02x%02x", abtRawUid[1], abtRawUid[2], abtRawUid[3]);
printf("%02x%02x%02x%02x", abtRawUid[4], abtRawUid[5], abtRawUid[6], abtRawUid[7]);
break;
case 3:
printf("%02x%02x%02x", abtRawUid[1], abtRawUid[2], abtRawUid[3]);
printf("%02x%02x%02x", abtRawUid[5], abtRawUid[6], abtRawUid[7]);
printf("%02x%02x%02x%02x", abtRawUid[8], abtRawUid[9], abtRawUid[10], abtRawUid[11]);
break;
}
printf("\n");
printf("ATQA: %02x%02x\n SAK: %02x\n", abtAtqa[1], abtAtqa[0], abtSak);
if (szAts > 1) { // if = 1, it's not actual ATS but error code
printf(" ATS: ");
nfc.PrintHex(abtAts, szAts);
}
printf("\n");
// now reset UID
iso14443a_crc_append(abtHalt, 2);
transmit_bits(abtHalt, 4);
transmit_bits(abtUnlock1, 7);
// if (format) {
// transmit_bits(abtWipe, 1);
// transmit_bits(abtHalt, 4);
// transmit_bits(abtUnlock1, 7);
// }
transmit_bits(abtUnlock2, 1);
transmit_bits(abtWrite, 4);
transmit_bits(abtData, 18);
// if (format) {
// for (i = 3 ; i < 64 ; i += 4) {
// abtWrite[1] = (char) i;
// iso14443a_crc_append(abtWrite, 2);
// transmit_bits(abtWrite, 4);
// transmit_bits(abtBlank, 18);
// }
}
else {
Serial.print("Trying again...");
}
}
The code work up until the first transmit_bits command. I get the reponse 0x93 and 0x20 but I afterwards get and error with 0x81 which means there is a problem at the high level. I am thinking I must be missing a command of some sort.
If there are any volunteers for this, your help is much appreciated.