Інтеграція UHF RFID-зчитувачів (дальнього дії) у мобільні додатки
UHF RFID (860–960 МГц) — інший світ у порівнянні з HF NFC/MIFARE. Дальність читання 3–10 метрів, масове читання сотень тегів в секунду, пасивні теги вартістю $0.05–0.15 штука. Zebra RFD40, Chainway R6, Bluebird EF500 — мобільні UHF-зчитувачі з BLE або USB підключенням. Інтеграція таких пристроїв у мобільні додатки має свої особливості: LLRP протокол, EPC C1G2 стандарт, управління мощностю антени.
LLRP vs власний SDK
LLRP (Low Level Reader Protocol, ISO 15961) — стандартний протокол управління UHF-зчитувачами. Підтримується Impinj, Zebra, Alien. Дозволяє використовувати один код для різних зчитувачів. Для мобільного додатка через Wi-Fi підключення до зчитувача.
Власний SDK — Zebra RFID SDK, Chainway SDK. Простіший у використанні, закритий під конкретного виробника, працює через BLE або USB.
Для більшості мобільних зчитувачів з BLE — власний SDK. LLRP — для стаціонарних зчитувачів у мережі.
Zebra RFID SDK: практика
implementation("com.zebra.rfid.api3:rfid-api3:2.0.2.109")
class UhfReaderManager(private val context: Context) : RfidEventsListener {
private var reader: RFIDReader? = null
private val readers = Readers(context, ENUM_TRANSPORT.BLUETOOTH)
fun connect(deviceName: String) {
val readerDevices = readers.GetAvailableRFIDReaderList()
val targetDevice = readerDevices?.find { it.name == deviceName } ?: return
reader = targetDevice.RFIDReader
reader?.connect()
// Настройка параметрів читання
val params = reader?.Config?.antennaConfigurations?.get(0)
params?.transmitPowerIndex = 270 // ~30 dBm, максимальна мощність
params?.receiveFrequency = 55 // оптимально для більшості регіонів
reader?.Config?.setAntennaConfiguration(params)
// События
reader?.Events?.apply {
addEventsListener(this@UhfReaderManager)
setHandheldEvent(true) // кнопка триггера на зчитувачі
setTagReadEvent(true)
setInventoryStartEvent(true)
setInventoryStopEvent(true)
}
}
// Запуск інвентаризації з фільтрацією за маскою EPC
fun startInventoryWithFilter(epcMask: String?) {
val startTrigger = TriggerInfo().apply {
startTrigger.triggerType = START_TRIGGER_TYPE.START_TRIGGER_TYPE_IMMEDIATE
}
val stopTrigger = TriggerInfo().apply {
stopTrigger.triggerType = STOP_TRIGGER_TYPE.STOP_TRIGGER_TYPE_DURATION
stopTrigger.stopTriggerByDuration.duration = 5000 // 5 секунд
}
val tagFilter = if (epcMask != null) {
TagFilter().apply {
tagPattern = epcMask
tagPatternBitCount = epcMask.length * 4
filterAction = FILTER_ACTION.FILTER_ACTION_STATE_AWARE_FILTERING_ACTION_UNSPECIFIED
}
} else null
reader?.Actions?.Inventory?.perform(startTrigger, stopTrigger, tagFilter)
}
override fun eventReadNotify(e: RfidReadEvents) {
e.ReadEventData.TagData.forEach { tag ->
onTagRead(
epc = tag.TagID,
rssi = tag.PeakRSSI.toInt(),
antennaId = tag.AntennaID.toInt(),
readCount = tag.ReadCount
)
}
}
override fun eventStatusNotify(rfidStatusEvents: RfidStatusEvents) {
when (rfidStatusEvents.StatusEventData.statusEventType) {
STATUS_EVENT_TYPE.INVENTORY_START_EVENT -> onInventoryStarted()
STATUS_EVENT_TYPE.INVENTORY_STOP_EVENT -> onInventoryCompleted()
STATUS_EVENT_TYPE.HANDHELD_TRIGGER_EVENT -> {
val triggerType = rfidStatusEvents.StatusEventData.HandheldTriggerEventData.handheldEvent
if (triggerType == HANDHELD_TRIGGER_EVENT_TYPE.HANDHELD_TRIGGER_PRESSED) {
startInventoryWithFilter(null)
}
}
}
}
}
transmitPowerIndex = 270 — мощність у одиницях зчитувача. У кожного виробника своя шкала. Zebra RFD40: 0–270 = 0–30 dBm. Надмірна мощність у закритому складі → переотражання → помилкові читання з сусідніх зон.
Регуляторні обмеження частот
UHF RFID працює в різних діапазонах:
- США/Канада (FCC): 902–928 МГц
- Європа/Росія (ETSI): 865–868 МГц
- Китай: 920–925 МГц
Мобільний додаток повинен настроювати регіональний діапазон зчитувача:
reader?.Config?.setRegulatoryConfig(
RegulatoryConfig().apply {
region = REGULATORY_REGION.REGULATORY_REGION_ETSI // або FCC, China
enableHoppingChannels = true
}
)
Використання FCC-діапазону в Росії — порушення РЧС. Зчитувачі з регіональними firmware-блокуваннями не дадуть вибрати невірний регіон, але перевірити варто.
Продуктивність: 500 тегів в секунду
UHF-зчитувач може читати кілька сотень тегів в секунду. Потік тегів не можна обробляти на головному потоці — UI заморозиться:
private val tagChannel = Channel<TagReadData>(capacity = Channel.UNLIMITED)
// У BroadcastReceiver/callback зчитувача — лише кладемо в канал
override fun eventReadNotify(e: RfidReadEvents) {
e.ReadEventData.TagData.forEach { tag ->
tagChannel.trySend(tag) // не блокує, не кидає виключення
}
}
// Обробка в окремій корутині
fun processTagsInBackground() {
scope.launch(Dispatchers.Default) {
for (tag in tagChannel) {
val epc = tag.TagID
inventoryRepository.recordRead(epc, tag.PeakRSSI.toInt())
}
}
}
Терміни
Інтеграція Zebra/Chainway UHF BLE-зчитувача з базовою інвентаризацією та відображенням тегів: 5 днів. Розширене рішення з регіональними настройками, фільтрацією, EPC декодуванням та WMS-синхронізацією: 1–2 тижні.







