Перейти к содержанию
Материалы раздела

MFRC522

Модуль управляет RFID/NFC-считывателем MFRC522 и работает с картами на частоте 13,56 МГц.

Поддерживаемые типы карт

  • MIFARE Classic (1K, 2K, 4K, Mini)
  • MIFARE Plus (SL0, SL1, SL2, SL3)
  • MIFARE Ultralight (стандартный, C, EV1, Nano)
  • NTAG (213, 215, 216, 413, 424)
  • MIFARE DESFire (EV1, EV2)
  • ISO/IEC 14443-4 (EMV карты)

Быстрый старт

Для использования этого модуля нужно сделать следующее:

  1. Настройте UART для связи с MFRC522:

    uart.setup(uart.UART1, 115200, 8, uart.PARITY_NONE, uart.STOP_1)
    
  2. Инициализируйте модуль MFRC522:

    mfrc522.init(uart.UART1, -1, mfrc522.GAIN_43)
    
  3. Прочитайте UID карты:

    uid = mfrc522.uid(uart.UART1, true)
    if uid then
        print("UID: " .. uid)
    end
    

mfrc522.init([uart_id], [rst_pin], [gain])

Инициализирует модуль MFRC522. Все параметры опциональны — если не указаны, используются значения по умолчанию для данной платы.

Аргументы:

  • uart_id (необязательное целое число): идентификатор UART порта.
  • rst_pin (необязательное целое число): номер GPIO пина для сброса модуля.
  • gain (необязательное целое число): усиление приёмника.

Возвращает: true при успешной инициализации, false при ошибке.

-- Инициализация со значениями по умолчанию для данной платы
ok = mfrc522.init()

-- Инициализация с указанием только порта
ok = mfrc522.init(uart.UART1)

-- Инициализация с явным указанием всех параметров
ok = mfrc522.init(uart.UART1, -1, mfrc522.GAIN_48)
if ok then
    print("MFRC522 initialized")
end

mfrc522.reinit(uart_id)

Повторно инициализирует модуль MFRC522 с сохранёнными параметрами.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: true при успешной инициализации, false при ошибке.

-- Переинициализация после ошибки
mfrc522.reinit(uart.UART1)

mfrc522.deinit(uart_id)

Деинициализирует модуль MFRC522: выключает антенну, переводит чип в power-down и опускает линию RST.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: true при успехе, false при ошибке.

-- Освобождение ресурса считывателя
mfrc522.deinit(uart.UART1)

mfrc522.uid(uart_id, halt, [result_type])

Читает UID карты, поднесённой к считывателю.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • halt (булево): true - остановить карту после чтения (HALT), false - оставить активной.
  • result_type (необязательное целое число): формат возвращаемого UID:
    • 0 (по умолчанию): строка UID в hex (верхний регистр)
    • 1: UID как список чисел (несколько возвращаемых значений)
    • 2: UID как uint32 big-endian (для 4-байтного UID)
    • 3: таблица с полями str, int, tab, sak, atqa, sz, flags, type
    • 4: UID как десятичная строка little-endian (Ur)
    • 5: UID в формате Wiegand 26 с запятой (Uc)
    • 6: UID в формате Wiegand 26 с точкой (Ud)
    • 7: UID как бинарная строка (сырые байты)

Возвращает: значение UID в выбранном формате или nil, если карта не обнаружена.

-- Чтение UID с остановкой карты
uid = mfrc522.uid(uart.UART1, true)
if uid then
    print("UID: " .. uid)
end

-- Чтение UID без остановки (для последующих операций)
uid = mfrc522.uid(uart.UART1, false)

-- Полная информация о карте
info = mfrc522.uid(uart.UART1, false, 3)
if info then
    print(info.str, info.type)
end

mfrc522.scan([format | result_type], [key_type], [key], [ul_pwd], [scan_mode])

mfrc522.scan(callback, …)

mfrc522.scan([result_type], callback, …)

mfrc522.scan([result_type], [key_type], [key], [ul_pwd], [scan_mode], callback, …)

Сканирует карту с параметрами по умолчанию для данной платы (UART/RST/gain).

На каждом вызове scan для микросхемы применяется один из трёх сценариев (параметр scan_mode, по умолчанию 2):

  • 0: базовый режим. После сканирования RF-поле выключается.
  • 1: контроль чипа. Перед сканированием вызывается getver; если ответа нет или версия некорректная, выполняется deinit/init.
  • 2: one-shot. Перед каждым вызовом выполняется init, после каждого вызова — deinit.

