
UID и номер карты: форматы, Wiegand и преобразование
Почему UID считывателя не совпадает с номером на карте, как работают прямой и обратный порядок байтов, десятичные номера и Wiegand, и как выполнить преобразование вручную или в ODRFID и ODNFC.
UID, номер на корпусе карты и номер в СКУД — не обязательно одно и то же. Считыватель получает последовательность байтов или поля протокола, а затем прошивка, конфигуратор или прикладная программа выбирает, как их показать. Поэтому одна карта может одновременно иметь несколько правильных номеров.
Почему напечатанный номер отличается
На карту могут нанести:
- UID в HEX, слитно или с разделителями;
- тот же UID с обратным порядком байтов;
- UID как одно десятичное число;
- пару Facility Code + Card Number для конкретного формата Wiegand;
- только Card Number без Facility Code;
- номер, назначенный производителем, эмитентом или базой СКУД и вообще не вычисляемый из UID.
Надпись сама по себе не определяет алгоритм. Две одинаково выглядящие карты могут быть выпущены с разными правилами нумерации. Для проверки нужны как минимум напечатанный номер и исходные байты, прочитанные без преобразования.
Что именно читает устройство
| Носитель | Исходный идентификатор | Что важно учитывать |
|---|---|---|
| MIFARE, NTAG и другие ISO/IEC 14443 | UID длиной обычно 4, 7 или 10 байт | 7- и 10-байтовый UID нельзя без потерь поместить в Wiegand 26 |
| EM-Marine EM4100 | 5 байт идентификатора | на карте часто печатают последние байты как десятичное число или пару полей |
| HID Prox | закодированный профиль с полями, например H10301 | Facility Code и Card Number уже являются частью выбранного профиля |
| UHF EPC Gen2 | EPC, а отдельно может быть TID | EPC и TID — не NFC UID; правило печати задаёт поставщик метки |
| Смартфон или Wallet | UID, PAN или токен — в зависимости от сценария | значение может отличаться от номера пластиковой карты и меняться |
UID удобен как идентификатор, но не является паролем или криптографическим доказательством подлинности. Ограничения смартфонов и мобильных кошельков разобраны в статье «Телефон вместо пропуска».
Один UID в разных форматах
Возьмём четырёхбайтовый UID 7A 40 3A B9.
| Представление | Правило | Результат |
|---|---|---|
| HEX, прямой порядок | байты выводятся в порядке чтения | 7A403AB9 |
| HEX с разделителями | меняется только оформление | 7A:40:3A:B9 |
| HEX, обратный порядок | порядок байтов разворачивается | B93A407A |
| Десятичное big-endian | 0x7A403AB9 как одно число | 2051029689 |
| Десятичное little-endian | 0xB93A407A как одно число | 3107602554 |
| Десятичные байты | каждый байт переводится отдельно | 122 64 58 185 |
| Wiegand 26, поля | FC = 0x3A, CN = 0x407A | 058,16506 |
| Wiegand 26, данные одним числом | 0x3A407A без битов паритета | 3817594 |
Ведущие нули являются частью отображаемого формата: 058,16506 и
58,16506 содержат те же числовые поля, но могут не совпасть при текстовом
импорте. Разделитель — запятая или точка — также относится только к текстовому
представлению.
Длинные UID
Для UID 04A1B2C3D4E5F6 прямое десятичное представление равно
1303689068602870, а после разворота байтов — 69495546249912580.
Обычный 32-битный тип данных для таких значений мал. В JavaScript требуется
BigInt, а в контроллере с ограниченной арифметикой безопаснее хранить UID как
байты или HEX-строку.
Если старая СКУД принимает только Wiegand 26, длинный UID придётся усечь, свернуть по согласованному алгоритму или сопоставить с отдельным номером в базе. Любой такой вариант может создать совпадения между разными UID.
Wiegand: интерфейс и формат данных
Словом Wiegand называют сразу две разные вещи:
- электрический интерфейс с линиями
D0иD1; - битовый профиль передаваемого номера.
Например, распространённый H10301 содержит 26 бит:
P | Facility Code: 8 бит | Card Number: 16 бит | P
Крайние биты — контроль чётности, а полезные данные занимают 24 бита.
Запись 058,16506 показывает два полезных поля. По проводам передаётся битовый
кадр без запятой, точки и десятичных символов.
Кроме Wiegand 26 применяются 34-, 35-, 37-битные и пользовательские профили. У них отличаются ширина и положение полей, правила чётности, а иногда Facility Code отсутствует. Совпадение длины кадра ещё не гарантирует одинаковую интерпретацию: например, разные 37-битные профили могут делить данные на поля по-разному.
| Пример профиля | Всего бит | Полезные поля |
|---|---|---|
| H10301 | 26 | Facility Code 8 бит + Card Number 16 бит |
| H10306 | 34 | Facility Code 16 бит + Card Number 16 бит |
| Corporate 1000 | 35 | Company bit 1 бит + Facility Code 12 бит + Card Number 20 бит |
| H10302 | 37 | Card Number 35 бит, без Facility Code |
| H10304 | 37 | Facility Code 16 бит + Card Number 19 бит |
H10302 и H10304 имеют одинаковую длину. Считывателю или контроллеру нужно заранее сообщить, какой именно 37-битный профиль используется.
Проверка совместимости со СКУД
До массовой регистрации карт зафиксируйте:
- длину кадра в битах;
- ширину и положение Facility Code и Card Number;
- какие байты UID используются и какие отбрасываются;
- порядок байтов и порядок битов;
- правила чётности;
- требуемые ведущие нули и текстовый разделитель при импорте;
- поведение для 7- и 10-байтовых UID.
Проверьте несколько реальных карт, включая номера с нулями в начале полей. Если контроллер уже хранит карты, сначала получите его эталонный результат и подберите преобразование считывателя под него.
Преобразование вручную
Для примера 7A403AB9:
- Разбейте UID на байты:
7A 40 3A B9. - Для обратного порядка переставьте байты целиком, не отдельные символы:
B9 3A 40 7A. - Для десятичного little-endian переведите
B93A407Aиз HEX: получится3107602554. - Для принятого в этом примере отображения Wiegand 26 возьмите
3A 40 7A:0x3A = 58,0x407A = 16506. - Дополните поля нулями до
058,16506.
Преобразования вручную через Google Таблицы и Excel
Формулы ниже рассчитаны ровно на восемь HEX-символов в A1. В русской
локали используется ;; если таблица ожидает запятые между аргументами,
замените разделители функций.
| Результат | Формула |
|---|---|
| Прямое десятичное число | =HEX2DEC(A1) |
| HEX с обратным порядком байтов | =MID(A1;7;2)&MID(A1;5;2)&MID(A1;3;2)&MID(A1;1;2) |
| Десятичное little-endian | =HEX2DEC(MID(A1;7;2)&MID(A1;5;2)&MID(A1;3;2)&MID(A1;1;2)) |
| Facility Code | =HEX2DEC(MID(A1;5;2)) |
| Card Number | =HEX2DEC(MID(A1;3;2)&MID(A1;1;2)) |
058,16506 | =TEXT(HEX2DEC(MID(A1;5;2));"000")&","&TEXT(HEX2DEC(MID(A1;3;2)&MID(A1;1;2));"00000") |
| Данные Wiegand 26 одним числом | =HEX2DEC(MID(A1;5;2)&MID(A1;3;2)&MID(A1;1;2)) |

