# 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) : 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 # 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 > **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 1–3 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/`. - [x] (Mac) ~~Prove the RFID **encode** end-to-end.~~ Done — chip written by the Urovo and read back by the **Chafon** (independent reader): `0000044A5600D4CDA1FAB932` → `sku=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`.