Tag-Speicher schreiben

Der Speicherorganisation eines RFID-Tags mit 1 kByte EEPROM-Speicher wurde bereits im vorherigen Kapitel beschrieben (siehe: Tag-Speicher lesen).


Programmbeispiel 3: Schreiben eines Read/Write Blocks

Funktionsbeschreibung:

Mit Programm 3 kann ein Datenblock (Read/Write Block, maximal 16 Zeichen) gelesen, geschrieben oder gelöscht werden, wobei die einzugebende Blockadresse auf Gültigkeit überprüft wird (kein Sector Trailer, nicht Block 0, keine Bereichsüberschreitung). Werden weniger als 16 Zeichen vorgegeben, so sind die restlichen Zeichen mit NULL (Ascii-Zahl 0) vorbelegt. Es werden immer alle 16 Zeichen eines Blockes geschrieben. Beim Löschen eines Blockes werden alle 16 Zeichen mit NULL beschrieben. Vor dem Lesen, Schreiben oder Löschen wird der RFID-Tag auf eine gültige UID-Nummer überprüft. Welche und wieviele UID-Nummern gültig sind, können im Deklarationsteil des Programms (im Beispiel mit 3 UID-Nummern) in einem Array vorgegeben werden. Ebenso könnte die Länge der UID-Nummern auf 7 Byte geändert werden. Da ich keinen RFID-Tag mit 7 Byte UID-Nummer derzeit besitze, habe ich das aber nicht getestet.

//Angabe der UID-Laenge der UID-Nummern der gueltigen RFID-Tags:
const byte uidSize = 4; //UID-Nummern mit 4 Byte
byte gueltUID[][uidSize] =
{ {0x05, 0x00, 0xF6, 0xB0},
  {0x40, 0x3A, 0x76, 0x89},
  {0x44, 0x74, 0xA3, 0xEA}
};

Wurde ein gültiger RFID-Tag erkannt, wird die vorher gewählte Funktion (Lesen, Schreiben oder Löschen des Blocks) durchgeführt. Wurde Lesen gewählt, wird nur der Blockinhalt ausgegeben. Wurde Schreiben oder Löschen des Blocks gewählt, wird vor und nach dem Schreiben oder Löschen des Blocks der Blockinhalt ausgegeben. Wird kein gültiger RFID-Tag erkannt, so wird nach einer vorgegebenen Wartezeit (Vorgabe derzeit: 10 Sekunden) abgebrochen. Danach kann wieder eine neue Blockadresse eingeben werden.


Verwendete Libraries:

Neben den Libraries SPI und MFRC522 benötigt das Programm auch meine Library MySerialRead (Version 2.0), die hier heruntergeladen werden kann: Serieller Monitor


Hinweis: Das Programm funktioniert nur mit RFID-Tags mit 1 kByte Speicher, da Tags mit 4 kByte Speicher eine andere Speicherorganisation haben (32 Sektoren mit je 4 Blöcken und 8 Sektoren mit je 16 Blöcken).


Im Beispiel 3 finden folgende Funktionen der MFRC522-Library Verwendung:

(Es wird nur eine neue Funktion gegenüber den Beispielen 1 und 2 vorgestellt)

MFR.MIFARE_Write(byte, *byte, byte);


Beschreibung der neuen Funktion:

byte MIFARE_Write(byte blockAdd, byte* writeBuf, byte sizeOfBuf)

Funktion: Schreibt die in einem Array gespeicherten Daten in einen Block auf dem RFID-Tag

Parameter: blockAdd: Blockadresse (0 <= blockAdd <= (Blockanzahl - 1))

                  writeBuf: Zeiger auf ein Array (Arraygröße 16 Byte), mit den zu schreibenden Daten

                  sizeOfBuf: Größe des Arrays in Byte

Rückgabe: StatusCode 0 bis 8 (= Aufzählungstyp enum in der Library), Bedeutung siehe Funktion "PCD_Authenticate()"

Beispiel:

byte blockAdresse = 2;

byte writeBuf[16] = "Hallo Welt";

status = MFR.MIFARE_Write(blockAdresse, writeBuf, 16);

if (status != MFRC522::STATUS_OK)
{
  Serial.print("Status Schreiben: ");
  Serial.println(MFR.GetStatusCodeName(status));
}
else Serial.println("Zeichen geschrieben!");

Bemerkung: Vor dem Schreiben des Blocks muss der Sektor, der den Block enthält, zuerst mit der PCD_Authenticate()-Funktion authentifiziert werden!


Hier nun das Programm:

Zum Testen beim Seriellen Monitor das Zeilenende auf Zeilenumbruch (CR) stellen!

//RFID Programm 3
//Schreiben eines Blocks
//Code fuer Arduino
//Author Retian
//Version 1.0


//Prototypen
byte leseBlock(byte);
bool bestaetige(void);


#include <MySerialRead.h>
#include <MFRC522.h>
#include <SPI.h>


