yourovo/ASSESSMENT.md
type-two 6d9a49ea42 Fix the EPC scheme; verify encode independently with the Chafon
The SKU<->EPC layout inherited from daemon.cs was WRONG. daemon.cs packs 6 bytes
of SKU + 4 bytes of release_id + an 0xEC01 marker, and flagged itself as
unverified. The shop's actual scheme -- in pliceclogs/rfid-daemon/index.js
(buildEpcPayload/parseEpcData), which is what the extension and every existing
tag use -- is a single 96-bit big-endian integer:

    value = sku(14 digits) * 10^9 + releaseId

Checked against that file's own worked example: 20260321001001 + 35332 ->
20260321001001000035332. epc_decode also reproduces its guard, returning None
when value/10^9 exceeds 14 digits, so a factory tag (E28069...) can never be
reported to the operator as a real SKU.

This matters: parseEpcData rejects the EC01 layout outright, so every tag the
Windows daemon has written is invisible to the extension. ASSESSMENT.md now
carries that as a red open item against daemon.cs.

Verified end-to-end with an INDEPENDENT reader. ASSESSMENT.md sec.4 parked the
Chafon as HID-keyboard-only, but that is Windows-specific -- on macOS it exposes
a CH340 bridge and its documented serial protocol works. New chafon.py (read-only:
inventory + read EPC, CRC-16/0x8408 framing) scans the field and sees:

    0000044A5600D4CDA1FAB932  -> sku=20260722180000 rel=424242   (Urovo-written)
    E28069950000600625F600D3  -> factory/blank  (x12, rest of roll)
    126D5125FFA000067932EC01  -> factory/blank  (earlier EC01 tag, now rejected)

That last line is the bug demonstrated: a tag written earlier today under the old
scheme is unrecognised, exactly as the extension would have treated it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 16:45:07 +10:00

14 KiB
Raw Blame History

Urovo D812R+ RFID print+encode — full assessment & handover

Session date: 2026-07-22 (Windows 11 bench machine). Goal: make the Urovo D812R+ (RFID) print a record label and program the UHF chip in one pass, driven from the PriceGod browser extension — replacing the current two-step flow (Chafon gun programs a chip → Dymo 450 prints a paper label → stick paper over chip).


