- Modul MFRC522 (RC522) beroperasi pada 13,56 MHz (HF) dan umum untuk membaca/menulis MIFARE Classic 1K/4K.
- Panduan ini mencakup: wiring, instalasi library, baca UID, baca blok, tulis blok, hingga ganti key sektor (opsional).
- Contoh untuk Arduino UNO/Nano, Mega, ESP8266, dan ESP32, plus tips jarak baca & troubleshooting.
Dasar Teori: RC522 & Kartu MIFARE Classic
RC522 adalah IC reader RFID/NFC 13,56 MHz yang berkomunikasi melalui SPI. Kartu MIFARE Classic 1K terdiri dari 16 sektor, masing-masing 4 blok (total 64 blok, 0–63). Setiap blok = 16 byte.
- Block 0 (sektor 0) memuat UID & data pabrik → jangan ditulis.
- Trailer block tiap sektor (blok ke-3) berisi Key A, Access Bits, dan Key B.
- Default key pabrik sering
FF FF FF FF FF FF
.
Komponen & Peralatan
- Arduino UNO/Nano (5 V) atau Mega, atau ESP8266/ESP32 (3,3 V).
- Modul RC522 + kartu/tag MIFARE Classic 1K.
- Kabel jumper, (opsional) breadboard, dan level shifter bila MCU 5 V.
- Power 3,3 V stabil untuk RC522.
Skema Koneksi
Arduino UNO / Nano (SPI Hardware)
RC522 | Arduino |
---|---|
SDA/SS | D10 |
SCK | D13 |
MOSI | D11 |
MISO | D12 |
RST | D9 |
3.3V | 3.3V |
GND | GND |
Arduino Mega 2560
RC522 | Mega |
---|---|
SDA/SS | D53 (atau sesuaikan di kode) |
SCK | D52 |
MOSI | D51 |
MISO | D50 |
RST | D5 (contoh) |
3.3V | 3.3V |
GND | GND |
ESP8266 (NodeMCU)
RC522 | ESP8266 |
---|---|
SDA/SS | D8 |
SCK | D5 |
MOSI | D7 |
MISO | D6 |
RST | D3 |
3.3V | 3V3 |
GND | GND |
ESP32 (DevKit)
RC522 | ESP32 |
---|---|
SDA/SS | GPIO 5 |
SCK | GPIO 18 |
MOSI | GPIO 23 |
MISO | GPIO 19 |
RST | GPIO 22 |
3.3V | 3V3 |
GND | GND |
Catatan: beberapa board menandai pin “SDA” sebagai “SS”. Pada SPI, “SDA” di RC522 = Slave Select.
Layanan Kami :
Instalasi Library & Contoh Dasar
- Arduino IDE → Tools → Manage Libraries…
- Cari dan pasang “MFRC522 by GithubCommunity” (fork miguelbalboa).
- Pastikan SPI library tersedia (bawaan Arduino).
Contoh: Baca UID & Tipe Kartu
// --- Baca UID & Tipe Kartu (UNO/Nano: SS=10, RST=9) ---
#include <SPI.h>
#include <MFRC522.h>
constexpr uint8_t SS_PIN = 10;
constexpr uint8_t RST_PIN = 9;
MFRC522 rfid(SS_PIN, RST_PIN);
void setup() {
Serial.begin(115200);
SPI.begin();
rfid.PCD_Init();
rfid.PCD_SetAntennaGain(rfid.RxGain_max); // opsional: sedikit menambah jarak
Serial.println(F("RC522 siap. Tempelkan kartu..."));
}
void loop() {
if (!rfid.PICC_IsNewCardPresent()) return;
if (!rfid.PICC_ReadCardSerial()) return;
Serial.print(F("UID: "));
for (byte i = 0; i < rfid.uid.size; i++) {
if (rfid.uid.uidByte[i] < 0x10) Serial.print(F("0"));
Serial.print(rfid.uid.uidByte[i], HEX);
if (i != rfid.uid.size - 1) Serial.print(F(":"));
}
Serial.println();
MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
Serial.print(F("Tipe: "));
Serial.println(rfid.PICC_GetTypeName(piccType));
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
Membaca & Menulis Blok Data (MIFARE Classic)
Peta sektor/blok: setiap sektor memiliki 4 blok; tiga blok data + satu trailer. Contoh sektor 1 = blok 4,5,6,7 (blok 7 = trailer).
Baca Blok (Auth Key A default)
#include <SPI.h>
#include <MFRC522.h>
constexpr uint8_t SS_PIN = 10;
constexpr uint8_t RST_PIN = 9;
MFRC522 rfid(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
void setup() {
Serial.begin(115200);
SPI.begin();
rfid.PCD_Init();
for (byte i=0; i<6; i++) key.keyByte[i] = 0xFF; // default FF..FF
Serial.println(F("Siap baca blok. Tempelkan kartu..."));
}
bool authenticateBlock(byte block) {
MFRC522::StatusCode status = rfid.PCD_Authenticate(
MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(rfid.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Auth gagal: "));
Serial.println(rfid.GetStatusCodeName(status));
return false;
}
return true;
}
void loop() {
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
byte block = 4; // blok data pertama sektor 1
if (!authenticateBlock(block)) { rfid.PICC_HaltA(); rfid.PCD_StopCrypto1(); return; }
byte buffer[18]; byte size = sizeof(buffer);
MFRC522::StatusCode status = rfid.MIFARE_Read(block, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Baca gagal: "));
Serial.println(rfid.GetStatusCodeName(status));
} else {
Serial.print(F("Isi block ")); Serial.print(block); Serial.print(F(": "));
for (byte i=0; i<16; i++) {
if (buffer[i] < 0x10) Serial.print(F("0"));
Serial.print(buffer[i], HEX); Serial.print(F(" "));
}
Serial.println();
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
Tulis Blok (16 byte)
Pilih blok data (mis. blok 4/5/6). Jangan menulis ke trailer block.
#include <SPI.h>
#include <MFRC522.h>
constexpr uint8_t SS_PIN = 10;
constexpr uint8_t RST_PIN = 9;
MFRC522 rfid(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
void makeBlockFromString(const String& s, byte out[16]) {
for (byte i=0; i<16; i++) out[i] = 0x20; // padding spasi
for (byte i=0; i<16 && i < s.length(); i++) out[i] = (byte)s[i];
}
void setup() {
Serial.begin(115200);
SPI.begin();
rfid.PCD_Init();
for (byte i=0; i<6; i++) key.keyByte[i] = 0xFF; // default
Serial.println(F("Siap tulis block. Tempelkan kartu..."));
}
void loop() {
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
byte block = 4; // tulis ke blok 4
auto st = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &rfid.uid);
if (st != MFRC522::STATUS_OK) {
Serial.print(F("Auth gagal: ")); Serial.println(rfid.GetStatusCodeName(st));
rfid.PICC_HaltA(); rfid.PCD_StopCrypto1(); return;
}
byte dataBlock[16];
makeBlockFromString("KANASOLUSI.ID", dataBlock); // ≤16 char
st = rfid.MIFARE_Write(block, dataBlock, 16);
if (st != MFRC522::STATUS_OK) {
Serial.print(F("Tulis gagal: ")); Serial.println(rfid.GetStatusCodeName(st));
} else {
Serial.print(F("Berhasil tulis block ")); Serial.println(block);
}
// Verifikasi
byte buffer[18]; byte size = sizeof(buffer);
st = rfid.MIFARE_Read(block, buffer, &size);
if (st == MFRC522::STATUS_OK) {
Serial.print(F("Verif: "));
for (byte i=0;i<16;i++) Serial.write(buffer[i]);
Serial.println();
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
Hapus (Zero-fill) Blok
// Zero-fill blok data (pastikan sudah auth!)
byte zeros[16]; memset(zeros, 0x00, 16);
auto st = rfid.MIFARE_Write(block, zeros, 16);
if (st == MFRC522::STATUS_OK) Serial.println(F("Block di-nol-kan."));
(Opsional) Ubah Key & Access Bits Sektor (Advanced)
Trailer block (16 byte): KeyA(6)
+ AccessBits(3)
+ User(1)
+ KeyB(6)
. Contoh mengganti Key A/B sektor 1 (trailer = blok 7) ke nilai kustom dengan Access Bits standar.
byte newTrailer[16] = {
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5, // Key A
0xFF,0x07,0x80,0x69, // Access Bits + user data
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5 // Key B
};
byte trailerBlock = 7; // sektor 1
// Auth pakai key lama (mis. FF..FF) sebelum menulis
auto st = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &rfid.uid);
if (st == MFRC522::STATUS_OK) {
st = rfid.MIFARE_Write(trailerBlock, newTrailer, 16);
if (st == MFRC522::STATUS_OK) Serial.println(F("Trailer updated. Gunakan key baru selanjutnya."));
}
Menyimpan Data Terstruktur
Angka (int32) + Checksum XOR
long value = 123456;
byte buf[16] = {0};
buf[0] = (byte)((value >> 24) & 0xFF);
buf[1] = (byte)((value >> 16) & 0xFF);
buf[2] = (byte)((value >> 8) & 0xFF);
buf[3] = (byte)(value & 0xFF);
buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; // checksum sederhana
// tulis buf ke block 4 setelah auth
Teks (Nama, ID)
Gunakan 2 blok bertetangga (mis. 4 & 5) untuk string lebih panjang. Simpan length di awal dan pad dengan spasi.
Optimasi & Kualitas Bacaan
- Jarak & orientasi: antena RC522 kecil; jarak praktik ~2–4 cm. Ubah sudut kartu.
- Gain antena:
PCD_SetAntennaGain(RxGain_max)
membantu sedikit. - Hindari logam di dekat antena; bisa mengganggu medan.
- Catu 3,3 V stabil; suplai bersama MCU butuh arus memadai.
- Timing: beri jeda setelah operasi tulis; hindari spam perintah.
Troubleshooting
Gejala / Status | Penyebab | Solusi |
---|---|---|
STATUS_TIMEOUT |
Jarak/orientasi buruk | Dekatkan kartu, ubah sudut, naikkan gain antena |
STATUS_ERROR saat auth |
Key salah | Coba default FF FF FF FF FF FF , pastikan sektor & blok tepat |
Tulis sukses tapi baca tak berubah | Baca blok salah | Pastikan indeks blok benar; hindari trailer |
Reader tidak terdeteksi | Wiring SPI salah | Cek MOSI/MISO/SCK/SS & supply 3,3 V |
Modul panas | Salah tegangan/short | Pastikan 3,3 V; cek kabel & solder |
Sektor terkunci | Access Bits salah | Pakai kartu cadangan; pelajari pola bit & inversi sebelum menulis |
Checklist Produksi
- Gunakan tag asli; hindari UID clone untuk sistem serius.
- Definisikan skema data (offset, panjang, checksum, versi).
- Implementasi retry & timeout jelas di firmware.
- Log semua operasi tulis & kegagalan ke konsol/log.
- Enkripsi di aplikasi (server/host), kartu menyimpan ID/pointer saja.
FAQ
1) Apa perbedaan RC522 vs PN532?
RC522 ekonomis dan cukup untuk MIFARE Classic. PN532 lebih lengkap (Type A/B, P2P, emulasi kartu) dan kompatibilitas NFC ponsel lebih baik.
2) Bisa simpan NDEF agar terbaca ponsel?
Bisa di Classic, tetapi dukungan ponsel tidak konsisten. Untuk kompatibilitas luas gunakan NTAG21x atau reader PN532.
3) Kenapa tulis data gagal?
Biasanya karena salah key atau menulis trailer block. Pastikan autentikasi Key A/B benar dan tulis ke blok data.
4) Aman dihubungkan langsung ke 5 V UNO?
Tidak disarankan. RC522 butuh 3,3 V. Gunakan level shifter pada sinyal dari MCU 5 V.
5) Batas jarak baca?
Umumnya 2–4 cm. Pengaruh orientasi, antena, dan lingkungan signifikan.
Contoh Proyek Mini: Kartu Member
- Block 4:
NAME[0..15]
(16 byte) - Block 5:
ID[0..3], CHKSUM[1], RESERVED[...]
Reader menampilkan nama & ID; jika checksum cocok → akses OK.
Lampiran: Template Kode Serbaguna (Baca/Tulis/Erase)
#include <SPI.h>
#include <MFRC522.h>
// === PIN (ubah sesuai board) ===
constexpr uint8_t SS_PIN = 10; // UNO/Nano:10, Mega:53, ESP8266:D8, ESP32:5
constexpr uint8_t RST_PIN = 9; // UNO/Nano:9, Mega:5, ESP8266:D3, ESP32:22
constexpr byte BLOCK_TO_USE = 4; // Hindari block 0 & trailer (3,7,11,...)
MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
void printHex(const byte *buf, byte len) {
for (byte i=0; i<len; i++) { if (buf[i] < 0x10) Serial.print("0"); Serial.print(buf[i], HEX); Serial.print(" "); }
}
bool authBlock(byte block) {
auto s = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &mfrc522.uid);
if (s != MFRC522::STATUS_OK) { Serial.print("Auth fail: "); Serial.println(mfrc522.GetStatusCodeName(s)); return false; }
return true;
}
bool readBlock(byte block, byte out[18]) {
byte size = 18;
auto s = mfrc522.MIFARE_Read(block, out, &size);
if (s != MFRC522::STATUS_OK) { Serial.print("Read fail: "); Serial.println(mfrc522.GetStatusCodeName(s)); return false; }
return true;
}
bool writeBlock(byte block, const byte in[16]) {
auto s = mfrc522.MIFARE_Write(block, (byte*)in, 16);
if (s != MFRC522::STATUS_OK) { Serial.print("Write fail: "); Serial.println(mfrc522.GetStatusCodeName(s)); return false; }
return true;
}
void setup() {
Serial.begin(115200);
SPI.begin();
mfrc522.PCD_Init();
mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);
for (byte i=0;i<6;i++) key.keyByte[i] = 0xFF; // default
Serial.println("\\n[RC522 READY] Tempelkan kartu...");
}
void loop() {
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) return;
Serial.print("UID: "); printHex(mfrc522.uid.uidByte, mfrc522.uid.size); Serial.println();
if (!authBlock(BLOCK_TO_USE)) { HALT(); return; }
// === TULIS ===
byte dataBlock[16] = "KANASOLUSI.ID "; // 16 byte
if (writeBlock(BLOCK_TO_USE, dataBlock)) Serial.println("Write OK");
// === BACA ===
byte buffer[18];
if (readBlock(BLOCK_TO_USE, buffer)) {
Serial.print("Read: "); printHex(buffer, 16); Serial.print(" | ASCII: ");
for (byte i=0;i<16;i++) Serial.write(isPrintable(buffer[i]) ? buffer[i] : '.');
Serial.println();
}
// === ERASE (opsional) ===
// byte zeros[16]; memset(zeros, 0x00, 16); writeBlock(BLOCK_TO_USE, zeros);
HALT();
}
void HALT(){
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
Saat ini belum tersedia komentar.