Первый параметр определяет режим работы:

  • Строка — форматирует данные через sprintf-парсер (синтаксис см. в mfrc522.sprintf).
  • Число — возвращает UID в указанном формате result_type (аналогично параметру result_type в mfrc522.uid).
  • Функция — сканирует UID в режиме result_type = 1 и вызывает callback с UID-байтами.
  • Не указан / nil — возвращает UID как hex-строку (эквивалентно result_type = 0).

Аргументы:

  • format | result_type (необязательный, строка или число): строка форматирования или числовой код формата UID:
    • 0 (по умолчанию): строка UID в hex (верхний регистр)
    • 1: UID как список чисел (несколько возвращаемых значений)
    • 2: UID как uint32 big-endian (для 4-байтного UID)
    • 3: таблица с полями str, int, tab, sak, atqa, sz, flags, type
    • 4: UID как десятичная строка little-endian (Ur)
    • 5: UID в формате Wiegand 26 с запятой (Uc)
    • 6: UID в формате Wiegand 26 с точкой (Ud)
    • 7: UID как бинарная строка (сырые байты)
  • key_type (необязательное целое число): тип ключа mfrc522.KeyA или mfrc522.KeyB. По умолчанию KeyA.
  • key (необязательная строка): hex-строка ключа (12 символов). По умолчанию “FFFFFFFFFFFF”.
  • ul_pwd (необязательное целое число): пароль для NTAG/Ultralight. По умолчанию 0xFFFFFFFF.
  • scan_mode (необязательное целое число): режим обслуживания микросхемы (0, 1 или 2). По умолчанию 2.
  • callback (необязательно): функция, вызываемая только после успешного чтения UID. Все следующие аргументы передаются в неё перед UID. Короткая форма использует result_type = 1 и передаёт UID отдельными байтами. В качестве обработчика принимается только функция.

Возвращает: значение UID в выбранном формате, отформатированную строку (включая пустую) или nil если карта не обнаружена.

Если UID не найден, обработчик не вызывается и scan() не возвращает значений. После вызова обработчика функция возвращает true и его результаты либо false, err, если обработчик завершился ошибкой. Ошибки оборудования, аргументов и инициализации по-прежнему вызывают исключение.

-- Простейший вызов — UID в hex
uid = mfrc522.scan()
if uid then
    print("UID: " .. uid)
end

-- UID как uint32
uid_int = mfrc522.scan(2)

-- Полная информация о карте в виде таблицы
info = mfrc522.scan(3)
if info then
    print(info.str, info.type)
end

-- UID через sprintf-формат
result = mfrc522.scan("HU*")

-- Wiegand 26
result = mfrc522.scan("Uc")

-- Чтение блока MIFARE Classic с ключом
result = mfrc522.scan("HB4%*", mfrc522.KeyA, "A0A1A2A3A4A5")

-- Режим 1: проверка getver с авто-reinit при некорректном ответе
result = mfrc522.scan("HU*", mfrc522.KeyA, "FFFFFFFFFFFF", 0xFFFFFFFF, 1)

-- Режим 2: init/deinit на каждом вызове
uid = mfrc522.scan(nil, nil, nil, nil, 2)

-- Lua callback: UID приходит отдельными байтами (result_type = 1)
local function print_uid(...)
    local uid = ""
    for i = 1, select("#", ...) do
        uid = uid .. string.format("%02X", select(i, ...))
    end
    print("UID: " .. uid)
end

mfrc522.scan(print_uid)

-- Lua callback с пользовательскими аргументами перед UID
local function store_uid(offset, ...)
    modbus.setregs8(offset, ...)
end

mfrc522.scan(store_uid, 0)  -- store_uid(0, uid_byte1, uid_byte2, ...)

-- Передать UID напрямую в регистры Modbus
mfrc522.scan(modbus.setregs8, 0)

-- Проверка результата callback: nil/нет значений значит, что карты нет
local ok, err = mfrc522.scan(modbus.setregs8, 0)
if ok == false then
    print("Callback error:", err)
end

-- То же самое с явным форматом результата: UID как список байтов
mfrc522.scan(1, modbus.setregs8, 0)

-- UID как hex-строка в callback
mfrc522.scan(0, function(uid)
    print("UID: " .. uid)
end)

-- UID как таблица с полями str/int/tab/sak/atqa/sz/flags/type
mfrc522.scan(3, function(info)
    print(info.str, info.type)
end)

-- UID как бинарная строка, с префиксным аргументом offset
mfrc522.scan(7, function(offset, uid)
    modbus.setregs8(offset, uid)
end, 0)

-- Callback вместе с явным scan_mode
mfrc522.scan(1, nil, nil, nil, 2, modbus.setregs8, 0)

mfrc522.sprintf(uart_id, format, [key_type], [key], [ul_pwd], [halt])

