yourovo/ASSESSMENT.md
type-two e792e8315e Add next-session handover notes
The remaining gap to a single service is the DYMO M10 postal scale (USB HID
0922:8003/8004, ~130 lines of the Node daemon) -- everything else the extension
calls is already served. Scale was not plugged in today, so note that any port
written before it is connected is untested.

Also records the wanted Logitech C920 + cutting-mat dimension capture, and what
still works with the Urovo unplugged (previews, label layout, Dymo, Chafon).

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

15 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).

9. Next session — pick up here

The one thing standing between us and a single service. The extension calls only six endpoints: /status, /write-tag, /read-tag (all served by mac/daemon.py) plus /weight, /scale/start, /scale/stop (Node only). Port the DYMO M10 postal scale — USB HID 0922:8003/0922:8004, ~130 lines around index.js:1068-1200 — and the Node daemon retires for the extension's purposes: one process, one venv, one launchd agent. ⚠️ The scale was not plugged in on 2026-07-22, so any port written before it is connected is untested.

Also wanted: a Logitech C920 over a cutting mat to measure record/sleeve dimensions (the mat grid gives the scale reference). Not started.

Resuming without the Urovo attached (it stays at the shop): the daemon degrades cleanly — GET /devices reports present: false per device rather than failing, the Dymo and Chafon paths are independent, and mac/chafon.py runs standalone. Label layout work needs no hardware at all: GET /preview?size=large&artist=… renders a PNG, and python label.py small large dumps both profiles to /tmp.

Don't re-litigate: the RFID encode is proven (Urovo wrote, Chafon read back independently); the EPC scheme is settled and matches the Node daemon; the Dymo needs no driver. All three are documented in mac/README.md.

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.