RFID Tags mit Datenstruct nutzen

Hallo,
ich hab mich mal mit dem Thema RFID beschäftigt. Die lib dazu ist ja ziemlich umständlich zu verwenden finde ich. Mein Wunsch war es eine komplette Struct zu verarbeiten und das mit möglichst beliebigen Inhalt und fast beliebiger Länge, letzlich ohne dabei über Sektoren und Blöcke nachdenken zu müssen. Natürlich hab ich mir bei der Gelegenheit einen Tag komplett zerschossen und bei einem zweiten ein paar Sektoren :wink:
Herausgekommen ist jetzt aber ein fast eigenständiger Tab für die IDE der sich so bei verschieden Sketchen nutzen lässt. In dem verwendeten Beispiel ist das lesen und schreiben etwas sinnfrei, es soll aber ja auch nur die Verwendung zeigen. Die Anwendung ist aber, wie ich finde einfach und man muss sich fast keinen Kopf mehr um Sektoren und Blöcke machen.

Ich hab das Beispiel jetzt mehrfach getestet , eventuell findet ja jemand noch einen Bug oder hat noch eine Idee für eine sinvolle Erweiterung.

Heinz

/* Beispielsketch: RFID Tag mit den Daten einer beliebigen Struct
    lesen und schreiben
    Mai.2021
*/
#include <SPI.h>      //include the SPI bus library
#include <MFRC522.h>  //include the RFID reader library

#define SS_PIN 10  //slave select pin
#define RST_PIN 9  //reset pin

MFRC522 mfrc522(SS_PIN, RST_PIN);  // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key;          //create a MIFARE_Key struct named 'key', which will hold the card information

#define STARTBLOCK 8 // möglich ab 1 ab hier wird geschrieben

// Testdaten
struct data {
  byte a ;
  int b ;
  int c;
  float f;
  char text[20];

};
data mydata;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  anzeige(mydata);

  SPI.begin();
  mfrc522.PCD_Init();
  Serial.println("Scan a MIFARE Classic card");
  Serial.print(F("Länge der Struct ")); Serial.println(sizeof(data));

  // Prepare the security key for the read and write functions.
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;  //keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  bool newcard = askForNewCard();     // Abfrage Tag erkannt
  if (newcard) {                      
    fuellen(mydata);                  // Daten in struct schreiben
    anzeige(mydata);                  // die erst mal anzeigen
    WriteStruct(STARTBLOCK, mydata);  // auf RFID schreiben
    loeschen(mydata);                 // struct löschen
    ReadStruct(STARTBLOCK, mydata);   // erneut lesen
    anzeige(mydata);                  // anzeigen

  }
  resetForNewCard(newcard);         // immer bearbeiten
}

// Daten in Struct löschen
void loeschen(data &d) {
  Serial.println(F(" Daten löschen"));
  d.a = 0; // daten zum teste löschen
  d.b = 0;
  d.c = 0;
  d.f = 0;
  strcpy(d.text, "");
}

// struct beschreiben
void fuellen (data &d) {
  d.a ++; // daten zum testen schreiben und ändern
  d.b +=10;
  d.c = 1023;
  d.f = 123.45;
  strcpy (d.text, "einen Text nutzen");

}

// daten in Struct anzeigen
void anzeige(data d) {
  Serial.println(F("Daten aus Struct anzeigen"));
  Serial.println(d.a);
  Serial.println(d.b);
  Serial.println(d.c);
  Serial.println(d.f);
  Serial.println(d.text);
  Serial.println();
}

Der Tab

/* Tab zur Nutzung von RFDI Tags MIFARE Classic card 1K
  Als Tab zum Sketch hinzufügen 
  beinhaltet folgende Funktionen.
  Datum Mai 2021 
  askForNewCard   erkennt einen aufgelegten Tag
  resetForNewCard bendet Verbindung zum Tag
  readBlock       einen Block lesen (im Wesentlichen aus der Lib)
  writeBlock      einen Block schreiben
  readStruct      liest gesamte Datenstruktur
  writeStruct     schreibt gesamte Datenstruct
  Name der verwendeten Datenstrutur "data" 
------------------------------------------------------------
 */
bool askForNewCard() {

  // Abfrage neue Karte vorhanden
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return (0);
  }

  // Karte auswählen
  if ( ! mfrc522.PICC_ReadCardSerial())
  {
    return (0);
  }
  Serial.println("erkannt");
  return (1); // Returncode 1 Karte vorhanden

}

// ------------- Reset für neue Karte------
void resetForNewCard(bool newcard) {
  static bool done;
  static uint32_t altzeit;
  if (newcard == true) {
    altzeit = millis();
    done = false;
  }
  if (millis() - altzeit >= 5000UL && done == 0) {
    Serial.println(F("bereit Karte auflegen"));
    // Halt PICC
    mfrc522.PICC_HaltA();
    // Stop encryption on PCD
    mfrc522.PCD_StopCrypto1();
    done = true;
  }
}

