Arduino #40. Программа копировщик ключей RFID (Mifare) Частота работы ключей 13,56 МГц
Программа копировщик ключей RFID (Mifare)
Понадобилось в очередной раз сделать дубликат ключа от домофона. Прежде чем писать что-то своё, порыскал по Интернету и нашёл интересный проект копировальщика всяких разных ключей. Так как мне нужен только ключ с частотой работы 13,56 МГц MIFARE, а модуль RFID-RC522 для таких ключей покупал давным давно, на посмотреть и даже что-то копировал, используя стандартные примеры библиотеки, то решил написать уже конкретный копировщик. Смотрите далее, что получилось.
Программа работает через последовательный порт. Загрузите Arduino IDE. Подключите Arduino UNO, с установленным шилдом копировщика, к USB компьютера. Откройте окно с последовательным портом. В него будет выводиться вся необходимая информация.
Вначале выводится заставка программы, пояснения по управлению, определяется подключение модуля RFID-RC522 и его версия. Если всё нормально, то будет короткий сигнал и программа выдаст сообщение о режиме работы: Чтение. Надпись о чтении ключа-оригинала, с которого будет делаться копия. Светодиод режима работы светится зелёным.
Pic 1. Копировщик RFID-RC522 v1.0
После прикладывания ключа, происходит чтение UID и выводится его значения в шестнадцатиричном коде. Обычно это первые 4 байта блока #0 сектора #0. Программа остаётся в режиме чтения, можно читать следующие ключи, смотреть их UID.
Pic 2. Копировщик RFID-RC522 v1.0
Для перехода к записи прочитанного ранее ключа, следует в порт отправить символ 'm'. Программа перейдёт к режиму записи. Светодиод режима работы переключится на красный цвет. На экране будет сообщение о режиме работы и приглашение поднести ключ-болванку, на которую и будет записан ранее считанный UID.
Pic 3. Копировщик RFID-RC522 v1.0
Перед записью оригинального UID на ключ-болванку проверяется её UID, если это отличный от оригинала, то производится запись UID и выводится сообщение об успешной записи. Программа остаётся в режиме записи и можно ещё записать несколько ключей-болванок.
Pic 4. Копировщик RFID-RC522 v1.0
Если попытаться записать на ключ-болванку, которая имеет точно такой же UID как и ключ-оригинал, то на экран выводится их UID в шестнадцатиричном коде и сообщение, что запись не требуется и выдаётся сигнал ошибки. Если попробовать записать UID на ключ-болванку, которая имеет повреждение или не предназначена для перезаписи, то программа выдаст сигнал ошибки и соответствующее сообщение.
Pic 5. Копировщик RFID-RC522 v1.0
Для перехода к режиму очистки и восстановления необходимо в порт отправить символ 'm'. Световой сигнал режима очистки будет синего цвета. После поднесения и удержания ключа, происходит очистка его блоков. После успешной очистки выводится сигнал и сообщение. Программа автоматически переходит к чтению ключа. Светодиод меняет цвет на зелёный.
Pic 6. Копировщик RFID-RC522 v1.0
Если есть интерес посмотреть дамп памяти ключа, то в порт отправить символ 'd'. Светодиод режимов погаснет. Поднесите ключ к считывателю и удерживайте его до полного окончания вывода на экран дампа памяти ключа.
Pic 7. Копировщик RFID-RC522 v1.0
По завершению считывания программа переходит в режим чтения и светодиод переключается на зелёный цвет. Обычные ключи имеют 1к памяти и она организована в 16 секторов по 4 блока, в каждом блоке по 16 байт, где обычно первые 4 байта блока #0 сектора #0 являются UID ключа.
Pic 8. Копировщик RFID-RC522 v1.0
Схема копировальщика
Полноценную схему не привожу, устройство достаточно простое, привожу таблицу соединений.
ПОДКЛЮЧЕНИЕ
-----------
Типовое подключение модуля mfrc522-MFRC522
Typical pin layout used:
--------------------------------------------------------
MFRC522 Arduino Arduino Arduino
Reader/PCD Uno/101 Mega Nano v3
Signal Pin Pin Pin Pin
--------------------------------------------------------
SPI SS SDA(SS) 10 53 D10
SPI MOSI MOSI 11 / ICSP-4 51 D11
SPI MISO MISO 12 / ICSP-1 50 D12
SPI SCK SCK 13 / ICSP-3 52 D13
RST RST 9
RGB-Led Arduino Uno
R - 2
G - 4
B - 5
ComA -[2,4 kOHm]- 5V
Beeper Arduino Uno
1 -[200]- 6
2 - GND
Скетч
Скетч перерабатывался из программы оригинала довольно сильно, всё, что не нужно выкинул, остальное привёл в надлежащий порядок, что-то оптимизировал, что-то добавил своё. Скетч снабдил подробными пояснениями.
/**********************************************************************
Проект по копированию ключей mfrc522 MIFARE 13,56 MHz
2025-04-11 Mr.ALB Начало на основе проекта
https://github.com/SibMan54/Arduino-mfrc522-iButton-Duplicator.git
2025-04-11 Mr.ALB Переработал программу под MIFARE 13,56 MHz
Удалось записать старые ключи, которые думал, что
они не записываются. UID ключа вводил в программе.
В режиме записи код из указанного keyID, а не
считанного с образца. Пока так.
2025-04-12 Mr.ALB Полностью копировщик v1.0. Читает ключ-оригинал,
записывает считанный ключ-оригинал в ключ-болванку,
очищает и восстанавливает подпорченный ключ.
Выводит дамп памяти ключа (символ 'd').
Хотелось бы сделать запись считанного ключа
в EEPROM, это уже потом, в следующей модификации.
Пока же вполне успешно копирует ключи.
ПРИМЕЧАНИЕ:
Сделал шилд для Arduino UNO, заметил такую особенность, что если
модуль MFRS522 ближе чем 25 мм к плате шилда, то модуль не работает.
Скорее его антенна не работает из-за помех платы, так как там
отверстия металлизированные.
ПОДКЛЮЧЕНИЕ
-----------
Типовое подключение модуля mfrc522-MFRC522
Typical pin layout used:
--------------------------------------------------------
MFRC522 Arduino Arduino Arduino
Reader/PCD Uno/101 Mega Nano v3
Signal Pin Pin Pin Pin
--------------------------------------------------------
SPI SS SDA(SS) 10 53 D10
SPI MOSI MOSI 11 / ICSP-4 51 D11
SPI MISO MISO 12 / ICSP-1 50 D12
SPI SCK SCK 13 / ICSP-3 52 D13
RST RST 9
RGB-Led Arduino Uno
R - 2
G - 4
B - 5
ComA -[2,4 kOHm]- 5V
Beeper Arduino Uno
1 -[200]- 6
2 - GND
2025-04-12 v1.0
Скетч использует 14596 байт (45%) памяти устройства.
Глобальные переменные используют 354 байт (17%).
Проект на сайте:
https://mralb.ru/sections/programming/arduino_40.php
Архив проекта: https://disk.yandex.ru/d/9w8T_0PoacZDHg
*********************************************************************/
// Подключаем библиотеки#include<SPI.h>#include<MFRC522.h>#include"pitches.h"// Ноты// Настройки#define RST_PIN 9 // mfrc522#define SS_PIN 10 // mfrc522#define R_Led 2 // Red RGB-Led#define G_Led 4 // Green RGB-Led#define B_Led 5 // Blue RGB-Led#define RGB_LED_COM_ANODE // Общий Анод, если общий катод,
; // то закомментировать#ifdef RGB_LED_COM_ANODE
// Определение сигналов для включения светодиода с общим анодом#define LED_ON LOW#define LED_OFF HIGH#else// Определение сигналов для включения светодиода с общим катодом#define LED_ON HIGH#define LED_OFF LOW#endif#define speakerPin 6 // Спикер, он же buzzer, он же beeper
; // Спикер не активный!MFRC522 mfrc522(SS_PIN, RST_PIN); // Создаем объект MFRC522MFRC522::MIFARE_Key key; // Объект ключа размер 6byte addr[8]; // временный буферbyte keyID[8]; // ID ключа для записиbool statOk =false; // статус успешной операции,
; // чтобы запись, очистка не проиходила по кругу// Режим работы копировальщикаenum emMode
{
md_read,
md_write,
md_clearRepair
};
emMode copierMode = md_read; // Начальный режим: чтение ключа// Массив надписейconstchar* msg[] =
{
"Mr.ALB","v1.0","2025-04-12","Mode: ","Чтение","Ключ найден",
};
enum emMsg
{
msAutor,
msVer,
msDateVer,
msMode,
msRead,
msKeyFound
};
bool flag =false;
/****************************** Фунукия: Настройка *****************************/voidsetup()
{
Serial.begin(9600);
while (!Serial); // Ожидание подключение последовательного портаdelay(100); // Задержка для чистоты вывода// Конфигурация пиновpinMode(speakerPin,OUTPUT);
soundStartOK(); // Звук включенияpinMode(R_Led,OUTPUT); // RGB-led redpinMode(G_Led,OUTPUT); // RGB-led greenpinMode(B_Led,OUTPUT); // RGB-led blue// Проверим работу RGB-Led
fnLedOn(R_Led); delay(500);
fnLedOn(G_Led); delay(500);
fnLedOn(B_Led); delay(500);
// Конфигурация MFRC522SPI.begin(); // Инициализируем SPI интерфейс
mfrc522.PCD_Init(); // Инициализируем MFRC522Serial.println(F("Программа копировщик ключей RFID (Mifare)"));
Serial.println(F(" Частота работы ключей 13,56 МГц"));
Serial.print (F(" ")); Serial.println(msg[msAutor]);
Serial.print (F(" ")); Serial.println(msg[msVer]);
Serial.print (F(" ")); Serial.println(msg[msDateVer]);
Serial.println();
Serial.println(F("Примечание:"));
Serial.println
(F(" * Для смены режима - отправьте в порт символ 'm'"));
Serial.println
(F(" * Дамп памяти ключа - отправьте в порт символ 'd'"));
Serial.println
(F(" * Mode: Чтение|Запись|Очистка-Восстановление"));
Serial.println();
fnShowReaderDetails(); // Информация об модуле RFID-MFRC522Serial.println
(F("------------------------------------------------------"));
Serial.println();
Serial.print(msg[msMode]);
Serial.println(msg[msRead]);
flag =true;
fnLedOn(G_Led);
}
// end setup()unsignedlong stTimer =millis();
voidloop()
{
char echo;
while ((millis() - stTimer < 100) ||!echo)
{
echo =Serial.read(); // Чтение порта// Переключатель режима чтение|запись|очисткаif (echo =='m')
{
Serial.println();
Serial.print(msg[msMode]);
flag =true;
switch (copierMode)
{
case md_read:// 1 Было: Чтение
copierMode = md_write; // Переход на ЗаписьSerial.println(F("Запись"));
fnLedOn(R_Led);
break;
case md_write:// 2 Было: Запись
copierMode = md_clearRepair; // Переход на ОчисткуSerial.println(F("Очистка и Восстановление"));
fnLedOn(B_Led);
break;
case md_clearRepair:// 3 Было: Очистка
copierMode = md_read; // Переход на ЧтениеSerial.println(msg[msRead]);
fnLedOn(G_Led);
}
delay(1);
soundStep();
statOk =false;
}
// Получение полного дампа памяти ключаif (echo =='d')
{
flag =true;
soundStep();
Serial.println(F("-------------------------------------"));
fnPrintDumpInfo();
fnLedOn(G_Led);
Serial.print(msg[msMode]);
Serial.println(msg[msRead]);
}
}
// if (millis() - stTimer < 100) // Задержка на 100 мс// return; // Вернёмся в начало loop()// Выбор режима работыswitch (copierMode)
{
case md_read:// Режим: Чтение ключаif (searchMifare()) // Поиск и чтение ключа
{
soundReadOk();
copierMode = md_read;
}
break;
case md_write:// Режим: Запись ключа
write2Mifare(); // Запись ключаbreak;
case md_clearRepair:// Режим: Очистка ключаif (!statOk)
clear2Mifare();
else
{ // Переходим в режим чтения
copierMode = md_read;
fnLedOn(G_Led);
Serial.println();
Serial.print(msg[msMode]);
Serial.println(msg[msRead]);
delay(500);
}
}
// end switch
stTimer =millis(); // Обновим значение задержки
}
// end loop()/****************************** ********* ФУНКЦИИ ************ *****************************//******************************************************** Функция: Проверка подключения и версии модуля MFRC522 *******************************************************/void fnShowReaderDetails()
{
// Версия программного обеспечения MFRC522byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
Serial.print(F("Модуль RFID-MFRC522 версия прошивки: 0x"));
Serial.print(v,HEX);
// Если возвращает 0x00, 0xFF, 0x80, то проблемы с подключением MFRC522if ((v == 0x00) || (v == 0xFF) || (v == 0x80))
{
Serial.println();
Serial.println
(F("ПРЕДУПРЕЖДЕНИЕ: Сбой связи, проверьте подключение MFRC522"));
soundErrorBeep();
while (1); // Остановим программу
}
// Определяем версию программного обеспечения MFRC522if (v == 0x91)
Serial.print(F(" = v1.0"));
elseif (v == 0x92)
Serial.print(F(" = v2.0"));
elseif (v == 0x12)
Serial.print(F(" = v3.0"));
elseSerial.print(F("(Неизвестная)"));
Serial.println("");
soundStep();
}
// end fnShowReaderDetails/******************************************************** Функция: Вывод информации с ключа *******************************************************/void fnPrintDumpInfo()
{
statOk =false;
do {
if (flag)
{
fnAllLedOff();
Serial.println(F("Поднесите ключ к модулю MFRC522"));
}
// Ожидаем ключchar echo;
while (!mfrc522.PICC_IsNewCardPresent())
{
echo =Serial.read();
if (echo =='m'|| echo =='d')
{
soundStep();
return;
}
}
if (flag)
{
Serial.println(F("Считывание данных ключа"));
flag =false;
}
if (!mfrc522.PICC_ReadCardSerial())
//return; // Если ошибка, то выходимcontinue;
// Выводит отладочную информацию о плате;// автоматически вызывается функция PICC_HaltA()
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
flag =true; // Поднимаем флаг надписи сканирования
statOk =true;
} while (!statOk);
soundReadOk();
delay(1000);
}
// end fnPrintDumpInfo()/********************************* Функция: Поиск и чтение ключа ********************************/bool searchMifare()
{
bool result =false;
for (byte i = 0; i < 6; i++)
{
key.keyByte[i] = 0xFF;
}
if (flag)
{
Serial.println(F("Приложите Ключ-ОРИГИНАЛ"));
flag =false;
}
// Ожидаем считывания меткиif (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial())
{
flag =true;
// Копируем UID метки в массивSerial.println();
Serial.println(msg[msKeyFound]);
Serial.print(F("Размер UID = "));
Serial.println(mfrc522.uid.size);
Serial.print(F("UID ключа в addr["));
Serial.print(mfrc522.uid.size);
Serial.print(F("]:"));
for (byte i = 0; i < mfrc522.uid.size; i++)
{
addr[i] = mfrc522.uid.uidByte[i];
Serial.print(F(" 0x"));
if (addr[i] < 10)
Serial.print("0");
Serial.print(addr[i],HEX);
}
Serial.println();
mfrc522.PICC_HaltA(); // Останавливаем считывание меткиSerial.print(F("Массив keyID["));
Serial.print(mfrc522.uid.size);
Serial.print(F("]: "));
for (byte i = 0; i < mfrc522.uid.size; i++)
{
keyID[i] = addr[i];
Serial.print(" 0x");
if (keyID[i] < 10)
Serial.print("0");
Serial.print(keyID[i],HEX);
}
Serial.println();
Serial.println();
result =true;
}
return result;
}
// end searchMifare()/********************************* Функция: Запись UID ключа ********************************/bool write2Mifare()
{
bool result =false;
bool Check =false;
// Подготовка массива UID ключаfor (byte i = 0; i < 6; i++)
{
key.keyByte[i] = 0xFF;
}
if (flag)
{
Serial.println(F("Приложите Ключ-БОЛВАНКУ"));
flag =false;
}
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial())
{
// Считываем UID ключаfor (byte i = 0; i < mfrc522.uid.size; i++)
{
addr[i] = mfrc522.uid.uidByte[i];
// Выводим значение байта приложенного ключаSerial.print(F("addr["));
Serial.print(i);
Serial.print(F("]: 0x"));
if (addr[i] < 10)
Serial.print("0");
Serial.println(addr[i],HEX);
// Выводим значение байта, который хотим записать в UIDSerial.print(F("keyID["));
Serial.print(i);
Serial.print(F("]: 0x"));
if (keyID[i] < 10)
Serial.print(F("0"));
Serial.println(keyID[i],HEX);
// Проверяем код для записи с кодом приложенного ключаif (addr[i] != keyID[i])
{ // Если хоть один не совпадает, то выходим из for
Check =true;
break;
}
}
// end forif (Check) // Проверка приложенного ключа = true
{
// Если коды не совпадают пишем новый UID на меткуif (mfrc522.MIFARE_SetUid(keyID, mfrc522.uid.size,true))
{
Serial.println(F("Ключ успешно скопирован!"));
soundReadOk();
result =true;
statOk = result;
}
else
{
Serial.println(F("Ошибка копирования!"));
soundErrorBeep();
}
}
else
{
Serial.println(F("Это тот же ключ. Запись не требуется."));
soundErrorBeep();
}
delay(1000);
Serial.println();
flag =true;
}
mfrc522.PICC_HaltA(); // Останавливаем считывание метки
mfrc522.PCD_StopCrypto1();
return result;
}
// end write2Mifare()/********************************* Функция: Очистка ключа ********************************/bool clear2Mifare()
{
bool result =false;
if (flag)
{
Serial.println(F("Приложите Ключ для его очистки и восстановления"));
flag =false;
}
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial())
{
Serial.println(msg[msKeyFound]);
// Очищаем ключfor (byte i = 0; i < 16; i++)
{
bytebuffer[18];
memset(buffer, 0,sizeof(buffer));
if (mfrc522.MIFARE_Write(i,buffer, 16))
{
Serial.print(F("Блок "));
Serial.print(i);
Serial.println(F(" очищен"));
soundStep();
delay(30);
}
else
{
Serial.print(F("Не удалось очистить блок "));
Serial.println(i);
soundErrorBeep();
result =false;
//return result; ?
}
}
// end for// Останавливаем считывание ключа
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
soundReadOk();
Serial.println(F("Ключ успешно очищен"));
result =true;
flag =true;
}
elseif (mfrc522.MIFARE_UnbrickUidSector(false))
{
Serial.println
(F("Очищен сектор #0, записано в UID: 0x01 0x02 0x03 0x04"));
Serial.println(F("Теперь карта снова должна быть отзывчивой."));
soundReadOk();
result =true;
flag =true;
}
statOk = result;
return result;
}
// end clear2Mifare()/************************************* Функция: Выключение светодиодов ************************************/void fnAllLedOff()
{
digitalWrite(R_Led, LED_OFF);
digitalWrite(G_Led, LED_OFF);
digitalWrite(B_Led, LED_OFF);
}
// end fnAllLedOff()/************************************* Функция: Включение светодиода ************************************/void fnLedOn(byte led)
{
fnAllLedOff();
digitalWrite(led, LED_ON);
}
// end fnLedOn()//************************************//************** ЗВУКИ ***************//************************************/************************************* Функция: Звук ОК ************************************/void soundReadOk()
{
for (int i = 400; i < 6000; i = i * 1.5)
{
tone(speakerPin, i);
delay(20);
}
noTone(speakerPin);
}
// end soundReadOk()/************************************ Функция: Звук "очередной шаг" ***********************************/void soundStep()
{
for (int i = 2500; i < 6000; i = i * 1.5)
{
tone(speakerPin, i);
delay(10);
}
noTone(speakerPin);
}
// end soundStep()/************************************ Функция: Звук "ERROR" ***********************************/void soundErrorBeep()
{
for (int j = 0; j < 3; j++)
{
for (int i = 1000; i < 2000; i = i * 1.1)
{
tone(speakerPin, i);
delay(10);
}
delay(50);
for (int i = 1000; i > 500; i = i * 1.9)
{
tone(speakerPin, i);
delay(10);
}
delay(50);
}
noTone(speakerPin);
}
// end soundErrorBeep()/********************************** Функция: Звук "Включение" *********************************/void soundStartOK()
{
tone(speakerPin, NOTE_A7); delay(100);
tone(speakerPin, NOTE_G7); delay(100);
tone(speakerPin, NOTE_E7); delay(100);
tone(speakerPin, NOTE_C7); delay(100);
tone(speakerPin, NOTE_D7); delay(100);
tone(speakerPin, NOTE_B7); delay(100);
tone(speakerPin, NOTE_F7); delay(100);
tone(speakerPin, NOTE_C7); delay(100);
noTone(speakerPin);
}
// end soundStartOK()
Реализация
Это устройство используется довольно редко, по необходимости, поэтому полноценную конструкцию делать, на мой взгляд, не имеет смысла. Решил сделать небольшой шилд. Его основа – монтажная плата размерами 40*60 мм. На этой плате установлены: бипер (пассивный), RGB-светодиод, панелька для подключения модуля RFID-RC522, опорная стойка под модулем RFID-RC522.
Включен режим очистки и восстановления. Светодиод светится синим.
Pic 19. Копировщик RFID-RC522 v1.0
Ракурс при котором видно, что модуль RFID-RC522 приподнят над платой шилда, чтобы была устойчивая работа. Если модуль прижать к плате шилда, то работа модуля становится неустойчивой и выдает код 0x80 при его включении.
Pic 20. Копировщик RFID-RC522 v1.0
Возможно что-то ещё будет меняться, тогда тут дополню, а пока и так всё работает.