yourovo/ASSESSMENT.md
type-two b014205a5b macOS: raw-TSPL Urovo daemon — no vendor SDK needed
ASSESSMENT.md §7 assumed the RFID encode was locked inside the Windows-only
GTSPL_SDK.dll and would need a USBPcap sniff or the GTSPL Java jar to recover.
It didn't. The DLL is a managed .NET assembly, and its IL shows every RFID call
is a one-line String.Concat -> ASCII -> WritePrinter. Recovered from ldstr order
plus argument order, and confirmed by a second independent decompile:

    UHF WRITE H,2,12,E,"<24 hex>"    UHF READ/QUERY ...
    UHF GEN2 EPC|TID|USER|ACCESS|KILL <action>,"<pw>"
    SET RFID <tagType>,<rw_pos>,<void>,<try>,<err>,<speed>,<retry>   (dots, not mm)
    &DEFAULT,i    &CALIBRATE,A,R    PRINT <set>, <copy>

So the whole recipe is plain ASCII over the printer's bulk endpoint and ports to
macOS with no vendor binaries at all.

- mac/urovo.py   USB transport + full GTSPL command set, status decoding, the
                 ribbon-latch recovery, SKU<->EPC packing ported from daemon.cs.
- mac/label.py   PIL bitmap renderer -> TSPL BITMAP. Media-profile driven, so both
                 shop stocks work (small 51x19 and large 55x24 on the 24mm web),
                 with a safe-area inset so the design can't clip at the edges.
- mac/daemon.py  :7790 HTTP service, same routes/JSON/CORS as daemon.cs, plus a
                 /preview route that renders a label as PNG without printing one.

Verified on the bench from macOS: printer identifies (MODEL:UROVO-D812R), the
latched 0x08 "out of ribbon" clears with SET RIBBON OFF + FORMFEED, and labels
print correctly oriented and dark on the 24x55 stock.

NOT yet verified: the RFID encode itself. It is wired and the printer accepts the
command, but the loaded thermal stock has no inlays so nothing has been written to
a chip. Needs a PET RFID label at the antenna. The SKU<->EPC byte layout also
remains unconfirmed against existing Chafon-written tags.

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

220 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.exe`**no .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`; `.exe`s 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-bound**`GTSPL_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 command** — `writeUHF("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.
- [x] (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: it is wired and the printer accepts the
command, but no chip has been written yet (the 24 mm thermal stock has no inlays).
Put a PET RFID label at the antenna and `POST /print-encode`, then read back.
- [ ] 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.
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`.