initial commit

This commit is contained in:
2021-10-02 11:41:29 +02:00
commit ae7421a5b3
26 changed files with 3512 additions and 0 deletions

900
PN532.cpp Normal file
View File

@@ -0,0 +1,900 @@
/**************************************************************************/
/*!
@file PN532.cpp
@author Adafruit Industries & Seeed Studio
@license BSD
*/
/**************************************************************************/
#include "Arduino.h"
#include "PN532.h"
#include "PN532_debug.h"
#include <string.h>
#define HAL(func) (_interface->func)
PN532::PN532(PN532Interface &interface)
{
_interface = &interface;
}
/**************************************************************************/
/*!
@brief Setups the HW
*/
/**************************************************************************/
void PN532::begin()
{
HAL(begin)();
HAL(wakeup)();
}
/**************************************************************************/
/*!
@brief Prints a hexadecimal value in plain characters
@param data Pointer to the uint8_t data
@param numBytes Data length in bytes
*/
/**************************************************************************/
void PN532::PrintHex(const uint8_t *data, const uint32_t numBytes)
{
#ifdef ARDUINO
for (uint8_t i = 0; i < numBytes; i++) {
if (data[i] < 0x10) {
Serial.print(" 0");
} else {
Serial.print(' ');
}
Serial.print(data[i], HEX);
}
Serial.println("");
#else
for (uint8_t i = 0; i < numBytes; i++) {
printf(" %2X", data[i]);
}
printf("\n");
#endif
}
/**************************************************************************/
/*!
@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 data
@param numBytes Data length in bytes
*/
/**************************************************************************/
void PN532::PrintHexChar(const uint8_t *data, const uint32_t numBytes)
{
#ifdef ARDUINO
for (uint8_t i = 0; i < numBytes; i++) {
if (data[i] < 0x10) {
Serial.print(" 0");
} else {
Serial.print(' ');
}
Serial.print(data[i], HEX);
}
Serial.print(" ");
for (uint8_t i = 0; i < numBytes; i++) {
char c = data[i];
if (c <= 0x1f || c > 0x7f) {
Serial.print('.');
} else {
Serial.print(c);
}
}
Serial.println("");
#else
for (uint8_t i = 0; i < numBytes; i++) {
printf(" %2X", data[i]);
}
printf(" ");
for (uint8_t i = 0; i < numBytes; i++) {
char c = data[i];
if (c <= 0x1f || c > 0x7f) {
printf(".");
} else {
printf("%c", c);
}
printf("\n");
}
#endif
}
/**************************************************************************/
/*!
@brief Checks the firmware version of the PN5xx chip
@returns The chip's firmware version and ID
*/
/**************************************************************************/
uint32_t PN532::getFirmwareVersion(void)
{
uint32_t response;
pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION;
if (HAL(writeCommand)(pn532_packetbuffer, 1)) {
return 0;
}
// read data packet
int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
if (0 > status) {
return 0;
}
response = pn532_packetbuffer[0];
response <<= 8;
response |= pn532_packetbuffer[1];
response <<= 8;
response |= pn532_packetbuffer[2];
response <<= 8;
response |= pn532_packetbuffer[3];
return response;
}
/**************************************************************************/
/*!
Writes an 8-bit value that sets the state of the PN532's GPIO pins
@warning This function is provided exclusively for board testing and
is dangerous since it will throw an error if any pin other
than the ones marked "Can be used as GPIO" are modified! All
pins that can not be used as GPIO should ALWAYS be left high
(value = 1) or the system will become unstable and a HW reset
will be required to recover the PN532.
pinState[0] = P30 Can be used as GPIO
pinState[1] = P31 Can be used as GPIO
pinState[2] = P32 *** RESERVED (Must be 1!) ***
pinState[3] = P33 Can be used as GPIO
pinState[4] = P34 *** RESERVED (Must be 1!) ***
pinState[5] = P35 Can be used as GPIO
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
bool PN532::writeGPIO(uint8_t pinstate)
{
// Make sure pinstate does not try to toggle P32 or P34
pinstate |= (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
// Fill command buffer
pn532_packetbuffer[0] = PN532_COMMAND_WRITEGPIO;
pn532_packetbuffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate; // P3 Pins
pn532_packetbuffer[2] = 0x00; // P7 GPIO Pins (not used ... taken by I2C)
DMSG("Writing P3 GPIO: ");
DMSG_HEX(pn532_packetbuffer[1]);
DMSG("\n");
// Send the WRITEGPIO command (0x0E)
if (HAL(writeCommand)(pn532_packetbuffer, 3))
return 0;
return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)));
}
/**************************************************************************/
/*!
Reads the state of the PN532's GPIO pins
@returns An 8-bit value containing the pin state where:
pinState[0] = P30
pinState[1] = P31
pinState[2] = P32
pinState[3] = P33
pinState[4] = P34
pinState[5] = P35
*/
/**************************************************************************/
uint8_t PN532::readGPIO(void)
{
pn532_packetbuffer[0] = PN532_COMMAND_READGPIO;
// Send the READGPIO command (0x0C)
if (HAL(writeCommand)(pn532_packetbuffer, 1))
return 0x0;
HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
/* READGPIO response without prefix and suffix should be in the following format:
byte Description
------------- ------------------------------------------
b0 P3 GPIO Pins
b1 P7 GPIO Pins (not used ... taken by I2C)
b2 Interface Mode Pins (not used ... bus select pins)
*/
DMSG("P3 GPIO: "); DMSG_HEX(pn532_packetbuffer[7]);
DMSG("P7 GPIO: "); DMSG_HEX(pn532_packetbuffer[8]);
DMSG("I0I1 GPIO: "); DMSG_HEX(pn532_packetbuffer[9]);
DMSG("\n");
return pn532_packetbuffer[0];
}
/**************************************************************************/
/*!
@brief Configures the SAM (Secure Access Module)
*/
/**************************************************************************/
bool PN532::SAMConfig(void)
{
pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION;
pn532_packetbuffer[1] = 0x01; // normal mode;
pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second
pn532_packetbuffer[3] = 0x01; // use IRQ pin!
DMSG("SAMConfig\n");
if (HAL(writeCommand)(pn532_packetbuffer, 4))
return false;
return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)));
}
/**************************************************************************/
/*!
Sets the MxRtyPassiveActivation uint8_t 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] = PN532_COMMAND_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 (HAL(writeCommand)(pn532_packetbuffer, 5))
return 0x0; // no ACK
return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)));
}
/***** 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.
@param timeout The number of tries before timing out
@param inlist If set to true, the card will be inlisted
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
bool PN532::readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout, bool inlist)
{
pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET;
pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later)
pn532_packetbuffer[2] = cardbaudrate;
if (HAL(writeCommand)(pn532_packetbuffer, 3)) {
return 0x0; // command failed
}
// read data packet
if (HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) {
return 0x0;
}
// check some basic stuff
/* ISO14443A card response should be in the following format:
byte Description
------------- ------------------------------------------
b0 Tags Found
b1 Tag Number (only one used in this example)
b2..3 SENS_RES
b4 SEL_RES
b5 NFCID Length
b6..NFCIDLen NFCID
*/
if (pn532_packetbuffer[0] != 1)
return 0;
uint16_t sens_res = pn532_packetbuffer[2];
sens_res <<= 8;
sens_res |= pn532_packetbuffer[3];
DMSG("ATQA: 0x"); DMSG_HEX(sens_res);
DMSG("SAK: 0x"); DMSG_HEX(pn532_packetbuffer[4]);
DMSG("\n");
/* Card appears to be Mifare Classic */
*uidLength = pn532_packetbuffer[5];
for (uint8_t i = 0; i < pn532_packetbuffer[5]; i++) {
uid[i] = pn532_packetbuffer[6 + i];
}
if (inlist) {
inListedTag = pn532_packetbuffer[1];
}
return 1;
}
/***** Mifare Classic Functions ******/
/**************************************************************************/
/*!
Indicates whether the specified block number is the first block
in the sector (block 0 relative to the current sector)
*/
/**************************************************************************/
bool PN532::mifareclassic_IsFirstBlock (uint32_t uiBlock)
{
// Test if we are in the small or big sectors
if (uiBlock < 128)
return ((uiBlock) % 4 == 0);
else
return ((uiBlock) % 16 == 0);
}
/**************************************************************************/
/*!
Indicates whether the specified block number is the sector trailer
*/
/**************************************************************************/
bool PN532::mifareclassic_IsTrailerBlock (uint32_t uiBlock)
{
// Test if we are in the small or big sectors
if (uiBlock < 128)
return ((uiBlock + 1) % 4 == 0);
else
return ((uiBlock + 1) % 16 == 0);
}
/**************************************************************************/
/*!
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 bytes
key value
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
uint8_t PN532::mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData)
{
uint8_t i;
// Hang on to the key and uid data
memcpy (_key, keyData, 6);
memcpy (_uid, uid, uidLen);
_uidLen = uidLen;
// Prepare the authentication command //
pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */
pn532_packetbuffer[1] = 1; /* Max card numbers */
pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A;
pn532_packetbuffer[3] = blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */
memcpy (pn532_packetbuffer + 4, _key, 6);
for (i = 0; i < _uidLen; i++) {
pn532_packetbuffer[10 + i] = _uid[i]; /* 4 bytes card ID */
}
if (HAL(writeCommand)(pn532_packetbuffer, 10 + _uidLen))
return 0;
// Read the response packet
HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
// Check if the response is valid and we are authenticated???
// for an auth success it should be bytes 5-7: 0xD5 0x41 0x00
// Mifare auth error is technically byte 7: 0x14 but anything other and 0x00 is not good
if (pn532_packetbuffer[0] != 0x00) {
DMSG("Authentification failed\n");
return 0;
}
return 1;
}
/**************************************************************************/
/*!
Tries to read an entire 16-bytes data block at the specified block
address.
@param blockNumber The block number to authenticate. (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)
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
uint8_t PN532::mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data)
{
DMSG("Trying to read 16 bytes from block ");
DMSG_INT(blockNumber);
/* Prepare the command */
pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE;
pn532_packetbuffer[1] = 1; /* Card number */
pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */
pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */
/* Send the command */
if (HAL(writeCommand)(pn532_packetbuffer, 4)) {
return 0;
}
/* Read the response packet */
HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
/* If byte 8 isn't 0x00 we probably have an error */
if (pn532_packetbuffer[0] != 0x00) {
return 0;
}
/* Copy the 16 data bytes to the output buffer */
/* Block content starts at byte 9 of a valid response */
memcpy (data, pn532_packetbuffer + 1, 16);
return 1;
}
/**************************************************************************/
/*!
Tries to write an entire 16-bytes data block at the specified block
address.
@param blockNumber The block number to authenticate. (0..63 for
1KB cards, and 0..255 for 4KB cards).
@param data The byte array that contains the data to write.
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
uint8_t PN532::mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data)
{
/* Prepare the first command */
pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE;
pn532_packetbuffer[1] = 1; /* Card number */
pn532_packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */
pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */
memcpy (pn532_packetbuffer + 4, data, 16); /* Data Payload */
/* Send the command */
if (HAL(writeCommand)(pn532_packetbuffer, 20)) {
return 0;
}
/* Read the response packet */
return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)));
}
/**************************************************************************/
/*!
Formats a Mifare Classic card to store NDEF Records
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
uint8_t PN532::mifareclassic_FormatNDEF (void)
{
uint8_t sectorbuffer1[16] = {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1};
uint8_t sectorbuffer2[16] = {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1};
uint8_t sectorbuffer3[16] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// Note 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 must be used for key A
// for the MAD sector in NDEF records (sector 0)
// Write block 1 and 2 to the card
if (!(mifareclassic_WriteDataBlock (1, sectorbuffer1)))
return 0;
if (!(mifareclassic_WriteDataBlock (2, sectorbuffer2)))
return 0;
// Write key A and access rights card
if (!(mifareclassic_WriteDataBlock (3, sectorbuffer3)))
return 0;
// Seems that everything was OK (?!)
return 1;
}
/**************************************************************************/
/*!
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 (0 = none, 0x01 =
"http://www.", etc.)
@param url The uri text to write (max 38 characters).
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
uint8_t PN532::mifareclassic_WriteNDEFURI (uint8_t sectorNumber, uint8_t uriIdentifier, const char *url)
{
// Figure out how long the string is
uint8_t len = strlen(url);
// Make sure we're within a 1K limit for the sector number
if ((sectorNumber < 1) || (sectorNumber > 15))
return 0;
// Make sure the URI payload is between 1 and 38 chars
if ((len < 1) || (len > 38))
return 0;
// 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 - 23);
sectorbuffer3[len - 23] = 0xFE;
}
// Now write all three blocks back to the card
if (!(mifareclassic_WriteDataBlock (sectorNumber * 4, sectorbuffer1)))
return 0;
if (!(mifareclassic_WriteDataBlock ((sectorNumber * 4) + 1, sectorbuffer2)))
return 0;
if (!(mifareclassic_WriteDataBlock ((sectorNumber * 4) + 2, sectorbuffer3)))
return 0;
if (!(mifareclassic_WriteDataBlock ((sectorNumber * 4) + 3, sectorbuffer4)))
return 0;
// Seems that everything was OK (?!)
return 1;
}
/***** Mifare Ultralight Functions ******/
/**************************************************************************/
/*!
Tries to read an entire 4-bytes 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)
*/
/**************************************************************************/
uint8_t PN532::mifareultralight_ReadPage (uint8_t page, uint8_t *buffer)
{
/* Prepare the command */
pn532_packetbuffer[0] = PN532_COMMAND_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 (HAL(writeCommand)(pn532_packetbuffer, 4)) {
return 0;
}
/* Read the response packet */
HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
/* If byte 8 isn't 0x00 we probably have an error */
if (pn532_packetbuffer[0] == 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 bytes or 4 */
/* pages at a time ... we simply discard the last 12 */
/* bytes */
memcpy (buffer, pn532_packetbuffer + 1, 4);
} else {
return 0;
}
// Return OK signal
return 1;
}
/**************************************************************************/
/*!
Tries to write an entire 4-bytes data buffer at the specified page
address.
@param page The page number to write into. (0..63).
@param buffer The byte array that contains the data to write.
@returns 1 if everything executed properly, 0 for an error
*/
/**************************************************************************/
uint8_t PN532::mifareultralight_WritePage (uint8_t page, uint8_t *buffer)
{
/* Prepare the first command */
pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE;
pn532_packetbuffer[1] = 1; /* Card number */
pn532_packetbuffer[2] = MIFARE_CMD_WRITE_ULTRALIGHT; /* Mifare UL Write cmd = 0xA2 */
pn532_packetbuffer[3] = page; /* page Number (0..63) */
memcpy (pn532_packetbuffer + 4, buffer, 4); /* Data Payload */
/* Send the command */
if (HAL(writeCommand)(pn532_packetbuffer, 8)) {
return 0;
}
/* Read the response packet */
return (0 < HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer)));
}
/**************************************************************************/
/*!
@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)
{
uint8_t i;
pn532_packetbuffer[0] = 0x40; // PN532_COMMAND_INDATAEXCHANGE;
pn532_packetbuffer[1] = inListedTag;
if (HAL(writeCommand)(pn532_packetbuffer, 2, send, sendLength)) {
return false;
}
int16_t status = HAL(readResponse)(response, *responseLength, 1000);
if (status < 0) {
return false;
}
if ((response[0] & 0x3f) != 0) {
DMSG("Status code indicates an error\n");
return false;
}
uint8_t length = status;
length -= 1;
if (length > *responseLength) {
length = *responseLength; // silent truncation...
}
for (uint8_t i = 0; i < length; i++) {
response[i] = response[i + 1];
}
*responseLength = length;
return true;
}
/**************************************************************************/
/*!
@brief 'InLists' a passive target. PN532 acting as reader/initiator,
peer acting as card/responder.
*/
/**************************************************************************/
bool PN532::inListPassiveTarget()
{
pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET;
pn532_packetbuffer[1] = 1;
pn532_packetbuffer[2] = 0;
DMSG("inList passive target\n");
if (HAL(writeCommand)(pn532_packetbuffer, 3)) {
return false;
}
int16_t status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 30000);
if (status < 0) {
return false;
}
if (pn532_packetbuffer[0] != 1) {
return false;
}
inListedTag = pn532_packetbuffer[1];
return true;
}
int8_t PN532::tgInitAsTarget(const uint8_t* command, const uint8_t len, const uint16_t timeout){
int8_t status = HAL(writeCommand)(command, len);
if (status < 0) {
return -1;
}
status = HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout);
if (status > 0) {
return 1;
} else if (PN532_TIMEOUT == status) {
return 0;
} else {
return -2;
}
}
/**
* Peer to Peer
*/
int8_t PN532::tgInitAsTarget(uint16_t timeout)
{
const uint8_t command[] = {
PN532_COMMAND_TGINITASTARGET,
0,
0x00, 0x00, //SENS_RES
0x00, 0x00, 0x00, //NFCID1
0x40, //SEL_RES
0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, // POL_RES
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF,
0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, 0x00, 0x00, //NFCID3t: Change this to desired value
0x0a, 0x46, 0x66, 0x6D, 0x01, 0x01, 0x10, 0x02, 0x02, 0x00, 0x80, // LLCP magic number, version parameter and MIUX
0x00
};
return tgInitAsTarget(command, sizeof(command), timeout);
}
int16_t PN532::tgGetData(uint8_t *buf, uint8_t len)
{
buf[0] = PN532_COMMAND_TGGETDATA;
if (HAL(writeCommand)(buf, 1)) {
return -1;
}
int16_t status = HAL(readResponse)(buf, len, 3000);
if (0 >= status) {
return status;
}
uint16_t length = status - 1;
if (buf[0] != 0) {
DMSG("status is not ok\n");
return -5;
}
for (uint8_t i = 0; i < length; i++) {
buf[i] = buf[i + 1];
}
return length;
}
bool PN532::tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{
if (hlen > (sizeof(pn532_packetbuffer) - 1)) {
if ((body != 0) || (header == pn532_packetbuffer)) {
DMSG("tgSetData:buffer too small\n");
return false;
}
pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA;
if (HAL(writeCommand)(pn532_packetbuffer, 1, header, hlen)) {
return false;
}
} else {
for (int8_t i = hlen - 1; i >= 0; i--){
pn532_packetbuffer[i + 1] = header[i];
}
pn532_packetbuffer[0] = PN532_COMMAND_TGSETDATA;
if (HAL(writeCommand)(pn532_packetbuffer, hlen + 1, body, blen)) {
return false;
}
}
if (0 > HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 3000)) {
return false;
}
if (0 != pn532_packetbuffer[0]) {
return false;
}
return true;
}
int16_t PN532::inRelease(const uint8_t relevantTarget){
pn532_packetbuffer[0] = PN532_COMMAND_INRELEASE;
pn532_packetbuffer[1] = relevantTarget;
if (HAL(writeCommand)(pn532_packetbuffer, 2)) {
return 0;
}
// read data packet
return HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
}
uint8_t PN532::ntag21x_auth(const uint8_t *key)
{
uint8_t i;
// Prepare the authentication command //
pn532_packetbuffer[0] = 0x42;
pn532_packetbuffer[1] = 0x1B;
memcpy (pn532_packetbuffer + 2, key, 4);
if (HAL(writeCommand)(pn532_packetbuffer, 6))
return 0;
// Read the response packet
HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer));
// Check if the response is valid and we are authenticated???
if (pn532_packetbuffer[0] != 0x00) {
DMSG("\nAuthentification failed\n");
return 0;
}
return 1;
}