Форматирует данные карты по заданному шаблону. Мощная функция для чтения и форматирования данных с RFID карт.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • format (строка): строка форматирования (см. ниже).
  • key_type (необязательное целое число): тип ключа mfrc522.KeyA или mfrc522.KeyB. По умолчанию KeyA.
  • key (необязательная строка): hex-строка ключа (12 символов). По умолчанию “FFFFFFFFFFFF”.
  • ul_pwd (необязательное целое число): пароль для NTAG/Ultralight. По умолчанию 0xFFFFFFFF.
  • halt (необязательное булево): остановить карту после операции.

Возвращает: отформатированную строку или nil если карта не обнаружена.

Синтаксис форматирования

Режимы вывода:

  • a - ASCII режим
  • d - десятичный режим
  • h - hex (нижний регистр)
  • H - hex (верхний регистр)
  • D - UID как десятичное число (uint32)

Работа с UID:

  • U* - весь UID
  • U~ - весь UID (байты в обратном порядке)
  • Un - байт UID с индексом n (с 0)
  • Un:m - байты UID от n до m включительно
  • Ur - UID как uint32 (little-endian)
  • Uc - Wiegand 26 через запятую (058,16506)
  • Ud - Wiegand 26 через точку (058.16506)

Работа с блоками (MIFARE Classic):

  • BN%* - весь блок N
  • BN%~ - весь блок N (в обратном порядке)
  • BN%n - байт n блока N
  • BN%n:m - байты от n до m блока N

Специальные символы:

  • S - SAK байт карты
  • P - PAN (для EMV карт) или UID
  • \n - новая строка
  • \t - табуляция
-- Вывод UID в hex формате
result = mfrc522.sprintf(uart.UART1, "HU*")
print(result)  -- "04A1B2C3D4E5F6"

-- UID в формате Wiegand 26
result = mfrc522.sprintf(uart.UART1, "Uc")
print(result)  -- "058,16506"

-- Чтение блока 4 с ключом по умолчанию
result = mfrc522.sprintf(uart.UART1, "HB4%*")

-- Чтение блока с пользовательским ключом
result = mfrc522.sprintf(uart.UART1, "HB4%*", mfrc522.KeyA, "A0A1A2A3A4A5")

-- Комбинированный вывод
result = mfrc522.sprintf(uart.UART1, "UID: HU*\nType: S")

mfrc522.read_ul(uart_id, page)

Читает страницу (4 байта) с карты Ultralight/NTAG.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • page (целое число): номер страницы (0-255, зависит от типа карты).

Возвращает: строка с данными (4 байта) или nil, error_message.

-- Сначала нужно прочитать UID чтобы активировать карту
uid = mfrc522.uid(uart.UART1, false)

-- Чтение страницы 4 (первая пользовательская страница)
data, err = mfrc522.read_ul(uart.UART1, 4)
if data then
    print("Data: " .. string.tohex(data))
else
    print("Error: " .. err)
end

mfrc522.write_ul(uart_id, page, data)

Записывает 4 байта на страницу карты Ultralight/NTAG.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • page (целое число): номер страницы (0-255).
  • data (строка или 4 числа): данные для записи (4 байта).

Возвращает: true при успехе или nil, error_message.

-- Активируем карту
uid = mfrc522.uid(uart.UART1, false)

-- Запись строки
ok, err = mfrc522.write_ul(uart.UART1, 4, "TEST")

-- Запись байтов
ok, err = mfrc522.write_ul(uart.UART1, 5, string.char(0x01, 0x02, 0x03, 0x04))

mfrc522.read_classic(uart_id, block, [key_type], [key])

Читает блок (16 байт) с карты MIFARE Classic с аутентификацией.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • block (целое число): номер блока (0-255, зависит от типа карты).
  • key_type (необязательное целое число): тип ключа mfrc522.KeyA (по умолчанию) или mfrc522.KeyB.
  • key (необязательная строка): hex-строка ключа (12 символов). По умолчанию “FFFFFFFFFFFF”.

Возвращает: строка с данными (16 байт) или nil, error_message.

-- Сначала нужно прочитать UID чтобы активировать карту
uid = mfrc522.uid(uart.UART1, false)

-- Чтение блока 4 как текстовой строки (16 байт)
data, err = mfrc522.read_classic(uart.UART1, 4)
if data then
    print("Data: " .. data)
else
    print("Error: " .. err)
end

-- Проверка ожидаемой строки в блоке
if data == "Hello MIFARE!!!\0" then
    print("Text matched")
end

mfrc522.write_classic(uart_id, block, data, [key_type], [key])

Записывает 16 байт в блок карты MIFARE Classic с аутентификацией.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • block (целое число): номер блока (0-255).
  • data (строка или таблица): данные для записи (16 байт как строка или таблица из 16 чисел).
  • key_type (необязательное целое число): тип ключа mfrc522.KeyA (по умолчанию) или mfrc522.KeyB.
  • key (необязательная строка): hex-строка ключа (12 символов). По умолчанию “FFFFFFFFFFFF”.

