The SKU<->EPC layout inherited from daemon.cs was WRONG. daemon.cs packs 6 bytes
of SKU + 4 bytes of release_id + an 0xEC01 marker, and flagged itself as
unverified. The shop's actual scheme -- in pliceclogs/rfid-daemon/index.js
(buildEpcPayload/parseEpcData), which is what the extension and every existing
tag use -- is a single 96-bit big-endian integer:
value = sku(14 digits) * 10^9 + releaseId
Checked against that file's own worked example: 20260321001001 + 35332 ->
20260321001001000035332. epc_decode also reproduces its guard, returning None
when value/10^9 exceeds 14 digits, so a factory tag (E28069...) can never be
reported to the operator as a real SKU.
This matters: parseEpcData rejects the EC01 layout outright, so every tag the
Windows daemon has written is invisible to the extension. ASSESSMENT.md now
carries that as a red open item against daemon.cs.
Verified end-to-end with an INDEPENDENT reader. ASSESSMENT.md sec.4 parked the
Chafon as HID-keyboard-only, but that is Windows-specific -- on macOS it exposes
a CH340 bridge and its documented serial protocol works. New chafon.py (read-only:
inventory + read EPC, CRC-16/0x8408 framing) scans the field and sees:
0000044A5600D4CDA1FAB932 -> sku=20260722180000 rel=424242 (Urovo-written)
E28069950000600625F600D3 -> factory/blank (x12, rest of roll)
126D5125FFA000067932EC01 -> factory/blank (earlier EC01 tag, now rejected)
That last line is the bug demonstrated: a tag written earlier today under the old
scheme is unrecognised, exactly as the extension would have treated it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
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'sdaemon.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+printlabelencode 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; .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 ~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)
- 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).
- "Out of ribbon" (status
08) is a latch. The printer defaults to thermal-transfer (expects a ribbon).SET RIBBON OFFswitches to direct thermal — but a latched08(e.g. after opening the head / pulling the roll back) is NOT cleared bySET RIBBON OFFalone; it needs a power cycle, OR a formfeed + SET RIBBON OFF (which the daemon now does automatically on a non-00start). - 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 to00. Do not judge success on the immediate post-print byte — poll until00(the daemon'sWaitReady), and verify the actual result by eye / a separate tag read. - 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
detectUSBreturns null/no printer, wait or replug. (COM4/COM5 on this laptop are the Urovo's Bluetooth SPP links — ignore them.) - 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.
- 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 usingFramework64\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.dllis a managed .NET assembly, so decompiling its IL reveals every RFID call is a one-lineString.Concat→ ASCII →WritePrinter.writeUHF("H",2,12,"E",epc)emits exactly:UHF WRITE H,2,12,E,"<24 hex chars>"\r\nThe whole vocabulary (
UHF READ/QUERY,UHF GEN2 *,SET RFID,&DEFAULT,i,&CALIBRATE,A,R) is inmac/README.md. Option 2 below is therefore fully unblocked and option 1 (the Java jar) is unnecessary. A working macOS daemon now lives inmac/— raw TSPL over libusb, same:7790contract, 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:
- 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(alsogtspl_sdk.jarin 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 becausewriteUHFdoes the RFID encoding for you. - 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 whilewriteUHFruns 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 Noderfid-daemon). - 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
Epcclass if needed. - Wire PriceGod buttons →
:7790+ the Mode A/B toggle. - Match the label layout to the extension's
labels.jsdesign 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 andmac/. - (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
Epcclass indaemon.cs— it is WRONG. The shop's real scheme (used bypliceclogs/rfid-daemonand now bymac/urovo.py) is a single 96-bit big-endian integersku * 10^9 + releaseId, not 6B sku + 4B releaseId +0xEC01. The Node daemon'sparseEpcDatarejects theEC01layout outright, so every tag the Windows daemon has written is invisible to the extension and needs rewriting. - Confirm
SET RFID OFFis genuinely honoured by this firmware rather than silently ignored — the absence ofVOID0on the last test print is the only evidence so far. - Resolve the
:7790collision betweenmac/daemon.pyand the Noderfid-daemon(both bind it; the Node one owns the Chafon write path, scale and inventory routes).
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.