RFID · разбор

RFID и Arduino: модуль RC522, метки и чтение номера

Карта без батарейки, поднесли — и дверь открылась. Разбираем, откуда метка берёт энергию, почему ключ от подъезда чаще всего не читается этим модулем, что происходит за те полсекунды, пока карта у считывателя, и что на самом деле лежит внутри карты. Схемы на странице живые: их можно двигать и щёлкать. Все числа — из даташитов, исходников библиотеки и карточек наших модулей.

Короткий ответ для нетерпеливых. Модуль RC522 работает на частоте 13,56 МГц. Металлические «таблетки» и большинство домофонных брелоков — это другие технологии, и он их не увидит никогда, сколько ни меняй программу. Что именно лежит у вас в кармане — разбираем во втором разделе.

Что вообще делает RFID-модуль

Внутри карты нет ни батарейки, ни кнопки. Есть микросхема размером с песчинку и несколько витков провода по краю пластика — антенна. Считыватель непрерывно создаёт вокруг себя переменное магнитное поле. Когда карта попадает в это поле, в её витках наводится ток — метка оживает ровно на то время, пока её держат рядом.

Дальше начинается разговор. Считыватель спрашивает, метка отвечает — но отвечает хитро: собственного передатчика у неё тоже нет, поэтому она то нагружает, то отпускает свою антенну, и считыватель чувствует эти лёгкие «подмигивания» в собственном поле. Поэтому дальность у такой связи всегда маленькая: это не радио на километр, а связь двух катушек, лежащих почти вплотную.

Откуда метка берёт энергию: батарейки внутри нетRC522модуль и его рамка-антеннаметка0 мм25 мм50 мм75 мм100 ммСила поля у метки48%Метка проснулась и отвечает
Подвиньте ползунок: поле спадает не плавно, а обвалом — вдвое дальше значит вчетверо-впятеро слабее. Поэтому «дальнобойного» RC522 не бывает: даташит обещает до 50 мм, а типовая красная плата уверенно берёт метку в паре сантиметров.

Отсюда сразу два практических вывода. Первый: «увеличить дальность» у RC522 по-настоящему нельзя — можно подкрутить усиление приёмника и добавить сантиметр, но метр не получится. Второй: любая помеха между модулем и меткой мешает. Металлический стол под платой, толстый корпус, фольгированная наклейка — всё это отбирает у метки энергию.

Главный вопрос: увидит ли RC522 ваш ключ

Половина неудач с RFID случается ещё до первой строчки кода — человек покупает модуль, чтобы «прочитать ключ от подъезда», и получает молчание. Дело не в модуле. Словом «ключ» называют три совершенно разные вещи, и переходов между ними не существует.

Какой у вас ключ — и увидит ли его RC522катушка внутри пластикаБрелок-капля от домофонаЧаще всего 125 кГц (EM-Marine)RC522 не видитЧто нужно: Модуль на 125 кГц, например RDM6300Брелоки 125 кГц и 13,56 МГц внешне неразличимы:частоту скажет модель домофона или проверка.Три разных мира, и переходов между ними нет:касание, без радиотаблетки 1-Wire125 кГцEM-Marine, домофоны13,56 МГц — окно RC522пропуска, проездные, метки
Разница между 125 кГц и 13,56 МГц — это не «немного другая настройка»: частоты отличаются в сто с лишним раз, и антенна каждой метки настроена строго на свою. Никакая прошивка не заставит RC522 увидеть брелок на 125 кГц.
Что за ключКак выглядитЧастотаRC522Чем читать
Таблетка (Touch Memory)Металлический кружок 16 мм, прикладывают вплотнуюРадио нет: контактНе видитДва провода и резистор 4,7 кОм
Брелок-капля от домофонаПластиковая капля с колечкомЧаще 125 кГцНе видитМодуль на 125 кГц (RDM6300)
Пропуск, карта, меткаПластиковая карта или круглая наклейка13,56 МГцЧитаетRC522 подходит
Транспортная картаКарта метро или автобуса13,56 МГцТолько номерRC522 подходит, но данные закрыты
Банковская картаКарта со значком бесконтактной оплаты13,56 МГцПолезного не даётДля проектов не годится

Как понять, что у вас. Металлическая таблетка — это контакт, ей нужен не считыватель, а два провода. Пластиковая капля или тонкая круглая метка — нужно узнавать: в старых домофонах это почти всегда 125 кГц, в новых встречаются и карты 13,56 МГц. Внешне они неразличимы, поэтому надёжный способ один: посмотреть модель домофона или спросить в обслуживающей организации. Пропуск на работу и транспортная карта — почти всегда 13,56 МГц.