181
PN532.h Normal file
View File

@@ -0,0 +1,181 @@
/**************************************************************************/
/*!
@file PN532.h
@author Adafruit Industries & Seeed Studio
@license BSD
*/
/**************************************************************************/
#ifndef __PN532_H__
#define __PN532_H__
#include <stdint.h>
#include "PN532Interface.h"
// PN532 Commands
#define PN532_COMMAND_DIAGNOSE (0x00)
#define PN532_COMMAND_GETFIRMWAREVERSION (0x02)
#define PN532_COMMAND_GETGENERALSTATUS (0x04)
#define PN532_COMMAND_READREGISTER (0x06)
#define PN532_COMMAND_WRITEREGISTER (0x08)
#define PN532_COMMAND_READGPIO (0x0C)
#define PN532_COMMAND_WRITEGPIO (0x0E)
#define PN532_COMMAND_SETSERIALBAUDRATE (0x10)
#define PN532_COMMAND_SETPARAMETERS (0x12)
#define PN532_COMMAND_SAMCONFIGURATION (0x14)
#define PN532_COMMAND_POWERDOWN (0x16)
#define PN532_COMMAND_RFCONFIGURATION (0x32)
#define PN532_COMMAND_RFREGULATIONTEST (0x58)
#define PN532_COMMAND_INJUMPFORDEP (0x56)
#define PN532_COMMAND_INJUMPFORPSL (0x46)
#define PN532_COMMAND_INLISTPASSIVETARGET (0x4A)
#define PN532_COMMAND_INATR (0x50)
#define PN532_COMMAND_INPSL (0x4E)
#define PN532_COMMAND_INDATAEXCHANGE (0x40)
#define PN532_COMMAND_INCOMMUNICATETHRU (0x42)
#define PN532_COMMAND_INDESELECT (0x44)
#define PN532_COMMAND_INRELEASE (0x52)
#define PN532_COMMAND_INSELECT (0x54)
#define PN532_COMMAND_INAUTOPOLL (0x60)
#define PN532_COMMAND_TGINITASTARGET (0x8C)
#define PN532_COMMAND_TGSETGENERALBYTES (0x92)
#define PN532_COMMAND_TGGETDATA (0x86)
#define PN532_COMMAND_TGSETDATA (0x8E)
#define PN532_COMMAND_TGSETMETADATA (0x94)
#define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88)
#define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90)
#define PN532_COMMAND_TGGETTARGETSTATUS (0x8A)
#define PN532_RESPONSE_INDATAEXCHANGE (0x41)
#define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B)
#define PN532_MIFARE_ISO14443A (0x00)
// Mifare Commands
#define MIFARE_CMD_AUTH_A (0x60)
#define MIFARE_CMD_AUTH_B (0x61)
#define MIFARE_CMD_READ (0x30)
#define MIFARE_CMD_WRITE (0xA0)
#define MIFARE_CMD_WRITE_ULTRALIGHT (0xA2)
#define MIFARE_CMD_TRANSFER (0xB0)
#define MIFARE_CMD_DECREMENT (0xC0)
#define MIFARE_CMD_INCREMENT (0xC1)
#define MIFARE_CMD_STORE (0xC2)
// Prefixes for NDEF Records (to identify record type)
#define NDEF_URIPREFIX_NONE (0x00)
#define NDEF_URIPREFIX_HTTP_WWWDOT (0x01)
#define NDEF_URIPREFIX_HTTPS_WWWDOT (0x02)
#define NDEF_URIPREFIX_HTTP (0x03)
#define NDEF_URIPREFIX_HTTPS (0x04)
#define NDEF_URIPREFIX_TEL (0x05)
#define NDEF_URIPREFIX_MAILTO (0x06)
#define NDEF_URIPREFIX_FTP_ANONAT (0x07)
#define NDEF_URIPREFIX_FTP_FTPDOT (0x08)
#define NDEF_URIPREFIX_FTPS (0x09)
#define NDEF_URIPREFIX_SFTP (0x0A)
#define NDEF_URIPREFIX_SMB (0x0B)
#define NDEF_URIPREFIX_NFS (0x0C)
#define NDEF_URIPREFIX_FTP (0x0D)
#define NDEF_URIPREFIX_DAV (0x0E)
#define NDEF_URIPREFIX_NEWS (0x0F)
#define NDEF_URIPREFIX_TELNET (0x10)
#define NDEF_URIPREFIX_IMAP (0x11)
#define NDEF_URIPREFIX_RTSP (0x12)
#define NDEF_URIPREFIX_URN (0x13)
#define NDEF_URIPREFIX_POP (0x14)
#define NDEF_URIPREFIX_SIP (0x15)
#define NDEF_URIPREFIX_SIPS (0x16)
#define NDEF_URIPREFIX_TFTP (0x17)
#define NDEF_URIPREFIX_BTSPP (0x18)
#define NDEF_URIPREFIX_BTL2CAP (0x19)
#define NDEF_URIPREFIX_BTGOEP (0x1A)
#define NDEF_URIPREFIX_TCPOBEX (0x1B)
#define NDEF_URIPREFIX_IRDAOBEX (0x1C)
#define NDEF_URIPREFIX_FILE (0x1D)
#define NDEF_URIPREFIX_URN_EPC_ID (0x1E)
#define NDEF_URIPREFIX_URN_EPC_TAG (0x1F)
#define NDEF_URIPREFIX_URN_EPC_PAT (0x20)
#define NDEF_URIPREFIX_URN_EPC_RAW (0x21)
#define NDEF_URIPREFIX_URN_EPC (0x22)
#define NDEF_URIPREFIX_URN_NFC (0x23)
#define PN532_GPIO_VALIDATIONBIT (0x80)
#define PN532_GPIO_P30 (0)
#define PN532_GPIO_P31 (1)
#define PN532_GPIO_P32 (2)
#define PN532_GPIO_P33 (3)
#define PN532_GPIO_P34 (4)
#define PN532_GPIO_P35 (5)
class PN532
{
public:
PN532(PN532Interface &interface);
void begin(void);
// Generic PN532 functions
bool SAMConfig(void);
uint32_t getFirmwareVersion(void);
bool writeGPIO(uint8_t pinstate);
uint8_t readGPIO(void);
bool setPassiveActivationRetries(uint8_t maxRetries);
/**
* @brief Init PN532 as a target
* @param timeout max time to wait, 0 means no timeout
* @return > 0 success
* = 0 timeout
* < 0 failed
*/
int8_t tgInitAsTarget(uint16_t timeout = 0);
int8_t tgInitAsTarget(const uint8_t* command, const uint8_t len, const uint16_t timeout = 0);
int16_t tgGetData(uint8_t *buf, uint8_t len);
bool tgSetData(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0);
int16_t inRelease(const uint8_t relevantTarget = 0);
// ISO14443A functions
bool inListPassiveTarget();
bool readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 1000, bool inlist = false);
bool inDataExchange(uint8_t *send, uint8_t sendLength, uint8_t *response, uint8_t *responseLength);
// Mifare Classic functions
bool mifareclassic_IsFirstBlock (uint32_t uiBlock);
bool mifareclassic_IsTrailerBlock (uint32_t uiBlock);
uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData);
uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data);
uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data);
uint8_t mifareclassic_FormatNDEF (void);
uint8_t mifareclassic_WriteNDEFURI (uint8_t sectorNumber, uint8_t uriIdentifier, const char *url);
// Mifare Ultralight functions
uint8_t mifareultralight_ReadPage (uint8_t page, uint8_t *buffer);
uint8_t mifareultralight_WritePage (uint8_t page, uint8_t *buffer);
uint8_t ntag21x_auth(const uint8_t *key);
// Help functions to display formatted text
static void PrintHex(const uint8_t *data, const uint32_t numBytes);
static void PrintHexChar(const uint8_t *pbtData, const uint32_t numBytes);
uint8_t *getBuffer(uint8_t *len) {
*len = sizeof(pn532_packetbuffer) - 4;
return pn532_packetbuffer;
};
private:
uint8_t _uid[7]; // ISO14443A uid
uint8_t _uidLen; // uid len
uint8_t _key[6]; // Mifare Classic key
uint8_t inListedTag; // Tg number of inlisted tag.
uint8_t pn532_packetbuffer[64];
PN532Interface *_interface;
};
#endif

