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-BufferSerial.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:
Abbildung 4-1: Ausgabe am Seriellen Monitor des RFID Programm 3