Hardware schematics

Wire-by-wire build reference for the door units. Raspberry Pi 4 — 40-pin header, BCM numbering. Pin 1 is the corner nearest the microSD.

Building a new unit
New units default to the Grounds pinout

Every new Sentry is wired to the Grounds pinout: strike on GPIO 18, exit button on GPIO 20, and GPIO 14/15 left free as the UART for the PN532 fob reader — so a reader can be fitted on day one or added later without moving a single wire. The fleet blueprint defaults to these pins.

Door strike
GPIO18
Exit button
GPIO20
Fob reader UART
GPIO14 / GPIO15
Not part of the standard — set per unit
You are looking at the reference build below.Defaults live in fleet/blueprints/door.yaml

The Grounds

Door Sentry · door_id 1Fleet standard — new units copy this

The reference build. Strike on GPIO 18 so the UART (GPIO 14/15) stays free for the PN532 fob reader, button on GPIO 20, three spare relay channels wired, and a USB QR scanner. If you are building a new door, build this one.

Relay active-HIGH per unit — The strike fires when the pin goes HIGH. Config: relay_active_low false (the code default).Camera rotation 270° per unit — Mounted sideways, so the door screen pillarboxes back. Specific to THIS unit's mounting — not part of the standard.GM77 QR scanner (USB)fleet/devices/grounds-door.yaml

The printed enclosure

Loading the model…

Drag to orbit, scroll to zoom, click a part name to isolate it. Printed envelope 151.5 × 300 × 77 mm.

What the CAD pins down
  • The three printed parts and how they stack (they share one coordinate space).
  • Lens centre (0, 117.78); Ø8.6 lens hole through the pod face.
  • Print orientation - bezel-UP, forced. A pull-out barb's catch face is an undercut bezel-down, and chamfering it to print destroys the catch.
Not in the model
  • Where the Pi, relay and exit button mount inside the housing. The internal bosses exist in the print but their component assignments are not recorded anywhere — do not infer them from the geometry.
  • The wall-mount hole pattern and fixings.
Source
Onshape — Roll Call Door Access Unit v2 / Roll Call Unit v3 - Native. Mesh extracted 2026-08-28T00:00:00.000Z · 9,534 triangles. The CAD is the source of truth; this repo carries only an extracted mesh, so re-extract it after any CAD change (recipe in units.yaml).

The 40-pin header

RelayKeyestudio 4-channel relayExit buttonMomentary push-switchNFC readerPN532 V3 module (UART/HSU)Unverified40-pin header, BCM numbering. Pin 1 is the corner nearest the microSD.
pin 1 ↓ (microSD corner)VCC → 3V313V3VCC25V3GPIO245V5GPIO3GND6GND7GPIO4Module RXD → Pi TXD8GPIO14GND9GNDModule TXD → Pi RXD10GPIO1511GPIO17IN1 → door strike12GPIO1813GPIO2714GND15GPIO22IN2 → spare16GPIO23173V3IN3 → spare18GPIO2419GPIO1020GND21GPIO9IN4 → spare22GPIO2523GPIO1124GPIO825GND26GPIO727ID_SD28ID_SC29GPIO530GND31GPIO632GPIO1233GPIO1334GND35GPIO1936GPIO1637GPIO26Switch leg A38GPIO20Switch leg B → GND39GND40GPIO21

Connections

WirePhysicalSignalNotes
VCCpin 25V
GNDpin 9GND
IN1 → door strikepin 12GPIO18GPIO 18 — the fleet standard strike pin. Keeps GPIO 14/15 free for the reader's UART.
IN2 → sparepin 16GPIO23
IN3 → sparepin 18GPIO24
IN4 → sparepin 22GPIO25
Switch leg Apin 38GPIO20
Switch leg B → GNDpin 39GND
VCC → 3V3pin 13V33.3V, not 5V.
GNDpin 6GND
Module RXD → Pi TXDpin 8GPIO14Crossed. TX→TX is the classic silent-reader mistake.
Module TXD → Pi RXDpin 10GPIO15Crossed — TX meets the other side's RX.

Strike output — the 12V side

Wire Relay 1's screw terminals. Most gym strikes are fail-secure (locked until powered), which is COM + NO. A fail-safe strike energises to LOCK, which is COM + NC instead.

12V PSU (+) ─────────► [ COM ]  Relay 1
                       [ NO  ] ──────► Door strike (+)
12V PSU (−) ◄──────────────────────────  Door strike (−)

The Pi only powers the relay coils via 5V, about 80 mA per energised channel. The strike's 12V never touches the Pi.

Exit button

A plain momentary push-switch, two wires, no polarity. Active-LOW.

GPIO 20 (pin 38) ──────[  BUTTON  ]──────  GND (pin 39)

The Pi enables an internal pull-up, so the pin idles HIGH and a press pulls it LOW.

The things that cost hours

A serial login console jams the UART
`serial-getty@ttyS0` plus `console=serial0` in cmdline.txt left the reader silent while comms looked fine. Mask the getty and strip `console=serial0,115200` from /boot/firmware/cmdline.txt.
The mini-UART drops the first byte after idle
Prefix EVERY PN532 command with the wake preamble `55 55`. Without it GetFirmwareVersion works but SAMConfig and InListPassiveTarget fail silently — which reads like a dead reader.
Card serial mapping
`card_serial = "Mx" + uid.hex()`, matched via `door_logic.card_serial_for`. Proven with the owner fob UID ee41325a → Mxee41325a.
A 5V relay opto can be marginal on 3.3V GPIO
If Relay 1 won't click when the strike pin goes HIGH, move the module VCC to 3.3V — that lowers the opto reference and makes a 3.3V trigger reliable — or check for a VCC/JD-VCC jumper.

Bring-up

  1. Power off first — Pi off and the 12V strike supply unplugged before any wire moves.
  2. Reader (any unit with one fitted) — DIP both OFF, free the serial port (raspi-config → Serial Port → login shell NO, hardware YES; `enable_uart=1` + `dtoverlay=disable-bt`; disable hciuart), reboot, then `nfc-poll` should read a tag. That proves VCC, GND, the TX↔RX crossover, the DIP switch and the freed serial port all at once.
  3. Strike — `pinctrl set <strike-pin> op dh` lights Relay 1's LED and clicks the strike; `op dl` releases. Repeat on the spare channels to prove them.
  4. Button — Press it. The strike fires, once the service is running on the right pin.

Generated from docs/hardware/units.yaml. Two components claiming the same pin fails the build.