Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ bridge core or the shared crates.

## Where the contracts are

- **The physical stack.** `hardware/shared/dimensions.py` decides the heights
- **The physical stack.** `hardware/shared/dimensions/case.py` decides the heights
that let the plate sit flush over the board, and validates that they sum
correctly on import.

Expand Down
6 changes: 3 additions & 3 deletions docs/cad.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ carries `CASE_CENTER_OFFSET_Y_MM`.
Both printable parts are generated in **assembly coordinates**: the case floor at
z = 0, the plate occupying the top 3 mm. `board-assembly` therefore moves neither
of them. That is deliberate — if the plate ever stops meeting the case, it is a
real error in `dimensions.py` rather than a positioning mistake in a view, and
real error in `hardware/shared/dimensions/` rather than a positioning mistake in a view, and
the render shows it.

## Shared measurements

Shared CAD measurements live in `hardware/shared/dimensions.py`, which
validates itself on import. Among other things it checks that the internal stack
Shared CAD measurements live in `hardware/shared/dimensions/`, grouped by
board, case, panel, and tile plate. The package validates itself on import. Among other things it checks that the internal stack
— floor, Pi cavity, board, gap, plate — sums exactly to the case height, that
every plate screw lands on the case ledge rather than over the PCB, that no
support boss collides with an LED or a Hall sensor, and that every control-panel feature
Expand Down
9 changes: 5 additions & 4 deletions docs/hardware.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ Pi Zero 2 W. There is no microcontroller and no separate schematic source tree.

- [`hardware/shared/wiring.py`](../hardware/shared/wiring.py) owns net names,
GPIO assignments, expander mapping, and LED chain order.
- [`hardware/shared/components.py`](../hardware/shared/components.py) owns stable
component identities and physical package metadata.
- [`hardware/shared/dimensions.py`](../hardware/shared/dimensions.py) owns the
board envelope and feature positions shared with CAD.
- [`hardware/shared/components/`](../hardware/shared/components/) contains one
approved-product file per part, with its identity and physical package metadata.
- [`hardware/shared/dimensions/board.py`](../hardware/shared/dimensions/board.py) owns
the playing grid and PCB envelope; the neighboring case, panel, and tile-plate
modules own their physical measurements.
- [`hardware/pcb/definition/board.py`](../hardware/pcb/definition/board.py) composes
the reviewed typed component assemblies and connectivity.
- [`hardware/pcb/generated/bom.md`](../hardware/pcb/generated/bom.md) is the
Expand Down
4 changes: 2 additions & 2 deletions hardware/cad/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ per view, named `<project>.png` or `<project>-<view>.png`.

## Shared modules

`../shared/dimensions.py` is the authoritative source for measurements shared across
projects. It derives the playing span from square size and grid count, derives
`../shared/dimensions/` owns measurements shared across projects, with one
module per physical object. It derives the playing span from square size and grid count, derives
the plate span from fit clearance, and validates the vertical stack, the control
panel layout, the board support positions and the plate fixings. Project READMEs
describe intent rather than duplicating those values. Dimension tests run the
Expand Down
8 changes: 4 additions & 4 deletions hardware/pcb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ footprint paths link to generated schematic symbols. No frozen identity ledger e

## Component registry

`definition/parts/catalog.py` owns one `PcbPart` entry per native product. Each entry
ties an approved `ComponentSpec` to its typed electronic-model constructor, native
footprint, library label, nominal value, and default purpose. Add or update common
PCB metadata there; `PCB_PARTS` is the keyed lookup for schematic and validation code.
`definition/parts/<component>.py` owns each native footprint and its `PcbPart`
binding: approved product, typed electronic model, library label, nominal value,
and purpose. `parts/catalog.py` only enumerates those bindings as `PCB_PARTS` for
schematic and validation code; `parts/part.py` defines the checked binding type.

Assemblies pass a typed `*_PART` entry, an explicit stable reference, position,
rotation, and assembly name to `place()`. They override `purpose` only when the role
Expand Down
6 changes: 4 additions & 2 deletions hardware/pcb/definition/assemblies/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
from pcb.definition.parts import catalog as parts
from shared import dimensions, wiring
from shared import electronics as p
from shared.panel import PANEL_BUTTONS
from shared.panel_buttons import PANEL_BUTTONS

ASSEMBLY_PART_COUNT = 22