Автоматически: строка форматирования
ODRFID и программируемые ODNFC могут выполнить распространённые преобразования до передачи результата. Наличие конкретного кода зависит от модели и версии прошивки.
Для UID 7A403AB9 используются такие строки:
| Строка | Результат |
|---|---|
HU* | 7A403AB9 |
HU~ | B93A407A |
Ur | 3107602554 |
Uc | 058,16506 |
Ud | 058.16506 |
HU2:0 | 3A407A |
Uc и Ud рассчитаны на четырёхбайтовый UID и реализуют именно показанное
выше деление на 8-битный Facility Code и 16-битный Card Number. Для другой
длины UID или другого профиля используйте собственный код на ODNFC.
Для меток 125 кГц предусмотрены отдельные сокращения: e выводит Card Number
HID Prox или компактный десятичный номер EM-Marine, E — поля
Facility Code.Card Number, а D — объединённое десятичное значение профиля
HID или расширенный десятичный номер EM-Marine. Семантика этих кодов зависит
от протокола метки, модели и прошивки, поэтому после настройки проверьте
результат на нескольких известных картах.
Строку можно задать в ODRFIDConfig, веб-интерфейсе, настройке
rfid-format или AT-командой:
AT+F=Uc
AT+P
Полный синтаксис, экранирование символов и чтение блоков описаны в справочнике строки форматирования.
Lua на ODNFC
Встроенный модуль может сразу вернуть нужный вариант:
local uid_hex = mfrc522.scan("HU*")
local uid_dec = mfrc522.scan("Ur")
local wiegand = mfrc522.scan("Uc")
Для собственного профиля запросите байты UID и соберите поля в программе:
local function format_w26(...)
local b = {...}
if #b ~= 4 then
return nil, "для этого правила нужен UID из 4 байт"
end
local facility = b[3]
local card = b[2] * 256 + b[1]
return string.format("%03d,%05d", facility, card)
end
local result
mfrc522.scan(function(...)
result = format_w26(...)
end)
В рабочей программе лучше передавать результат из callback сразу в Modbus, OSDP или сетевой обработчик, а не полагаться на глобальную переменную.
MicroPython или серверная обработка
Если UID уже получен как HEX-строка:
uid = bytes.fromhex("7A403AB9")
direct_hex = uid.hex().upper()
reverse_hex = uid[::-1].hex().upper()
direct_decimal = int.from_bytes(uid, "big")
reverse_decimal = int.from_bytes(uid, "little")
payload = uid[2::-1] # 3A 40 7A — правило именно этого примера
facility = payload[0]
card_number = int.from_bytes(payload[1:], "big")
wiegand_text = f"{facility:03d},{card_number:05d}"
В ODNFC на MicroPython строка из настройки передаётся в
RFID.read(fmt=...); нестандартное преобразование можно выполнить после
чтения. Для обмена между системами полезно передавать одновременно исходный
UID и вычисленный номер: это упрощает диагностику и смену формата.
Какой формат выбрать
- Для новой системы храните исходный UID или EPC без потерь и отдельно — правило отображения.
- Для существующей СКУД повторите её длину Wiegand, поля, порядок и чётность.
- Для HID-клавиатуры выберите строку, которую ожидает Excel, 1С или форма.
- Для Modbus, OSDP и сетевого API передавайте сырые байты, если преобразование удобнее выполнять централизованно.
- Не смешивайте в одном поле NFC UID, PAN, UHF EPC и произвольный номер из базы без явного указания типа идентификатора.
Если номер на карте не получается ни одним известным преобразованием, это,
скорее всего, отдельный номер эмитента. Его нужно импортировать как
соответствие исходный идентификатор → напечатанный номер, а не пытаться
восстановить неизвестную формулу.