TL;DR

  • Built a working Windows daemon (pricegod-urovo-daemon, C#/.NET Framework, http://localhost:7790) that drives the Urovo via the GTSPL SDK and does print + RFID-encode in one pass. Drop-in for the extension's daemon.js (same port, same JSON, CORS *).
  • Printing works — dark, correct, on real thermal stock, at the right size (54×24). The daemon even auto-clears the recurring "out of ribbon" latch on every print.
  • RFID read/encode path proven — the Urovo reads UCODE-9 chips and the writeUHF+printlabel encode sequence is wired and fires cleanly.
  • The blocker to the actual one-pass dream is the LABELS, not the printer: the shop's glossy "U9 65×35mm PET" RFID labels are not direct-thermal — a direct-thermal printer can't mark them (only a faint emboss). Need direct-thermal UHF RFID paper labels.
  • Chafon H-102 on Windows is a dead end for writing — it outputs as an HID keyboard, not serial; the SDK can't reach it to program tags without a chronically-unreliable device-side mode flip. Recommend: Chafon = scanner (reads/looks-up), Urovo = all writing.

1. What was built (in this repo)

File What
daemon.cs The service. HttpListener on :7790, CORS *, drives the Urovo via GTSPL_SDK.dll.
chafcheck.cs Probe for the Chafon reader via native UHFPrimeReader.dll (P/Invoke).
start-daemon.cmd / build.cmd Run / rebuild (rebuild uses the always-present .NET Framework csc.exeno .NET SDK needed).
README.md How to run + routes.
PROGRESS.md Running log.
*.dll Vendor binaries needed to build/run on Windows: GTSPL_SDK.dll, GTSPL_SDK_C.dll, zlib.net.dll (Urovo), UHFPrimeReader.dll, hidapi.dll (Chafon).

Build with build.cmd (rebuilds pricegod-urovo-daemon.exe; .exes are git-ignored). Run with start-daemon.cmd.

Daemon routes (:7790, JSON)

method path body returns
GET /status {ok,ready,status,statusText,printer}
POST /print-encode {sku,releaseId,artist?,title?,genre?,style?,info?,price?,condition?,w?,h?,gap?,dir?} {ok,epc,status,note,...}
POST /write-tag alias of /print-encode (the daemon.js name) same
POST /print-test same body, prints only, no RFID (safe on plain thermal)
GET /read-tag / /read-raw tag EPC / all memory banks
POST /calibrate / /recover RFID auto-cal / ribbon-off+feed clear

2. The proven Urovo print+encode recipe ( portable — this is what transfers to the Mac)

Every step below is the exact GTSPL SDK call sequence the daemon uses. Almost all of them are plain TSPL commands (sendcommand) — the only special one is the RFID write. This recipe is device knowledge, not language knowledge; it should replicate on any GTSPL binding (Windows .NET, Java/GTSPL-1.1.8.jar, Android, etc.):

detectUSB / openport                      # connect to "Urovo D812Riplus(RFID)"
SET RIBBON OFF                            # direct thermal (no ribbon). REQUIRED, repeatedly (see §3)
<if status != "00">: formfeed; SET RIBBON OFF   # clear a latched "out of ribbon"
SET TEAR ON
GAP 2 mm,0 mm                             # gapped die-cut labels (Dymo 54×24). For RFID rolls that
                                         #   blind the gap sensor: GAP 0 mm,0 mm (continuous)
SIZE 54 mm,24 mm                          # label size (was 65×35 for the PET RFID stock)
DENSITY 15                                # MAX darkness — needed; lower barely marks
DIRECTION 1                               # flip to 0 if the label prints rotated
CLS (clearbuffer)
writeUHF("H", 2, 12, "E", <24-hex EPC>)   # ⭐ THE RFID ENCODE: hex, EPC bank ("E"), word 2, 12 bytes (96-bit EPC)
TEXT/QRCODE ... (printerfont/qrcode)      # the visible label
SET RIBBON OFF                            # re-assert immediately before printing
printlabel("1","1")                       # fires PRINT + ENCODE together, one pass
<wait for status to return to "00">       # RFID+feed leaves it "busy" for ~13s (see §3)
readUHF("H",2,12,"E") / query_UHF         # optional verify (note: the encoded tag has fed past the
                                         #   antenna by now, so on-printer read-back is unreliable —
                                         #   verify with a separate reader / Chafon scan instead)

SKU ↔ EPC scheme (v1, in the Epc class): the 96-bit EPC (12 bytes) = 6 bytes 14-digit timestamp SKU + 4 bytes release_id + 2 bytes 0xEC01 marker. ⚠️ Unverified against the Mac/Chafon scheme — if existing tags use a different layout, change ONLY the Epc class; nothing else depends on the byte order. (Best check: scan an existing Chafon-written tag and compare.)


3. Hard-won gotchas (the Urovo D812R+ specifically)

  1. PET labels aren't direct-thermal. The glossy "U9 65×35mm PET" RFID labels only emboss — they will never print dark on a direct-thermal printer. This cost hours (looked like a density/ribbon/printer fault; it was the media). Scratch test: drag a nail across a blank label — direct-thermal paper leaves a gray streak, PET/plain leaves nothing. Buy direct-thermal RFID paper labels (see §5).
  2. "Out of ribbon" (status 08) is a latch. The printer defaults to thermal-transfer (expects a ribbon). SET RIBBON OFF switches to direct thermal — but a latched 08 (e.g. after opening the head / pulling the roll back) is NOT cleared by SET RIBBON OFF alone; it needs a power cycle, OR a formfeed + SET RIBBON OFF (which the daemon now does automatically on a non-00 start).
  3. The status byte lies right after a print. During the RFID-encode + feed the reader returns garbage codes (20, 31, 45, FC) for 13s, then settles to 00. Do not judge success on the immediate post-print byte — poll until 00 (the daemon's WaitReady), and verify the actual result by eye / a separate tag read.
  4. USB drops on power-off. Powering the printer off removes it from the USB bus; it re-enumerates a few seconds after power-on. If detectUSB returns null/no printer, wait or replug. (COM4/COM5 on this laptop are the Urovo's Bluetooth SPP links — ignore them.)
  5. Label-size calibration is on the printer: hold the FEED button while powering on, wait for 5 beeps — it auto-calibrates gap/label size. Needed after changing stock.
  6. Talk to it via the GTSPL SDK, not the Windows spooler. Raw TSPL through the Seagull print driver/spooler did not reliably deliver commands (esp. SET RIBBON OFF); the SDK's direct USB path does. Build without a .NET SDK using Framework64\v4.0.30319\csc.exe.

4. The Chafon H-102 situation (why it's parked)

The two CF-H102 guns, on Windows, put their tag reads on the HID keyboard channel (USB-HID, or BLE "LE GATT HID device" when paired) — not the CH340 serial the SDK needs. Proven: chafcheck (OpenDevice+GetTagUii) returns 0xFFFFFF12 (COMM_TIMEOUT, "reader not answering") at every baud on both guns; a raw serial read with DTR+RTS asserted got 0 bytes while triggering.

To write tags via the SDK the gun must be in serial/command mode, flipped device-side (config barcode / the sideloaded Android app) — which the user reports is chronically flaky (Mac too). The Windows Chafon SDK has serial / USB-HID / network paths only — no BLE — so Bluetooth isn't a path for the gun here either.

Recommendation: stop fighting it. Use the Chafon as a keyboard scanner (trigger → types the EPC → focus a search box to look up a tag; 100% reliable in the mode it's already in) and let the Urovo do all writing. Once direct-thermal RFID labels arrive, the Chafon isn't needed to program anything.


5. What to buy (unblocks the one-pass dream)

Direct-thermal UHF RFID paper labels, all of:

  • Direct thermal (⚠ not "thermal transfer", not "PET/PP") — search "direct thermal RFID label"
  • Paper face (direct-thermal coated paper)
  • UHF RFID inlay, EPC Gen2, chip NXP UCODE 9 (matches existing tags/reader)
  • 54×24 mm (or 55×25 — the Dymo size), roll compatible with the D812R
  • Ask the seller: "compatible with Urovo/TSC D812R RFID printer"

The existing PET tags aren't wasted — a reader can still program them; you'd just keep printing+sticking paper for those. New DT-RFID paper labels are what enable single-pass.


6. Two-mode design (agreed)

  • Mode A — "Separate" (today's flow, existing stock): program chip + print Dymo separately, stick paper on top. On Windows the encode step is the flaky Chafon (see §4) — recommend leaning on the Urovo instead where possible.
  • Mode B — "Combined / one-pass" (the goal): one button → Urovo prints + encodes together on DT-RFID labels. Daemon path (/print-encode) is built.

A Settings toggle picks the mode; the extension change is tiny (same daemon.js contract). Wiring the PriceGod buttons → :7790 was not done yet (next step).


7. For the Mac: getting the Urovo to program chips over there

UPDATE 2026-07-22 (later the same day) — SOLVED, and more cheaply than expected. The RFID-encode command did not need a USB sniff or the Java jar. GTSPL_SDK.dll is a managed .NET assembly, so decompiling its IL reveals every RFID call is a one-line String.Concat → ASCII → WritePrinter. writeUHF("H",2,12,"E",epc) emits exactly:

UHF WRITE H,2,12,E,"<24 hex chars>"\r\n

The whole vocabulary (UHF READ/QUERY, UHF GEN2 *, SET RFID, &DEFAULT,i, &CALIBRATE,A,R) is in mac/README.md. Option 2 below is therefore fully unblocked and option 1 (the Java jar) is unnecessary. A working macOS daemon now lives in mac/ — raw TSPL over libusb, same :7790 contract, printing verified on the 24 mm stock. Options 13 below are kept for historical context.

The daemon.cs is Windows-boundGTSPL_SDK.dll is a Windows DLL; it won't run on macOS. But the recipe in §2 is portable. Options, best first:

  1. GTSPL Java SDK (most direct). The vendor SDK ships a cross-platform Java library: Documents/Software V1.1.2/drive-download-.../20241129_GTSPL_Java_Library/lib/GTSPL-1.1.8.jar (also gtspl_sdk.jar in the Android samples). Java runs on macOS — write a small program that mirrors §2 (openport/sendcommand/writeUHF/printlabel). ⚠ Verify the jar's USB transport works on macOS (the Android build assumes Android USB; desktop USB may need a native lib or a serial/USB-CDC path). This is the cleanest port because writeUHF does the RFID encoding for you.
  2. Raw TSPL over USB (no SDK). ~90% of §2 is plain TSPL text you can send straight to the Urovo's USB device on macOS (e.g. write to the raw printer device, or CUPS -o raw). The one missing piece is the RFID-encode commandwriteUHF("H",2,12,"E",epc) is an SDK helper wrapping the printer's TSPL RFID command, and its exact bytes weren't captured this session. To get them: sniff the USB writes while writeUHF runs on Windows (USBPcap/Wireshark), or check the Urovo/Gainscha TSPL RFID command reference. Once you have that command string, the whole thing is raw TSPL and trivially portable (even into the existing Node rfid-daemon).
  3. Extend the existing Mac rfid-daemon (Node) to open the Urovo and emit the §2 sequence (needs the RFID command bytes from option 2).

Reality check for "can the Urovo program chips on Mac?": yes, mechanically — it's the same USB printer speaking TSPL. The only real work is the transport (get bytes to the printer on macOS) and the RFID-encode command (use the Java writeUHF, or capture its bytes). Everything else — ribbon-off, geometry, density, one-pass timing — is documented above and identical.


8. Open items / next steps

  • Buy direct-thermal UHF RFID paper labels (§5) — the true unblock.
  • Verify the SKU↔EPC scheme (§2) against an existing tag; adjust Epc class if needed.
  • Wire PriceGod buttons → :7790 + the Mode A/B toggle.
  • Match the label layout to the extension's labels.js design more faithfully (current TSPL layout is functional, not pixel-perfect).
  • (Optional) On Windows, decide Chafon = scanner-only vs. chase command mode.
  • (Mac) Try option 1 or 2 in §7 to program chips there. Done — see §7 update and mac/.
  • (Mac) Prove the RFID encode end-to-end. Done — chip written by the Urovo and read back by the Chafon (independent reader): 0000044A5600D4CDA1FAB932sku=20260722180000, releaseId=424242.
  • 🔴 Fix the Epc class in daemon.cs — it is WRONG. The shop's real scheme (used by pliceclogs/rfid-daemon and now by mac/urovo.py) is a single 96-bit big-endian integer sku * 10^9 + releaseId, not 6B sku + 4B releaseId + 0xEC01. The Node daemon's parseEpcData rejects the EC01 layout outright, so every tag the Windows daemon has written is invisible to the extension and needs rewriting.
  • Confirm SET RFID OFF is genuinely honoured by this firmware rather than silently ignored — the absence of VOID0 on the last test print is the only evidence so far.
  • Resolve the :7790 collision between mac/daemon.py and the Node rfid-daemon (both bind it; the Node one owns the Chafon write path, scale and inventory routes).

See also the shop's memory notes: urovo-d812r-ribbon-fix, urovo-d812r-rfid-label-gap-issue, urovo-d812r-pet-labels-not-thermal, urovo-windows-print-encode-daemon, chafon-h102-windows-hid-mode.