Skip to content

Commit

Permalink
Merge pull request #1 from ajxv/afi-read-write
Browse files Browse the repository at this point in the history
Afi read write
  • Loading branch information
ajxv authored Nov 18, 2024
2 parents cf61c3a + 5d5d22f commit bdede9a
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 0 deletions.
92 changes: 92 additions & 0 deletions PN5180ISO15693.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,98 @@ ISO15693ErrorCode PN5180ISO15693::readSingleBlock(uint8_t *uid, uint8_t blockNo,
return ISO15693_EC_OK;
}

/**
* @brief Writes the Application Family Identifier (AFI) to a tag.
*
* This method constructs and sends a command to write the AFI value to the specified UID.
* The command format includes both flags and UID, followed by the AFI value.
* Debug output is enabled if DEBUG is defined, printing the command to the Serial console.
*
* @param uid Pointer to an array containing the UID of the tag (8 bytes).
* @param afi The AFI value to be written to the tag.
* @return ISO15693ErrorCode indicating the success or failure of the operation.
*/
ISO15693ErrorCode PN5180ISO15693::writeAFI(uint8_t *uid, uint8_t afi) {
// Construct the command for writing AFI
uint8_t writeAFICommand[] = { 0x22, 0x27, 0, 0, 0, 0, 0, 0, 0, 0, afi }; // UID has LSB first!
for (int i = 0; i < 8; i++) {
writeAFICommand[2 + i] = uid[i];
}

#ifdef DEBUG
Serial.print("Write AFI Command: ");
for (int i = 0; i < sizeof(writeAFICommand); i++) {
Serial.print(writeAFICommand[i], HEX);
Serial.print(" ");
}
Serial.println();
#endif

uint8_t* resultPtr;
ISO15693ErrorCode rc = issueISO15693Command(writeAFICommand, sizeof(writeAFICommand), &resultPtr);
return rc;
}

/**
* @brief Reads the Application Family Identifier (AFI) from a tag.
*
* This method uses the getSystemInfo command to retrieve the AFI value.
* The AFI value is included in the system information response if the
* tag supports and has an AFI value set.
*
* @param uid Pointer to an array containing the UID of the tag (8 bytes).
* @param afi Pointer to store the read AFI value.
* @return ISO15693ErrorCode indicating the success or failure of the operation.
*/
ISO15693ErrorCode PN5180ISO15693::readAFI(uint8_t *uid, uint8_t *afi) {
uint8_t sysInfo[] = { 0x22, 0x2b, 0, 0, 0, 0, 0, 0, 0, 0 }; // Get System Info command

// Copy UID into command buffer (LSB first)
for (int i = 0; i < 8; i++) {
sysInfo[2+i] = uid[i];
}

#ifdef DEBUG
PN5180DEBUG(F("Read AFI using System Info command: "));
for (int i=0; i<sizeof(sysInfo); i++) {
PN5180DEBUG(formatHex(sysInfo[i]));
PN5180DEBUG(" ");
}
PN5180DEBUG("\n");
#endif

uint8_t *readBuffer;
ISO15693ErrorCode rc = issueISO15693Command(sysInfo, sizeof(sysInfo), &readBuffer);
if (rc != ISO15693_EC_OK) {
return rc;
}

// Check info flags to see if AFI is supported/present
uint8_t infoFlags = readBuffer[1];
if (!(infoFlags & 0x02)) { // AFI flag not set
return ISO15693_EC_OPTION_NOT_SUPPORTED;
}

// Calculate position of AFI in response buffer
uint8_t *p = &readBuffer[10]; // Start after UID

// Skip DSFID if present
if (infoFlags & 0x01) { // DSFID flag
p++;
}

// Read AFI value
*afi = *p;

#ifdef DEBUG
PN5180DEBUG(F("AFI value read: 0x"));
PN5180DEBUG(formatHex(*afi));
PN5180DEBUG("\n");
#endif

return ISO15693_EC_OK;
}