Про копирование чужих ключей. Домофон и его система доступа — общее имущество дома, а дубликаты выдаёт обслуживающая организация. Эта страница объясняет, как устроена технология, и не является инструкцией по изготовлению дубликатов чужих ключей. Учиться удобнее и честнее на метках из комплекта модуля: они чистые, заводские ключи к ним известны, и портить нечего.

Что на самом модуле

Красная плата RC522 — это микросхема NXP MFRC522, кварц на 27,12 МГц (частота поля получается делением пополам — те самые 13,56 МГц) и рамка антенны, занимающая половину платы. Сбоку — планка из восьми контактов, которую почти всегда нужно припаять самому: в пакетике она лежит отдельно, а просто вставленные в отверстия штырьки контакта не дают. Это причина номер один всех «модуль не работает».

КонтактЧто этоКуда на Uno
SDAВыбор устройства на шине SPI (он же SS или CS)Вывод 10
SCKТакт шины: его задаёт платаВывод 13
MOSIДанные от платы к модулюВывод 11
MISOДанные от модуля к платеВывод 12
IRQСигнал «есть метка», нужен редкоНе подключаем
GNDОбщий проводGND
RSTСброс модуля; низкий уровень усыпляет егоВывод 9
3.3VПитание. Только 3,3 вольтаВывод 3.3V

Две подписи сбивают с толку. SDA здесь — не линия шины I2C, а обычный вывод выбора устройства на шине SPI: искать рядом с ним SCL бесполезно. А в характеристиках модуля перечислены сразу три интерфейса — SPI, UART и I2C: это умеет сама микросхема, но на гребёнку выведен только SPI, поэтому подключать модуль нужно именно к нему. Про саму шину — в соседнем разборе про модули и способы подключения.

Подключение: выводы зависят от платы

Проводов семь, и на разных платах они идут на разные выводы. Выберите свою плату — схема перестроится.

Семь проводов: слева плата, справа модульUnoRC522вывод 3.3V3.3VGNDGNDвывод 9RSTвывод 10SDAвывод 11MOSIвывод 12MISOвывод 13SCKВывод 3.3V на Uno рассчитан на 50 мА — модулю хватит, но вешать на него что-то ещё уже нестоит.
Питание модуля — только 3,3 В: по даташиту предел для микросхемы 3,6 В, а 4 В уже разрушают её. Сигнальные провода у пятивольтовых плат формально тоже выше нормы: в учебной схеме это работает годами, в изделии между платой и модулем ставят согласование уровней.

Питание — только 3,3 вольта. Даташит разрешает микросхеме от 2,5 до 3,6 В, а 4 В названы предельным значением, за которым начинается разрушение. Пять вольт на вывод питания — это не «с запасом», это конец модуля. При этом сигнальные провода у Uno и Nano пятивольтовые, и формально они тоже выше нормы: предел входа — питание плюс полвольта, то есть около 3,8 В. В учебной схеме такое соединение работает у миллионов людей, но если вы делаете изделие, между платой и модулем ставят преобразователь уровней, а лучше берут плату на 3,3 В.

Ток. Когда поле включено, микросхема по даташиту берёт до 100 мА, в типовом режиме — десятки миллиампер. Вывод 3.3V у Uno рассчитан на 50 мА, и обычно модулю этого хватает; а вот у недорогих Nano этот вывод намного слабее, и как раз там появляются «метка читается через раз» и перезагрузки платы. Лечится отдельным стабилизатором на 3,3 В и конденсатором рядом с модулем.

Первый скетч: номер метки

Библиотека называется MFRC522 — она есть в каталоге библиотек и ставится в два клика. Скетч ниже печатает номер метки и её тип. Строка с версией в самом начале — бесплатная проверка связи: если там 0x91 или 0x92, модуль отвечает.

// Читаем номер метки модулем RC522.
// Схема (Arduino Uno): SDA - 10, SCK - 13, MOSI - 11, MISO - 12,
// RST - 9, GND - GND, а питание модуля - ТОЛЬКО с вывода 3.3V.
#include <SPI.h>       // шина SPI: по ней плата разговаривает с модулем
#include <MFRC522.h>   // библиотека модуля RC522

const uint8_t PIN_SDA = 10;   // выбор устройства на шине, он же SS
const uint8_t PIN_RST = 9;    // сброс модуля

MFRC522 reader(PIN_SDA, PIN_RST);

void setup() {
  Serial.begin(9600);
  SPI.begin();                          // без этой строки модуль будет молчать
  reader.PCD_Init();
  reader.PCD_DumpVersionToSerial();     // версия в мониторе = связь есть
  Serial.println(F("Поднесите метку к модулю"));
}