56
PN532Interface.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef __PN532_INTERFACE_H__
#define __PN532_INTERFACE_H__
#include <stdint.h>
#define PN532_PREAMBLE (0x00)
#define PN532_STARTCODE1 (0x00)
#define PN532_STARTCODE2 (0xFF)
#define PN532_POSTAMBLE (0x00)
#define PN532_HOSTTOPN532 (0xD4)
#define PN532_PN532TOHOST (0xD5)
#define PN532_ACK_WAIT_TIME (10) // ms, timeout of waiting for ACK
#define PN532_INVALID_ACK (-1)
#define PN532_TIMEOUT (-2)
#define PN532_INVALID_FRAME (-3)
#define PN532_NO_SPACE (-4)
#define REVERSE_BITS_ORDER(b) b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; \
b = (b & 0xCC) >> 2 | (b & 0x33) << 2; \
b = (b & 0xAA) >> 1 | (b & 0x55) << 1
class PN532Interface
{
public:
virtual void begin() = 0;
virtual void wakeup() = 0;
/**
* @brief write a command and check ack
* @param header packet header
* @param hlen length of header
* @param body packet body
* @param blen length of body
* @return 0 success
* not 0 failed
*/
virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) = 0;
/**
* @brief read the response of a command, strip prefix and suffix
* @param buf to contain the response data
* @param len lenght to read
* @param timeout max time to wait, 0 means no timeout
* @return >=0 length of response without prefix and suffix
* <0 failed to read response
*/
virtual int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 1000) = 0;
};
#endif

20
PN532_debug.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef __DEBUG_H__
#define __DEBUG_H__
//#define DEBUG
#include "Arduino.h"
#ifdef DEBUG
#define DMSG(args...) Serial.print(args)
#define DMSG_STR(str) Serial.println(str)
#define DMSG_HEX(num) Serial.print(' '); Serial.print(num, HEX)
#define DMSG_INT(num) Serial.print(' '); Serial.print(num)
#else
#define DMSG(args...)
#define DMSG_STR(str)
#define DMSG_HEX(num)
#define DMSG_INT(num)
#endif
#endif

30
README.md Normal file
View File

