Asquarete

The reader wasn't the reader — a minerals inspection app on an unbranded UHF handheld

A minerals inspection company in Belgium was scanning RFID tags and reading raw hex. We replaced that with an app that shows the lot, the mineral and the location on the pull of a trigger — after discovering the device was not the reader the project had been scoped around.

Device
OEM UHF handheld (Impinj E710 module)
Role
Reverse-engineered SDK integration + offline Android app
Updated
4 Sept 2026

Stack · Android, Kotlin, Jetpack Compose, Room, Apache POI, JNI / C++, Cloudflare Workers

A minerals inspection company had already bought the hardware and already had tags on their lots. What they did not have was anything that made a scan mean something. Inspectors pulled the trigger in a generic vendor utility and got back a hex string — an EPC, correct and completely useless to the person holding the device. Matching that string to a lot meant going back to a spreadsheet, or to paper.

The ask was small and precise: pull the trigger, see the right details.

What the project was scoped around, and what was actually in the box

The engagement was scoped against a Zebra reader API, because that is what the device was believed to be. It wasn't.

Static inspection of the working demo app that shipped on the device showed something else entirely: not a Zebra reader, not an RScja or Chainway unit, but an OEM Android tablet on an Allwinner A523 board with a UHF module built into the chassis. The radio was an Impinj E710. The app talked to it over a raw serial port at 115200 baud, through a device node the vendor had aliased — /dev/rfid, pointing at /dev/ttyS3.

There was no SDK to integrate. There was an APK, on a device in another country.

Working without the device

The hardware stayed in Belgium for the whole build. That ruled out the usual loop of trying something and watching what the reader does, so the integration was recovered by decompiling the app that already worked on the device and reading how it drove the module — which classes it called, in what order, with what parameters.

That produced a working integration path. It also produced the single most useful thing we found.

The thing that cost the most and appears in no document

Once the serial layer was wired up, every command timed out. The port opened cleanly. The baud rate was right. Nothing came back — a -79 request timeout, every time.

The port was opening onto an unpowered radio. The module sits behind a GPIO power rail that nothing raises at boot, so opening the UART succeeds and then talks to a dead chip. Two GPIO lines have to be written first, in the order the stock app writes them in its own power-on routine.

No vendor documentation states this. It was found by reading what the working app did before it opened the port. We have since written it up in full, because it will happen to somebody else:

What was built

Three flows, designed for someone who is an inspector and not a technician:

Inspect. Pull the trigger, get a card: mineral name large, lot and location beneath it, the EPC demoted to a footnote where it belongs. An unrecognised tag gets a clean empty state and a single button — tag this — rather than an error.

Commissioning. Scan a fresh tag, bind it to a lot, done. Re-assign and unbind for tags that outlive their lot, with a TID fallback for tags shipped with a blank EPC.

Lots. The catalogue, imported from the spreadsheet the company already maintained rather than retyped into a new system.

Later work added the things an inspection is actually for: per-lot quantity and quality fields — weight, moisture, grade, sample, seal — a formal inspection certificate exported as a spreadsheet, sign-off, and per-lot scan history so a lot's chain of custody can be reconstructed after the fact.

Details worth keeping

Status

Shipped, with an illustrated Phase-1 guide handed over for the pilot. A second phase — count and audit mode, multi-device, online licence enforcement — is specified and currently gated off.

Similar hardware on your desk?

Send the device model, the SDK, and what's failing. You get a written feasibility read within 24 hours.

Get a feasibility read

Another case study