Files
parking_solution/wiki/entities/dingtian-dt008-reader.md
T
julian f6e35bbebf
Build desktop / desktop (push) Successful in 4m16s
Build & push images / images (push) Successful in 2m46s
CI / check (push) Successful in 38s
fix(reader): correct the QR reader's identity — Dingtian DT-008, not "GEE"
An early wrong assumption named the QR/RFID access reader "GEE" /
"GEE/Fondvision" / "GEE-QR-ER80" (and summarized a raw GEE PDF as its
datasheet). There is no GEE device — it's the Dingtian DT-008
(dingtian-tech.com/en_us/qr_code_reader.html), the same vendor as the relay
board, which is why it integrates the identical HTTP-GET-push way.

Code:
- Driver symbol geeQrReaderDriver → dingtianQrReaderDriver; label →
  "Dingtian DT-008 QR/RFID reader (HTTP push)"; comments/description rewritten
  to the real DT-008 facts (Wiegand 26/34, TCP/IP, USB, RS485 — not RS-232;
  QR/barcode + ID/IC/NFC — not DataMatrix/1D).
- Persisted driverId "gee-qr-reader" → "dingtian-qr-reader" (the registry
  lookup key + the row created on assign in qr-reader.ts).
- Migration 0015 rewrites existing devices.driver_id rows so configured readers
  keep resolving (applied to the dev DB — 2 rows; the booth applies it on boot).
  Behaviour is unchanged: naming + the persisted id only.

Wiki + memory:
- Renamed entities/gee-qr-er80.md → dingtian-dt008-reader.md and
  sources/gee-qr-er80.md → dingtian-dt008.md; rewrote both to the real DT-008
  product-page specs while KEEPING all the verified-on-hardware protocol facts
  (cjihao serial, .jsp path, Connection: close). Fixed every cross-reference +
  "GEE" mention in 6 other pages. Memory gee-reader-serial-binding →
  dingtian-reader-serial-binding. The only surviving "GEE" mentions are
  deliberate naming-correction notes, the raw PDF filename, and the
  append-only log history.

Full workspace build/lint/test green; dev DB readers verified resolving to the
registered dingtian-qr-reader driver.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
2026-06-28 17:39:15 +02:00

8.0 KiB
Raw Blame History

type, tags, sources, updated, status
type tags sources updated status
entity
parking
hardware
readers
qr
dingtian
dingtian-dt008
2026-06-28 open

Dingtian DT-008 (QR/RFID access reader)

The project's QR / barcode / RFID reader — Dingtian DT-008, the same vendor as the dingtian-relay (which is why it integrates the same HTTP-push way). A static optical + card scanner for QR / barcode (simple) and ID/IC/NFC cards. This is the ticket-encoding scanner (read at the pay station / exit lane) and a path for QR or RFID subscription credentials. Product page: dingtian-tech.com/en_us/qr_code_reader.html. (See dingtian-dt008 / raw/.)

Naming correction (2026-06-28). Through most of this project this reader was wrongly called "GEE" / "GEE/Fondvision" / "GEE-QR-ER80" — a bad early assumption. There is no GEE device; it is the Dingtian DT-008. The driver id was renamed gee-qr-reader → dingtian-qr-reader (migration 0015 rewrites existing rows). All protocol/behaviour facts below were observed on the real hardware and remain correct — only the device identity was wrong.

What it is

  • QR/barcode + card. Reads QR / simple barcode AND ID/IC/NFC cards (~0.2 s, 0–10 cm). (An earlier note guessed "ER80-EM = 125 kHz EM4100 prox" — that was part of the same mis-id.)
  • Interfaces: Wiegand 26/34, TCP/IP, USB, RS485. (NOT RS-232 — that was a fictional-datasheet claim.) For our integration it talks HTTP over TCP/IP; Wiegand is the reader's output line on a valid read, not the host transport.
  • Power 9–16 V DC, 800 mA; 86×86×42 mm; −30…70 °C. Fits the disk-os-hardening.

How it integrates — HTTP-GET push, server replies the verdict (confirmed via SDK)

The protocol is settled by the qrcode-sdk (not serial as first guessed). The reader is configured (Windows tool QRCode_v1_6_5.exe) with a server IP/port and a "server language" (only picks the URL path, e.g. /qa/mcardsea.php). On each scan it HTTP-GETs the host:

GET /qa/mcardsea.php?cardid=<QR>&mjihao=<devId>&cjihao=<devSN>&status=<2 chars>&time=<utc>

cardid = scanned data; status low digit = direction (1=in / 0=out). The host replies JSON {data:[{cardid,cjihao,mjihao,status,time,output}],code:0} where reply status 1=valid (beep 2×) / 0=invalid (beep 1×), output 0=Access/1=WG26/2=WG34, time syncs the clock.