Возвращает: true при успехе или nil, error_message.

ВНИМАНИЕ: Запись в sector trailer блоки (3, 7, 11, 15, …) может заблокировать сектор навсегда! Sector trailer содержит ключи доступа и биты контроля доступа.

-- Активируем карту
uid = mfrc522.uid(uart.UART1, false)

-- Запись строки ровно 16 байт
ok, err = mfrc522.write_classic(uart.UART1, 4, "Hello MIFARE!!!\0")

-- Запись с пользовательским ключом B
ok, err = mfrc522.write_classic(
    uart.UART1,
    5,
    "Access granted!\0",
    mfrc522.KeyB,
    "A0A1A2A3A4A5"
)
if ok then
    print("Write successful")
else
    print("Error: " .. err)
end

-- Чтение обратно как строки
data, err = mfrc522.read_classic(uart.UART1, 4)
if data then
    print("Read back: " .. data)
end

mfrc522.set_classic_keys(uart_id, sector, keyA, keyB, [auth_keytype], [auth_key], [access_bits], [gpb])

Обновляет Key A и Key B в sector trailer выбранного сектора MIFARE Classic.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • sector (целое число): номер сектора (0-39).
  • keyA (строка): новый Key A, 12 hex-символов.
  • keyB (строка): новый Key B, 12 hex-символов.
  • auth_keytype (необязательное целое число): mfrc522.KeyA (по умолчанию) или mfrc522.KeyB.
  • auth_key (необязательная строка): текущий ключ для аутентификации. По умолчанию “FFFFFFFFFFFF”.
  • access_bits (необязательная строка): access bits (3 байта, 6 hex-символов). По умолчанию “FF0780”.
  • gpb (необязательное целое число): GPB байт (0-255). По умолчанию 0x69.

Возвращает: true при успехе или nil, error_message.

-- Смена ключей сектора 1 (trailer блок 7)
ok, err = mfrc522.set_classic_keys(
    uart.UART1,
    1,
    "A0A1A2A3A4A5",   -- новый Key A
    "B0B1B2B3B4B5",   -- новый Key B
    mfrc522.KeyA,
    "FFFFFFFFFFFF",   -- текущий ключ доступа
    "FF0780",         -- стандартные access bits
    0x69
)

if not ok then
    print("Key update error: " .. err)
end

-- После смены ключа читаем блок по новому ключу
data, err = mfrc522.read_classic(uart.UART1, 4, mfrc522.KeyB, "B0B1B2B3B4B5")
if data then
    print("Data: " .. data)
end

Структура памяти MIFARE Classic

MIFARE Classic 1K содержит 16 секторов по 4 блока (64 блока всего):

СекторБлокиНазначение
00-3Блок 0 — Manufacturer block (только чтение), 1-2 — данные, 3 — sector trailer
14-7Блоки 4-6 — данные, 7 — sector trailer
1560-63Блоки 60-62 — данные, 63 — sector trailer

Sector trailer (блоки 3, 7, 11, …):

  • Байты 0-5: Key A (не читается, возвращает нули)
  • Байты 6-9: Access bits (определяют права доступа)
  • Байты 10-15: Key B (может хранить данные если не используется)
-- Пример: чтение всех блоков сектора 1
uid = mfrc522.uid(uart.UART1, false)

for block = 4, 7 do
    local data, err = mfrc522.read_classic(uart.UART1, block)
    if data then
        print(string.format("Block %d: %s", block, data))
    else
        print(string.format("Block %d error: %s", block, err))
    end
end

MIFARE Plus SL0/SL3

Поддержка защищённых команд MIFARE Plus включается параметром CONFIG_LUA_RTOS_MFRC522_PLUS. Для сборок с модулем MFRC522 параметр включён по умолчанию; при необходимости его можно отключить в Kconfig. Реализация использует AES-128 из Mbed TLS в ESP-IDF. MIFARE Plus SE не поддерживается.

AES-ключ во всех функциях задаётся строкой из 32 шестнадцатеричных символов. Исходный ключ используется только во время вызова и затем удаляется из C-стека. После успешной аутентификации в объекте считывателя остаются только производные сессионные ключи SL3.

Сессия привязана к выбранному UID. Она сбрасывается при исчезновении или новом выборе карты, HALT, выключении поля, resetfield(), deinit(), ошибке связи или проверки MAC. После сброса нужно снова вызвать plus_auth().

mfrc522.plus_key_block(sector, key_type)

Возвращает номер AES-блока ключа сектора для MIFARE Plus.