def add_controls(board: pcbnew.BOARD) -> None:
Expand Down Expand Up @@ -150,7 +152,7 @@ def add_controls(board: pcbnew.BOARD) -> None:
assembly="controls",
extras={"Function": button.name},
)
pin = p.RaspberryPiHeaderPin[f"BUTTON_{button.name}_GPIO{button.gpio}"]
pin = button.header_pin
connect(
board,
button.net_name,
Expand Down
75 changes: 75 additions & 0 deletions hardware/pcb/definition/assemblies/led_link_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Published names for existing LED links; new links get endpoint names."""

from shared import wiring
from shared.hall_banks import SquarePosition

LEGACY_NAMES: dict[tuple[str, str], tuple[str, str]] = {
("A1", "B1"): ("N$207", "N$208"),
("B1", "C1"): ("N$225", "N$226"),
("C1", "D1"): ("N$227", "N$228"),
("D1", "E1"): ("N$115", "N$116"),
("E1", "F1"): ("N$117", "N$118"),
("F1", "G1"): ("N$119", "N$120"),
("G1", "H1"): ("N$121", "N$122"),
("H1", "H2"): ("LED_D8", "LED_C8"),
("H2", "G2"): ("N$123", "N$124"),
("G2", "F2"): ("N$125", "N$126"),
("F2", "E2"): ("N$127", "N$128"),
("E2", "D2"): ("N$129", "N$130"),
("D2", "C2"): ("N$131", "N$132"),
("C2", "B2"): ("N$133", "N$134"),
("B2", "A2"): ("N$135", "N$136"),
("A2", "A3"): ("LED_D16", "LED_C16"),
("A3", "B3"): ("N$137", "N$138"),
("B3", "C3"): ("N$139", "N$140"),
("C3", "D3"): ("N$141", "N$142"),
("D3", "E3"): ("N$143", "N$144"),
("E3", "F3"): ("N$145", "N$146"),
("F3", "G3"): ("N$147", "N$148"),
("G3", "H3"): ("N$149", "N$150"),
("H3", "H4"): ("LED_D24", "LED_C24"),
("H4", "G4"): ("N$151", "N$152"),
("G4", "F4"): ("N$153", "N$154"),
("F4", "E4"): ("N$155", "N$156"),
("E4", "D4"): ("N$157", "N$158"),
("D4", "C4"): ("N$159", "N$160"),
("C4", "B4"): ("N$161", "N$162"),
("B4", "A4"): ("N$163", "N$164"),
("A4", "A5"): ("LED_D32", "LED_C32"),
("A5", "B5"): ("N$165", "N$166"),
("B5", "C5"): ("N$167", "N$168"),
("C5", "D5"): ("N$169", "N$170"),
("D5", "E5"): ("N$171", "N$172"),
("E5", "F5"): ("N$173", "N$174"),
("F5", "G5"): ("N$175", "N$176"),
("G5", "H5"): ("N$177", "N$178"),
("H5", "H6"): ("LED_D40", "LED_C40"),
("H6", "G6"): ("N$179", "N$180"),
("G6", "F6"): ("N$181", "N$182"),
("F6", "E6"): ("N$183", "N$184"),
("E6", "D6"): ("N$185", "N$186"),
("D6", "C6"): ("N$189", "N$190"),
("C6", "B6"): ("N$191", "N$192"),
("B6", "A6"): ("N$193", "N$194"),
("A6", "A7"): ("LED_D48", "LED_C48"),
("A7", "B7"): ("N$195", "N$196"),
("B7", "C7"): ("N$197", "N$198"),
("C7", "D7"): ("N$199", "N$200"),
("D7", "E7"): ("N$201", "N$202"),
("E7", "F7"): ("N$203", "N$204"),
("F7", "G7"): ("N$205", "N$206"),
("G7", "H7"): ("N$209", "N$210"),
("H7", "H8"): ("LED_D56", "LED_C56"),
("H8", "G8"): ("N$211", "N$212"),
("G8", "F8"): ("N$213", "N$214"),
("F8", "E8"): ("N$215", "N$216"),
("E8", "D8"): ("N$217", "N$218"),
("D8", "C8"): ("N$219", "N$220"),
("C8", "B8"): ("N$221", "N$222"),
("B8", "A8"): ("N$223", "N$224"),
}


def for_squares(left: SquarePosition, right: SquarePosition) -> tuple[str, str]:
"""Keep an existing net identity or name a new physical connection."""
return LEGACY_NAMES.get((left.name, right.name), wiring.led_link_nets(left, right))
5 changes: 4 additions & 1 deletion hardware/pcb/definition/assemblies/power.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@

from pcb.definition.native import connect, place
from pcb.definition.parts import catalog as parts
from pcb.definition.parts.part import PcbPart
from shared import dimensions
from shared import electronics as p
from shared.electronics import EndpointResolver

ASSEMBLY_PART_COUNT = 9


def add_strip[Part: EndpointResolver](
board: pcbnew.BOARD,
part: parts.PcbPart[Part],
part: PcbPart[Part],
reference: str,
*,
assembly: str,
Expand Down
2 changes: 2 additions & 0 deletions hardware/pcb/definition/assemblies/sensing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from shared.electronics import CapacitorPin, HallSensorPin, Tca9554Pin
from shared.electronics import Tca9554Component as Tca9554

ASSEMBLY_PART_COUNT = 2


def add_sensor_banks(
board: pcbnew.BOARD,
Expand Down
92 changes: 17 additions & 75 deletions hardware/pcb/definition/assemblies/square.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import pcbnew

from pcb.definition.assemblies import led_link_names
from pcb.definition.native import connect, no_connect, place
from pcb.definition.parts import catalog as parts
from shared import dimensions, wiring
Expand All @@ -16,6 +17,8 @@
from shared.electronics import Sk9822Component as Sk9822
from shared.squares import BoardSquare

ASSEMBLY_PART_COUNT = 4


@dataclass(frozen=True)
class Square:
Expand All @@ -24,6 +27,10 @@ class Square:
hall_sensor: HallSensor


LED_BYPASS_OFFSET_MM = (0.0, -8.0)
HALL_BYPASS_OFFSET_MM = (0.0, -3.0)


def add_square(board: pcbnew.BOARD, *, board_square: BoardSquare) -> Square:
name = board_square.name
rank = board_square.position.rank
Expand Down Expand Up @@ -54,8 +61,8 @@ def add_square(board: pcbnew.BOARD, *, board_square: BoardSquare) -> Square:
parts.CAP_100N_PART,
f"C{8 + chain_index}",
at=(
lx + parts.CAPACITOR_LED_BYPASS_OFFSET_MM[0],
ly + parts.CAPACITOR_LED_BYPASS_OFFSET_MM[1],
lx + LED_BYPASS_OFFSET_MM[0],
ly + LED_BYPASS_OFFSET_MM[1],
),
assembly=assembly,
purpose="Local LED decoupling capacitor",
Expand All @@ -66,8 +73,8 @@ def add_square(board: pcbnew.BOARD, *, board_square: BoardSquare) -> Square:
parts.CAP_100N_PART,
f"C{72 + sensor_index}",
at=(
x + parts.CAPACITOR_HALL_BYPASS_OFFSET_MM[0],
y + parts.CAPACITOR_HALL_BYPASS_OFFSET_MM[1],
x + HALL_BYPASS_OFFSET_MM[0],
y + HALL_BYPASS_OFFSET_MM[1],
),
assembly=assembly,
purpose="Local Hall-sensor decoupling capacitor",
Expand Down Expand Up @@ -100,80 +107,15 @@ def connect_led_chain(board: pcbnew.BOARD, squares: Mapping[str, Square]) -> Non
chain = [squares[square.name].led for square in dimensions.BOARD_SQUARES.led_chain]
connect(board, wiring.LED_DATA_NET, chain[0].pin(Sk9822Pin.DATA_IN))
connect(board, wiring.LED_CLOCK_NET, chain[0].pin(Sk9822Pin.CLOCK_IN))
for (left, right), (data, clock) in zip(
pairwise(chain), LED_LINK_NAMES, strict=True
):
for left_square, right_square in pairwise(dimensions.BOARD_SQUARES.led_chain):
left = squares[left_square.name].led
right = squares[right_square.name].led
data, clock = led_link_names.for_squares(
left_square.position, right_square.position
)
connect(board, data, left.pin(Sk9822Pin.DATA_OUT), right.pin(Sk9822Pin.DATA_IN))
connect(
board, clock, left.pin(Sk9822Pin.CLOCK_OUT), right.pin(Sk9822Pin.CLOCK_IN)
)
no_connect(board, chain[-1].pin(Sk9822Pin.DATA_OUT))
no_connect(board, chain[-1].pin(Sk9822Pin.CLOCK_OUT))


# Fixed link names preserve stable netlist identities across builds.
LED_LINK_NAMES = (
("N$207", "N$208"),
("N$225", "N$226"),
("N$227", "N$228"),
("N$115", "N$116"),
("N$117", "N$118"),
("N$119", "N$120"),
("N$121", "N$122"),
("LED_D8", "LED_C8"),
("N$123", "N$124"),
("N$125", "N$126"),
("N$127", "N$128"),
("N$129", "N$130"),
("N$131", "N$132"),
("N$133", "N$134"),
("N$135", "N$136"),
("LED_D16", "LED_C16"),
("N$137", "N$138"),
("N$139", "N$140"),
("N$141", "N$142"),
("N$143", "N$144"),
("N$145", "N$146"),
("N$147", "N$148"),
("N$149", "N$150"),
("LED_D24", "LED_C24"),
("N$151", "N$152"),
("N$153", "N$154"),
("N$155", "N$156"),
("N$157", "N$158"),
("N$159", "N$160"),
("N$161", "N$162"),
("N$163", "N$164"),
("LED_D32", "LED_C32"),
("N$165", "N$166"),
("N$167", "N$168"),
("N$169", "N$170"),
("N$171", "N$172"),
("N$173", "N$174"),
("N$175", "N$176"),
("N$177", "N$178"),
("LED_D40", "LED_C40"),
("N$179", "N$180"),
("N$181", "N$182"),
("N$183", "N$184"),
("N$185", "N$186"),
("N$189", "N$190"),
("N$191", "N$192"),
("N$193", "N$194"),
("LED_D48", "LED_C48"),
("N$195", "N$196"),
("N$197", "N$198"),
("N$199", "N$200"),
("N$201", "N$202"),
("N$203", "N$204"),
("N$205", "N$206"),
("N$209", "N$210"),
("LED_D56", "LED_C56"),
("N$211", "N$212"),
("N$213", "N$214"),
("N$215", "N$216"),
("N$217", "N$218"),
("N$219", "N$220"),
("N$221", "N$222"),
("N$223", "N$224"),
)
Loading
Loading