This is host-in-the-loop and SYNCHRONOUS: the GET is the access query and our reply is the decision — it drives the reader's beep + output. So unlike a fire-and-forget reader, the endpoint must decide (valid/invalid, direction from status) and reply, then also emit a DeviceReadEvent on the read bus for the entry/exit/permit flows (parking-session, subscription) to open the barrier. (device-input-flow is the analogous push pattern; this one also returns a verdict.)

This explains the "no beep": feedback comes from the server's JSON reply, not locally. A non-JSON / missing reply ⇒ no beep even though the scan worked. So "no beep" ≠ "didn't scan."

  • Pushes over plain HTTP to our 10.0.10.x host (on the device subnet); no serial wiring, no Wiegand-decode hardware. Suits the host-in-the-loop model; autonomy is moot anyway (dingtian-relay has no onboard ACL).
  • Default IP 192.168.1.99 (set its server IP/port to this host in the vendor tool).

Resolved (2026-06-16)

  • Protocol = HTTP GET poll + JSON verdict (above). The earlier "serial/Wiegand, find the baud" open questions are moot — it's HTTP. Wiegand is the reader's output line on a valid read (the reply output field), not the host transport.

As-built (2026-06-16)

  • Endpoint GET/POST /qa/mcardsea.php (apps/server/src/routes/qr-reader.ts, public — the reader has no auth, sits on the device subnet). Parses cardid/mjihao/cjihao/status/time, runs the scan through the read dispatcher (permit match → permit flow; else transient exit), and replies the SDK verdict: status 1=valid(beep 2×)/0=invalid(beep 1×), output 0, time.
  • The read flows were refactored to return a ReadOutcome { accepted, direction, reason } so the endpoint's reply reflects the real accept/reject (the dispatcher decides AND opens the barrier via the flows). A fire-and-forget reader ignores the outcome.
  • Reader identity: the endpoint matches the device serial (cjihao) against each reader's config.serial to find its devices row; the dispatcher then resolves the relay that row is bound to (config.controllerId + relay) and opens it. See entry-exit-points.
  • Verified via inject: valid permit QR → status:1 + open; re-scan → permit exit (still valid); unknown QR → status:0; reader on a barrier-less lane → status:0.

Verified on hardware (2026-06-16)

Captured a real scan (vendor-emulator logger on :3000). The reader does scan, send, and beep — the earlier "no beep" was simply that no server was answering on :3000 with valid JSON. Real GET:

GET /qa/mcardsea.jsp?cardid=52020056&mjihao=1&cjihao=H05M2AFA&status=11&time=1781634494
from 10.0.10.7   (referer: http://www.fondvision.com — the OEM is Fondvision)
  • PATH carries the configured "server language" EXTENSION: this unit is set to JSP, so it GETs /qa/mcardsea.jsp — NOT .php. Our endpoint was registered at .php only → it would have 404'd the real reader. Fixed: the route now registers php/jsp/asp/aspx/cgi.
  • cjihao = H05M2AFA is the device serial — the value our endpoint matches against the reader's config.serial. So assign the reader with config.serial = "H05M2AFA" and bind it to a controller relay.
  • mjihao = 1 (device id). cardid = the scanned barcode (52020056). status=11.
  • The reader beeped on the vendor reply with status:0 — so it acts on the reply; 0 = invalid/1-beep as documented. A matching permit/session will return status:1 → 2-beep accept.

Assignment (as-built 2026-06-16)

A dedicated dingtian-qr-reader driver (device-registry, reader category) models the push reader: its one config field is serial (the cjihao the device reports). The admin assigns it in the first-run-setup like any device (normal UUID row id), enters the serial, and binds it to a controller relay. The QR endpoint resolves the reader by matching config.serial to the scan's cjihao — not by row id — so no DB hand-editing. Set the reader's server IP/port to this host in the vendor tool; assign + enter its serial + bind it here.

  • Verified via inject: assign dingtian-qr-reader {serial:"H05M2AFA"} bound to an access relay → a .jsp scan with that serial + a matching permit QR → status:1 (2-beep accept) + open; re-scan → permit exit; unknown card → status:0; unassigned serial → status:0 (no relay, graceful).
  • Note tcpip-reader is the WRONG model for this device (host-connects-out, a stub) — use dingtian-qr-reader.

Open / next

  • Re-test on hardware against the real app (now .jsp-aware + serial-resolved): scan → expect a status:1 2-beep when the QR matches a permit/open session.
  • output is replied as 0 (Access). Confirm on hardware whether the reader needs 1/2 (WG26/34) to drive its access line, vs. 0.

⚠️ Reply MUST set Connection: close (verified on hardware)

The reader sends Connection: keep-alive but only acts on the verdict (beep/output) once the TCP socket CLOSES. Fastify's default keeps the connection alive → the reader waits out a ~10 s keep-alive timeout before beeping, even though the server replied in ~15 ms. Every vendor demo replies Connection: close and shuts the socket. Fix: the endpoint sets reply.header("connection","close"). Symptom if regressed: correct accept/reject but a ~10 s lag before the beep. (The request arrives fast; the delay is entirely the reader waiting for close.)