04135b27cf
Hardware capture: the GEE/Fondvision reader (serial H05M2AFA) scans + sends + beeps fine — the earlier 'no beep' was just nothing answering :3000. Real request: GET /qa/mcardsea.jsp?cardid=...&cjihao=H05M2AFA&... — the 'server language' setting (JSP here) selects the URL EXTENSION, so it posts .jsp, not .php. Our route was .php-only and would have 404'd it. Register the endpoint at php/jsp/asp/aspx/cgi so it works whatever the device is configured to. cjihao (serial) is the lane key: assign the reader as lane_devices.id = its serial.
100 lines
5.8 KiB
Markdown
100 lines
5.8 KiB
Markdown
---
|
||
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=<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]], [[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 = <serial>`. 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`.
|
||
|
||
## 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 keys the lane on. So
|
||
assign the reader with **`lane_devices.id = "H05M2AFA"`** (+ an access device on the same lane).
|
||
- **`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.
|
||
|
||
## Open / next
|
||
|
||
- **Assign the reader** as `lane_devices.id = "H05M2AFA"`, category `reader`, on the same lane as an
|
||
access device, so the endpoint resolves the lane. (The setup wizard doesn't yet capture a reader's
|
||
serial as its id — manual row or a wizard tweak; see [[first-run-setup]].)
|
||
- Re-test against the real app endpoint (now `.jsp`-aware): scan → expect the GET to hit
|
||
`/qa/mcardsea.jsp` and a `status:1` 2-beep when the credential matches a permit/open session.
|