Add an end-to-end Pi quick start guide

Replaces the "Running at boot" section with a five-step guide that
starts from enabling the UART, the step that is easiest to miss and
the one that produces the "device not found" failure:

  1. enable the serial port with raspi-config (and answer No to the
     login shell, or the console holds the port)
  2. confirm which device exists, with a table mapping what you see
     to the --device value to pass
  3. install the service, with both possible failure messages and
     what each one means
  4. verify via systemd, the journal and the status page
  5. fall back to a foreground run and --list-ports when cards are
     not detected

Also fixes a contradiction: the section claimed the installer
defaults --device to /dev/rdm-reader when it is /dev/serial0, while
the options table three lines below said /dev/serial0. That mismatch
is exactly what leads someone to install against the wrong device.

Notes that --no-service does not skip the device checks, verified
against the script's control flow, and drops the raspi-config menu
number rather than assert one that varies by release.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-10-05 21:57:58 +03:00
parent 882b89d6da
commit bbd2861897
+134 -46
View File
@@ -21,7 +21,7 @@ Built for a Raspberry Pi, but it runs anywhere with a serial port.
- [Install](#install)
- [Usage](#usage)
- [Serving card data to a web app (`--serve`)](#serving-card-data-to-a-web-app---serve)
- [Running at boot (Raspberry Pi)](#running-at-boot-raspberry-pi)
- [Quick start on a Raspberry Pi](#quick-start-on-a-raspberry-pi)
- [Status LED](#status-led)
- [Wiring](#wiring)
- [The protocol](#the-protocol)
@@ -411,50 +411,145 @@ actually cost you something.
---
## Running at boot (Raspberry Pi)
## Quick start on a Raspberry Pi
The defaults already match the common wiring — **`/dev/serial0`** for the
reader on the Pi's UART and **GPIO 23** for the LED — so the LED pin and device
usually need no arguments at all:
End-to-end: enable the serial port, confirm the reader is visible, install the
service, verify it runs. Steps 1–2 change nothing on disk and are safe to run
first.
### 1. Enable the serial port
`/dev/serial0` is the Pi's on-board UART, and it does not exist until the UART
is switched on:
```bash
sudo raspi-config
# Interface Options -> Serial Port
# "Would you like a login shell to be accessible over serial?" -> No
# "Would you like the serial port hardware to be enabled?" -> Yes
sudo reboot
```
Menu numbering shifts between `raspi-config` releases, so navigate by name:
**Interface Options**, then **Serial Port**.
**Both answers matter.** Answering *Yes* to the login shell leaves a console on
the port, so the reader gets nothing: the app either reports `device not found`
or opens the port and never sees a card.
### 2. Confirm the device appears
```bash
ls -l /dev/serial0 /dev/rdm-reader 2>&1
```
| What you see | Wiring | Device to pass |
| --- | --- | --- |
| `/dev/serial0 -> ttyAMA0` (or `ttyS0`) | UART, header pins **8 / 10** | `--device /dev/serial0` |
| `/dev/rdm-reader` | USB-to-serial adapter | `--device /dev/rdm-reader` |
| `No such file or directory` for both | nothing openable yet | see below |
`/dev/rdm-reader` is created by the udev rule and matches a **USB** bridge chip,
so it can never exist on a UART setup. The two are mutually exclusive — a
service pointing at the wrong one exits `1` on every start.
If neither exists, ask what the system does see:
```bash
ls /dev/ttyUSB* /dev/ttyACM* /dev/ttyS* 2>/dev/null
dmesg | grep -iE "ttyUSB|ttyAMA|ch34|ftdi|cp210|pl2303" | tail
```
No `ttyUSB`/`ttyACM` line in `dmesg` means the adapter is not enumerating at
all — a cable, power or hardware problem that no `--device` value can fix.
If a `/dev/ttyUSB0` appears but `/dev/rdm-reader` does not, the adapter's chip
is not the CH340 the rule matches by default. See **Identifying the right
ttyUSB** below.
### 3. Install the service
The defaults already match the common wiring — `/dev/serial0` for the reader on
the UART, GPIO 23 for the LED:
```bash
sudo ./deploy/install.sh --origin https://your.web.app
```
To be explicit (and safe if the defaults ever change):
`deploy/install.sh` is the only installer — there is no `deploy.sh`. It sets up
a service account, a virtualenv, a udev rule and a systemd unit, then enables
the service.
Tell it which device you have, since the two are not interchangeable:
```bash
sudo ./deploy/install.sh --origin https://your.web.app \
--device /dev/serial0 \
--led-pin 23 \
--led-mode presence
sudo ./deploy/install.sh --device /dev/serial0 # UART, pins 8 / 10
sudo ./deploy/install.sh --device /dev/rdm-reader # USB adapter
```
| Argument | Why |
It refuses to enable the service unless the device exists **and** opens at
9600 baud, so a wrong port fails here instead of becoming a crash-looping unit
that burns its restart limit within seconds and then stays dead. Success looks
like:
```
/dev/serial0 opened at 9600 baud
```
If it stops instead, the message is one of exactly two:
| Message | Cause |
| --- | --- |
| `--device /dev/serial0` | The reader is wired to the UART pins (8 / 10). This is the default. For a USB-to-serial adapter pass `--device /dev/rdm-reader` instead — that alias is created by udev for USB adapters only, so it never exists on a UART setup |
| `--led-pin 23` | BCM GPIO 23, which is **physical pin 16**. The default |
| `--led-mode presence` | Lit while a card is on the reader, dark otherwise. The default |
| `<device> does not exist, so the service could never start` | step 1 or 2 not done, or the wrong of the two devices |
| `could not open <device> at 9600 baud` | device exists but is not usable — UART held by the serial console, or the user is not in `dialout` |
> **`--led-pin` is the BCM GPIO number, not the header pin.** If your LED wire
> is on physical pin 16, pass `--led-pin 23` (the default). Passing
> `--led-pin 16` would drive **GPIO 16**, which is physical pin 36 — a different
> pin entirely. The LED is optional: with no LED, pass `--led-mode presence`
> regardless and ignore the light, or add `--no-led` to the unit if you want
> the app to stop trying.
Nothing is enabled in either case, and re-running is safe: the account, udev
rule and virtualenv are all idempotent.
### Standalone install
### 4. Verify it runs
`deploy/install.sh` is the only installer — there is no `deploy.sh`.
```bash
systemctl status rdm-card-reader # state
systemctl is-enabled rdm-card-reader # should be 'enabled' (starts at boot)
journalctl -u rdm-card-reader -f # live log
ss -ltnp | grep 8765 # confirms something is listening
```
That installs a service account, a virtualenv, a systemd unit and a udev rule,
then enables the service. The device defaults to `/dev/rdm-reader`; if the
reader is on the Pi's UART instead, add `--device /dev/serial0` — see
**UART or USB?** above. The installer refuses to enable the service unless the
device exists and opens, so a misconfigured port fails during install rather
than as a crash-looping unit.
A healthy startup logs one line:
Useful installer options:
```
OK reader on /dev/serial0 ; bridge on http://127.0.0.1:8765 (mode: login)
```
Then tap a card and check — or just open **<http://127.0.0.1:8765/>** for the
status page, which shows the reader state and a live event log:
```bash
curl -sS http://127.0.0.1:8765/api/card | python3 -m json.tool
```
### 5. If cards are not detected
Run the reader in the foreground, using the venv the installer built, to see
the failure directly instead of through the journal:
```bash
sudo /opt/rdm-card-reader/.venv/bin/rdm-card-reader --port /dev/serial0
```
`--list-ports --verbose-ports` answers "is the port even there?" — it prints
candidates with `OK`/`---` (exists?) and then every identified port with its
`by-id` path, USB id and serial number:
```bash
sudo /opt/rdm-card-reader/.venv/bin/rdm-card-reader --list-ports --verbose-ports
```
A service that cannot see cards while the foreground run works usually means
the unit is pointed at the other device — compare its `ExecStart` (below) with
what `--list-ports` reports.
### Installer options
| Option | Default | Meaning |
| --- | --- | --- |
@@ -468,7 +563,16 @@ Useful installer options:
| `--user NAME` | `rdmcard` | Service account |
| `--no-service` | off | Install and configure, but do not enable the service |
The unit runs the app as:
> **`--no-service` does not skip the device checks.** They run before it, so an
> install with no reader attached still exits `1`.
> **`--led-pin` is the BCM GPIO number, not the header pin.** If your LED wire
> is on physical pin 16, pass `--led-pin 23` (the default). Passing
> `--led-pin 16` would drive **GPIO 16**, which is physical pin 36 — a different
> pin entirely. The LED is optional: with no LED, leave the default and ignore
> the light, or add `--no-led` to the unit to stop the app trying.
### What the unit runs
```
/opt/rdm-card-reader/.venv/bin/rdm-card-reader \
@@ -481,22 +585,6 @@ The unit runs the app as:
added. `--serve` is what makes the API available; without it the app only
prints events, which a service sends to the journal.
Afterwards:
```bash
systemctl status rdm-card-reader # state
systemctl is-enabled rdm-card-reader # should be 'enabled' (starts at boot)
journalctl -u rdm-card-reader -f # live log
ss -ltnp | grep 8765 # confirms something is listening
curl -sS http://127.0.0.1:8765/api/card # tap a card, then run this
```
A healthy startup logs one line:
```
OK reader on /dev/serial0 ; bridge on http://127.0.0.1:8765 (mode: login)
```
### Checking the reader from the command line
```bash