void loop() {
  if (!reader.PICC_IsNewCardPresent()) return;   // метки рядом нет
  if (!reader.PICC_ReadCardSerial()) return;     // номер не прочитался

  Serial.print(F("Номер метки: "));
  for (uint8_t i = 0; i < reader.uid.size; i++) {
    if (reader.uid.uidByte[i] < 0x10) Serial.print('0');
    Serial.print(reader.uid.uidByte[i], HEX);
    Serial.print(' ');
  }
  Serial.println();

  MFRC522::PICC_Type type = reader.PICC_GetType(reader.uid.sak);
  Serial.print(F("Тип метки: "));
  Serial.println(reader.PICC_GetTypeName(type));

  reader.PICC_HaltA();   // отпускаем метку, иначе номер печатается без конца
}

В мониторе порта появится что-то вроде Firmware Version: 0x92 = v2.0, а после поднесения карты — Номер метки: 04 A3 19 7C и Тип метки: MIFARE 1KB. Номер может быть из четырёх или из семи байт — это нормально, длину сообщает сама метка.

Ничего не читается: разбор по симптомам

Сначала выясните главное: модуль вообще отвечает плате или нет. Этот короткий скетч отвечает ровно на этот вопрос и не требует никакой метки.

// Проверка связи с модулем: отвечает он или нет.
// Запускать, когда метки не читаются и непонятно, кто виноват.
#include <SPI.h>
#include <MFRC522.h>

const uint8_t PIN_SDA = 10;
const uint8_t PIN_RST = 9;

MFRC522 reader(PIN_SDA, PIN_RST);

void setup() {
  Serial.begin(9600);
  SPI.begin();
  reader.PCD_Init();
  delay(50);   // даём модулю проснуться после сброса

  uint8_t version = reader.PCD_ReadRegister(MFRC522::VersionReg);
  Serial.print(F("Регистр версии: 0x"));
  Serial.println(version, HEX);

  if (version == 0x00 || version == 0xFF) {
    Serial.println(F("Связи нет совсем: питание 3.3 В, провода SPI, пайка"));
  } else if (version == 0x91 || version == 0x92) {
    Serial.println(F("Отвечает настоящий MFRC522, можно нести метку"));
  } else {
    Serial.println(F("Отвечает, но чип не фирменный: дальность может быть меньше"));
  }
}

void loop() {
}

Значения 0x00 и 0xFF в даташите NXP не описаны вообще — их печатает не модуль, а тишина на шине: провод оборван, питания нет, гребёнка не пропаяна или в скетче забыт SPI.begin(). Дальше — таблица по симптомам.

СимптомЧто проверитьЧто сделать
В мониторе версия 0x00 или 0xFFЭто не версия чипа, а «связи нет вообще». Проверьте питание на модуле тестеромПропаять гребёнку, проверить SPI-провода и не забыть SPI.begin() в скетче
Работает, только если прижать провода рукойГребёнка вставлена, но не припаяна — модули почти всегда продаются с отдельной планкой контактовПрипаять контакты и осмотреть пайку: холодные пятаки и перемычки видно под лупой
Модуль отвечает, метки не читаютсяПоднести метку вплотную и с обеих сторон платы; проверить напряжение питания под нагрузкойДать модулю честные 3,3 В от отдельного стабилизатора, метку держать в паре сантиметров
Плата перезагружается при поднесении меткиВ момент включения поля модуль резко берёт ток, слабый источник 3,3 В проседаетКонденсатор 10–100 мкФ по питанию модуля и отдельный стабилизатор вместо вывода платы
На Leonardo, Micro или Pro Micro тишинаУ этих плат шины SPI на выводах 11, 12 и 13 нетПодключить MOSI, MISO и SCK к разъёму ICSP
Первая метка читается, вторая — нетПосле работы с памятью карта осталась «занятой»В конце цикла вызывать PICC_HaltA() и PCD_StopCrypto1()
Заработало вместе с картой памяти или дисплеем — и всё сломалосьНа шине SPI несколько устройств, а недорогие модули карт памяти не отпускают линию MISOПроверить, снимая соседнее устройство; каждому устройству нужен свой вывод выбора
Читает только вплотную, брелок не видит совсемВерсия чипа не 0x91 и не 0x92 — под красной крышкой не фирменная микросхемаПоднять усиление приёмника в скетче; если не помогло — менять модуль
Номер карты каждый раз новыйТак ведут себя карты со случайным номером, в том числе банковскиеДля проектов брать обычные метки: заводской номер у них постоянный

О чём модуль разговаривает с меткой

