Перейти к содержанию

NDEF: данные на NFC-метках

Практическое руководство по NDEF: структура сообщений и записей, URI, Text, Smart Poster, Type 2 TLV, NTAG и запись через ODRFID или ODNFC.

NDEF (NFC Data Exchange Format) — стандартный контейнер для данных, которыми обмениваются NFC-устройства и метки. В NDEF можно записать ссылку, текст, MIME-данные или несколько связанных записей. Смартфон распознаёт формат и решает, что предложить пользователю: открыть сайт, показать текст или передать данные установленному приложению.

Для обычной метки со ссылкой вся цепочка выглядит так:

NTAG213
└── NFC Forum Type 2 Tag: Capability Container и TLV
    └── NDEF message
        └── URI record: https://unitx.pro

NDEF описывает только два нижних уровня в этом примере: формат сообщения и его размещение по правилам конкретного NFC Forum Tag Type. Это не модель чипа, не UID и не механизм защиты.

Для чего нужен NDEF

NDEF нужен, когда данные должны быть понятны разным телефонам, считывателям и приложениям без собственной структуры блоков:

  • ссылка на инструкцию, карточку товара или форму обратной связи;
  • короткий текст, инвентарный номер или описание объекта;
  • Smart Poster — URI с человекочитаемым заголовком;
  • MIME-данные или внешний тип записи для собственного приложения;
  • несколько записей в одном сообщении, например URI и дополнительная информация о ресурсе.

Если считыватель настроен только на выдачу UID, содержимое NDEF в этом потоке не появится. Программа должна отдельно прочитать пользовательскую память, найти NDEF-разметку и разобрать сообщение.

Четыре уровня, которые часто путают

УровеньПримерЧто определяет
Чип и радиопротоколNTAG213, MIFARE Ultralight, NFC-Aкоманды обмена и физическую память
NFC Forum Tag TypeType 2, Type 4, Type 5где искать NDEF и как безопасно читать или менять его
NDEF messageодно или несколько recordsграницы сообщения и порядок записей
NDEF recordURI, Text, Smart Poster, MIMEтип и полезную нагрузку одной записи

Одинаковая URI-запись NDEF может находиться на разных типах меток, но путь к ней будет различаться. Type 2 использует страницы памяти, Capability Container и TLV. Type 4 работает через приложение и файлы поверх ISO-DEP. Поэтому нельзя взять алгоритм записи страниц NTAG и применить его к DESFire только потому, что обе метки могут хранить NDEF.

Как устроено NDEF-сообщение

Сообщение состоит из одной или нескольких записей. У каждой записи есть заголовок, тип, необязательный ID и полезная нагрузка:

[flags + TNF] [type length] [payload length] [ID length?]
[type] [ID?] [payload]

В первом байте находятся флаги:

ФлагЗначение
MBпервая запись сообщения
MEпоследняя запись сообщения
CFпродолжение фрагментированной записи
SRShort Record: длина payload занимает один байт
ILв заголовке присутствует длина ID
TNFкак интерпретировать поле Type

TNF занимает младшие три бита заголовка:

TNFНазначение
0пустая запись
1NFC Forum Well Known Type: U, T, Sp и другие RTD
2MIME media type, например application/json
3absolute URI как Type; для обычной ссылки чаще применяют URI RTD
4NFC Forum External Type, например example.com:device
5неизвестный тип, payload интерпретирует приложение
6продолжение chunked record
7зарезервировано

Для одной короткой URI-записи одновременно установлены MB, ME и SR, а TNF равен 1. Поэтому первый байт получается 0xD1.

URI

URI RTD имеет тип U. Первый байт payload — код часто используемого префикса:

КодПрефикс
00без сокращения
01http://www.
02https://www.
03http://
04https://

За ним записывается оставшаяся часть URI в UTF-8. Сокращение экономит несколько байт и особенно полезно на небольших метках.

Text

Text RTD имеет тип T. Первый байт payload содержит кодировку и длину кода языка. Затем идут код языка, например ru, и сам текст:

[status] [r] [u] [UTF-8 text]

Бит 7 status выбирает UTF-16 или UTF-8, младшие 6 бит задают длину языка. Для новых записей обычно достаточно UTF-8.

Smart Poster

Smart Poster имеет тип Sp. Его payload — ещё одно NDEF-сообщение. Внутри обязательно находится URI, а рядом могут быть Text-заголовки на разных языках, предпочтительное действие, MIME-тип и размер ресурса.