#define rstPin 9
#define ssPin 10


MFRC522 MFR(ssPin, rstPin);
MFRC522::MIFARE_Key key;

MySerialRead sread;


byte sector;
byte sectorTrailer;
byte blockAdresse;
byte writeBuf[16];
byte readBuf[18];


//Schluessel-Nummer des KeyA und KeyB im Auslieferungszustand des RFID-Tags
byte myKey[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};


//Angabe der UID-Laenge der UID-Nummern der gueltigen RFID-Tags:
const byte gueltUIDSize = 4; //UID-Nummern mit 4 Byte
byte gueltUID[][gueltUIDSize] =
{ {0x05, 0x00, 0xF6, 0xB0},
  {0x40, 0x3A, 0x76, 0x89},
  {0x44, 0x74, 0xA3, 0xEA}
};


int ueWaZeit = 10000; //Ueberwachungszeit Zeitueberschreitung
byte status;


void setup() {
  Serial.begin(115200);
  SPI.begin();
  MFR.PCD_Init();
  for (byte i = 0; i < 6; i++) key.keyByte[i] = myKey[i];

  Serial.println(F("*** Schreiben oder Loeschen eines Blockes ***"));
  Serial.println(F("Achtung: Zeilenende auf Zeilenumbruch (CR) stellen!"));
}


void loop() {
  for (byte i = 0; i < 16; i++) writeBuf[i] = NULL; //Loesche Schreib-Buffer

  Serial.print(F("\nWarte auf Blockadresse: "));
  byte inWert;
  if (sread.readByte(&inWert, SERIAL_WAIT)) Serial.println(inWert);
  else
  {
    Serial.println(F("Fehler bei der Eingabe"));
    return;
  }

  //Pruefe, ob inWert gueltige Blockadresse ist
  //(kein Sector Trailer und nicht Block 0)
  if ((inWert + 1) % 4 == 0 || inWert == 0 || inWert > 63)
  {
    Serial.print(inWert);
    if (inWert == 0) Serial.println(F(" ist ungueltig, weil Herstellerdaten-Block!"));
    else if (inWert > 63) Serial.println(F(" ist ungueltig, weil ausserhalb Bereich!"));
    else Serial.println(F(" ist ungueltig, weil SectorTrailer!"));
    return;
  }
  else blockAdresse = inWert;

  //Ermittle den zugehoerigen Sector Trailer zum Block
  sectorTrailer = blockAdresse + 3 - (blockAdresse % 4);
  Serial.print(F("Blockadresse des zugehoerigen Sector Trailer: "));
  Serial.println(sectorTrailer);

  //Ermittle den Sektor des Blocks
  sector = (sectorTrailer + 1) / 4 - 1;
  Serial.print(F("Sektor des Blocks: "));
  Serial.println(sector);

  //Auswahl Dateneingabe, Loeschen oder Abbruch
  byte menueAuswahl;
  Serial.println(F("Warte auf Eingabe:"));
  Serial.println(F(" <R> ... Lese Block"));
  Serial.println(F(" <W> ... Schreibe Block"));
  Serial.println(F(" <C> ... Loesche Block"));
  Serial.println(F(" <Q> ... Abbruch"));
  Serial.print(F("Auswahl: "));
  while (!Serial.available());
  do {
    menueAuswahl = Serial.read();
  }
  while (menueAuswahl != 'R' &&
         menueAuswahl != 'W' &&
         menueAuswahl != 'C' &&
         menueAuswahl != 'Q');
  //Serial-Buffer leeren
  do
  {
    delay(1);
    Serial.read();
  }
  while (Serial.available()); //Warte auf Eingabe
  Serial.println(char(menueAuswahl));

  if (menueAuswahl == 'Q')
  {
    Serial.println(F("Abbruch durch Benutzer!"));
    return;
  }

  else if (menueAuswahl == 'R')
  {
    Serial.println(F("Lese Block!"));
  }

  else if (menueAuswahl == 'W')
  {
    //Eingabe der Daten fuer den Block
    Serial.println(F("Warte auf Daten (max. 16 Byte):"));
    for (byte i = 0; i < 16; i++)
    {
      while (!Serial.available());
      inWert = Serial.read();
      if (inWert == char(13)) break;
      writeBuf[i] = inWert;
    }
    //Abbruch, wenn keine Daten eingegeben wurden
    if (writeBuf[0] == char(0))
    {
      Serial.println(F("Abbruch! Keine Daten eingegeben!"));
      return;
    }
    Serial.print(F("Hex : "));
    for (byte i = 0; i < 16; i++)
    {
      if (writeBuf[i] < 16) Serial.print(F("0"));
      Serial.print(writeBuf[i], HEX);
      Serial.print(F(" "));
    }
    Serial.println();
    Serial.print(F("Char:"));
    for (byte i = 0; i < 16; i++)
    {
      if (writeBuf[i] < 32)
      {
        Serial.print(F(" "));
        Serial.print(F("NA"));
      }
      else
      {
        Serial.print(F("  "));
        Serial.print((char)writeBuf[i]);
      }
    }
    Serial.println();
    Serial.println(F("Schreibe Block! Bestaetigen mit <J>"));
    if (!bestaetige()) return;
  }

  else if (menueAuswahl == 'C')
  {
    Serial.println(F("Loesche Block! Bestaetigen mit <J>"));
    if (!bestaetige()) return;
  }

  //Auf gueltigen RFID-Tag warten
  Serial.println(F("Warte auf RFID-Tag ..."));
  bool uidPruefung = false;
  unsigned long startZeit = millis(); //Startzeit Zeitueberwachung
  do
  {
    //Pruefe auf Zeitueberschreitung
    if (millis() - startZeit > ueWaZeit)
    {
      Serial.println(F("Zeitueberschreitung!"));
      return;
    }
  }
  //Warte auf RFID-Tag und pruefe auf Gueltigkeit
  while (!MFR.PICC_IsNewCardPresent() || !MFR.PICC_ReadCardSerial());
  byte anzGueltUID = sizeof(gueltUID) / gueltUIDSize; //Anzahl der gueltigen UIDs
  for (byte i = 0; i < anzGueltUID; i++)
  {
    if (memcmp(MFR.uid.uidByte, gueltUID[i], gueltUIDSize) == 0) uidPruefung = true;
  }
  if (uidPruefung == false)
  {
    Serial.println(F("Abbruch! Kein gueltiger RFID-Tag!"));
    return;
  }

  //Gueltiger RFID-Tag gelesen
  Serial.print(F("Gueltiger RFID-Tag: "));
  for (byte i = 0; i < gueltUIDSize; i++)
  {
    if (MFR.uid.uidByte[i] < 16) Serial.print(F("0"));
    Serial.print(MFR.uid.uidByte[i], HEX);
    Serial.print(F(" "));
  }
  Serial.println();

  //Authentifizierung
  status = MFR.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, sectorTrailer, &key, &(MFR.uid));
  Serial.print(F("Status Authentifizierung: "));
  Serial.println(MFR.GetStatusCodeName(status));
  if (status != MFRC522::STATUS_OK) return;

  if (menueAuswahl == 'W' || menueAuswahl == 'C')
  {
    //Lese Block vor dem Schreiben
    Serial.println(F("Lese Block vor dem Schreiben:"));
    status = leseBlock(blockAdresse);
    if (status != MFRC522::STATUS_OK) return;

    //Schreibe auf Block
    Serial.print(F("Schreibe Block ... "));
    status = MFR.MIFARE_Write(blockAdresse, writeBuf, 16);
    if (status != MFRC522::STATUS_OK)
    {
      Serial.print(F("Status Schreiben: "));
      Serial.println(MFR.GetStatusCodeName(status));
      return;
    }
    Serial.println(F("geschrieben!"));

    //Lese Block nach dem Schreiben
    Serial.println(F("Lese Block nach dem Schreiben:"));
    status = leseBlock(blockAdresse);
  }
 
  else if (menueAuswahl == 'R')
  {
    status = leseBlock(blockAdresse);
  }

  // Aendert den Status von "Aktiv" auf "Halt"
  MFR.PICC_HaltA();
  // Beendet die Authentifizierung
  MFR.PCD_StopCrypto1();
}