Аргументы:

  • sector (целое число): номер сектора 0-39.
  • key_type (строка): "A" или "B".

Возвращает: номер блока ключа.

local key_a_sector_1 = mfrc522.plus_key_block(1, "A")
local key_b_sector_1 = mfrc522.plus_key_block(1, "B")

mfrc522.plus_auth(uart_id, key_block, key)

Создаёт или продолжает защищённую сессию с картой MIFARE Plus SL3. Если активной сессии нет, функция заново выбирает карту и выполняет FirstAuth. Если сессия для того же UID уже активна, выполняется FollowAuth с другим ключевым блоком без сброса счётчиков secure messaging.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • key_block (целое число): блок AES-ключа, например результат plus_key_block() или одна из констант PLUS_KEY_*.
  • key (строка): AES-128 ключ, ровно 32 hex-символа.

Возвращает: true при успехе или nil, error_message.

local key_block = mfrc522.plus_key_block(1, "A")
local ok, err = mfrc522.plus_auth(
    uart.UART1,
    key_block,
    "00112233445566778899AABBCCDDEEFF"
)

mfrc522.read_plus(uart_id, block, [count])

Читает от одного до трёх последовательных 16-байтных блоков через активную сессию SL3. Функция не выбирает карту повторно, поскольку это нарушило бы синхронизацию счётчиков secure messaging.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • block (целое число): номер первого блока, 0-65535.
  • count (необязательное целое число): число блоков 1-3, по умолчанию 1.

Возвращает: бинарную строку длиной count * 16 байт или nil, error_message.