Полсекунды у считывателя — это не одно действие, а короткий диалог по правилам стандарта. Пройдите его по шагам: станет понятно, откуда берётся номер, зачем нужна антиколлизия и в какой момент вообще появляется защита.

шаг 4 из 8
О чём модуль разговаривает с меткоймодуль RC522меткаЕсть кто рядом? (команда REQA)Я здесь, номер у меня короткий (ответ ATQA)Называйте номер по битам (антиколлизия)Мой номер: 04 A3 19 7CРаботаю с тобой (SELECT), а ты назови тип (SAK)Проверка ключом сектора: три шага в обе стороныЧитаю 16 байт из блока 4Спи (команда HALT)Вот он, тот самый UID. Обратите внимание: номер звучит открыто и ДО всякой проверки ключей.Поэтому «номер совпал — открываем дверь» защитой не является.
Пройдите разговор по шагам. Главное на четвёртом шаге: номер метки звучит в эфире открыто, до всякой проверки ключей — именно поэтому он годится как «имя», но не как «пароль».

Обратите внимание на четвёртый шаг. Номер метки звучит в эфире открыто, до всякой проверки ключей — так устроен сам стандарт, иначе считыватель не смог бы отличить две карты друг от друга. Из этого следует главное правило всех проектов с картами: номер метки — это имя, а не пароль. Он прекрасно подходит, чтобы узнать «кто пришёл», и совершенно не подходит, чтобы решать «пускать ли внутрь» там, где есть чего терять.

Что лежит внутри карты

Самая распространённая метка — MIFARE Classic на 1 килобайт. Килобайт звучит солидно, но своих данных туда влезает заметно меньше: часть памяти занята заводским номером и ключами.

выбран сектор 1 — по схеме можно щёлкать мышкой
Карта памяти MIFARE Classic 1K: 16 секторов по 4 блокаВсего 1024 байта, но под ваши данные остаётся 752: остальное занимают ключи и заводской блок.Сектор 0, блок 0Сектор 0, блок 1Сектор 0, блок 2Сектор 0, блок 3Сектор 1, блок 4Сектор 1, блок 5Сектор 1, блок 6Сектор 1, блок 7Сектор 2, блок 8Сектор 2, блок 9Сектор 2, блок 10Сектор 2, блок 11Сектор 3, блок 12Сектор 3, блок 13Сектор 3, блок 14Сектор 3, блок 15Сектор 4, блок 16Сектор 4, блок 17Сектор 4, блок 18Сектор 4, блок 19Сектор 5, блок 20Сектор 5, блок 21Сектор 5, блок 22Сектор 5, блок 23Сектор 6, блок 24Сектор 6, блок 25Сектор 6, блок 26Сектор 6, блок 27Сектор 7, блок 28Сектор 7, блок 29Сектор 7, блок 30Сектор 7, блок 31Сектор 8, блок 32Сектор 8, блок 33Сектор 8, блок 34Сектор 8, блок 35Сектор 9, блок 36Сектор 9, блок 37Сектор 9, блок 38Сектор 9, блок 39Сектор 10, блок 40Сектор 10, блок 41Сектор 10, блок 42Сектор 10, блок 43Сектор 11, блок 44Сектор 11, блок 45Сектор 11, блок 46Сектор 11, блок 47Сектор 12, блок 48Сектор 12, блок 49Сектор 12, блок 50Сектор 12, блок 51Сектор 13, блок 52Сектор 13, блок 53Сектор 13, блок 54Сектор 13, блок 55Сектор 14, блок 56Сектор 14, блок 57Сектор 14, блок 58Сектор 14, блок 59Сектор 15, блок 60Сектор 15, блок 61Сектор 15, блок 62Сектор 15, блок 63сектор 0сектор 15ваши данныеключи секторазаводской блок, только чтениеБлок 4 — ваши данные16 байт, куда можно писать что угодно после проверки ключомБлок 5 — ваши данные16 байт, куда можно писать что угодно после проверки ключомБлок 6 — ваши данные16 байт, куда можно писать что угодно после проверки ключомБлок 7 — трейлер сектораключ A (6 байт) · биты доступа (3) · свободный байт · ключ B (6 байт)
Щёлкните по любому столбцу — это сектор. В каждом секторе последний блок занят ключами, а самый первый блок всей карты записан на заводе. Испортить трейлер неверными правами доступа можно ровно один раз: сектор блокируется навсегда.

Память нарезана на 16 секторов, в каждом четыре блока по 16 байт. Последний блок сектора — служебный: в нём лежат два ключа и права доступа. Самый первый блок всей карты записан на заводе и защищён от записи. Итого под ваши данные остаётся 47 блоков — 752 байта.