byte leseBlock(byte blockAdd)
{
  byte sizeOfReadBuf = sizeof(readBuf);
  byte status = MFR.MIFARE_Read(blockAdd, readBuf, &sizeOfReadBuf);
  if (status == MFRC522::STATUS_OK)
  {
    Serial.print(F("Block "));
    Serial.print(blockAdd);
    Serial.print(F(" (HEX) : "));
    for (byte i = 0; i < 16; i++)
    {
      if (readBuf[i] < 16) Serial.print(F("0"));
      Serial.print(readBuf[i], HEX);
      Serial.print(F(" "));
    }
    Serial.println();
    Serial.print(F("Block "));
    Serial.print(blockAdd);
    Serial.print(F(" (Char):"));
    for (byte i = 0; i < 16; i++)
    {
      if (readBuf[i] < 32)
      {
        Serial.print(F(" "));
        Serial.print(F("NA"));
      }
      else
      {
        Serial.print(F("  "));
        Serial.print((char)readBuf[i]);
      }
    }
    Serial.println();
  }
  else
  {
    Serial.print(F("Status Lesen: "));
    Serial.println(MFR.GetStatusCodeName(status));
  }
  return status;
}


bool bestaetige()
{
  while (Serial.available()) //Serial-Buffer leeren
  {
    Serial.read();
    delay(1);
  }
  while (!Serial.available());
  if (Serial.read() != 'J')
  {
    Serial.println(F("Abbruch durch Benutzer!"));
    return false;
  }
  else return true;
}

Ausgabe am Seriellen Monitor:


Zurück zu Tag-Speicher lesen

Weiter zu Access Bits