@@ -0,0 +1,30 @@
## NFC library for Arduino
This is an Arduino library for PN532 to use NFC technology.
It works with
+ [NFC Shield](http://goo.gl/Cac2OH)
+ [Xadow NFC](http://goo.gl/qBZMt0)
+ [PN532 NFC/RFID controller breakout board](http://goo.gl/tby9Sw)
### Features
+ Support I2C, SPI and HSU of PN532
+ Read/write Mifare Classic Card
+ Works with [Don's NDEF Library](http://goo.gl/jDjsXl)
+ Support Peer to Peer communication(exchange data with android 4.0+)
+ Support [mbed platform](http://goo.gl/kGPovZ)
### Getting Started
1. Download [zip file](http://goo.gl/F6beRM) and
extract the 4 folders(PN532, PN532_SPI, PN532_I2C and PN532_HSU) into Arduino's libraries.
2. Downlaod [Don's NDEF library](http://goo.gl/ewxeAe) and extract it intro Arduino's libraries.
3. Follow the examples of the two libraries.
### To do
+ Card emulation
### Contribution
It's based on [Adafruit_NFCShield_I2C](http://goo.gl/pk3FdB).
[Seeed Studio](http://goo.gl/zh1iQh) adds SPI interface and peer to peer communication support.
@Don writes the [NDEF library](http://goo.gl/jDjsXl) to make it more easy to use.
@JiapengLi adds HSU interface.

261
emulatetag.cpp Normal file
View File

@@ -0,0 +1,261 @@
/**************************************************************************/
/*!
@file emulatetag.cpp
@author Armin Wieser
@license BSD
*/
/**************************************************************************/
#include "emulatetag.h"
#include "PN532_debug.h"
#include <string.h>
#define MAX_TGREAD
// Command APDU
#define C_APDU_CLA 0
#define C_APDU_INS 1 // instruction
#define C_APDU_P1 2 // parameter 1
#define C_APDU_P2 3 // parameter 2
#define C_APDU_LC 4 // length command
#define C_APDU_DATA 5 // data
#define C_APDU_P1_SELECT_BY_ID 0x00
#define C_APDU_P1_SELECT_BY_NAME 0x04
// Response APDU
#define R_APDU_SW1_COMMAND_COMPLETE 0x90
#define R_APDU_SW2_COMMAND_COMPLETE 0x00
#define R_APDU_SW1_NDEF_TAG_NOT_FOUND 0x6a
#define R_APDU_SW2_NDEF_TAG_NOT_FOUND 0x82
#define R_APDU_SW1_FUNCTION_NOT_SUPPORTED 0x6A
#define R_APDU_SW2_FUNCTION_NOT_SUPPORTED 0x81
#define R_APDU_SW1_MEMORY_FAILURE 0x65
#define R_APDU_SW2_MEMORY_FAILURE 0x81
#define R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x62
#define R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES 0x82
// ISO7816-4 commands
#define ISO7816_SELECT_FILE 0xA4
#define ISO7816_READ_BINARY 0xB0
#define ISO7816_UPDATE_BINARY 0xD6
typedef enum { NONE, CC, NDEF } tag_file; // CC ... Compatibility Container
bool EmulateTag::init(){
pn532.begin();
return pn532.SAMConfig();
}
void EmulateTag::setNdefFile(const uint8_t* ndef, const int16_t ndefLength){
if(ndefLength > (NDEF_MAX_LENGTH -2)){
DMSG("ndef file too large (> NDEF_MAX_LENGHT -2) - aborting");
return;
}
ndef_file[0] = ndefLength >> 8;
ndef_file[1] = ndefLength & 0xFF;
memcpy(ndef_file+2, ndef, ndefLength);
}
void EmulateTag::setUid(uint8_t* uid){
uidPtr = uid;
}
bool EmulateTag::emulate(const uint16_t tgInitAsTargetTimeout){
// http://www.nxp.com/documents/application_note/AN133910.pdf
uint8_t command[] = {
PN532_COMMAND_TGINITASTARGET,
0x05, // MODE: PICC only, Passive only
0x04, 0x00, // SENS_RES
0x00, 0x00, 0x00, // NFCID1
0x20, // SEL_RES
0x01, 0xFE, // Parameters to build POL_RES
0xA2, 0xA3, 0xA4,
0xA5, 0xA6, 0xA7,
0xC0, 0xC1, 0xC2,
0xC3, 0xC4, 0xC5,
0xC6, 0xC7, 0xFF,
0xFF,
0xAA, 0x99, 0x88, //NFCID3t (10 bytes)
0x77, 0x66, 0x55, 0x44,
0x33, 0x22, 0x11,
0, // length of general bytes
0 // length of historical bytes
};
if(uidPtr != 0){ // if uid is set copy 3 bytes to nfcid1
memcpy(command + 4, uidPtr, 3);
}
if(1 != pn532.tgInitAsTarget(command,sizeof(command), tgInitAsTargetTimeout)){
DMSG("tgInitAsTarget failed or timed out!");
return false;
}
uint8_t compatibility_container[] = {
0, 0x0F,
0x20,
0, 0x54,
0, 0xFF,
0x04, // T
0x06, // L
0xE1, 0x04, // File identifier
((NDEF_MAX_LENGTH & 0xFF00) >> 8), (NDEF_MAX_LENGTH & 0xFF), // maximum NDEF file size
0x00, // read access 0x0 = granted
0x00 // write access 0x0 = granted | 0xFF = deny
};
if(tagWriteable == false){
compatibility_container[14] = 0xFF;
}
tagWrittenByInitiator = false;
uint8_t rwbuf[128];
uint8_t sendlen;
int16_t status;
tag_file currentFile = NONE;
uint16_t cc_size = sizeof(compatibility_container);
bool runLoop = true;
while(runLoop){
status = pn532.tgGetData(rwbuf, sizeof(rwbuf));
if(status < 0){
DMSG("tgGetData failed!\n");
pn532.inRelease();
return true;
}
uint8_t p1 = rwbuf[C_APDU_P1];
uint8_t p2 = rwbuf[C_APDU_P2];
uint8_t lc = rwbuf[C_APDU_LC];
uint16_t p1p2_length = ((int16_t) p1 << 8) + p2;
switch(rwbuf[C_APDU_INS]){
case ISO7816_SELECT_FILE:
switch(p1){
case C_APDU_P1_SELECT_BY_ID:
if(p2 != 0x0c){
DMSG("C_APDU_P2 != 0x0c\n");
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
} else if(lc == 2 && rwbuf[C_APDU_DATA] == 0xE1 && (rwbuf[C_APDU_DATA+1] == 0x03 || rwbuf[C_APDU_DATA+1] == 0x04)){
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
if(rwbuf[C_APDU_DATA+1] == 0x03){
currentFile = CC;
} else if(rwbuf[C_APDU_DATA+1] == 0x04){
currentFile = NDEF;
}
} else {
setResponse(TAG_NOT_FOUND, rwbuf, &sendlen);
}
break;
case C_APDU_P1_SELECT_BY_NAME:
const uint8_t ndef_tag_application_name_v2[] = {0, 0x7, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 };
if(0 == memcmp(ndef_tag_application_name_v2, rwbuf + C_APDU_P2, sizeof(ndef_tag_application_name_v2))){
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
} else{
DMSG("function not supported\n");
setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
}
break;
}
break;
case ISO7816_READ_BINARY:
switch(currentFile){
case NONE:
setResponse(TAG_NOT_FOUND, rwbuf, &sendlen);
break;
case CC:
if( p1p2_length > NDEF_MAX_LENGTH){
setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen);
}else {
memcpy(rwbuf,compatibility_container + p1p2_length, lc);
setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);
}
break;
case NDEF:
if( p1p2_length > NDEF_MAX_LENGTH){
setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen);
}else {
memcpy(rwbuf, ndef_file + p1p2_length, lc);
setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);
}
break;
}
break;
case ISO7816_UPDATE_BINARY:
if(!tagWriteable){
setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
} else{
if( p1p2_length > NDEF_MAX_LENGTH){
setResponse(MEMORY_FAILURE, rwbuf, &sendlen);
}
else{
memcpy(ndef_file + p1p2_length, rwbuf + C_APDU_DATA, lc);
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
tagWrittenByInitiator = true;
uint16_t ndef_length = (ndef_file[0] << 8) + ndef_file[1];
if ((ndef_length > 0) && (updateNdefCallback != 0)) {
updateNdefCallback(ndef_file + 2, ndef_length);
}
}
}
break;
default:
DMSG("Command not supported!");
DMSG_HEX(rwbuf[C_APDU_INS]);
DMSG("\n");
setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
}
status = pn532.tgSetData(rwbuf, sendlen);
if(status < 0){
DMSG("tgSetData failed\n!");
pn532.inRelease();
return true;
}
}
pn532.inRelease();
return true;
}
void EmulateTag::setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset){
switch(cmd){
case COMMAND_COMPLETE:
buf[0] = R_APDU_SW1_COMMAND_COMPLETE;
buf[1] = R_APDU_SW2_COMMAND_COMPLETE;
*sendlen = 2 + sendlenOffset;
break;
case TAG_NOT_FOUND:
buf[0] = R_APDU_SW1_NDEF_TAG_NOT_FOUND;
buf[1] = R_APDU_SW2_NDEF_TAG_NOT_FOUND;
*sendlen = 2;
break;
case FUNCTION_NOT_SUPPORTED:
buf[0] = R_APDU_SW1_FUNCTION_NOT_SUPPORTED;
buf[1] = R_APDU_SW2_FUNCTION_NOT_SUPPORTED;
*sendlen = 2;
break;
case MEMORY_FAILURE:
buf[0] = R_APDU_SW1_MEMORY_FAILURE;
buf[1] = R_APDU_SW2_MEMORY_FAILURE;
*sendlen = 2;
break;
case END_OF_FILE_BEFORE_REACHED_LE_BYTES:
buf[0] = R_APDU_SW1_END_OF_FILE_BEFORE_REACHED_LE_BYTES;
buf[1] = R_APDU_SW2_END_OF_FILE_BEFORE_REACHED_LE_BYTES;
*sendlen= 2;
break;
}
}

71
emulatetag.h Normal file
View File

@@ -0,0 +1,71 @@
/**************************************************************************/
/*!
@file emulatetag.h
@author Armin Wieser
@license BSD
Implemented using NFC forum documents & library of libnfc
*/
/**************************************************************************/
#ifndef __EMULATETAG_H__
#define __EMULATETAG_H__
#include "PN532.h"
#define NDEF_MAX_LENGTH 128 // altough ndef can handle up to 0xfffe in size, arduino cannot.
typedef enum {COMMAND_COMPLETE, TAG_NOT_FOUND, FUNCTION_NOT_SUPPORTED, MEMORY_FAILURE, END_OF_FILE_BEFORE_REACHED_LE_BYTES} responseCommand;
class EmulateTag{
public:
EmulateTag(PN532Interface &interface) : pn532(interface), uidPtr(0), tagWrittenByInitiator(false), tagWriteable(true), updateNdefCallback(0) { }
bool init();
bool emulate(const uint16_t tgInitAsTargetTimeout = 0);
/*
* @param uid pointer to byte array of length 3 (uid is 4 bytes - first byte is fixed) or zero for uid
*/
void setUid(uint8_t* uid = 0);
void setNdefFile(const uint8_t* ndef, const int16_t ndefLength);
void getContent(uint8_t** buf, uint16_t* length){
*buf = ndef_file + 2; // first 2 bytes = length
*length = (ndef_file[0] << 8) + ndef_file[1];
}
bool writeOccured(){
return tagWrittenByInitiator;
}
void setTagWriteable(bool setWriteable){
tagWriteable = setWriteable;
}
uint8_t* getNdefFilePtr(){
return ndef_file;
}
uint8_t getNdefMaxLength(){
return NDEF_MAX_LENGTH;
}
void attach(void (*func)(uint8_t *buf, uint16_t length)) {
updateNdefCallback = func;
};
private:
PN532 pn532;
uint8_t ndef_file[NDEF_MAX_LENGTH];
uint8_t* uidPtr;
bool tagWrittenByInitiator;
bool tagWriteable;
void (*updateNdefCallback)(uint8_t *ndef, uint16_t length);
void setResponse(responseCommand cmd, uint8_t* buf, uint8_t* sendlen, uint8_t sendlenOffset = 0);
};
#endif

View File

@@ -0,0 +1,135 @@
#include <SPI.h>
#include <PN532_SPI.h>
#include <PN532Interface.h>
#include <PN532.h>
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
void setup()
{
Serial.begin(115200);
Serial.println("-------Peer to Peer HCE--------");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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);
// Set the max number of retry attempts to read from a card
// This prevents us from waiting forever for a card, which is
// the default behaviour of the PN532.
//nfc.setPassiveActivationRetries(0xFF);
// configure board to read RFID tags
nfc.SAMConfig();
}
void loop()
{
bool success;
uint8_t responseLength = 32;
Serial.println("Waiting for an ISO14443A card");
// set shield to inListPassiveTarget
success = nfc.inListPassiveTarget();
if(success) {
Serial.println("Found something!");
uint8_t selectApdu[] = { 0x00, /* CLA */
0xA4, /* INS */
0x04, /* P1 */
0x00, /* P2 */
0x07, /* Length of AID */
0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* AID defined on Android App */
0x00 /* Le */ };
uint8_t response[32];
success = nfc.inDataExchange(selectApdu, sizeof(selectApdu), response, &responseLength);
if(success) {
Serial.print("responseLength: "); Serial.println(responseLength);
nfc.PrintHexChar(response, responseLength);
do {
uint8_t apdu[] = "Hello from Arduino";
uint8_t back[32];
uint8_t length = 32;
success = nfc.inDataExchange(apdu, sizeof(apdu), back, &length);
if(success) {
Serial.print("responseLength: "); Serial.println(length);
nfc.PrintHexChar(back, length);
}
else {
Serial.println("Broken connection?");
}
}
while(success);
}
else {
Serial.println("Failed sending SELECT AID");
}
}
else {
Serial.println("Didn't find anything!");
}
delay(1000);
}
void printResponse(uint8_t *response, uint8_t responseLength) {
String respBuffer;
for (int i = 0; i < responseLength; i++) {
if (response[i] < 0x10)
respBuffer = respBuffer + "0"; //Adds leading zeros if hex value is smaller than 0x10
respBuffer = respBuffer + String(response[i], HEX) + " ";
}
Serial.print("response: "); Serial.println(respBuffer);
}
void setupNFC() {
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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();
}

View File

@@ -0,0 +1,69 @@
#include "SPI.h"
#include "PN532_SPI.h"
#include "emulatetag.h"
#include "NdefMessage.h"
PN532_SPI pn532spi(SPI, 10);
EmulateTag nfc(pn532spi);
uint8_t ndefBuf[120];
NdefMessage message;
int messageSize;
uint8_t uid[3] = { 0x12, 0x34, 0x56 };
void setup()
{
Serial.begin(115200);
Serial.println("------- Emulate Tag --------");
message = NdefMessage();
message.addUriRecord("http://www.seeedstudio.com");
messageSize = message.getEncodedSize();
if (messageSize > sizeof(ndefBuf)) {
Serial.println("ndefBuf is too small");
while (1) { }
}
Serial.print("Ndef encoded message size: ");
Serial.println(messageSize);
message.encode(ndefBuf);
// comment out this command for no ndef message
nfc.setNdefFile(ndefBuf, messageSize);
// uid must be 3 bytes!
nfc.setUid(uid);
nfc.init();
}
void loop(){
// uncomment for overriding ndef in case a write to this tag occured
//nfc.setNdefFile(ndefBuf, messageSize);
// start emulation (blocks)
nfc.emulate();
// or start emulation with timeout
/*if(!nfc.emulate(1000)){ // timeout 1 second
Serial.println("timed out");
}*/
// deny writing to the tag
// nfc.setTagWriteable(false);
if(nfc.writeOccured()){
Serial.println("\nWrite occured !");
uint8_t* tag_buf;
uint16_t length;
nfc.getContent(&tag_buf, &length);
NdefMessage msg = NdefMessage(tag_buf, length);
msg.print();
}
delay(1000);
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************/
/*!
This example will attempt to connect to an ISO14443A
card or tag and retrieve some basic information about it
that can be used to determine what type of card it is.
Note that you need the baud rate to be 115200 because we need to print
out the data and read from the card at the same time!
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
void setup(void) {
Serial.begin(115200);
Serial.println("Hello!");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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);
// Set the max number of retry attempts to read from a card
// This prevents us from waiting forever for a card, which is
// the default behaviour of the PN532.
nfc.setPassiveActivationRetries(0xFF);
// configure board to read RFID tags
nfc.SAMConfig();
Serial.println("Waiting for an ISO14443A card");
}
void loop(void) {
boolean success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength);
if (success) {
Serial.println("Found a card!");
Serial.print("UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print("UID Value: ");
for (uint8_t i=0; i < uidLength; i++)
{
Serial.print(" 0x");Serial.print(uid[i], HEX);
}
Serial.println("");
// wait until the card is taken away
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength)) {}
}
else
{
// PN532 probably timed out waiting for a card
Serial.println("Timed out waiting for a card");
}
}

View File

@@ -0,0 +1,170 @@
/**************************************************************************/
/*!
This example attempts to format a clean Mifare Classic 1K card as
an NFC Forum tag (to store NDEF messages that can be read by any
NFC enabled Android phone, etc.)
Note that you need the baud rate to be 115200 because we need to print
out the data and read from the card at the same time!
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
/*
We can encode many different kinds of pointers to the card,
from a URL, to an Email address, to a phone number, and many more
check the library header .h file to see the large # of supported
prefixes!
*/
// For a http://www. url:
const char * url = "seeedstudio.com";
uint8_t ndefprefix = NDEF_URIPREFIX_HTTP_WWWDOT;
// for an email address
//const char * url = "mail@example.com";
//uint8_t ndefprefix = NDEF_URIPREFIX_MAILTO;
// for a phone number
//const char * url = "+1 212 555 1212";
//uint8_t ndefprefix = NDEF_URIPREFIX_TEL;
void setup(void) {
Serial.begin(115200);
Serial.println("Looking for PN532...");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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();
}
void loop(void) {
uint8_t success; // Flag to check if there was an error with the PN532
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
bool authenticated = false; // Flag to indicate if the sector is authenticated
// Use the default key
uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
Serial.println("");
Serial.println("PLEASE NOTE: Formatting your card for NDEF records will change the");
Serial.println("authentication keys. To reformat your NDEF tag as a clean Mifare");
Serial.println("Classic tag, use the mifareclassic_ndeftoclassic example!");
Serial.println("");
Serial.println("Place your Mifare Classic card on the reader to format with NDEF");
Serial.println("and press any key to continue ...");
// Wait for user input before proceeding
while (!Serial.available());
// a key was pressed1
while (Serial.available()) Serial.read();
// Wait for an ISO14443A type card (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success)
{
// Display some basic information about the card
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
for (uint8_t i = 0; i < uidLength; i++) {
Serial.print(uid[i], HEX);
Serial.print(' ');
}
Serial.println("");
// Make sure this is a Mifare Classic card
if (uidLength != 4)
{
Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!");
return;
}
// We probably have a Mifare Classic card ...
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
// Try to format the card for NDEF data
success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 0, 0, keya);
if (!success)
{
Serial.println("Unable to authenticate block 0 to enable card formatting!");
return;
}
success = nfc.mifareclassic_FormatNDEF();
if (!success)
{
Serial.println("Unable to format the card for NDEF");
return;
}
Serial.println("Card has been formatted for NDEF data using MAD1");
// Try to authenticate block 4 (first block of sector 1) using our key
success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 4, 0, keya);
// Make sure the authentification process didn't fail
if (!success)
{
Serial.println("Authentication failed.");
return;
}
// Try to write a URL
Serial.println("Writing URI to sector 1 as an NDEF Message");
// Authenticated seems to have worked
// Try to write an NDEF record to sector 1
// Use 0x01 for the URI Identifier Code to prepend "http://www."
// to the url (and save some space). For information on URI ID Codes
// see http://www.ladyada.net/wiki/private/articlestaging/nfc/ndef
if (strlen(url) > 38)
{
// The length is also checked in the WriteNDEFURI function, but lets
// warn users here just in case they change the value and it's bigger
// than it should be
Serial.println("URI is too long ... must be less than 38 characters long");
return;
}
// URI is within size limits ... write it to the card and report success/failure
success = nfc.mifareclassic_WriteNDEFURI(1, ndefprefix, url);
if (success)
{
Serial.println("NDEF URI Record written to sector 1");
}
else
{
Serial.println("NDEF Record creation failed! :(");
}
}
// Wait a bit before trying again
Serial.println("\n\nDone!");
delay(1000);
Serial.flush();
while(Serial.available()) Serial.read();
}

View File

@@ -0,0 +1,157 @@
/**************************************************************************/
/*!
This example attempts to dump the contents of a Mifare Classic 1K card
Note that you need the baud rate to be 115200 because we need to print
out the data and read from the card at the same time!
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
void setup(void) {
// has to be fast to dump the entire memory contents!
Serial.begin(115200);
Serial.println("Looking for PN532...");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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();
Serial.println("Waiting for an ISO14443A Card ...");
}
void loop(void) {
uint8_t success; // Flag to check if there was an error with the PN532
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
uint8_t currentblock; // Counter to keep track of which block we're on
bool authenticated = false; // Flag to indicate if the sector is authenticated
uint8_t data[16]; // Array to store block data during reads
// Keyb on NDEF and Mifare Classic should be the same
uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
// Display some basic information about the card
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
for (uint8_t i = 0; i < uidLength; i++) {
Serial.print(uid[i], HEX);
Serial.print(' ');
}
Serial.println("");
if (uidLength == 4)
{
// We probably have a Mifare Classic card ...
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
// Now we try to go through all 16 sectors (each having 4 blocks)
// authenticating each sector, and then dumping the blocks
for (currentblock = 0; currentblock < 64; currentblock++)
{
// Check if this is a new block so that we can reauthenticate
if (nfc.mifareclassic_IsFirstBlock(currentblock)) authenticated = false;
// If the sector hasn't been authenticated, do so first
if (!authenticated)
{
// Starting of a new sector ... try to to authenticate
Serial.print("------------------------Sector ");Serial.print(currentblock/4, DEC);Serial.println("-------------------------");
if (currentblock == 0)
{
// This will be 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF for Mifare Classic (non-NDEF!)
// or 0xA0 0xA1 0xA2 0xA3 0xA4 0xA5 for NDEF formatted cards using key a,
// but keyb should be the same for both (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)
success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentblock, 1, keyuniversal);
}
else
{
// This will be 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF for Mifare Classic (non-NDEF!)
// or 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7 for NDEF formatted cards using key a,
// but keyb should be the same for both (0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)
success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentblock, 1, keyuniversal);
}
if (success)
{
authenticated = true;
}
else
{
Serial.println("Authentication error");
}
}
// If we're still not authenticated just skip the block
if (!authenticated)
{
Serial.print("Block ");Serial.print(currentblock, DEC);Serial.println(" unable to authenticate");
}
else
{
// Authenticated ... we should be able to read the block now
// Dump the data into the 'data' array
success = nfc.mifareclassic_ReadDataBlock(currentblock, data);
if (success)
{
// Read successful
Serial.print("Block ");Serial.print(currentblock, DEC);
if (currentblock < 10)
{
Serial.print(" ");
}
else
{
Serial.print(" ");
}
// Dump the raw data
nfc.PrintHexChar(data, 16);
}
else
{
// Oops ... something happened
Serial.print("Block ");Serial.print(currentblock, DEC);
Serial.println(" unable to read this block");
}
}
}
}
else
{
Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!");
}
}
// Wait a bit before trying again
Serial.println("\n\nSend a character to run the mem dumper again!");
Serial.flush();
while (!Serial.available());
while (Serial.available()) {
Serial.read();
}
Serial.flush();
}

View File

@@ -0,0 +1,172 @@
/**************************************************************************/
/*!
This examples attempts to take a Mifare Classic 1K card that has been
formatted for NDEF messages using mifareclassic_formatndef, and resets
the authentication keys back to the Mifare Classic defaults
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
#define NR_SHORTSECTOR (32) // Number of short sectors on Mifare 1K/4K
#define NR_LONGSECTOR (8) // Number of long sectors on Mifare 4K
#define NR_BLOCK_OF_SHORTSECTOR (4) // Number of blocks in a short sector
#define NR_BLOCK_OF_LONGSECTOR (16) // Number of blocks in a long sector
// Determine the sector trailer block based on sector number
#define BLOCK_NUMBER_OF_SECTOR_TRAILER(sector) (((sector)<NR_SHORTSECTOR)? \
((sector)*NR_BLOCK_OF_SHORTSECTOR + NR_BLOCK_OF_SHORTSECTOR-1):\
(NR_SHORTSECTOR*NR_BLOCK_OF_SHORTSECTOR + (sector-NR_SHORTSECTOR)*NR_BLOCK_OF_LONGSECTOR + NR_BLOCK_OF_LONGSECTOR-1))
// Determine the sector's first block based on the sector number
#define BLOCK_NUMBER_OF_SECTOR_1ST_BLOCK(sector) (((sector)<NR_SHORTSECTOR)? \
((sector)*NR_BLOCK_OF_SHORTSECTOR):\
(NR_SHORTSECTOR*NR_BLOCK_OF_SHORTSECTOR + (sector-NR_SHORTSECTOR)*NR_BLOCK_OF_LONGSECTOR))
// The default Mifare Classic key
static const uint8_t KEY_DEFAULT_KEYAB[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
void setup(void) {
Serial.begin(115200);
Serial.println("Looking for PN532...");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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();
}
void loop(void) {
uint8_t success; // Flag to check if there was an error with the PN532
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
bool authenticated = false; // Flag to indicate if the sector is authenticated
uint8_t blockBuffer[16]; // Buffer to store block contents
uint8_t blankAccessBits[3] = { 0xff, 0x07, 0x80 };
uint8_t idx = 0;
uint8_t numOfSector = 16; // Assume Mifare Classic 1K for now (16 4-block sectors)
Serial.println("Place your NDEF formatted Mifare Classic 1K card on the reader");
Serial.println("and press any key to continue ...");
// Wait for user input before proceeding
while (!Serial.available());
while (Serial.available()) Serial.read();
// Wait for an ISO14443A type card (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success)
{
// We seem to have a tag ...
// Display some basic information about it
Serial.println("Found an ISO14443A card/tag");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
Serial.println("");
// Make sure this is a Mifare Classic card
if (uidLength != 4)
{
Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!");
return;
}
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
Serial.println("");
Serial.println("Reformatting card for Mifare Classic (please don't touch it!) ... ");
// Now run through the card sector by sector
for (idx = 0; idx < numOfSector; idx++)
{
// Step 1: Authenticate the current sector using key B 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, BLOCK_NUMBER_OF_SECTOR_TRAILER(idx), 1, (uint8_t *)KEY_DEFAULT_KEYAB);
if (!success)
{
Serial.print("Authentication failed for sector "); Serial.println(numOfSector);
return;
}
// Step 2: Write to the other blocks
if (idx == 16)
{
memset(blockBuffer, 0, sizeof(blockBuffer));
if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 3, blockBuffer)))
{
Serial.print("Unable to write to sector "); Serial.println(numOfSector);
return;
}
}
if ((idx == 0) || (idx == 16))
{
memset(blockBuffer, 0, sizeof(blockBuffer));
if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 2, blockBuffer)))
{
Serial.print("Unable to write to sector "); Serial.println(numOfSector);
return;
}
}
else
{
memset(blockBuffer, 0, sizeof(blockBuffer));
if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 3, blockBuffer)))
{
Serial.print("Unable to write to sector "); Serial.println(numOfSector);
return;
}
if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 2, blockBuffer)))
{
Serial.print("Unable to write to sector "); Serial.println(numOfSector);
return;
}
}
memset(blockBuffer, 0, sizeof(blockBuffer));
if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)) - 1, blockBuffer)))
{
Serial.print("Unable to write to sector "); Serial.println(numOfSector);
return;
}
// Step 3: Reset both keys to 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
memcpy(blockBuffer, KEY_DEFAULT_KEYAB, sizeof(KEY_DEFAULT_KEYAB));
memcpy(blockBuffer + 6, blankAccessBits, sizeof(blankAccessBits));
blockBuffer[9] = 0x69;
memcpy(blockBuffer + 10, KEY_DEFAULT_KEYAB, sizeof(KEY_DEFAULT_KEYAB));
// Step 4: Write the trailer block
if (!(nfc.mifareclassic_WriteDataBlock((BLOCK_NUMBER_OF_SECTOR_TRAILER(idx)), blockBuffer)))
{
Serial.print("Unable to write trailer block of sector "); Serial.println(numOfSector);
return;
}
}
}
// Wait a bit before trying again
Serial.println("\n\nDone!");
delay(1000);
Serial.flush();
while(Serial.available()) Serial.read();
}

View File

@@ -0,0 +1,145 @@
/**************************************************************************/
/*!
Updates a sector that is already formatted for NDEF (using
mifareclassic_formatndef.pde for example), inserting a new url
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
/*
We can encode many different kinds of pointers to the card,
from a URL, to an Email address, to a phone number, and many more
check the library header .h file to see the large # of supported
prefixes!
*/
// For a http://www. url:
const char * url = "seeedstudio.com";
uint8_t ndefprefix = NDEF_URIPREFIX_HTTP_WWWDOT;
// for an email address
//const char * url = "mail@example.com";
//uint8_t ndefprefix = NDEF_URIPREFIX_MAILTO;
// for a phone number
//const char * url = "+1 212 555 1212";
//uint8_t ndefprefix = NDEF_URIPREFIX_TEL;
void setup(void) {
Serial.begin(115200);
Serial.println("Looking for PN532...");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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();
}
void loop(void) {
uint8_t success; // Flag to check if there was an error with the PN532
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
bool authenticated = false; // Flag to indicate if the sector is authenticated
// Use the default NDEF keys (these would have have set by mifareclassic_formatndef.pde!)
uint8_t keya[6] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 };
uint8_t keyb[6] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 };
Serial.println("Place your NDEF formatted Mifare Classic card on the reader to update the");
Serial.println("NDEF record and press any key to continue ...");
// Wait for user input before proceeding
while (!Serial.available());
// a key was pressed1
while (Serial.available()) Serial.read();
// Wait for an ISO14443A type card (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success)
{
// Display some basic information about the card
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
Serial.println("");
// Make sure this is a Mifare Classic card
if (uidLength != 4)
{
Serial.println("Ooops ... this doesn't seem to be a Mifare Classic card!");
return;
}
// We probably have a Mifare Classic card ...
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
// Check if this is an NDEF card (using first block of sector 1 from mifareclassic_formatndef.pde)
// Must authenticate on the first key using 0xD3 0xF7 0xD3 0xF7 0xD3 0xF7
success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, 4, 0, keyb);
if (!success)
{
Serial.println("Unable to authenticate block 4 ... is this card NDEF formatted?");
return;
}
Serial.println("Authentication succeeded (seems to be an NDEF/NFC Forum tag) ...");
// Authenticated seems to have worked
// Try to write an NDEF record to sector 1
// Use 0x01 for the URI Identifier Code to prepend "http://www."
// to the url (and save some space). For information on URI ID Codes
// see http://www.ladyada.net/wiki/private/articlestaging/nfc/ndef
if (strlen(url) > 38)
{
// The length is also checked in the WriteNDEFURI function, but lets
// warn users here just in case they change the value and it's bigger
// than it should be
Serial.println("URI is too long ... must be less than 38 characters!");
return;
}
Serial.println("Updating sector 1 with URI as NDEF Message");
// URI is within size limits ... write it to the card and report success/failure
success = nfc.mifareclassic_WriteNDEFURI(1, ndefprefix, url);
if (success)
{
Serial.println("NDEF URI Record written to sector 1");
Serial.println("");
}
else
{
Serial.println("NDEF Record creation failed! :(");
}
}
// Wait a bit before trying again
Serial.println("\n\nDone!");
delay(1000);
Serial.flush();
while(Serial.available()) Serial.read();
}

