Skip to content

librescoot-keycard (keycard-service)

Description

Handles NFC-based authentication for the scooter. Detects keycards via the PN7150 controller, checks UIDs against authorized and master lists, controls LED feedback, and publishes authentication events to Redis. Supports master learning mode on first boot, learn mode for replacing the authorized card set, and a Redis command interface for UID management.

Command-Line Options

  --device string       NFC device path (default: /dev/pn5xx_i2c2)
  --data-dir string     Directory for UID storage (default: /data/keycard)
  --redis string        Redis server address (default: localhost:6379)
  --log int             Log level 0-3 (0=error, 3=debug) (default: 2)
  --led-device string   I2C device for LP5562 LED (empty = script-based control)
  --led-address uint    I2C address for LP5562 LED (default: 48 / 0x30)
  --debug               Enable debug logging

Redis Operations

Hash: keycard (written)

Fields written on authentication:

  • authentication - passed when authorized UID detected
  • type - scooter
  • uid - UID of the card that authenticated

A successful auth sets a 10-second TTL on the entire keycard key, so all three fields (and any command-result written before it) expire together.

Fields written on command response:

  • command-result - Result of last management command (e.g. ok, count:3, card:<uid>, error:<reason>)

Published channel: keycard

  • authentication - Published when authorized keycard detected

List: scooter:keycard (consumed)

Management commands via LPUSH:

Command Response
list count:<n> then one card:<uid> per authorized card
count count:<n>
add:<uid> ok or error:<reason>
remove:<uid> ok or error:<reason> (cannot remove last card)
set-master:<uid> Replaces the master list with this single UID; NONE disables the physical master and leaves authorized cards intact; any other UID also clears the authorized list
learn:start Enter learn mode programmatically
learn:stop Exit learn mode, saving learned cards (additive)
learn:master:start Enter master teach-in mode; next non-registered tap is appended as an additional master (authorized list untouched)
learn:master:stop Exit master teach-in mode without committing
reset Reset all auth state (master + authorized cards)

Responses are written to keycard command-result.

Channel: keycard:events (published)

Transient per-tap progress events during teach-in flows, for real-time subscribers (installer, BLE bridge). Format <event> or <event>:<uid>:

  • Master teach-in: mode-entered:master, mode-exited:master, master-learned:<uid>, rejected:already-authorized:<uid>, error:save-failed:<uid>
  • Learn mode: card-learned:<uid> (queued for commit on learn:stop), card-duplicate:<uid>
  • Reset: reset

Hardware

NFC Reader

  • Chip: PN7150, I2C interface at /dev/pn5xx_i2c2
  • Library: github.com/librescoot/pn7150
  • UID format: uppercase hex string (e.g. 04A1B2C3D4E5F6)

LED Controllers

I2C LED (LP5562 tri-color):

  • I2C bus 2, address 0x30
  • Enabled with --led-device /dev/i2c-2
  • Green: authorized card; Red: unauthorized; Amber: lookup in progress and for the duration of learn mode; Blinking: master learning mode
  • Shared chip. vehicle-service writes the same LP5562 (also via I2C_SLAVE_FORCE on /dev/i2c-2, address 0x30) as the DBC blinker indicator when settings[scooter.dbc-blinker-led] is enabled. There is no arbitration: last writer wins, and blinker activity repaints whatever colour keycard-service left on the LED. keycard-service re-asserts the operating-mode, clock, enable and drive-current registers before every colour change, because vehicle-service leaves the chip on a lower drive current.
  • A kernel lp5562 driver is also bound to the chip and exposes /sys/class/leds/{R,G,B,W}. Both services bypass it deliberately.

PWM LEDs (learn mode):

  • /dev/pwm_led3, /dev/pwm_led7 — on during learn mode
  • Controlled via /usr/bin/ledcontrol.sh

Script-based fallback (when --led-device not set):

  • /usr/bin/greenled.sh — color/on/off
  • /usr/bin/ledcontrol.sh — PWM pattern control

Operational Modes

Master Learning Mode (first boot)

Activated at startup when no master UID is loaded (file missing or empty):

  1. RGB LED blinks (500ms) — waiting for master card
  2. First card presented becomes master UID, saved to master_uids.txt
  3. Authorized list cleared; RGB LED flashes once to confirm
  4. Switches to normal operation

Normal Operation

Card type LED Redis
Authorized Amber → Green flash Publish auth, write UID
Master Amber → enter learn mode —
Unauthorized Amber → Red flash —

Learn Mode

Activated by presenting master UID or via learn:start:

  1. PWM LEDs 3 + 7 turn on; the RGB LED stays amber for the whole session when learn mode was entered by a card tap
  2. Each new card presented: added to session queue, Green LED flash, then back to amber
  3. Present master UID again or learn:stop to exit
  4. Learned cards are appended to the authorized list in authorized_uids.txt (additive)
  5. If no cards learned: existing list unchanged
  6. PWM LEDs 3 + 7 turn off, RGB LED turns off (a green flash first if cards were added)

File Locations

Path Purpose
/data/keycard/authorized_uids.txt Authorized keycard UIDs (one per line)
/data/keycard/master_uids.txt Master UIDs (one per line; multiple masters supported)

Files are written atomically (write to .tmp, sync, rename).

Systemd Unit

  • Unit: librescoot-keycard.service
  • Binary: /usr/bin/keycard-service
  • Requires: valkey.service (redis.service before Librescoot 1.2)
  • After: valkey.service, librescoot-vehicle.service

Building

make build        # ARM
make build-host   # Host