// ------------einen Block lesen---------------
/* diese Funktion hab ich im Netz gefunden
 *  beruht im Wesentlichen auf ein Beispiel aus der Lib
 *  es wird geprüft ob die Blocknummer zulässig ist.
*/
byte readBlock(int blockNumber, byte *arrayAddress) {

  int largestModulo4Number = blockNumber / 4 * 4;
  int trailerBlock = largestModulo4Number + 3; //determine trailer block for the sector

  //authentication of the desired block for access
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));

  if (status != MFRC522::STATUS_OK) {
    Serial.print("PCD_Authenticate() failed (read): ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 3;//return "3" as error message
  }

  //reading a block
  byte buffersize = 18;//we need to define a variable with the read buffer size, since the MIFARE_Read method below needs a pointer to the variable that contains the size...
  status = mfrc522.MIFARE_Read(blockNumber, arrayAddress, &buffersize);//&buffersize is a pointer to the buffersize variable; MIFARE_Read requires a pointer instead of just a number
  if (status != MFRC522::STATUS_OK) {
    Serial.print("MIFARE_read() failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 4;//return "4" as error message
  }
  Serial.println("block was read");
  return 0;
}

//-------------Write specific block------------------
/* diese Funktion hab ich irgendwo gefunden
 *  beruht im Wesentlichen auf ein Beispiel aus der Lib
 *  es wird geprüft ob die Blocknummer zulässig ist.
*/
byte writeBlock(int blockNumber, byte *arrayAddress) {
  //this makes sure that we only write into data blocks. Every 4th block is a trailer block for the access/security info.
  int largestModulo4Number = blockNumber / 4 * 4;
  int trailerBlock = largestModulo4Number + 3; //determine trailer block for the sector
  
  if (blockNumber > 2 && (blockNumber + 1) % 4 == 0) {
    Serial.print(blockNumber);
    Serial.println(" is a trailer block:");
    return 2;
  }
  //Serial.print(blockNumber);
  //Serial.println(" is a data block:");

  //authentication of the desired block for access
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("PCD_Authenticate() failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 3;//return "3" as error message
  }

  //writing the block
  status = mfrc522.MIFARE_Write(blockNumber, arrayAddress, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("MIFARE_Write() failed: ");
    Serial.println(mfrc522.GetStatusCodeName(status));
    return 4;//return "4" as error message
  }
  Serial.println("block was written");
  return 0; // ohne fehler beenden
}

/* -------------Daten schreiben---------------------
 *  Parameter blocknr ist der erster Block der genutzt wird
 *  Struct wird komplett Bytweise in die Datenblöcke 
 *  geschrieben. TrailerBlocks werden übersprungen
*/
void WriteStruct(byte blocknr, struct data d) {
  byte block[16] = {0}; // datenblock für rfid
  byte *tempbytes = (byte*)&d; // hier Name der Struct beachten
  byte i = 0; // Pos im dataBlock
  byte status = 0;
  byte max = sizeof(data);

  Serial.println(F("RFID schreiben"));
  if (blocknr == 0) {
    Serial.print(F("BlockNr 0 nicht erlaubt"));
  }

  for (byte dataPos = 1; dataPos < max; dataPos++) {
    block[i] = tempbytes[dataPos - 1]; // byte aus Struct in dataBlock
    i++;
    if (dataPos % 16 == 0 || dataPos == max) { //
      if (blocknr > 2 && (blocknr + 1) % 4 == 0) {
        Serial.print(blocknr); Serial.println(F(" ist Trailerblock"));
        blocknr++;
      }
      Serial.print(F("Block ")); Serial.println(blocknr );
      for (byte i = 0; i <16; i++) { // ausgeben
        Serial.print(block[i], HEX); Serial.print(" ");
      }
      Serial.println();
      status = writeBlock(blocknr, block); // Block schreiben
      if (status != 0) {
        Serial.print(F("ReturnCode ")); Serial.println(status);
      }
      for (byte i = 0; i < 16; i++) {
        block[i] = 0;
      }
      i = 0;
      blocknr++;
      if (blocknr > 62 ) {
        Serial.println(F("Ende von RFID erreicht"));
      }
    }
  }

}
/*------------------ Daten lesen--------------- 
 *  Parameter blocknr ist der erster Block der genutzt wird
 *  Struct wird komplett Bytweise aus den Blöcken gelesen.
 *  TrailerBlocks werden übersprungen
 */
void ReadStruct(byte blocknr, struct data &d) {// hier Name der Struct beachten
  byte block[18] = {0}; // datenblock für rfid
  byte *tempbytes = (byte*)&d;// hier Name der Struct beachten
  byte bn = 0;
  byte max = sizeof(data);
  byte status = 0;

  Serial.println(F("RFID lesen"));
  for (int dataPos = 0; dataPos < max; dataPos++) {

    if (dataPos == 0 || dataPos % 16 == 0) {
      if (blocknr > 2 && (blocknr + 1) % 4 == 0) {
        Serial.print(blocknr); Serial.println(F(" ist Trailerblock"));
        blocknr++;
      }
      Serial.print(F("Block ")); Serial.println(blocknr );
      status = readBlock(blocknr, block);
      if (status != 0) {
        Serial.print(F("ReturnCode ")); Serial.println(status);
      }
      
      for (byte i = 0; i < 16; i++) { // ausgeben
        Serial.print(block[i], HEX); Serial.print(" ");
      }
      Serial.println(); 
      blocknr++;
      bn = 0;
      
    }
    tempbytes[dataPos] = block[bn];// Byte in struct schreiben
    bn++;

  }

}
1 Like

nice.
Aber, warum ein Misch Masch aus F-Makro und kein F-Makro?

 Serial.print("PCD_Authenticate() failed (read): ");

Hallo
stimmt, da müsse ich halt noch mal dran :wink:
Gruß Heinz