View File

@@ -0,0 +1,91 @@
// NTAG21x supports 4 bytes password to protect pages started from AUTH0
// AUTH0 defines the page address from which the password verification is required.
// Valid address range for byte AUTH0 is from 00h to FFh.
// If AUTH0 is set to a page address which is higher than the last page from the user configuration,
// the password protection is effectively disabled
#if 0 // Using PN532's SPI (Seeed NFC shield)
#include <SPI.h>
#include <PN532_SPI.h>
#include <PN532.h>
#include <NfcAdapter.h>
PN532_SPI intf(SPI, 10);
PN532 nfc = PN532(intf);
#else // Using PN532's I2C
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
PN532_I2C intf(Wire);
PN532 nfc = PN532(intf);
#endif
// Using PN532's UART (Grove NFC)
// #include <PN532_I2C.h>
// #include <PN532.h>
// #include <NfcAdapter.h>
// PN532_HSU intf(Serial1);
// PN532 nfc = PN532(intf);
uint8_t password[4] = {0x12, 0x34, 0x56, 0x78};
uint8_t buf[4];
uint8_t uid[7];
uint8_t uidLength;
void setup(void) {
Serial.begin(9600);
Serial.println("NTAG21x R/W");
nfc.begin();
nfc.SAMConfig();
}
void loop(void) {
Serial.println("wait for a tag");
// wait until a tag is present
while (!nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) {
}
// if NTAG21x enables r/w protection, uncomment the following line
// nfc.ntag21x_auth(password);
nfc.mifareultralight_ReadPage(3, buf);
int capacity = buf[2] * 8;
Serial.print(F("Tag capacity "));
Serial.print(capacity);
Serial.println(F(" bytes"));
uint8_t cfg_page_base = 0x29; // NTAG213
if (capacity == 0x3E) {
cfg_page_base = 0x83; // NTAG215
} else if (capacity == 0x6D) {
cfg_page_base = 0xE3; // NTAG216
}
// PWD page, set new password
nfc.mifareultralight_WritePage(cfg_page_base + 2, password);
// disable r/w
// | PROT | CFG_LCK | RFUI | NFC_CNT_EN | NFC_CNT_PWD_PROT | AUTHLIM (2:0) |
buf[0] = (1 << 7) | 0x0;
nfc.mifareultralight_WritePage(cfg_page_base + 1, buf);
// protect pages started from AUTH0
uint8_t auth0 = 0x10;
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
buf[3] = auth0;
nfc.mifareultralight_WritePage(cfg_page_base, buf);
// wait until the tag is removed
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) {
}
}

