--- type: entity tags: [parking, hardware, readers, qr] sources: [gee-qr-er80] updated: 2026-06-16 status: open --- # GEE-QR-ER80 (QR access reader) The project's **QR-code reader** (GEE NFC LIMITED). A static optical scanner for **QR / DataMatrix / 1D barcode**, optional ID/IC card. This is the **[[ticket-encoding|QR ticket]] scanner** the design called for — read at the pay station and exit lane — and a path for **QR [[permit]]** credentials. On hand: variant **`-Q-W`** (QR scanner; Wiegand/RS-232/RS-485). (See [[gee-qr-er80|datasheet summary]] / `raw/`.) ## What it is (and isn't) - **Optical, not RFID-prox.** Earlier we *assumed* "ER80-EM" = a 125 kHz EM4100 card reader — the datasheet corrects that: it's a **QR/barcode scanner**. The `-EM` in the original label was a mis-id; the real model is **GEE-QR-ER80**. Optional `D`/`C` variants add ID/IC card, but the unit on hand is **QR-only** (`-Q`). - **Multi-interface** (Wiegand 26/34, RS-232, RS-485, USB, TCP/IP); the `-W` variant exposes **Wiegand + RS-232/RS-485**. ## How it integrates — HTTP-GET push, server replies the verdict (confirmed via SDK) 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:** ``` 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. 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.) > **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. ## 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. - **Lane mapping:** the endpoint keys the reader's `lane_devices` id off the device **serial (`cjihao`)** for now — so assign the reader with `lane_devices.id = `. Refine when the setup wizard models the reader's server-side identity properly. - 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`. ## Open / to confirm on hardware - A **live scan** still hadn't reached the server during bring-up (no beep). With the real endpoint now replying the verdict, re-test: scan → expect a beep + a GET in the server log. If still nothing, it's the reader's scan/trigger/mode (not the server). - **Reader→lane identity:** confirm what the device actually sends as `cjihao`/`mjihao` and align the `lane_devices` assignment (the wizard doesn't yet capture the reader's serial as its id).