local data, err = mfrc522.read_plus(uart.UART1, 4, 2)
if data then
    print("Прочитано байт: " .. #data) -- 32
end

mfrc522.write_plus(uart_id, block, data, encrypted)

Записывает от одного до трёх последовательных блоков через активную сессию SL3.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • block (целое число): номер первого блока, 0-65535.
  • data (строка): бинарная строка длиной ровно 16, 32 или 48 байт.
  • encrypted (булево): true для зашифрованной записи, false для записи с MAC без шифрования. Для системных и ключевых блоков выше 0x00FF разрешено только значение true.

Возвращает: true при успехе или nil, error_message.

local block = assert(mfrc522.read_plus(uart.UART1, 4))
assert(mfrc522.write_plus(uart.UART1, 4, block, true))

mfrc522.plus_reset_auth(uart_id)

Завершает активную сессию SL3 и удаляет производные сессионные ключи. Если сессии уже нет, функция также возвращает успех.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: true при успехе или nil, error_message.

assert(mfrc522.plus_reset_auth(uart.UART1))

mfrc522.plus_write_perso(uart_id, block, data, expected_uid)

Записывает один 16-байтный блок персонализации карты в SL0. Перед каждой записью функция заново выбирает карту и проверяет уровень SL0 и точное совпадение UID.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • block (целое число): номер блока персонализации, 0-65535.
  • data (строка): ровно 16 бинарных байт, не hex-строка.
  • expected_uid (строка): UID без разделителей, 8, 14 или 20 hex-символов.

Возвращает: true при успехе или nil, error_message.

local function hex_bytes(hex)
    return (hex:gsub("..", function(byte)
        return string.char(tonumber(byte, 16))
    end))
end

local uid = "04A1B2C3D4E5F6"
local card_master_key = hex_bytes("00112233445566778899AABBCCDDEEFF")

assert(mfrc522.plus_write_perso(
    uart.UART1,
    mfrc522.PLUS_KEY_CARD_MASTER,
    card_master_key,
    uid
))

mfrc522.plus_commit_sl3(uart_id, expected_uid, l3_switch_key, confirmation)

Завершает персонализацию SL0 и переводит карту в SL3. Функция принимает только карту SL0 с указанным UID, выполняет CommitPerso, перезапускает RF-поле и проверяет тот же UID и итоговый уровень SL3. Если карта временно оказалась в SL1/SL2, для перехода используется ключ блока PLUS_KEY_L3_SWITCH (0x9003).

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • expected_uid (строка): UID без разделителей, 8, 14 или 20 hex-символов.
  • l3_switch_key (строка): ключ переключения в SL3, 32 hex-символа.
  • confirmation (строка): точная строка COMMIT и UID в верхнем регистре.

Возвращает: true при подтверждённом переходе в SL3 или nil, error_message.

Переход из SL0 необратим. Используйте только тестовую карту, заранее сохраните её UID и весь набор ключей. До вызова plus_commit_sl3() запишите как минимум Card Master, Card Configuration, L3 Switch и все секторные ключи, нужные приложению. После ошибки или потери питания считайте состояние карты неизвестным и проверьте её независимым инструментом перед повторной попыткой.

local uid = "04A1B2C3D4E5F6"
local l3_switch_key = "00112233445566778899AABBCCDDEEFF"

assert(mfrc522.plus_commit_sl3(
    uart.UART1,
    uid,
    l3_switch_key,
    "COMMIT " .. uid
))

mfrc522.read_cnt(uart_id, [counter_addr])

Читает 24-битный счётчик с карты Ultralight EV1 или NTAG21x.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • counter_addr (необязательное целое число): адрес счётчика (0-2 для UL EV1, обычно 0x02 для NTAG21x). По умолчанию 0.

Возвращает: значение счётчика (число 0-16777215) или nil, error_message.

-- Активируем карту
uid = mfrc522.uid(uart.UART1, false)

-- Чтение счётчика
count, err = mfrc522.read_cnt(uart.UART1, 0)
if count then
    print("Counter: " .. count)
end

mfrc522.incr_cnt(uart_id, increment, [counter_addr])

Атомарно увеличивает 24-битный счётчик на карте Ultralight EV1.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • increment (целое число): значение для увеличения (0-16777215).
  • counter_addr (необязательное целое число): адрес счётчика (0-2). По умолчанию 0.

Возвращает: true при успехе или nil, error_message.

Важно: Счётчик может только увеличиваться. Сброс невозможен. При достижении 0xFFFFFF счётчик блокируется навсегда.

-- Активируем карту
uid = mfrc522.uid(uart.UART1, false)

-- Увеличить счётчик на 1
ok, err = mfrc522.incr_cnt(uart.UART1, 1, 0)

Счётчики Ultralight/NTAG

Количество и адресация

Тип тегаСчётчиковАдресаОсобенности
Ultralight EV130, 1, 2Ручной инкремент через incr_cnt
NTAG213/215/2161только 2Автоинкремент при чтении через read_cnt
Ultralight (старый)0Счётчики не поддерживаются
Ultralight C0Счётчики не поддерживаются

Характеристики счётчиков

  • Разрядность: 24 бита (0 - 16 777 215)
  • Направление: только увеличение (нельзя уменьшить или сбросить)
  • Атомарность: операция инкремента атомарна, потеря питания не повредит значение
  • Переполнение: при достижении максимума карта возвращает ошибку NAK (0x04)
  • Блокировка: при переполнении счётчик блокируется навсегда

Примеры использования

-- Ultralight EV1: три независимых счётчика
uid = mfrc522.uid(uart.UART1, false)

local balance = mfrc522.read_cnt(uart.UART1, 0)  -- счётчик 0: баланс
local visits  = mfrc522.read_cnt(uart.UART1, 1)  -- счётчик 1: визиты
local bonus   = mfrc522.read_cnt(uart.UART1, 2)  -- счётчик 2: бонусы

-- Начислить 100 баллов
mfrc522.incr_cnt(uart.UART1, 100, 0)

-- NTAG21x: один счётчик с автоинкрементом
-- Каждое чтение увеличивает счётчик на 1 (если NFC_CNT_EN включен)
local scans = mfrc522.read_cnt(uart.UART1, 2)
print("Карта сканировалась " .. scans .. " раз")

Система лояльности с двумя счётчиками

Поскольку счётчики могут только расти, для реализации баланса баллов используйте два счётчика:

-- cnt0 = начислено всего
-- cnt1 = списано всего
-- баланс = cnt0 - cnt1

uid = mfrc522.uid(uart.UART1, false)

local earned = mfrc522.read_cnt(uart.UART1, 0) or 0
local spent  = mfrc522.read_cnt(uart.UART1, 1) or 0
local balance = earned - spent

print("Начислено: " .. earned)
print("Списано: " .. spent)
print("Баланс: " .. balance)

-- Начислить 50 баллов
mfrc522.incr_cnt(uart.UART1, 50, 0)

-- Списать 30 баллов
if balance >= 30 then
    mfrc522.incr_cnt(uart.UART1, 30, 1)
end

Различия NTAG21x и Ultralight EV1

ФункцияNTAG21xUltralight EV1
read_cntРаботает (авто +1)Работает
incr_cntНе поддерживаетсяРаботает
Сброс счётчикаНевозможенНевозможен
НазначениеAnti-tamperingLoyalty/Credits

NTAG21x — счётчик используется для защиты от подделок. Каждое чтение увеличивает значение, что позволяет серверу проверить, не была ли карта клонирована (у клона счётчик не синхронизирован).

Ultralight EV1 — счётчики предназначены для хранения баллов лояльности, количества поездок и т. п.

mfrc522.getver(uart_id)

Возвращает версию чипа MFRC522.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: версия чипа (0x91 или 0x92 для подлинных MFRC522).

ver = mfrc522.getver(uart.UART1)
print(string.format("Chip version: 0x%02X", ver))

mfrc522.setgain(uart_id, gain)

Устанавливает усиление приёмника.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • gain (целое число): значение усиления (используйте константы GAIN_*).

Возвращает: ничего.

mfrc522.setgain(uart.UART1, mfrc522.GAIN_48)

mfrc522.getgain(uart_id)

Возвращает текущее усиление приёмника.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: значение усиления.

gain = mfrc522.getgain(uart.UART1)

mfrc522.antenna_on(uart_id)

Включает антенну.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: ничего.

mfrc522.antenna_on(uart.UART1)

mfrc522.antenna_off(uart_id)

Выключает антенну. Также сбрасывает состояние активной карты.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: ничего.

mfrc522.antenna_off(uart.UART1)

mfrc522.resetfield(uart_id)

Сбрасывает RF поле (выключает и включает антенну).

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: ничего.

-- Сброс поля для повторного обнаружения карты
mfrc522.resetfield(uart.UART1)

Константы

Типы ключей

КонстантаОписание
mfrc522.KeyAКлюч типа A для MIFARE Classic
mfrc522.KeyBКлюч типа B для MIFARE Classic

MIFARE Plus

КонстантаЗначениеОписание
mfrc522.PLUS_SL00Уровень безопасности SL0
mfrc522.PLUS_SL11Уровень безопасности SL1
mfrc522.PLUS_SL22Уровень безопасности SL2
mfrc522.PLUS_SL33Уровень безопасности SL3
mfrc522.PLUS_KEY_CARD_MASTER0x9000Card Master Key
mfrc522.PLUS_KEY_CARD_CONFIGURATION0x9001Card Configuration Key
mfrc522.PLUS_KEY_L2_SWITCH0x9002Ключ переключения в SL2
mfrc522.PLUS_KEY_L3_SWITCH0x9003Ключ переключения в SL3
mfrc522.PLUS_KEY_SL1_AUTH0x9004Ключ аутентификации SL1

Усиление приёмника

КонстантаОписание
mfrc522.GAIN_1818 dB
mfrc522.GAIN_2323 dB
mfrc522.GAIN_3333 dB
mfrc522.GAIN_3838 dB
mfrc522.GAIN_4343 dB
mfrc522.GAIN_4848 dB (максимальное)
mfrc522.GAIN_MINМинимальное усиление
mfrc522.GAIN_MAXМаксимальное усиление

Полный пример

-- Инициализация MFRC522
if not mfrc522.init(uart.UART1, pio.GPIO26, mfrc522.GAIN_43) then
    print("MFRC522 init failed!")
    return
end

print("MFRC522 ready. Waiting for card...")

while true do
    -- Чтение UID
    uid = mfrc522.uid(uart.UART1, false)

    if uid then
        print("Card detected!")
        print("UID: " .. uid)
        print("Type: " .. card_type)

        -- Для Ultralight/NTAG карт - чтение данных
        if card_type:find("ULTRALIGHT") or card_type:find("NTAG") then
            data, err = mfrc522.read_ul(uart.UART1, 4)
            if data then
                print("Page 4: " .. string.tohex(data))
            end
        end

        -- Остановить карту
        mfrc522.uid(uart.UART1, true)

        -- Пауза перед следующим чтением
        tmr.delay(1000)
    end

    tmr.delay(100)
end

Пример работы с MIFARE Classic

mfrc522.init(uart.UART1, pio.GPIO26, mfrc522.GAIN_43)

uid = mfrc522.uid(uart.UART1, false)
if uid and card_type:find("Classic") then
    print("UID: " .. uid)

    -- Записали
    local ok, err = mfrc522.write_classic(uart.UART1, 4, "Hello MIFARE!!!\0")
    if not ok then
        print("Write error: " .. err)
    else
        -- Прочитали
        local data, err = mfrc522.read_classic(uart.UART1, 4)
        if data then
            print("Read back: " .. data)
        else
            print("Read error: " .. err)
        end
    end
end

Пример работы с защищённым сектором

Пример записи и чтения данных с использованием пользовательского ключа:

mfrc522.init(uart.UART1, pio.GPIO26, mfrc522.GAIN_43)

-- Пользовательский ключ (12 hex символов = 6 байт)
local MY_KEY = "A0B1C2D3E4F5"
local BLOCK = 4  -- блок данных в секторе 1

local uid = mfrc522.uid(uart.UART1, false)
if uid and card_type:find("Classic") then
    -- Записали с пользовательским ключом B
    local write_data = "Access granted!\0"
    local ok, err = mfrc522.write_classic(
        uart.UART1, BLOCK, write_data,
        mfrc522.KeyB, MY_KEY
    )

    if not ok then
        print("Write error: " .. err)
    else
        -- Прочитали тем же ключом
        local read_data, err = mfrc522.read_classic(
            uart.UART1, BLOCK,
            mfrc522.KeyB, MY_KEY
        )
        if read_data then
            print("Read back: " .. read_data)
        else
            print("Read error: " .. err)
        end
    end
end

Пример чтения EMV карты

mfrc522.init(uart.UART1, pio.GPIO26, mfrc522.GAIN_43)

-- Чтение PAN (номера карты) с банковской карты
pan = mfrc522.sprintf(uart.UART1, "P*")
if pan then
    print("PAN: " .. pan)
end

NDEF (NFC Data Exchange Format)

NDEF — стандартный формат хранения данных на NFC-тегах. Позволяет записывать URL, текст, контакты и другие данные, которые понимают все NFC-устройства.

mfrc522.ndef_read(uart_id)

Читает NDEF сообщение с карты Ultralight/NTAG.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.

Возвращает: таблицу с данными или nil, error_message.

Поля возвращаемой таблицы:

  • raw (строка): сырые байты NDEF сообщения
  • type (строка): “uri”, “text” или “unknown”
  • uri (строка): URL (если type == “uri”)
  • text (строка): текст (если type == “text”)
  • lang (строка): код языка (если type == “text”)
uid = mfrc522.uid(uart.UART1, false)

ndef, err = mfrc522.ndef_read(uart.UART1)
if ndef then
    print("Type: " .. ndef.type)
    if ndef.uri then
        print("URI: " .. ndef.uri)
    elseif ndef.text then
        print("Text: " .. ndef.text)
        print("Lang: " .. ndef.lang)
    end
end

mfrc522.ndef_write_uri(uart_id, uri, [prefix_code])

Записывает NDEF URI на карту Ultralight/NTAG.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • uri (строка): URL (без префикса если используется prefix_code).
  • prefix_code (необязательное целое число): код префикса (0x00-0x23). По умолчанию 0.

Коды префиксов:

КодПрефикс
0x00(нет)
0x01http://www.
0x02https://www.
0x03http://
0x04https://
0x05tel:
0x06mailto:

Возвращает: true при успехе или nil, error_message.

uid = mfrc522.uid(uart.UART1, false)

-- Записать https://example.com
mfrc522.ndef_write_uri(uart.UART1, "example.com", 0x04)

-- Записать полный URL без префикса
mfrc522.ndef_write_uri(uart.UART1, "https://example.com/path", 0x00)

mfrc522.ndef_write_text(uart_id, text, [lang])

Записывает NDEF текст на карту Ultralight/NTAG.

Аргументы:

  • uart_id (целое число): идентификатор UART порта.
  • text (строка): текст в UTF-8.
  • lang (необязательная строка): код языка. По умолчанию “en”.

Возвращает: true при успехе или nil, error_message.

uid = mfrc522.uid(uart.UART1, false)

-- Записать текст на английском
mfrc522.ndef_write_text(uart.UART1, "Hello World", "en")

-- Записать текст на русском
mfrc522.ndef_write_text(uart.UART1, "Привет мир", "ru")

Пример: NFC визитка

mfrc522.init(uart.UART1, pio.GPIO26, mfrc522.GAIN_43)

print("Поднесите NTAG карту для записи визитки...")

while true do
    uid = mfrc522.uid(uart.UART1, false)
    if uid then
        -- Записываем ссылку на сайт
        ok, err = mfrc522.ndef_write_uri(uart.UART1, "mycompany.com", 0x02)
        if ok then
            print("Визитка записана!")
        else
            print("Ошибка: " .. err)
        end

        mfrc522.uid(uart.UART1, true)  -- halt
        break
    end
    tmr.delay(100)
end

Пример: Чтение NFC метки

mfrc522.init(uart.UART1, pio.GPIO26, mfrc522.GAIN_43)

while true do
    uid = mfrc522.uid(uart.UART1, false)

    if uid and (card_type:find("NTAG") or card_type:find("ULTRALIGHT")) then
        local ndef = mfrc522.ndef_read(uart.UART1)

        if ndef and ndef.type == "uri" then
            print("Открыть: " .. ndef.uri)
            -- Здесь можно выполнить действие по URL
        end

        mfrc522.uid(uart.UART1, true)
        tmr.delay(1000)
    end

    tmr.delay(100)
end

Примечания

  • После каждого вызова uid() с halt=false карта остаётся активной для последующих операций
  • Для работы с MIFARE Classic используйте read_classic() / write_classic() или sprintf() для форматированного вывода
  • Не записывайте в sector trailer блоки (3, 7, 11, …) без понимания структуры access bits — это может заблокировать сектор
  • Счётчики на NTAG/Ultralight EV1 могут только увеличиваться