View File

@@ -0,0 +1,72 @@
// Clean resets a tag back to factory-like state
// For Mifare Classic, tag is zero'd and reformatted as Mifare Classic
// For Mifare Ultralight, tags is zero'd and left empty
#if 0 // Using PN532's SPI (Seeed NFC shield)
#include <SPI.h>
#include <PN532_SPI.h>
#include <PN532.h>
#include <NfcAdapter.h>
PN532_SPI intf(SPI, 10);
PN532 nfc = PN532(intf);
#else // Using PN532's I2C
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
PN532_I2C intf(Wire);
PN532 nfc = PN532(intf);
#endif
// Using PN532's UART (Grove NFC)
// #include <PN532_I2C.h>
// #include <PN532.h>
// #include <NfcAdapter.h>
// PN532_HSU intf(Serial1);
// PN532 nfc = PN532(intf);
uint8_t password[4] = {0x12, 0x34, 0x56, 0x78};
uint8_t buf[4];
uint8_t uid[7];
uint8_t uidLength;
void setup(void) {
Serial.begin(9600);
Serial.println("NTAG21x R/W");
nfc.begin();
nfc.SAMConfig();
}
void loop(void) {
Serial.println("wait for a tag");
// wait until a tag is present
while (!nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) {
}
// if NTAG21x enables r/w protection, uncomment the following line
// nfc.ntag21x_auth(password);
nfc.mifareultralight_ReadPage(3, buf);
int capacity = buf[2] * 8;
Serial.print(F("Tag capacity "));
Serial.print(capacity);
Serial.println(F(" bytes"));
for (int i=4; i<capacity/4; i++) {
nfc.mifareultralight_ReadPage(i, buf);
nfc.PrintHexChar(buf, 4);
}
// wait until the tag is removed
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)) {
}
}

