yourovo/ASSESSMENT.md
Monster Robot Party 3056fd70fc Windows Urovo D812R+ print+encode daemon + full assessment
- pricegod-urovo-daemon: C# :7790 HTTP service, drives the Urovo D812R+ via
  the GTSPL SDK; prints a label AND encodes the UHF chip in one pass. Drop-in
  for the PriceGod extension's daemon.js (same :7790, CORS *).
- chafcheck: Chafon H-102 UHFPrimeReader P/Invoke probe.
- ASSESSMENT.md: everything done + learned this session -- the ribbon-latch
  fix, PET-labels-are-not-direct-thermal finding, unreliable-status-byte
  quirk, SKU->EPC scheme, the Chafon HID-mode dead end, and how to port the
  print+encode recipe to the Mac (GTSPL Java SDK / raw TSPL).

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

12 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

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.

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.