Skip to main content

python-fi50x

A transport-agnostic Python driver for the FI-50X UHF RFID reader (ISO-18000-6C / EPC Gen2) over its ASCII-over-UART protocol.

The library knows only the reader protocol — framing, the command set, typed responses, error-code handling and CRC-16 verification. It talks to a pluggable transport, so the same driver runs against a real serial port or a scripted fake in tests, and can be wrapped by any service (a RabbitMQ worker, a REST API, a CLI) on top.

  • Import name: fi50x
  • Distribution name: python-fi50x (like python-decoupleimport decouple)

Features

  • Inventory: single (Q) and multi-tag (U, U<slotQ>) EPC reads, drained to completion and de-duplicated
  • Memory read/write (R/W) with correct hex address/length framing and word-length validation
  • Tag management: access password (P), kill (K), lock/unlock (L), select/filter (T)
  • Read-with-EPC (UR/QR), TX power and frequency-regulation get/set
  • Typed results (Tag, ReadResult, WriteResult) and a typed exception hierarchy for reader error codes
  • Gen2 CRC-16 verification of EPC reads
  • TagTracker helper to turn inventory sweeps into new/lost tag events
  • Thread-safe: a transport lock serialises full command/response transactions

Scope note: GPIO, frequency tuning, baud switching, command logging and the AA binary protocol are not implemented yet. See doc/PROTOCOL.md for the complete protocol reference.

Installation

pip install python-fi50x

Requires Python ≥ 3.8 and a serial connection to the reader (USB-UART). From source:

git clone https://github.com/pavelkim/python-fi50x
cd python-fi50x
pip install -e .[dev]

Quickstart

from fi50x import RFIDReader, Bank, Regulation

# Open the serial port (default baud 38400).
reader = RFIDReader.open("/dev/ttyUSB0")

print("firmware:", reader.firmware_version())
print("reader id:", reader.refresh_reader_id())

# Optional configuration
reader.set_regulation(Regulation.EU)   # 865–868 MHz
reader.set_power_dbm(20)               # register = dBm + 2, handled for you

# Inventory — read every tag currently in the field
for tag in reader.inventory().tags:
    print(tag.epc, "crc_ok" if tag.crc_valid else "crc?")

# Read 4 words of USER memory, then write two words back
data = reader.read(Bank.USER, address=0, length=4).data
reader.write(Bank.USER, address=0, data="DEADBEEF")   # leading zeros preserved

reader.close()

RFIDReader is also a context manager (with RFIDReader.open(port) as reader:). For tests or non-serial links, construct it directly with a Transport: RFIDReader(my_transport).

Continuous scanning with new/lost detection

from fi50x import RFIDReader, TagTracker

reader = RFIDReader.open("/dev/ttyUSB0")
tracker = TagTracker(miss_tolerance=2)   # tolerate a couple of missed sweeps

for result in reader.stream_inventory(interval=0.1):
    delta = tracker.update(tag.epc for tag in result.tags)
    for epc in delta.new:
        print("+ NEW ", epc)
    for epc in delta.lost:
        print("- LOST", epc)

A ready-to-run version of this is python-fi50x-scan.py:

./python-fi50x-scan.py --port /dev/ttyUSB0 --mode changes   # only new/lost tags
./python-fi50x-scan.py --port /dev/ttyUSB0 --mode stream     # every tag, every sweep
./python-fi50x-scan.py --help

Error handling

Reader error codes are raised as typed exceptions (all subclasses of ReaderError), so callers can react to specific conditions:

from fi50x import MemoryLockedError, InsufficientPowerError, NoTagError

try:
    reader.write(Bank.EPC, 2, "30001234")
except MemoryLockedError:
    ...   # bank is locked
except InsufficientPowerError:
    ...   # move the tag closer / raise power and retry
except NoTagError:
    ...   # nothing in the field

Configuration

The example script reads config from CLI flags, falling back to environment variables (a local .env is loaded if python-decouple is installed). See .env.example:

Variable Default Meaning
SERIAL_PORT /dev/ttyUSB0 Serial device
BAUDRATE 38400 UART baud rate
TIMEOUT 0.5 Serial read timeout (s)
TX_POWER 20 TX power in dBm (−2…25)
REGULATION 05 Regulation code (05 = EU)

Development

pip install -e .[dev]
pytest                 # run the test suite (no hardware needed — uses a fake transport)
tox                    # test across Python versions
tox -e build           # build the sdist + wheel into dist/

Tests run entirely against a scripted FakeTransport, so no reader is required.

Protocol reference

The full FI-50X command specification — framing, every command, error codes, regulation and baud tables, examples and the Gen2 memory map — is in doc/PROTOCOL.md. Original vendor datasheets are in doc/.

License

MIT © Pavel Kim

Release files for python-fi50x 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for python-fi50x 0.2.0
File Size Uploaded
python_fi50x-0.2.0.tar.gz 21.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for python-fi50x 0.2.0
File Interpreter ABI Platform
python_fi50x-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.2 kB

Release files / python_fi50x-0.2.0.tar.gz

Download URL python_fi50x-0.2.0.tar.gz
Size 21.8 kB
Tags Source
SHA-256 checksum
How to use checksums
abbc8eec7cd1179b3ec77be22ca9c84b34de23dacac83bf796d9c3744ada0c2c
BLAKE2b-256 checksum
How to use checksums
35c4125a7d1e783880a7df6dc5ff098d427f2fb4814c3cb6dec407c6be5c22fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / python_fi50x-0.2.0-py3-none-any.whl

Download URL python_fi50x-0.2.0-py3-none-any.whl
Size 19.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fe4db5b77598ff545bc369e2783b6301c445596d71e4d3c08aa108cb8ac5b888
BLAKE2b-256 checksum
How to use checksums
c5e371f3aebd0e54cb8f36121f95ed52faf64d07725595dacf309ee993510619
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page