View File

@@ -0,0 +1,51 @@
// snep_test.ino
// send a SNEP message to adnroid and get a message from android
#include "SPI.h"
#include "PN532_SPI.h"
#include "llcp.h"
#include "snep.h"
PN532_SPI pn532spi(SPI, 10);
SNEP nfc(pn532spi);
void setup()
{
Serial.begin(115200);
Serial.println("-------Peer to Peer--------");
}
uint8_t message[] = {
0xD2, 0xA, 0xB, 0x74,0x65, 0x78, 0x74, 0x2F, 0x70, 0x6C,
0x61, 0x69, 0x6E, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77,
0x6F, 0x72, 0x6C, 0x64};
uint8_t buf[128];
void loop()
{
nfc.write(message, sizeof(message));
delay(3000);
int16_t len = nfc.read(buf, sizeof(buf));
if (len > 0) {
Serial.println("get a SNEP message:");
for (uint8_t i = 0; i < len; i++) {
Serial.print(buf[i], HEX);
Serial.print(' ');
}
Serial.print('\n');
for (uint8_t i = 0; i < len; i++) {
char c = buf[i];
if (c <= 0x1f || c > 0x7f) {
Serial.print('.');
} else {
Serial.print(c);
}
}
Serial.print('\n');
}
delay(3000);
}

View File

@@ -0,0 +1,55 @@
// send a NDEF message to adnroid or get a NDEF message
//
// note: [NDEF library](https://github.com/Don/NDEF) is needed.
#include "SPI.h"
#include "PN532_SPI.h"
#include "snep.h"
#include "NdefMessage.h"
PN532_SPI pn532spi(SPI, 10);
SNEP nfc(pn532spi);
uint8_t ndefBuf[128];
void setup()
{
Serial.begin(115200);
Serial.println("-------Peer to Peer--------");
}
void loop()
{
#if 1
Serial.println("Send a message to Android");
NdefMessage message = NdefMessage();
message.addUriRecord("http://www.seeedstudio.com");
int messageSize = message.getEncodedSize();
if (messageSize > sizeof(ndefBuf)) {
Serial.println("ndefBuf is too small");
while (1) {
}
}
message.encode(ndefBuf);
if (0 >= nfc.write(ndefBuf, messageSize)) {
Serial.println("Failed");
} else {
Serial.println("Success");
}
delay(3000);
#else
Serial.println("Get a message from Android");
int msgSize = nfc.read(ndefBuf, sizeof(ndefBuf));
if (msgSize > 0) {
NdefMessage msg = NdefMessage(ndefBuf, msgSize);
msg.print();
Serial.println("\nSuccess");
} else {
Serial.println("failed");
}
delay(3000);
#endif
}

View File

@@ -0,0 +1,145 @@
/**************************************************************************/
/*!
This example will wait for any ISO14443A card or tag, and
depending on the size of the UID will attempt to read from it.
If the card has a 4-byte UID it is probably a Mifare
Classic card, and the following steps are taken:
- Authenticate block 4 (the first block of Sector 1) using
the default KEYA of 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF
- If authentication succeeds, we can then read any of the
4 blocks in that sector (though only block 4 is read here)
If the card has a 7-byte UID it is probably a Mifare
Ultralight card, and the 4 byte pages can be read directly.
Page 4 is read by default since this is the first 'general-
purpose' page on the tags.
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
void setup(void) {
Serial.begin(115200);
Serial.println("Hello!");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("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();
Serial.println("Waiting for an ISO14443A Card ...");
}
void loop(void) {
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
// Display some basic information about the card
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
Serial.println("");
if (uidLength == 4)
{
// We probably have a Mifare Classic card ...
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
// Now we need to try to authenticate it for read/write access
// Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
Serial.println("Trying to authenticate block 4 with default KEYA value");
uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// Start with block 4 (the first block of sector 1) since sector 0
// contains the manufacturer data and it's probably better just
// to leave it alone unless you know what you're doing
success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya);
if (success)
{
Serial.println("Sector 1 (Blocks 4..7) has been authenticated");
uint8_t data[16];
// If you want to write something to block 4 to test with, uncomment
// the following line and this text should be read back in a minute
// data = { 'a', 'd', 'a', 'f', 'r', 'u', 'i', 't', '.', 'c', 'o', 'm', 0, 0, 0, 0};
// success = nfc.mifareclassic_WriteDataBlock (4, data);
// Try to read the contents of block 4
success = nfc.mifareclassic_ReadDataBlock(4, data);
if (success)
{
// Data seems to have been read ... spit it out
Serial.println("Reading Block 4:");
nfc.PrintHexChar(data, 16);
Serial.println("");
// Wait a bit before reading the card again
delay(1000);
}
else
{
Serial.println("Ooops ... unable to read the requested block. Try another key?");
}
}
else
{
Serial.println("Ooops ... authentication failed: Try another key?");
}
}
if (uidLength == 7)
{
// We probably have a Mifare Ultralight card ...
Serial.println("Seems to be a Mifare Ultralight tag (7 byte UID)");
// Try to read the first general-purpose user page (#4)
Serial.println("Reading page 4");
uint8_t data[32];
success = nfc.mifareultralight_ReadPage (4, data);
if (success)
{
// Data seems to have been read ... spit it out
nfc.PrintHexChar(data, 4);
Serial.println("");
// Wait a bit before reading the card again
delay(1000);
}
else
{
Serial.println("Ooops ... unable to read the requested page!?");
}
}
}
}

27
license.txt Normal file
View File

@@ -0,0 +1,27 @@
Software License Agreement (BSD License)
Copyright (c) 2012, Adafruit Industries
Copyright (c) 2013, Seeed Technology Inc.
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.

309
llcp.cpp Normal file
View File

@@ -0,0 +1,309 @@
#include "llcp.h"
#include "PN532_debug.h"
// LLCP PDU Type Values
#define PDU_SYMM 0x00
#define PDU_PAX 0x01
#define PDU_CONNECT 0x04
#define PDU_DISC 0x05
#define PDU_CC 0x06
#define PDU_DM 0x07
#define PDU_I 0x0c
#define PDU_RR 0x0d
uint8_t LLCP::SYMM_PDU[2] = {0, 0};
inline uint8_t getPType(const uint8_t *buf)
{
return ((buf[0] & 0x3) << 2) + (buf[1] >> 6);
}
inline uint8_t getSSAP(const uint8_t *buf)
{
return buf[1] & 0x3f;
}
inline uint8_t getDSAP(const uint8_t *buf)
{
return buf[0] >> 2;
}
int8_t LLCP::activate(uint16_t timeout)
{
return link.activateAsTarget(timeout);
}
int8_t LLCP::waitForConnection(uint16_t timeout)
{
uint8_t type;
mode = 1;
ns = 0;
nr = 0;
// Get CONNECT PDU
DMSG("wait for a CONNECT PDU\n");
do {
if (2 > link.read(headerBuf, headerBufLen)) {
return -1;
}
type = getPType(headerBuf);
if (PDU_CONNECT == type) {
break;
} else if (PDU_SYMM == type) {
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return -2;
}
} else {
return -3;
}
} while (1);
// Put CC PDU
DMSG("put a CC(Connection Complete) PDU to response the CONNECT PDU\n");
ssap = getDSAP(headerBuf);
dsap = getSSAP(headerBuf);
headerBuf[0] = (dsap << 2) + ((PDU_CC >> 2) & 0x3);
headerBuf[1] = ((PDU_CC & 0x3) << 6) + ssap;
if (!link.write(headerBuf, 2)) {
return -2;
}
return 1;
}
int8_t LLCP::waitForDisconnection(uint16_t timeout)
{
uint8_t type;
// Get DISC PDU
DMSG("wait for a DISC PDU\n");
do {
if (2 > link.read(headerBuf, headerBufLen)) {
return -1;
}
type = getPType(headerBuf);
if (PDU_DISC == type) {
break;
} else if (PDU_SYMM == type) {
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return -2;
}
} else {
return -3;
}
} while (1);
// Put DM PDU
DMSG("put a DM(Disconnect Mode) PDU to response the DISC PDU\n");
// ssap = getDSAP(headerBuf);
// dsap = getSSAP(headerBuf);
headerBuf[0] = (dsap << 2) + (PDU_DM >> 2);
headerBuf[1] = ((PDU_DM & 0x3) << 6) + ssap;
if (!link.write(headerBuf, 2)) {
return -2;
}
return 1;
}
int8_t LLCP::connect(uint16_t timeout)
{
uint8_t type;
mode = 0;
dsap = LLCP_DEFAULT_DSAP;
ssap = LLCP_DEFAULT_SSAP;
ns = 0;
nr = 0;
// try to get a SYMM PDU
if (2 > link.read(headerBuf, headerBufLen)) {
return -1;
}
type = getPType(headerBuf);
if (PDU_SYMM != type) {
return -1;
}
// put a CONNECT PDU
headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_CONNECT >> 2);
headerBuf[1] = ((PDU_CONNECT & 0x03) << 6) + LLCP_DEFAULT_SSAP;
uint8_t body[] = " urn:nfc:sn:snep";
body[0] = 0x06;
body[1] = sizeof(body) - 2 - 1;
if (!link.write(headerBuf, 2, body, sizeof(body) - 1)) {
return -2;
}
// wait for a CC PDU
DMSG("wait for a CC PDU\n");
do {
if (2 > link.read(headerBuf, headerBufLen)) {
return -1;
}
type = getPType(headerBuf);
if (PDU_CC == type) {
break;
} else if (PDU_SYMM == type) {
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return -2;
}
} else {
return -3;
}
} while (1);
return 1;
}
int8_t LLCP::disconnect(uint16_t timeout)
{
uint8_t type;
// try to get a SYMM PDU
if (2 > link.read(headerBuf, headerBufLen)) {
return -1;
}
type = getPType(headerBuf);
if (PDU_SYMM != type) {
return -1;
}
// put a DISC PDU
headerBuf[0] = (LLCP_DEFAULT_DSAP << 2) + (PDU_DISC >> 2);
headerBuf[1] = ((PDU_DISC & 0x03) << 6) + LLCP_DEFAULT_SSAP;
if (!link.write(headerBuf, 2)) {
return -2;
}
// wait for a DM PDU
DMSG("wait for a DM PDU\n");
do {
if (2 > link.read(headerBuf, headerBufLen)) {
return -1;
}
type = getPType(headerBuf);
if (PDU_CC == type) {
break;
} else if (PDU_DM == type) {
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return -2;
}
} else {
return -3;
}
} while (1);
return 1;
}
bool LLCP::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{
uint8_t type;
uint8_t buf[3];
if (mode) {
// Get a SYMM PDU
if (2 != link.read(buf, sizeof(buf))) {
return false;
}
}
if (headerBufLen < (hlen + 3)) {
return false;
}
for (int8_t i = hlen - 1; i >= 0; i--) {
headerBuf[i + 3] = header[i];
}
headerBuf[0] = (dsap << 2) + (PDU_I >> 2);
headerBuf[1] = ((PDU_I & 0x3) << 6) + ssap;
headerBuf[2] = (ns << 4) + nr;
if (!link.write(headerBuf, 3 + hlen, body, blen)) {
return false;
}
ns++;
// Get a RR PDU
int16_t status;
do {
status = link.read(headerBuf, headerBufLen);
if (2 > status) {
return false;
}
type = getPType(headerBuf);
if (PDU_RR == type) {
break;
} else if (PDU_SYMM == type) {
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return false;
}
} else {
return false;
}
} while (1);
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return false;
}
return true;
}
int16_t LLCP::read(uint8_t *buf, uint8_t length)
{
uint8_t type;
int16_t status;
// Get INFO PDU
do {
status = link.read(buf, length);
if (2 > status) {
return -1;
}
type = getPType(buf);
if (PDU_I == type) {
break;
} else if (PDU_SYMM == type) {
if (!link.write(SYMM_PDU, sizeof(SYMM_PDU))) {
return -2;
}
} else {
return -3;
}
} while (1);
uint8_t len = status - 3;
ssap = getDSAP(buf);
dsap = getSSAP(buf);
headerBuf[0] = (dsap << 2) + (PDU_RR >> 2);
headerBuf[1] = ((PDU_RR & 0x3) << 6) + ssap;
headerBuf[2] = (buf[2] >> 4) + 1;
if (!link.write(headerBuf, 3)) {
return -2;
}
for (uint8_t i = 0; i < len; i++) {
buf[i] = buf[i + 3];
}
nr++;
return len;
}

75
llcp.h Normal file
View File

@@ -0,0 +1,75 @@
#ifndef __LLCP_H__
#define __LLCP_H__
#include "mac_link.h"
#define LLCP_DEFAULT_TIMEOUT 20000
#define LLCP_DEFAULT_DSAP 0x04
#define LLCP_DEFAULT_SSAP 0x20
class LLCP {
public:
LLCP(PN532Interface &interface) : link(interface) {
headerBuf = link.getHeaderBuffer(&headerBufLen);
ns = 0;
nr = 0;
};
/**
* @brief Actiave PN532 as a target
* @param timeout max time to wait, 0 means no timeout
* @return > 0 success
* = 0 timeout
* < 0 failed
*/
int8_t activate(uint16_t timeout = 0);
int8_t waitForConnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT);
int8_t waitForDisconnection(uint16_t timeout = LLCP_DEFAULT_TIMEOUT);
int8_t connect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT);
int8_t disconnect(uint16_t timeout = LLCP_DEFAULT_TIMEOUT);
/**
* @brief write a packet, the packet should be less than (255 - 2) bytes
* @param header packet header
* @param hlen length of header
* @param body packet body
* @param blen length of body
* @return true success
* false failed
*/
bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0);
/**
* @brief read a packet, the packet will be less than (255 - 2) bytes
* @param buf the buffer to contain the packet
* @param len lenght of the buffer
* @return >=0 length of the packet
* <0 failed
*/
int16_t read(uint8_t *buf, uint8_t len);
uint8_t *getHeaderBuffer(uint8_t *len) {
uint8_t *buf = link.getHeaderBuffer(len);
len -= 3; // I PDU header has 3 bytes
return buf;
};
private:
MACLink link;
uint8_t mode;
uint8_t ssap;
uint8_t dsap;
uint8_t *headerBuf;
uint8_t headerBufLen;
uint8_t ns; // Number of I PDU Sent
uint8_t nr; // Number of I PDU Received
static uint8_t SYMM_PDU[2];
};
#endif // __LLCP_H__

