- 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>
200 lines
12 KiB
Markdown
200 lines
12 KiB
Markdown
# 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 ~1–3s (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 1–3s, 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-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.
|
||
- [ ] (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`.
|