Smart Poster полезен как структурированный контейнер, но интерфейс телефона может показать только ссылку. Заголовок и предлагаемое действие не являются командой для операционной системы.

MIME и External Type

MIME-запись удобна, когда payload уже имеет стандартный тип данных. External Type подходит для собственного протокола приложения. В обоих случаях универсальный телефон обычно не знает, что делать с содержимым без установленного приложения. Для публичной метки с сайтом URI RTD совместимее.

Разбор URI по байтам

Запись ссылки https://unitx.pro занимает 14 байт:

D1 01 0A 55 04 75 6E 69 74 78 2E 70 72 6F
БайтыЗначение
D1MB=1, ME=1, SR=1, TNF=1
01длина Type: 1 байт
0Aдлина payload: 10 байт
55ASCII U, то есть URI RTD
04сокращённый префикс https://
75 ... 6FUTF-8 строка unitx.pro

Это только NDEF record. На Type 2 Tag он помещается в NDEF Message TLV:

03 0E D1 01 0A 55 04 75 6E 69 74 78 2E 70 72 6F FE

03 обозначает NDEF Message TLV, 0E — длину сообщения, FE — Terminator TLV. Перед NDEF могут находиться служебные Lock Control и Memory Control TLV; корректная программа не должна без необходимости стирать их.

Если сообщение короче 255 байт, длина TLV занимает один байт. Для 255 байт и более используется расширенная форма:

03 FF <length high> <length low> ... FE

Type 2 Tag: Capability Container

На MIFARE Ultralight и NTAG страница 3 содержит Capability Container (CC). Например, заводской CC для NTAG213:

E1 10 12 00
БайтЗначение
E1magic number NDEF
10версия Type 2 Tag mapping 1.0
12размер NDEF-области: 0x12 × 8 = 144 байта
00свободное чтение и запись

CC сообщает логический объём NDEF, а не весь объём кремния. Например, в datasheet NTAG215 указано 504 байта пользовательской памяти, но CC объявляет 496 байт NDEF-области; у NTAG216 это 888 и 872 байта соответственно.

На некоторых метках биты страницы 3 программируются только из 0 в 1. Неверно записанный размер нельзя исправить обычной перезаписью. Поэтому инициализацию полностью пустого CC следует выполнять только после точного определения модели чипа.

Практическая ёмкость

Таблица показывает верхнюю границу для одного NDEF Message TLV без служебного префикса. Lock Control, Memory Control и другие TLV уменьшают доступный объём.

МеткаNDEF-область по CCМаксимум NDEF message без других TLV
MIFARE Ultralight Nano40 байт37 байт
MIFARE Ultralight / EV1 8048 байт45 байт
MIFARE Ultralight EV1 164128 байт125 байт
NTAG213144 байта141 байт
NTAG215496 байт491 байт
NTAG216872 байта867 байт

У заводского NTAG213 перед NDEF часто присутствует пятибайтовый Lock Control TLV. Если сохранить его, как делает ODRFIDKit Web, для самого сообщения останется до 136 байт. Именно фактический предел, прочитанный с метки, нужно сравнивать с размером сообщения.

NFC Forum Tag Types

NDEF не ограничен NTAG:

Tag TypeБазовая технологияТипичное хранение
Type 2NFC-ACC и TLV в линейных страницах
Type 3NFC-Fатрибуты и блоки NFC-F
Type 4NFC-A или NFC-B, ISO-DEPNDEF application, CC file и NDEF file
Type 5NFC-V, ISO 15693CC и TLV в памяти NFC-V

Конкретный чип поддерживает NDEF только при наличии правильной разметки или приложения. Например, ISO-DEP сам по себе ещё не означает, что на карте создано NDEF-приложение Type 4.

Запись через ODRFIDKit Web

ODRFIDKit Web подключается к считывателю через Web Serial. NDEF-режим предназначен для совместимых NFC Forum Type 2 Tag:

  • MIFARE Ultralight и Ultralight Nano;
  • MIFARE Ultralight EV1 80 и 164;
  • NTAG213, NTAG215 и NTAG216.

Режим не форматирует MIFARE Classic и не работает с Type 4 NDEF application. Это намеренная граница: у Classic запись NDEF требует MAD, новых ключей и изменения sector trailers, а у Type 4 — команд ISO-DEP и файловой модели.