Чтобы прочитать или записать блок, нужно сначала предъявить ключ сектора. У новой метки оба ключа заводские: шесть байт FF. Скетч ниже пишет в блок 4 короткую строку и тут же читает её обратно.

// Пишем в карту своё и читаем обратно: сектор 1, блок 4.
// Блок 0 и последний блок каждого сектора не трогаем: там заводской
// номер и ключи, а испорченный трейлер убивает сектор навсегда.
#include <SPI.h>
#include <MFRC522.h>

const uint8_t PIN_SDA = 10;
const uint8_t PIN_RST = 9;
const uint8_t BLOCK = 4;              // первый блок сектора 1
const char TEXT[] = "TREMA IDE";      // не длиннее 16 знаков

MFRC522 reader(PIN_SDA, PIN_RST);
MFRC522::MIFARE_Key key;

void setup() {
  Serial.begin(9600);
  SPI.begin();
  reader.PCD_Init();
  for (uint8_t i = 0; i < 6; i++) key.keyByte[i] = 0xFF;   // заводской ключ
  Serial.println(F("Поднесите карту MIFARE Classic"));
}

void loop() {
  if (!reader.PICC_IsNewCardPresent()) return;
  if (!reader.PICC_ReadCardSerial()) return;

  MFRC522::StatusCode status = reader.PCD_Authenticate(
    MFRC522::PICC_CMD_MF_AUTH_KEY_A, BLOCK, &key, &(reader.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Ключ не подошёл: "));
    Serial.println(reader.GetStatusCodeName(status));
    reader.PICC_HaltA();
    reader.PCD_StopCrypto1();
    return;
  }

  uint8_t data[16];                     // в блок уходит ровно 16 байт
  for (uint8_t i = 0; i < 16; i++) {
    data[i] = i < strlen(TEXT) ? TEXT[i] : 0;
  }
  status = reader.MIFARE_Write(BLOCK, data, 16);
  Serial.println(status == MFRC522::STATUS_OK ? F("Записано") : F("Записать не вышло"));

  uint8_t buffer[18];                   // 16 байт данных плюс 2 байта проверки
  uint8_t size = sizeof(buffer);
  status = reader.MIFARE_Read(BLOCK, buffer, &size);
  if (status == MFRC522::STATUS_OK) {
    Serial.print(F("В блоке лежит: "));
    for (uint8_t i = 0; i < 16; i++) {
      Serial.print(buffer[i] == 0 ? ' ' : (char)buffer[i]);
    }
    Serial.println();
  }

  reader.PICC_HaltA();        // отпускаем карту
  reader.PCD_StopCrypto1();   // без этой строки следующая карта не прочитается
}

Три вещи, о которые спотыкаются все. Буфер для чтения должен быть на 18 байт, а не на 16: вместе с данными приходят два байта проверки. Записывается всегда ровно 16 байт — короткую строку надо дополнять нулями. И, самое важное, не трогайте служебный блок сектора: одна неверная комбинация прав доступа блокирует сектор необратимо, восстановить его нельзя ничем.

Свой список меток

Дальше обычно хочется, чтобы «свои» метки что-то включали. Самый простой вариант — список номеров прямо в программе: понятно, надёжно, но каждую новую метку приходится вписывать и заливать скетч заново.

// Свои метки открывают замок, чужие - нет.
// ВАЖНО: номер метки передаётся открыто, до всякой защиты. Это
// учебная модель замка, а не система безопасности.
#include <SPI.h>
#include <MFRC522.h>

const uint8_t PIN_SDA = 10;
const uint8_t PIN_RST = 9;
const uint8_t PIN_RELAY = 4;     // модуль реле включается низким уровнем
const uint16_t OPEN_MS = 2000;   // сколько держим замок открытым

MFRC522 reader(PIN_SDA, PIN_RST);

// Впишите сюда номера своих меток - их печатает первый скетч.
const uint8_t KNOWN_COUNT = 2;
const uint8_t KNOWN[KNOWN_COUNT][4] = {
  { 0xDE, 0xAD, 0xBE, 0xEF },
  { 0x12, 0x34, 0x56, 0x78 }
};

bool isKnown(const MFRC522::Uid &uid) {
  if (uid.size != 4) return false;         // здесь разбираем только 4 байта
  for (uint8_t card = 0; card < KNOWN_COUNT; card++) {
    bool same = true;
    for (uint8_t i = 0; i < 4; i++) {
      if (uid.uidByte[i] != KNOWN[card][i]) same = false;
    }
    if (same) return true;
  }
  return false;
}

void setup() {
  Serial.begin(9600);
  pinMode(PIN_RELAY, OUTPUT);
  digitalWrite(PIN_RELAY, HIGH);   // реле выключено
  SPI.begin();
  reader.PCD_Init();
}

void loop() {
  if (!reader.PICC_IsNewCardPresent()) return;
  if (!reader.PICC_ReadCardSerial()) return;

  if (isKnown(reader.uid)) {
    Serial.println(F("Метка своя - открываю"));
    digitalWrite(PIN_RELAY, LOW);
    delay(OPEN_MS);
    digitalWrite(PIN_RELAY, HIGH);
  } else {
    Serial.println(F("Чужая метка"));
  }

  reader.PICC_HaltA();
}

Взрослее — хранить список в EEPROM: это память, которая переживает выключение питания. У ATmega328P её ровно 1 килобайт, ячейка выдерживает не менее 100 000 перезаписей. Четырёхбайтные номера влезут туда парой сотен штук — на класс или на семью хватит с запасом. В скетче ниже первая приложенная метка становится мастер-меткой: ею включают режим добавления, а следующая метка попадает в список.

// Список своих меток живёт в EEPROM и переживает выключение.
// Первая приложенная метка становится мастер-меткой: ею включают
// режим добавления, следующая метка попадает в список.
#include <SPI.h>
#include <MFRC522.h>
#include <EEPROM.h>

const uint8_t PIN_SDA = 10;
const uint8_t PIN_RST = 9;
const uint8_t PIN_LED = 5;

const uint8_t ADDR_MAGIC = 0;    // признак «список уже заведён»
const uint8_t ADDR_COUNT = 1;    // сколько меток в списке
const uint8_t ADDR_MASTER = 2;   // мастер-метка, 4 байта
const uint8_t ADDR_LIST = 6;     // дальше по 4 байта на метку
const uint8_t MAGIC = 0xA5;
const uint8_t MAX_CARDS = 20;
const unsigned long ADD_WINDOW = 10000;   // окно добавления, мс

MFRC522 reader(PIN_SDA, PIN_RST);
bool addMode = false;
unsigned long addUntil = 0;

void readUid(uint16_t addr, uint8_t *out) {
  for (uint8_t i = 0; i < 4; i++) out[i] = EEPROM.read(addr + i);
}

bool sameUid(const uint8_t *a, const uint8_t *b) {
  for (uint8_t i = 0; i < 4; i++) {
    if (a[i] != b[i]) return false;
  }
  return true;
}

int8_t findCard(const uint8_t *uid) {
  uint8_t count = EEPROM.read(ADDR_COUNT);
  uint8_t stored[4];
  for (uint8_t i = 0; i < count; i++) {
    readUid(ADDR_LIST + i * 4, stored);
    if (sameUid(stored, uid)) return i;
  }
  return -1;
}

void addCard(const uint8_t *uid) {
  uint8_t count = EEPROM.read(ADDR_COUNT);
  if (count >= MAX_CARDS) {
    Serial.println(F("Список полон"));
    return;
  }
  // update пишет ячейку только при изменении - бережём ресурс памяти
  for (uint8_t i = 0; i < 4; i++) EEPROM.update(ADDR_LIST + count * 4 + i, uid[i]);
  EEPROM.update(ADDR_COUNT, count + 1);
  Serial.println(F("Метка добавлена"));
}

void setup() {
  Serial.begin(9600);
  pinMode(PIN_LED, OUTPUT);
  SPI.begin();
  reader.PCD_Init();

  if (EEPROM.read(ADDR_MAGIC) != MAGIC) {
    EEPROM.update(ADDR_COUNT, 0);
    Serial.println(F("Список пуст: первая метка станет мастер-меткой"));
  } else {
    Serial.print(F("Меток в списке: "));
    Serial.println(EEPROM.read(ADDR_COUNT));
  }
}

void loop() {
  if (addMode && millis() > addUntil) {
    addMode = false;
    digitalWrite(PIN_LED, LOW);
    Serial.println(F("Окно добавления закрылось"));
  }

  if (!reader.PICC_IsNewCardPresent()) return;
  if (!reader.PICC_ReadCardSerial()) return;

  if (reader.uid.size != 4) {
    Serial.println(F("Пример рассчитан на метки с номером из 4 байт"));
    reader.PICC_HaltA();
    return;
  }

  uint8_t uid[4];
  for (uint8_t i = 0; i < 4; i++) uid[i] = reader.uid.uidByte[i];

  if (EEPROM.read(ADDR_MAGIC) != MAGIC) {
    for (uint8_t i = 0; i < 4; i++) EEPROM.update(ADDR_MASTER + i, uid[i]);
    EEPROM.update(ADDR_MAGIC, MAGIC);
    Serial.println(F("Мастер-метка запомнена"));
    reader.PICC_HaltA();
    return;
  }

  uint8_t master[4];
  readUid(ADDR_MASTER, master);

  if (sameUid(master, uid)) {
    addMode = !addMode;
    addUntil = millis() + ADD_WINDOW;
    digitalWrite(PIN_LED, addMode ? HIGH : LOW);
    Serial.println(addMode ? F("Приложите новую метку") : F("Добавление отменено"));
  } else if (addMode) {
    if (findCard(uid) < 0) {
      addCard(uid);
    } else {
      Serial.println(F("Такая метка уже есть"));
    }
    addMode = false;
    digitalWrite(PIN_LED, LOW);
  } else if (findCard(uid) >= 0) {
    Serial.println(F("Метка своя - открываю"));
    digitalWrite(PIN_LED, HIGH);
    delay(1500);
    digitalWrite(PIN_LED, LOW);
  } else {
    Serial.println(F("Чужая метка"));
  }

  reader.PICC_HaltA();
}

Здесь важна одна тонкость: запись ведётся через update, а не write — так ячейка перезаписывается только при настоящем изменении и живёт дольше. И помните, что обычная заливка скетча EEPROM не трогает, а вот операция «записать загрузчик» стирает её целиком. Подробнее про виды памяти платы — в разборе «Память Arduino».

Замок: чем управлять и как не сжечь плату

Электрозамок или защёлка — это уже силовая нагрузка. Соленоид на 12 вольт берёт больше половины ампера, катушка обычного реле — около 70 мА, а вывод платы рассчитан на 20. Поэтому между Arduino и замком всегда стоит посредник: модуль реле с оптопарой или силовой ключ. Питание замка — отдельное, от своего источника, и общий провод у него с платой один.

Две ловушки, о которых редко пишут. Первая: недорогие модули реле включаются низким уровнем, а вывод платы в первые миллисекунды после сброса ещё не настроен как выход — замок может дёрнуться при каждом включении питания. Первой же строкой в setup() ставьте вывод в неактивное состояние. Вторая: электромагнитные защёлки не рассчитаны на длительное удержание — у типовых замков это 3–10 секунд подряд. Схему «пока карта приложена, замок открыт» так строить нельзя, нужен таймер на секунду-две, как в скетче выше.

Если управлять нужно не замком, а мотором или приводом — это соседняя тема, она разобрана в статье про моторы и драйвер L298N.

Честно про защиту

Проект «дверь открывается по карте» — прекрасное учебное задание и плохая система безопасности, и важно понимать почему. Номер метки передаётся открыто, до всякой проверки. Шифр, которым закрыта память MIFARE Classic, исследователи публично разобрали ещё в 2008 году: ключ восстанавливается меньше чем за секунду на обычном компьютере. Сами разработчики библиотеки прямо предупреждают, что использовать номер карты как удостоверение личности в системах доступа нельзя.

Это не повод не делать проект — это повод понимать его границы. Шкафчик в кабинете информатики, отметка присутствия на уроке, «своя» карта для запуска станка, игровой квест с картами-ключами — отличные применения. Настоящая входная дверь, сейф или касса — нет.

Что купить

Главное правило покупки: смотрите на частоту в характеристиках, а не в названии. Метка и считыватель должны быть из одного мира — 13,56 МГц или 125 кГц.

RFID-модуль RC522

Тот самый красный модуль на 13,56 МГц, о котором вся страница. В комплекте уже лежат карта и брелок — начинать можно сразу, ничего больше не докупая. Питание 3,3 В, подключение по SPI.

280 ₽
Открыть карточку
арт. 117 · цена и наличие на 24.08.2026

Модуль на 125 кГц RDM6300

Если ваш брелок оказался из мира 125 кГц, нужен такой модуль: он читает метки EM4100 и отдаёт номер по обычному последовательному порту. Питание 5 В, антенна выносная.

268 ₽
Открыть карточку
арт. 336 · цена и наличие на 24.08.2026

Метки-брелоки на 125 кГц

Чистые метки к модулю на 125 кГц: пригодятся, чтобы попробовать технологию, не трогая настоящие ключи от подъезда. К RC522 они не подойдут — это другой диапазон.

45 ₽
Открыть карточку
арт. 449 · цена и наличие на 24.08.2026

Модуль реле на 1 канал

Посредник между платой и замком: сам берёт от платы миллиамперы, а коммутирует настоящую нагрузку. Включается низким уровнем — не забудьте про это в setup().

90 ₽
Открыть карточку
арт. 52 · цена и наличие на 24.08.2026

Замок электромагнитный, 12 В

Для проекта «открывается по карте»: питание отдельное, 12 вольт, ток до 0,8 А. Включать короткими импульсами — паспорт разрешает не больше десяти секунд подряд.

837 ₽
Открыть карточку
арт. 565 · цена и наличие на 24.08.2026

Модуль NFC PN532

Старший брат RC522: те же 13,56 МГц, но умеет больше типов карт и разговаривает с телефоном. Питание 3,3–5 В, на выбор три способа подключения. Нужен, когда RC522 упирается в свои ограничения.

451 ₽
Открыть карточку
арт. 747 · цена и наличие на 24.08.2026

Эта тема в Trema IDE

  • Библиотека ставится из нашего каталога — MFRC522 лежит на российском зеркале, без похода на чужие сайты и ручной распаковки архивов.
  • Монитор порта под рукой. Номера меток удобно смотреть прямо в среде: скетч печатает, монитор показывает, ничего доустанавливать не нужно.
  • Ошибки объясняются по-русски. Если библиотека не установлена или забыт SPI.begin(), среда переводит сообщение компилятора на человеческий язык и подсказывает, что делать.
  • Готовые примеры в разделе полезных скетчей — с русскими комментариями, включая работу с памятью платы.

Вопросы и ответы

Коротко о главном

Если чего-то не хватает — напишите нам через форму поддержки, мы дополняем страницы по реальным вопросам.

Прочитает ли RC522 ключ от домофона?

Чаще всего нет, и дело не в программе. Модуль RC522 работает на частоте 13,56 МГц, а домофонные ключи — это обычно либо контактная металлическая таблетка, либо брелок на 125 кГц. Это разные технологии: антенна каждой метки настроена строго на свою частоту, и переходов между ними не существует. Для брелоков на 125 кГц нужен другой модуль, например RDM6300, а таблетке считыватель не нужен вовсе — ей хватает двух проводов и резистора.

Модуль RC522 не отвечает, в мониторе 0x00 или 0xFF. Что делать?

Это не версия чипа, а признак того, что связи с модулем нет совсем. Проверьте три вещи по порядку. Первое: припаяна ли планка контактов — модули почти всегда продаются с ней отдельно, а просто вставленные штырьки контакта не дают. Второе: есть ли на модуле 3,3 вольта. Третье: стоит ли в скетче SPI.begin() и не перепутаны ли выводы SDA и RST местами.

Можно ли питать RC522 от 5 вольт?

Нет. Микросхеме модуля по даташиту положено от 2,5 до 3,6 вольта, а 4 вольта названы предельным значением, за которым она разрушается. Питание берут только с вывода 3.3V. Сигнальные провода у пятивольтовых плат формально тоже выше нормы, но в учебной схеме они работают; в изделии между платой и модулем ставят преобразователь уровней или берут плату на 3,3 вольта.

На каком расстоянии RC522 читает метку?

Даташит обещает до 50 миллиметров при удачной антенне, а типовая красная плата уверенно берёт метку в пределах двух-трёх сантиметров. Так работает сама физика: метка питается от поля считывателя, а поле спадает очень быстро — вдвое дальше значит в несколько раз слабее. Увеличить дальность можно разве что настройкой усиления приёмника, и речь идёт о сантиметре, а не о метре.

Сколько своих данных помещается в карту?

У самой ходовой карты MIFARE Classic на 1 килобайт под ваши данные остаётся 752 байта. Память нарезана на 16 секторов по четыре блока, блок — это 16 байт. Последний блок каждого сектора занят ключами и правами доступа, а самый первый блок всей карты записан на заводе и защищён от записи. Читать и писать блок можно только после того, как предъявлен ключ сектора; у новой метки он заводской — шесть байт FF.

Можно ли сделать замок, который открывается картой?

Как учебный проект — да, и это отличная задача: шкафчик, ящик стола, отметка присутствия, квест с картами-ключами. Как настоящую защиту — нет. Номер метки передаётся в эфир открыто, ещё до всякой проверки ключей, а шифр, которым закрыта память MIFARE Classic, публично разобран исследователями ещё в 2008 году. Сам замок при этом питают отдельно и включают через реле или силовой ключ: вывод платы такой ток не выдержит.

Дальше по теме

Замок, который открывается кодом с кнопок — урок 29. Какие вообще бывают модули и как их подключают — Модули для Arduino. Как поставить библиотеку — Библиотеки Arduino. Куда девать прочитанные номера — Память Arduino. Идеи для проектов — Проекты на Arduino.