/*
* Write single block, code=21
*
Expand Down
2 changes: 2 additions & 0 deletions PN5180ISO15693.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class PN5180ISO15693 : public PN5180 {

public:
PN5180ISO15693(uint8_t SSpin, uint8_t BUSYpin, uint8_t RSTpin);
ISO15693ErrorCode writeAFI(uint8_t *uid, uint8_t afi);
ISO15693ErrorCode readAFI(uint8_t *uid, uint8_t *afi);

private:
ISO15693ErrorCode issueISO15693Command(uint8_t *cmd, uint8_t cmdLen, uint8_t **resultPtr);
Expand Down
201 changes: 201 additions & 0 deletions examples/PN5180-readWriteAFI/PN5180-readWriteAFI.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Include required libraries for NFC communication
#include <PN5180.h> // Base library for PN5180 NFC reader
#include <PN5180ISO15693.h> // Specific library for ISO15693 protocol support

// Define pins based on the board type
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_AVR_NANO)
// Pin definitions for Arduino boards
#define PN5180_NSS 10 // Chip select pin
#define PN5180_BUSY 9 // Busy status pin
#define PN5180_RST 7 // Reset pin
#elif defined(ARDUINO_ARCH_ESP32)
// Pin definitions for ESP32 boards
#define PN5180_NSS 16
#define PN5180_BUSY 5
#define PN5180_RST 17
#else
#error Please define your pinout here!
#endif

// Initialize NFC reader with defined pins
PN5180ISO15693 nfc(PN5180_NSS, PN5180_BUSY, PN5180_RST);

void setup() {
// Initialize serial communication
Serial.begin(115200);

// Print welcome message and available commands
Serial.println(F("PN5180 AFI Read/Write Demo"));
Serial.println(F("Commands:"));
Serial.println(F("R - Read AFI"));
Serial.println(F("W - Write AFI"));
Serial.println(F("X - Cancel operation"));

// Initialize NFC reader
nfc.begin();
nfc.reset();
nfc.setupRF();
}

// Function to print tag's UID in hex format with colons
void printUID(uint8_t *uid) {
Serial.print(F("UID: "));
for (int i = 0; i < 8; i++) {
Serial.print(uid[7 - i], HEX); // Print in reverse order as per ISO15693 standard
if (i < 7) Serial.print(":"); // Add colon separator between bytes
}
Serial.println();
}

// Function to wait for a tag to be presented
// Returns true if tag is found, false if operation is cancelled
bool waitForTag(uint8_t *uid) {
Serial.println(F("Waiting for tag... (Press X to cancel)"));
while (true) {
// Check for cancel command
if (Serial.available()) {
char input = Serial.read();
while (Serial.available()) Serial.read(); // Clear buffer
if (input == 'X' || input == 'x') {
Serial.println(F("Operation cancelled"));
return false;
}
}

// Try to detect tag
if (nfc.getInventory(uid) == ISO15693_EC_OK) {
printUID(uid);
return true;
}
delay(100); // Small delay to prevent CPU hogging
}
}

// Function to read AFI value from tag
void readAFIValue() {
uint8_t uid[8], afi;

// Wait for tag to be presented
if (!waitForTag(uid)) {
return;
}

// Try to read AFI value
if (nfc.readAFI(uid, &afi) == ISO15693_EC_OK) {
Serial.print(F("Current AFI: "));
Serial.println(afi, HEX);
} else {
Serial.println(F("Failed to read AFI value"));
}
}

// Function to read user input from Serial
// Returns complete string without newline characters
String readSerialInput() {
String input = "";

// Wait for data to be available
while (!Serial.available()) {
delay(10);
}

// Read characters until newline
while (Serial.available()) {
char c = Serial.read();
if (c == '\n' || c == '\r') {
if (input.length() > 0) {
break;
}
} else {
input += c;
}
delay(2); // Small delay for reliable serial reading
}

while (Serial.available()) Serial.read(); // Clear remaining buffer
return input;
}

// Function to write new AFI value to tag
void writeAFIValue() {
uint8_t uid[8];

Serial.println(F("Enter new AFI value (in hex, e.g., CC):"));
String afiInput = readSerialInput();

// Validate input is valid hexadecimal
for (unsigned int i = 0; i < afiInput.length(); i++) {
if (!isHexadecimalDigit(afiInput.charAt(i))) {
Serial.println(F("Invalid hex value entered. Operation cancelled."));
return;
}
}

// Convert hex string to byte
uint8_t afiValue = (uint8_t)strtol(afiInput.c_str(), NULL, 16);

// Wait for tag
if (!waitForTag(uid)) {
return;
}

// Try to write new AFI value
if (nfc.writeAFI(uid, afiValue) == ISO15693_EC_OK) {
Serial.println(F("AFI value written successfully"));
delay(100); // Small delay before next operation
} else {
Serial.println(F("Failed to write AFI value"));
}
}

// Function to read single command character from Serial
char readCommand() {
String input = "";

// Wait for input
while (!Serial.available()) {
delay(10);
}

// Read until newline, ignoring CR/LF
while (Serial.available()) {
char c = Serial.read();
if (c != '\n' && c != '\r') {
input += c;
}
delay(2);
}

// Clear any remaining characters
while (Serial.available()) {
Serial.read();
}

// Return first character or null if empty
return input.length() > 0 ? input[0] : '\0';
}

// Main program loop
void loop() {
Serial.println(F("\nEnter command (R/W):"));
char command = readCommand();

// Process command
switch (command) {
case 'R':
case 'r':
readAFIValue();
break;
case 'W':
case 'w':
writeAFIValue();
break;
case '\0': // Empty input
break;
default:
Serial.println(F("Invalid command. Use 'R' for read or 'W' for write."));
break;
}

delay(100); // Small delay before next command
}

0 comments on commit bdede9a

Please sign in to comment.