Порядок работы

  1. Подключите ODRFID-M или ODRFID-N и разрешите браузеру доступ к последовательному порту.
  2. Поднесите Type 2 метку и нажмите NDEF.
  3. Нажмите Перечитать NDEF. Программа проверит CC, найдёт NDEF TLV и сохранит служебные TLV перед ним.
  4. Выберите URI, Text или Smart Poster. Поле байтов и индикатор ёмкости обновляются сразу.
  5. Проверьте содержимое и доступный объём, подтвердите изменение памяти и нажмите Записать и проверить.
  6. Не убирайте метку, пока приложение не сообщит о завершении проверки.

Если CC полностью нулевой, приложение отдельно предложит его инициализировать. Не включайте этот флажок для неизвестной модели. Если CC уже содержит другое ненулевое значение, программа откажется перезаписывать его автоматически.

При записи веб-приложение:

  • сверяет размер CC с профилем обнаруженной метки;
  • сохраняет TLV перед существующим NDEF;
  • временно ставит Terminator TLV, чтобы незавершённый payload не выглядел готовым сообщением;
  • записывает данные, а заголовок с фактической длиной публикует последним;
  • повторно читает все изменённые страницы и сравнивает их побайтово.

Приложение не устанавливает биты блокировки и не переводит метку в необратимый режим «только чтение». Если память защищена паролем, сначала задайте действующий пароль в панели Ultralight. Ошибка аутентификации или физической записи остановит операцию.

Подключение считывателя, назначение остальных кнопок и работа с памятью MIFARE Classic и T5577 показаны в полной инструкции ODRFIDKit Web.

Как выглядит NDEF-режим

Редактор можно открыть до подключения считывателя, подготовить URI, Text или Smart Poster и сразу увидеть готовые байты. После чтения метки он дополнительно показывает её тип, Capability Container, фактическую ёмкость NDEF и текущее содержимое; только тогда становятся доступны операции чтения и записи.

Консольная утилита ndef_writer.py

У нас есть также консольная утилита def_writer.py для ODRFID. Он формирует записи через ndeflib и поддерживает:

  • URI и Text;
  • Smart Poster с заголовком, действием, MIME-типом, размером и PNG-иконкой;
  • пакетную подготовку URI или Text из файла;
  • MIFARE Classic 1K через MAD1 и NDEF AID E103;
  • Type 2 метки Ultralight и NTAG.

Зависимости:

python3 -m pip install ndeflib pyserial crc

Примеры для Linux:

# URI
python3 ndef_writer.py -p /dev/ttyACM0 uri "https://unitx.pro"

# UTF-8 Text с кодом языка
python3 ndef_writer.py -p /dev/ttyACM0 text "Оборудование UnitX" --lang ru

# Smart Poster
python3 ndef_writer.py -p /dev/ttyACM0 poster "https://unitx.pro" \
  --title "ru:Оборудование UnitX"

# Подробный обмен AT-командами
python3 ndef_writer.py -v -p /dev/ttyACM0 uri "https://unitx.pro"

В macOS порт обычно имеет вид /dev/cu.usbmodem..., в Windows — COM3 или другой номер из диспетчера устройств. Глобальные параметры -p, -b, -v и --cloff указываются перед подкомандой.

Пакетный режим ожидает в каждой непустой строке данные и идентификатор оператора:

https://unitx.pro/item/001    ITEM-001
https://unitx.pro/item/002    ITEM-002
python3 ndef_writer.py -p /dev/ttyACM0 batch tags.txt --type uri

Утилита запрашивает подтверждение для каждой метки, но серийный процесс всё равно должен включать независимое чтение результата и выборочную проверку телефоном.

Важное ограничение MIFARE Classic

Для Classic утилита использует MAD1, стандартный NDEF Key A D3F7D3F7D3F7, меняет sector trailers и каталог приложений в секторе 0. Поддерживается раскладка MIFARE Classic 1K.

Перед первой операцией:

  1. сохраните полный дамп исходной карты;
  2. используйте отдельную тестовую Classic 1K с заводскими ключами;
  3. проверьте чтение именно теми телефонами и приложениями, которые будут использоваться;
  4. не запускайте format на пропуске или карте с чужой структурой секторов.

Команда format использует переданный --key как текущий Key B для аутентификации, затем стирает существующую конфигурацию доступа и возвращает оба ключа и access bits sector trailer к заводским значениям:

python3 ndef_writer.py -p /dev/ttyACM0 format --key FFFFFFFFFFFF

Для обычных смартфонных меток практичнее NTAG и веб-режим Type 2: MIFARE Classic NDEF поддерживается не всеми телефонами и требует гораздо более инвазивного форматирования.

Работа на ODNFC

В ODNFC с прошивкой Lua высокоуровневый модуль mfrc522 может читать и записывать NDEF непосредственно в сценарии считывателя:

-- После настройки UART и mfrc522:
ok, err = mfrc522.ndef_write_uri(uart.UART1, "unitx.pro", 0x04)
if not ok then
    print("NDEF write error:", err)
end

ndef, err = mfrc522.ndef_read(uart.UART1)
if ndef then
    print(ndef.type, ndef.uri or ndef.text)
else
    print("NDEF read error:", err)
end

Доступные функции:

  • mfrc522.ndef_read(uart_id) возвращает raw NDEF и разбирает первую URI или Text record;
  • mfrc522.ndef_write_uri(uart_id, uri, prefix_code) создаёт одну короткую URI record;
  • mfrc522.ndef_write_text(uart_id, text, lang) создаёт одну короткую Text record в UTF-8.

Lua API ODNFC работает с Type 2 Ultralight/NTAG. Размер raw NDEF ограничен 256 байтами, а строковое представление первой URI или Text record — 199 байтами. Smart Poster, MIME, External Type и сообщения из нескольких записей можно увидеть в raw, но высокоуровневый разбор вернёт для первой неподдерживаемой записи тип unknown.

Сигнатуры, коды URI и полный пример приведены в справочнике модуля MFRC522.

ODNFC подходит, когда реакция на метку должна выполняться автономно: проверить URI или текст, включить выход, записать событие или передать результат на сервер. ODRFID удобнее для подготовки меток на рабочем месте.

Совместимость со смартфонами

Для публичной метки выбирайте самый простой формат, достаточный для задачи:

СценарийРекомендация
Открыть веб-страницуодна URI record на NTAG
Показать подпись рядом со ссылкойSmart Poster, но рассчитывать прежде всего на URI
Передать короткий текстодна UTF-8 Text record
Открыть функцию собственного приложенияMIME или External Type и тест на обеих ОС
Хранить много данныхзаписать короткий URI на серверный ресурс

Поведение зависит от модели телефона, версии ОС, настроек NFC, состояния экрана и установленных приложений. Проверяйте не только чтение в технической NFC-утилите, но и пользовательский сценарий: уведомление, текст, переход по ссылке и работа без вашего приложения.

Защита и безопасность

NDEF по умолчанию читается любым находящимся рядом совместимым устройством. Поэтому:

  • не храните в открытом payload пароль, API key, постоянный bearer token или персональные данные;
  • для индивидуальной ссылки используйте короткоживущий или отзывной идентификатор, а права проверяйте на сервере;
  • не считайте UID секретом или криптографическим доказательством;
  • перед включением password protection проверьте, останется ли требуемое чтение доступным обычным телефонам;
  • делайте необратимую блокировку только после чтения, записи, повторного чтения и проверки на целевых устройствах.

NDEF Signature RTD может подтверждать целостность и происхождение записей, но не шифрует payload и не определяет сам по себе инфраструктуру доверия, проверку сертификатов или отзыв ключей.

Если метка не читается

  1. Сначала определите тип чипа. Убедитесь, что программа правильно сопоставила значение Tag Type с типом метки.
  2. Прочитайте CC. Для Type 2 проверьте сигнатуру E1, объявленный размер и разрешение на чтение.
  3. Разберите TLV от страницы 4. Учтите NULL, Lock Control и Memory Control TLV; NDEF не обязан начинаться первым байтом.
  4. Проверьте длины. TLV и запись NDEF не должны выходить за CC или фактически считанные данные.
  5. Проверьте флаги. В NDEF-сообщении с одной записью нужны и MB, и ME; короткая запись с однобайтной длиной должна иметь SR.
  6. Проверьте защиту. CC, биты блокировки, пароль и конфигурационные страницы — независимые ограничения.
  7. Сравните с другим приложением и телефоном. Успешное чтение памяти без преобразования ещё не гарантирует, что операционная система поддерживает этот тип записи.

Стандарты и документы