UHF RFID Plugin para Flutter
Plugin Flutter para integración con lectores UHF RFID R2000 (SDK v2.5) y escáner de códigos de barras/QR en dispositivos PDA7100 y similares.
Características
UHF RFID
- Inicializar/cerrar lector UHF
- Inventario continuo y único de tags EPC Gen2
- Lectura/escritura de datos en bancos EPC, TID, USER y RESERVED
- Configuración de potencia (5-33 dBm)
- Configuración de región de frecuencia (USA, EU, China, Korea)
- Filtros de inventario
- Soporte para botón físico (trigger)
- Operaciones de Lock/Kill de tags
- Lectura de temperatura del módulo
Escáner de Barcode/QR
- Inicializar/cerrar escáner
- Escaneo de códigos de barras y QR
- Timeout configurable (1-10 segundos)
- Selección de modo (Broadcast o Focus Input)
- Resultados en tiempo real via stream
Requisitos
- Flutter >= 3.3.0
- Android SDK >= 21 (Android 5.0+)
- Dispositivo PDA con módulo UHF R2000 integrado
Instalación
Dependencia Local
dependencies:
uhf_rfid_plugin:
path: ../uhf_rfid_plugin
Desde Git
dependencies:
uhf_rfid_plugin:
git:
url: https://github.com/tu-usuario/uhf_rfid_plugin.git
Configuración Android
En android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 21
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
}
Uso Básico
Importar el Plugin
import 'package:uhf_rfid_plugin/uhf_rfid_plugin.dart';
UHF RFID
Inicialización
// Inicializar el lector
final success = await UhfRfidPlugin.init();
if (success) {
print('Lector UHF inicializado correctamente');
// Obtener información del hardware
final version = await UhfRfidPlugin.getHardwareVersion();
final temp = await UhfRfidPlugin.getTemperature();
print('Hardware: $version, Temperatura: $temp°C');
}
// Al terminar, cerrar el lector
await UhfRfidPlugin.close();
Inventario Continuo
// Suscribirse al stream de tags
final subscription = UhfRfidPlugin.tagStream.listen((RfidTag tag) {
print('Tag detectado: ${tag.epc}');
print('RSSI: ${tag.rssi} dBm');
print('Lecturas: ${tag.count}');
});
// Iniciar inventario continuo
await UhfRfidPlugin.startInventory();
// ... escanear tags ...
// Detener inventario
await UhfRfidPlugin.stopInventory();
// Cancelar suscripción al salir
subscription.cancel();
Inventario Único
// Realizar una lectura única (500ms de timeout)
final List<RfidTag> tags = await UhfRfidPlugin.inventoryOnce(timeout: 500);
print('Tags encontrados: ${tags.length}');
for (final tag in tags) {
print('EPC: ${tag.epc}, RSSI: ${tag.rssi}');
}
Configurar Potencia
// Establecer potencia de lectura y escritura (5-33 dBm)
await UhfRfidPlugin.setPower(readPower: 26, writePower: 26);
// Obtener potencia actual
final power = await UhfRfidPlugin.getPower();
if (power != null) {
print('Potencia lectura: ${power['readPower']} dBm');
print('Potencia escritura: ${power['writePower']} dBm');
}
Configurar Región de Frecuencia
// Establecer región
await UhfRfidPlugin.setRegion(FrequencyRegion.usa);
// Opciones disponibles:
// - FrequencyRegion.usa (902.75 - 927.25 MHz)
// - FrequencyRegion.europe (865.7 - 867.5 MHz)
// - FrequencyRegion.china (920.625 - 924.375 MHz)
// - FrequencyRegion.korea (917.1 - 923.3 MHz)
// Obtener región actual
final region = await UhfRfidPlugin.getRegion();
print('Región actual: $region');
Leer Datos de un Tag
// Leer banco TID (identificador único del tag - solo lectura)
final tid = await UhfRfidPlugin.readTagData(
bank: MemoryBank.tid,
startAddr: 0,
length: 6, // palabras de 16 bits
);
print('TID: $tid');
// Leer banco USER (memoria de usuario)
final userData = await UhfRfidPlugin.readTagData(
bank: MemoryBank.user,
startAddr: 0,
length: 16,
password: '00000000', // Contraseña de acceso (8 hex chars)
timeout: 1000, // Timeout en ms
);
print('User Data: $userData');
// Leer banco EPC
final epc = await UhfRfidPlugin.readTagData(
bank: MemoryBank.epc,
startAddr: 2, // El EPC comienza en la palabra 2
length: 6,
);
Escribir Datos en un Tag
// Escribir en banco USER
final success = await UhfRfidPlugin.writeTagData(
bank: MemoryBank.user,
startAddr: 0,
data: 'AABBCCDD11223344', // Datos en hexadecimal
password: '00000000',
timeout: 1000,
);
if (success) {
print('Datos escritos correctamente');
}
// Escribir nuevo EPC
await UhfRfidPlugin.writeTagEpc(
epc: 'E20000000000000000001234',
password: '00000000',
timeout: 1000,
);
Filtros de Inventario
// Aplicar filtro para leer solo tags con EPC que comience con "E200"
await UhfRfidPlugin.setInventoryFilter(
filterData: 'E200',
bank: MemoryBank.epc,
startAddr: 2, // EPC comienza en palabra 2
matching: true, // true = incluir coincidencias, false = excluir
);
// Realizar inventario (solo tags que coincidan)
await UhfRfidPlugin.startInventory();
// ... escanear ...
// Cancelar filtro
await UhfRfidPlugin.cancelInventoryFilter();
Bloquear un Tag
// Bloquear banco USER
await UhfRfidPlugin.lockTag(
lockObject: LockObject.user,
lockType: LockType.lock,
password: 'AABBCCDD', // Contraseña de acceso
timeout: 1000,
);
// Opciones de LockObject:
// - accessPassword: Contraseña de acceso
// - killPassword: Contraseña de kill
// - epc: Banco EPC
// - tid: Banco TID
// - user: Banco USER
// Opciones de LockType:
// - unlock: Desbloquear
// - lock: Bloquear
// - permaLock: Bloqueo permanente (irreversible)
// - permaUnlock: Desbloqueo permanente
Destruir un Tag (Kill)
// ⚠️ ADVERTENCIA: Esta operación es IRREVERSIBLE
// El tag quedará permanentemente inutilizado
await UhfRfidPlugin.killTag(
killPassword: 'AABBCCDD', // NO puede ser 00000000
timeout: 1000,
);
Escáner de Códigos de Barras/QR
Inicialización
// Inicializar el escáner
final success = await UhfRfidPlugin.initBarcode();
if (success) {
print('Escáner de barcode inicializado');
}
// Al terminar, cerrar el escáner
await UhfRfidPlugin.closeBarcode();
Escanear Códigos
// Suscribirse al stream de resultados
final subscription = UhfRfidPlugin.barcodeStream.listen((BarcodeResult result) {
print('Código: ${result.barcode}');
print('Longitud: ${result.length}');
print('Hora: ${result.dateTime}');
});
// Iniciar escaneo (se detiene automáticamente al leer un código)
await UhfRfidPlugin.startBarcodeScan();
// O detener manualmente
await UhfRfidPlugin.stopBarcodeScan();
// Cancelar suscripción al salir
subscription.cancel();
Configurar Timeout
// Establecer tiempo máximo de escaneo
// Valores permitidos: 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 ms
await UhfRfidPlugin.setBarcodeTimeout(5000); // 5 segundos
Configurar Modo de Escaneo
// Modo Broadcast (recomendado) - resultados via stream
await UhfRfidPlugin.setBarcodeScanMode(BarcodeScanMode.broadcast);
// Modo Focus Input - inserta texto en el campo con foco
await UhfRfidPlugin.setBarcodeScanMode(BarcodeScanMode.focusInput);
Botón Físico (Trigger)
El plugin detecta los eventos del botón físico del PDA y los envía a Flutter. El plugin no ejecuta ninguna acción automática al presionar el botón; la aplicación Flutter decide qué hacer con estos eventos.
Escuchar Eventos del Botón
// Escuchar eventos del botón físico
UhfRfidPlugin.buttonStream.listen((TriggerButtonEvent event) {
if (event.isPressed) {
print('Botón presionado');
// Decidir qué acción realizar, por ejemplo:
// - Iniciar inventario RFID
// - Iniciar escaneo de barcode
// - Cualquier otra acción personalizada
} else if (event.isReleased) {
print('Botón soltado');
// Decidir qué acción realizar al soltar
}
});
Ejemplo: Controlar Inventario con el Botón
UhfRfidPlugin.buttonStream.listen((TriggerButtonEvent event) async {
if (event.isPressed) {
// Iniciar inventario al presionar
await UhfRfidPlugin.startInventory();
} else if (event.isReleased) {
// Detener inventario al soltar
await UhfRfidPlugin.stopInventory();
}
});
Ejemplo: Escanear Barcode con el Botón
UhfRfidPlugin.buttonStream.listen((TriggerButtonEvent event) async {
if (event.isPressed) {
await UhfRfidPlugin.startBarcodeScan();
} else if (event.isReleased) {
await UhfRfidPlugin.stopBarcodeScan();
}
});
Habilitar/Deshabilitar el Botón
// Habilitar/deshabilitar la detección del botón
await UhfRfidPlugin.setTriggerButtonEnabled(true);
// Verificar estado
final enabled = await UhfRfidPlugin.isTriggerButtonEnabled();
// Verificar si está escaneando actualmente
final reading = await UhfRfidPlugin.isReading();
Referencia de API
Métodos UHF RFID
| Método | Descripción |
|---|---|
init() |
Inicializa el lector UHF |
close() |
Cierra la conexión con el lector |
getHardwareVersion() |
Obtiene la versión del hardware |
startInventory() |
Inicia inventario continuo |
stopInventory() |
Detiene inventario continuo |
inventoryOnce({timeout}) |
Realiza inventario único |
setPower({readPower, writePower}) |
Configura potencia (5-33 dBm) |
getPower() |
Obtiene potencia actual |
setRegion(region) |
Configura región de frecuencia |
getRegion() |
Obtiene región actual |
readTagData({...}) |
Lee datos de un tag |
writeTagData({...}) |
Escribe datos en un tag |
writeTagEpc({...}) |
Escribe nuevo EPC |
lockTag({...}) |
Bloquea banco de memoria |
killTag({...}) |
Destruye tag (IRREVERSIBLE) |
setInventoryFilter({...}) |
Aplica filtro |
cancelInventoryFilter() |
Cancela filtro |
getFrequencyPoints() |
Obtiene frecuencias en kHz |
setFrequencyPoints(points) |
Configura frecuencias |
getTemperature() |
Obtiene temperatura del módulo |
setTriggerButtonEnabled(enabled) |
Habilita/deshabilita botón físico |
isTriggerButtonEnabled() |
Verifica si el botón está habilitado |
isReading() |
Verifica si está escaneando |
Métodos Barcode/QR
| Método | Descripción |
|---|---|
initBarcode() |
Inicializa el escáner |
closeBarcode() |
Cierra el escáner |
startBarcodeScan() |
Inicia escaneo |
stopBarcodeScan() |
Detiene escaneo |
setBarcodeScanMode(mode) |
Configura modo de escaneo |
setBarcodeTimeout(timeout) |
Configura timeout (1000-10000ms) |
isBarcodeInitialized() |
Verifica si está inicializado |
Streams
| Stream | Tipo | Descripción |
|---|---|---|
tagStream |
Stream<RfidTag> |
Tags RFID detectados |
buttonStream |
Stream<TriggerButtonEvent> |
Eventos del botón físico |
barcodeStream |
Stream<BarcodeResult> |
Códigos de barras escaneados |
Modelos de Datos
RfidTag
class RfidTag {
final String epc; // Código EPC en hexadecimal
final int rssi; // Intensidad de señal en dBm
final int count; // Número de lecturas
final int? antenna; // Antena que detectó el tag
}
BarcodeResult
class BarcodeResult {
final String barcode; // Código escaneado
final String rawData; // Datos sin procesar
final int length; // Longitud del código
final int timestamp; // Timestamp en milisegundos
DateTime get dateTime; // Fecha y hora del escaneo
}
TriggerButtonEvent
class TriggerButtonEvent {
final int keyCode; // Código de la tecla (F3, F4, F7, 134, 137)
final String action; // "down" (presionado) o "up" (soltado)
final bool isReading; // Estado actual del lector RFID
bool get isPressed; // true si action == "down"
bool get isReleased; // true si action == "up"
}
Nota: El plugin detecta los siguientes códigos de tecla físicos:
KEYCODE_F3- Dispositivos C510xKEYCODE_F4- Dispositivos 6100KEYCODE_F7- Dispositivos H3100134,137- Teclas de escaneo PDA generales
Enumeraciones
MemoryBank
enum MemoryBank {
reserved(0), // Passwords (acceso/kill)
epc(1), // Electronic Product Code
tid(2), // Tag Identifier (solo lectura)
user(3); // Memoria de usuario
}
FrequencyRegion
enum FrequencyRegion {
china('CHN'), // 920.625 - 924.375 MHz
usa('USA'), // 902.75 - 927.25 MHz
europe('EU'), // 865.7 - 867.5 MHz
korea('KOREA'); // 917.1 - 923.3 MHz
}
LockObject
enum LockObject {
accessPassword, // Contraseña de acceso
killPassword, // Contraseña de kill
epc, // Banco EPC
tid, // Banco TID
user; // Banco USER
}
LockType
enum LockType {
unlock, // Desbloquear
lock, // Bloquear
permaLock, // Bloqueo permanente
permaUnlock; // Desbloqueo permanente
}
BarcodeScanMode
enum BarcodeScanMode {
broadcast, // Resultados via barcodeStream
focusInput; // Resultados al campo de texto con foco
}
Manejo de Errores
Todos los métodos pueden lanzar UhfException:
try {
await UhfRfidPlugin.startInventory();
} on UhfException catch (e) {
print('Error: ${e.message}');
}
Ejemplo Completo
Ver el directorio example/ para una aplicación de demostración completa que incluye:
- Escaneo RFID con visualización en tiempo real
- Vista de detalles de tag (TID, USER data)
- Configuración de potencia y región
- Escaneo de códigos de barras/QR con historial
- Timeout configurable
- Soporte para botón físico
Solución de Problemas
Error: "Reader not initialized"
- Verificar que el dispositivo tenga el módulo UHF hardware
- Asegurar que se llamó a
init()antes de cualquier operación
Error: "Failed to load native library"
- Verificar que las librerías .so estén en las carpetas correctas
- Confirmar que el ABI del dispositivo es compatible (arm64-v8a o armeabi-v7a)
Tags no detectados
- Aumentar la potencia con
setPower()(máximo 33 dBm) - Verificar la región de frecuencia correcta para tu país
- Acercar los tags al lector
- Verificar que no haya filtros activos con
cancelInventoryFilter()
Escritura fallida
- Verificar que el tag no esté bloqueado
- Usar la contraseña correcta (por defecto: 00000000)
- El tag debe estar dentro del rango durante toda la operación
- Aumentar la potencia de escritura
Escáner de barras no funciona
- Verificar que se llamó a
initBarcode()primero - Comprobar que el modo está configurado como
broadcast - Aumentar el timeout si los códigos no se detectan
- Verificar que el escáner no esté siendo usado por otra app
Dispositivos Compatibles
- WYUAN PDA3109
- PDA7100
- Otros dispositivos con módulo UHF R2000 y decodificador de barcode
Notas Importantes
-
Solo Android: Este SDK es exclusivo para Android debido a las dependencias de hardware específicas.
-
Dispositivo Específico: Diseñado para dispositivos PDA con módulo UHF R2000 integrado.
-
Permisos: No requiere permisos especiales ya que la comunicación es por puerto serial interno.
-
Ciclo de Vida: Siempre llamar
close()ycloseBarcode()al salir de la aplicación. -
Thread Safety: Las operaciones se ejecutan en threads separados para no bloquear la UI.
-
Kill Tag: La operación
killTag()es IRREVERSIBLE y destruye permanentemente el tag.
Licencia
MIT License - Ver archivo LICENSE para más detalles.