20
mac_link.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "mac_link.h"
#include "PN532_debug.h"
int8_t MACLink::activateAsTarget(uint16_t timeout)
{
pn532.begin();
pn532.SAMConfig();
return pn532.tgInitAsTarget(timeout);
}
bool MACLink::write(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{
return pn532.tgSetData(header, hlen, body, blen);
}
int16_t MACLink::read(uint8_t *buf, uint8_t len)
{
return pn532.tgGetData(buf, len);
}

51
mac_link.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef __MAC_LINK_H__
#define __MAC_LINK_H__
#include "PN532.h"
class MACLink {
public:
MACLink(PN532Interface &interface) : pn532(interface) {
};
/**
* @brief Activate PN532 as a target
* @param timeout max time to wait, 0 means no timeout
* @return > 0 success
* = 0 timeout
* < 0 failed
*/
int8_t activateAsTarget(uint16_t timeout = 0);
/**
* @brief write a PDU packet, the packet should be less than (255 - 2) bytes
* @param header packet header
* @param hlen length of header
* @param body packet body
* @param blen length of body
* @return true success
* false failed
*/
bool write(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0);
/**
* @brief read a PDU packet, the packet will be less than (255 - 2) bytes
* @param buf the buffer to contain the PDU packet
* @param len lenght of the buffer
* @return >=0 length of the PDU packet
* <0 failed
*/
int16_t read(uint8_t *buf, uint8_t len);
uint8_t *getHeaderBuffer(uint8_t *len) {
return pn532.getBuffer(len);
};
private:
PN532 pn532;
};
#endif // __MAC_LINK_H__

122
snep.cpp Normal file
View File

@@ -0,0 +1,122 @@
#include "snep.h"
#include "PN532_debug.h"
int8_t SNEP::write(const uint8_t *buf, uint8_t len, uint16_t timeout)
{
if (0 >= llcp.activate(timeout)) {
DMSG("failed to activate PN532 as a target\n");
return -1;
}
if (0 >= llcp.connect(timeout)) {
DMSG("failed to set up a connection\n");
return -2;
}
// response a success SNEP message
headerBuf[0] = SNEP_DEFAULT_VERSION;
headerBuf[1] = SNEP_REQUEST_PUT;
headerBuf[2] = 0;
headerBuf[3] = 0;
headerBuf[4] = 0;
headerBuf[5] = len;
if (0 >= llcp.write(headerBuf, 6, buf, len)) {
return -3;
}
uint8_t rbuf[16];
if (6 > llcp.read(rbuf, sizeof(rbuf))) {
return -4;
}
// check SNEP version
if (SNEP_DEFAULT_VERSION != rbuf[0]) {
DMSG(F("The received SNEP message's major version is different, me: "));
DMSG(SNEP_DEFAULT_VERSION);
DMSG(", their: ");
DMSG(rbuf[0]);
DMSG("\n");
// To-do: send Unsupported Version response
return -4;
}
// expect a put request
if (SNEP_RESPONSE_SUCCESS != rbuf[1]) {
DMSG("Expect a success response\n");
return -4;
}
llcp.disconnect(timeout);
return 1;
}
int16_t SNEP::read(uint8_t *buf, uint8_t len, uint16_t timeout)
{
if (0 >= llcp.activate(timeout)) {
DMSG("failed to activate PN532 as a target\n");
return -1;
}
if (0 >= llcp.waitForConnection(timeout)) {
DMSG("failed to set up a connection\n");
return -2;
}
uint16_t status = llcp.read(buf, len);
if (6 > status) {
return -3;
}
// check SNEP version
// in case of platform specific bug, shift SNEP message for 4 bytes.
// tested on Nexus 5, Android 5.1
if (SNEP_DEFAULT_VERSION != buf[0] && SNEP_DEFAULT_VERSION == buf[4]) {
for (uint8_t i = 0; i < len - 4; i++) {
buf[i] = buf[i + 4];
}
}
if (SNEP_DEFAULT_VERSION != buf[0]) {
DMSG(F("SNEP->read: The received SNEP message's major version is different, me: "));
DMSG(SNEP_DEFAULT_VERSION);
DMSG(", their: ");
DMSG(buf[0]);
DMSG("\n");
// To-do: send Unsupported Version response
return -4;
}
// expect a put request
if (SNEP_REQUEST_PUT != buf[1]) {
DMSG("Expect a put request\n");
return -4;
}
// check message's length
uint32_t length = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8) + buf[5];
// length should not be more than 244 (header + body < 255, header = 6 + 3 + 2)
if (length > (status - 6)) {
DMSG("The SNEP message is too large: ");
DMSG_INT(length);
DMSG_INT(status - 6);
DMSG("\n");
return -4;
}
for (uint8_t i = 0; i < length; i++) {
buf[i] = buf[i + 6];
}
// response a success SNEP message
headerBuf[0] = SNEP_DEFAULT_VERSION;
headerBuf[1] = SNEP_RESPONSE_SUCCESS;
headerBuf[2] = 0;
headerBuf[3] = 0;
headerBuf[4] = 0;
headerBuf[5] = 0;
llcp.write(headerBuf, 6);
return length;
}

49
snep.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef __SNEP_H__
#define __SNEP_H__
#include "llcp.h"
#define SNEP_DEFAULT_VERSION 0x10 // Major: 1, Minor: 0
#define SNEP_REQUEST_PUT 0x02
#define SNEP_REQUEST_GET 0x01
#define SNEP_RESPONSE_SUCCESS 0x81
#define SNEP_RESPONSE_REJECT 0xFF
class SNEP {
public:
SNEP(PN532Interface &interface) : llcp(interface) {
headerBuf = llcp.getHeaderBuffer(&headerBufLen);
};
/**
* @brief write a SNEP packet, the packet should be less than (255 - 2 - 3) bytes
* @param buf the buffer to contain the packet
* @param len lenght of the buffer
* @param timeout max time to wait, 0 means no timeout
* @return >0 success
* =0 timeout
* <0 failed
*/
int8_t write(const uint8_t *buf, uint8_t len, uint16_t timeout = 0);
/**
* @brief read a SNEP packet, the packet will be less than (255 - 2 - 3) bytes
* @param buf the buffer to contain the packet
* @param len lenght of the buffer
* @param timeout max time to wait, 0 means no timeout
* @return >=0 length of the packet
* <0 failed
*/
int16_t read(uint8_t *buf, uint8_t len, uint16_t timeout = 0);
private:
LLCP llcp;
uint8_t *headerBuf;
uint8_t headerBufLen;
};
#endif // __SNEP_H__