From f67c1ead87e34d06cd00b5218520f759489648df Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Tue, 16 Jun 2026 12:05:05 +0200 Subject: [PATCH] wiki: ER80 protocol = HTTP GET poll + JSON verdict (from QRCode SDK) The QRCode SDK v1.6.5 settles the reader protocol (supersedes the earlier serial guess). On each scan the reader HTTP-GETs the host (/qa/mcardsea.php?cardid&mjihao&cjihao&status&time); the host replies JSON {data:[{...,status,output}],code:0}. Reply status 1=valid(beep 2x)/0=invalid (beep 1x); output 0=Access/1=WG26/2=WG34; time syncs the clock. The GET's status low digit is the direction (1=in/0=out). Key: the beep/accept is decided by the SERVER REPLY, not locally -- the 'no beep' during bring-up was a plain-text reply, not a scan failure. Host-in-the- loop and synchronous. 'Server language' only selects the URL path; transport is plain HTTP. New source page qrcode-sdk; updated gee-qr-er80 (protocol resolved), index. --- wiki/entities/gee-qr-er80.md | 59 ++++++++++++++++++---------------- wiki/index.md | 3 +- wiki/log.md | 17 ++++++++++ wiki/sources/qrcode-sdk.md | 62 ++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 wiki/sources/qrcode-sdk.md diff --git a/wiki/entities/gee-qr-er80.md b/wiki/entities/gee-qr-er80.md index 03f68c8..78e6b01 100644 --- a/wiki/entities/gee-qr-er80.md +++ b/wiki/entities/gee-qr-er80.md @@ -23,37 +23,42 @@ scanner** the design called for — read at the pay station and exit lane — an - **Multi-interface** (Wiegand 26/34, RS-232, RS-485, USB, TCP/IP); the `-W` variant exposes **Wiegand + RS-232/RS-485**. -## How it fits our architecture — host-side reader +## How it integrates — HTTP-GET push, server replies the verdict (confirmed via SDK) -It's a **host-side identity source** ([[entry-exit-readers]]): the host sees the scan, decides, and -commands the relay — exactly the model the read-driven flows already use. A scan becomes a -`DeviceReadEvent` on the internal `read` bus (`kind: "qr"`/`"ticket"`, value = the scanned string), -consumed by the **exit flow** and the **QR-permit** path ([[parking-session]], [[permit]]) — both -**already built**. So integration is a new **`ReaderDevice` adapter**, no business-logic change -([[device-adapter-pattern]]). +The protocol is settled by the **[[qrcode-sdk|QRCode SDK v1.6.5]]** (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:** -- **Interface choice:** prefer **RS-232/RS-485 (serial)** for the QR variant — Wiegand can't carry a - variable-length QR string well (it's built for fixed-width card IDs). The host reads scans over a - serial port (USB-serial on TX/RX, or the USB variant); no Wiegand-decode hardware needed. This - suits [[offline-first]] (local serial, no network) and sidesteps the [[dingtian-relay|Dingtian]]'s - lack of an onboard card list (we never wanted autonomy here anyway). -- **LED/BEEP control lines** mean the host can drive scan feedback (accepted/rejected) — nice for an - unmanned lane. -- **Linux-supported**, 4–15 VDC — fits the [[disk-os-hardening|appliance]]. +``` +GET /qa/mcardsea.php?cardid=&mjihao=&cjihao=&status=<2 chars>&time= +``` +`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. -## Open questions (block the adapter) +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]], [[permit]]) to open the +barrier. ([[device-input-flow]] is the analogous push pattern; this one also returns a verdict.) -1. **RS-232/RS-485 protocol:** baud rate + frame format, and confirm a QR scan emits as an **ASCII - string** (expected) terminated by CR/LF vs. a framed/checksummed protocol. This is the one fact - the adapter needs and the datasheet omits. Resolve via vendor docs or by observing the port on a - scan. -2. **Serial vs. USB-CDC vs. TCP/IP** for *this* unit: `-W` is Wiegand/RS-232/485. Decide the host - wiring (a USB-RS232/485 adapter on TX/RX, most likely). -3. **Does Wiegand mode even carry QR?** Likely Wiegand is for the optional *card* modes only; confirm - so we don't design a Wiegand path for QR. +> **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). +- **Linux-supported**, 4–15 VDC, default IP `192.168.1.99` — fits the [[disk-os-hardening|appliance]]. + +## 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. ## Next -A `ReaderDevice` driver in `packages/devices` (serial transport) emitting `read` events. Add it to -the [[device-registry]] under the `reader` category so the [[first-run-setup]] wizard can assign it -per lane. Pending open-question #1 (the serial frame) before coding. +A backend route (like `routes/devices.ts` for the Dingtian push) that parses the GET, **decides** +(reuse the permit/exit lookup), replies the JSON verdict, and emits on the `read` bus. The +[[device-registry]] `reader` entry can model it for [[first-run-setup]] (server IP/port are set in +the vendor tool; the app side is the endpoint). Build-ready — protocol fully known. diff --git a/wiki/index.md b/wiki/index.md index ea83e78..4d9f009 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -7,7 +7,7 @@ updated: 2026-06-14 # Index Content catalog for the wiki. Start at [[overview]]. Maintained on every ingest. -Counts: 2 sources · 19 entities · 24 concepts · 5 decision records. +Counts: 3 sources · 19 entities · 24 concepts · 5 decision records. ## Overview & navigation - [[overview]] — the top-level synthesis and entry point. @@ -17,6 +17,7 @@ Counts: 2 sources · 19 entities · 24 concepts · 5 decision records. ## Sources - [[parking-system-architecture]] — design notes: stack, threat model, devices, UHPPOTE, ESP32, readers, BOM, open decisions. - [[gee-qr-er80]] — datasheet: GEE QR access reader (QR/DM/1D; Wiegand/RS-232/485/USB/TCP; Linux). +- [[qrcode-sdk]] — QRCode SDK v1.6.5: the reader's HTTP-GET-poll protocol + JSON verdict (beep/output). ## Entities — technology stack - [[technology-stack]] — the full stack table; all MIT/Apache/BSD, chosen to avoid lock-in. diff --git a/wiki/log.md b/wiki/log.md index cc713f5..6f11c59 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -623,3 +623,20 @@ guarantee. Recorded in [[dingtian-relay]] (new Hardening section). [[entry-exit-readers]], [[index]]. - OPEN (blocks the adapter): the RS-232/485 **frame + baud** — is a QR scan an ASCII CR/LF string (expected) or framed? Datasheet omits it; resolve via vendor docs or by observing the port. + +## [2026-06-16] ingest+test | ER80 protocol = HTTP GET poll + JSON verdict (SDK) +- Hardware bring-up: configured the reader via the vendor Windows tool (server IP/port + "server + language"). Moved it to 10.0.10.7. It pings (source-pin must be 10.0.10.203 — trap recurs). No + beep on scans — initially looked like "not scanning." +- Found the QRCode SDK v1.6.5 (`QRCode_sdk - QRCode_v1_6_5/sdk/`). Protocol SETTLED, supersedes the + serial guess in [[gee-qr-er80]]: reader does **HTTP GET** `/qa/mcardsea.php?cardid&mjihao&cjihao& + status&time` on each scan; server replies **JSON** `{data:[{...,status,output}],code:0}`. Reply + `status` 1=valid(beep 2×)/0=invalid(beep 1×); `output` 0=Access/1=WG26/2=WG34; `time` syncs clock. + `status` low digit in the GET = direction (1=in/0=out). +- KEY: feedback/beep is decided by the SERVER REPLY, not locally → the "no beep" was my catch-all + replying plain "OK" not the JSON verdict, NOT a scan failure. Host-in-the-loop + SYNCHRONOUS. +- "Server language" (JSP/PHP/C#/ASP/CGI) only selects the URL PATH; transport is plain HTTP. +- New source [[qrcode-sdk]]; updated [[gee-qr-er80]] (protocol resolved, serial open-Qs dropped), + [[index]]. SDK kept in place (bulky+binaries), not copied to raw/. +- NEXT: backend route — parse GET, DECIDE (reuse permit/exit lookup), reply JSON verdict, emit on + read bus. Refactor read flows to RETURN an outcome so the reply can reflect accept/reject. diff --git a/wiki/sources/qrcode-sdk.md b/wiki/sources/qrcode-sdk.md new file mode 100644 index 0000000..7d48d78 --- /dev/null +++ b/wiki/sources/qrcode-sdk.md @@ -0,0 +1,62 @@ +--- +type: source +tags: [parking, hardware, readers, qr, protocol, sdk] +sources: [qrcode-sdk] +updated: 2026-06-16 +--- + +# Source: QRCode SDK v1.6.5 (GEE/Dingtian QR reader) + +Vendor SDK for the QR access reader ([[gee-qr-er80]]; also branded Dingtian). Defines the +reader↔server **HTTP protocol** — the missing piece the datasheet omitted. Files at +`QRCode_sdk - QRCode_v1_6_5/sdk/` (config tool `QRCode_v1_6_5.exe`, demos in C#/PHP/VC++, protocol +docs `readme.txt`, `qrcode_HTTP_GET.txt`, `VC++/how to.txt`). **Not copied into `raw/`** — bulky + +binaries; this summary is the faithful capture. Entity: [[gee-qr-er80]]. + +## The protocol — HTTP GET poll, server replies the verdict + +The reader is configured (via the Windows tool) with a **server IP/port + "server language"** +(JSP/PHP/C#/ASP/CGI — this only selects the URL path, e.g. `/qa/mcardsea.php`; transport is plain +HTTP either way). **On each scan** the reader sends: + +``` +GET /qa/mcardsea.php?cardid=445D2C&mjihao=1&cjihao=HW256097&status=11&time=1540402036 HTTP/1.0 +``` + +| Param | Meaning | +| --- | --- | +| `cardid` | **the scanned QR/barcode data** (or card id) | +| `mjihao` | device id (machine number) | +| `cjihao` | device serial number | +| `status` | **2 chars**: high = valid `1`/invalid `0` (reader's own pre-check), low = direction **`1`=in / `0`=out**. A 1-char status = fail. | +| `time` | UTC time | + +**Server → reader reply (JSON) — this is the access DECISION and drives the beep + output:** + +```json +{"data":[{"cardid":"","cjihao":0,"mjihao":1,"status":1,"time":"","output":2}],"code":0,"message":""} +``` + +| Reply field | Meaning (from the C# demo comments) | +| --- | --- | +| `status` | **`1` = valid → buzzer 2×; `0` = invalid → buzzer 1×** | +| `output` | **`0` = Access, `1` = WG26, `2` = WG34** — output line/format driven on a valid read | +| `time` | UTC — **can sync the device clock** | +| `code` | `0` = success | + +> **Implication (explains the "no beep"):** the reader's beep/accept is decided by the **server's +> reply**, not locally. A non-JSON / missing reply ⇒ no valid feedback ⇒ no beep, even though the +> scan succeeded. So "no beep" ≠ "didn't scan" — it means the server didn't answer with the verdict. + +## Integration consequence + +This is **host-in-the-loop, synchronous**: the GET *is* the access query; our JSON reply *is* the +decision. So the backend endpoint must **decide (valid/invalid + direction) and reply** — richer +than a fire-and-forget read. Direction comes from the `status` low digit. See [[gee-qr-er80]] + +[[device-input-flow]]. + +## Defaults / misc + +- Default device IP `192.168.1.99` (`readme.txt`). +- Demos: PHP `qa/mcardsea.php` (minimal echo, status 0), C# raw-socket server on :80 (full parse), + VC++ raw HTTP example. All show the same GET-in / JSON-out contract.