Record Accepted PCB Placement And Fit
Align component groups, repair affected routing, and add preservation audits. Assign the confirmed U2 footprint and update pull-ups to 2 kΩ across design files. Document user acceptance, switch to an A3 worksheet, and refresh PCB exports.
@@ -8,6 +8,26 @@ Working memory, not an implementation timeline. Source is authoritative; begin w
|
||||
- README was refined from the user's already-thinned version, not restored from Git. New `docs/getting_started.md`, `docs/user_administration.md`, and `docs/firmware_update.md` provide operating procedures; `docs/web_administration.md` is now a user guide and its original contract is preserved under `docs/devel/`. Flash layout and PCB research moved into the developer archive; contextual test/hardware/provenance READMEs remain beside artifacts.
|
||||
- Documentation relocation adds no new device evidence and does not change historical acceptance limits. Firmware, generated assets, CAD and historical hash JSON are untouched. Hardware scripts have only relocated-note reference/protection adjustments; Python syntax was checked without executing generators. Documentation links/anchors and `git diff --check` were checked; no firmware build, upload, erase, or hardware test was performed.
|
||||
|
||||
## PCB accepted-fit labels and worksheet — complete
|
||||
|
||||
- Removed visible `USB ACCESS - VERIFY` and title comment `USB / antenna geometry pending; verify module pinout / assembly`; replaced the duplicated pending-clearance caption with `Nominal RS-232 metal verified; RF qualification not established`. Specific RF and connector cautions remain; this is user acceptance, not certification.
|
||||
- Changed only the native worksheet from A4 to A3 landscape (420 × 297 mm), keeping all absolute board coordinates (carrier X=100…182, Y=100…180 mm). No physical translation/reroute/net change. `update_presentation.py` guards locks/concurrent changes, uses `SaveBoard(..., True)` and exact parsed-delta/idempotence checks. Generator and PDF exporter no longer emit the stale pending captions; original historical baselines remain unchanged.
|
||||
- Placement PDF now explicitly includes the border/title block at 1:1; PCB SVG/PNG and carrier-only 3D refreshed. Full-page render is `hardware/PCB/validation/pcb-placement-page.png`. Print the A3 PDF at actual size, not fit-to-A4. Schematic/OLED fit template are unaffected.
|
||||
- PASS: cleanup twice, full group/retained-copper/settings audit, U1/silk/stale-geometry regression, fresh PCB DRC/parity (all zero), independent routing checks, two footprint tests, legacy cleanup compatibility, Python syntax and `git diff --check` (after stripping KiCad SVG trailing whitespace). Rendered full A3 PDF visually checked: PCB and bottom-right title block are well separated; copper preview and 3D also inspected. KiCad emits known enum-property assertions but completes. No firmware/device tests, fabrication outputs or orders.
|
||||
|
||||
## PCB group alignment and user acceptance — complete
|
||||
|
||||
- Current record: `docs/devel/hardware/PCB/user-acceptance.md`. This supersedes older pending-user-fit/USB/male-map/hardware-selection statuses below without upgrading their original evidence. User reports visual component fit and USB clearance correct, male pinout/header/footprint equivalence and pin-by-pin review; purchased Omron B3F-1000 and 2 kΩ 1% metal-film resistors. M3 carrier/daughterboard hardware, 10 mm brass standoffs into heat inserts and M2 display hardware approved. Intended/reported assembled module elevation is 10 mm; 8.5 mm female plus described 2.5 mm male is not an independently verified mating-height calculation. RS-232 prongs clear per user.
|
||||
- Retained U2 body centre X=156.970 mm (local 56.970); OLED +0.970 X, resistor group −0.840 X / −1.3315 Y, button group −1.280 X. Horizontal groups retained. Resistor body Y=131.4185…133.9185 leaves 2.0815 mm nominal clearance to both RS-232 and OLED PCBs; OLED/button body gap 5.25 mm. U1/measured USB, U2, outline, carrier holes and RF area unchanged.
|
||||
- Native board mutated with `SaveBoard(..., True)`, no whole-board regeneration/reset. 476/587 original copper items retained exactly, 111 removed and 503 added during incremental clearance/connectivity repair; current 938 segments / 41 vias. Snapshot `validation/pcb-before-group-alignment.kicad_pcb`, migration manifest and full parsed-content/retained-copper/body-geometry audit added. Project/protected preference hashes unchanged; original A0/U1 evidence preserved. U1 validator verifies original integration against the pre-alignment snapshot, then the exact authorized delta; no blanket exclusions/rebaselining.
|
||||
- R1–R3 now `2k 1%` in PCB/schematic/BOM/generator/validator. Purchased-part and U2 status fields synchronized; previously completed U2 footprint assignment and strict zero-parity validators preserved. User accepts nonisolated USB and reports successful 3.3 V operation, onboard OLED pull-ups and existing RF/power/ground routing. Backfeed safety, regulator margin, pull-up resistance/rail and manufacturer RF/EMC qualification remain unverified; no invented values or new hardware-test claims.
|
||||
- PASS: schematic ERC/exact nets/firmware pin contract, PCB physical DRC and parity (all zero), continuous routing clearance/edge/RF checks, group/preservation audit, U1/silk idempotence and stale-geometry rejection, both footprint tests, DB9 source/CAD validator. Minimum independent copper/NPTH gap 0.252762 mm; edge gap 1.900 mm. Silkscreen (94 items), DB9 projection, 1:1 placement PDF, PCB SVG/PNG/3D and schematic PDF/SVG/PNG refreshed. Missing module courtyards and absence of module assembly 3D models remain explicit limits. No Gerbers/order/firmware changes or hardware tests.
|
||||
|
||||
## U2 schematic footprint — assigned
|
||||
|
||||
- User confirmed the male Adafruit module has the reference pinout/header mapping/footprint. Assigned only schematic U2's `Footprint` field to the existing PCB identifier `Carrier:MAX3243_Reference_Provisional`; PCB, placement, routing, resistors and BOM unchanged. Legacy hidden status metadata remains historical, not current assignment status.
|
||||
- Both validators now require the assignment and clean parity. Fresh PCB validator and schematic ERC pass: zero physical DRC violations, unrouted connections, parity issues and ERC violations. KiCad emitted enum-property assertions but completed successfully. Review exports were not regenerated; no fabrication or hardware qualification is implied. Earlier expected-U2-warning records below are historical.
|
||||
|
||||
## PCB presentation labels — removed and exports refreshed
|
||||
|
||||
- Removed PCB title suffix `draft`, revision suffix `DRAFT`, front-silkscreen `DRAFT - NOT FOR FAB`, and title-comment prefix `NOT FOR FABRICATION -` (also in the PDF exporter). Generator no longer emits them. Specific technical cautions, hidden metadata, footprint identifiers, filenames and schematic remain unchanged; this is not fabrication approval.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Button selection: Omron B3F-1000
|
||||
|
||||
> **Current status (2026-09-22):** [User acceptance and group alignment](user-acceptance.md) supersedes the earlier pending user-fit, USB-clearance, male-map, procurement and mounting-hardware items below. Original research/evidence remains historical; acceptance is not independent qualification.
|
||||
|
||||
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||
|
||||
Verified 2026-09-20 against the Omron datasheet and installed KiCad libraries. This research covers three active-low GPIO-to-GND buttons.
|
||||
|
||||
**Integration decision:** the A0 schematic now implements B3F-1000 switches with one **2.2 kΩ, 1%, at least 0.125 W external pull-up** per input. The lower-current/internal-pull-up-only discussion below records the selection rationale, not an unselected schematic option. Firmware is unchanged. Exact resistor MPN, supply tolerance/budget and stock remain release checks.
|
||||
**Integration decision:** the A0 schematic now implements B3F-1000 switches with one **2 kΩ, 1%, metal-film external pull-up (design rating at least 0.125 W)** per input. The lower-current/internal-pull-up-only discussion below records the selection rationale, not an unselected schematic option. Firmware is unchanged. User reports B3F-1000 and resistors purchased. Exact resistor MPN/wattage and measured supply margin remain unspecified.
|
||||
|
||||
## Schematic-ready recommendation
|
||||
|
||||
@@ -92,7 +94,7 @@ Inspected `/usr/share/kicad/footprints/Button_Switch_THT.pretty/SW_TH_Tactile_Om
|
||||
|
||||
Omron's 10 µA / 1 V minimum is explicitly a **reference value**, not an unconditional low-current reliability guarantee. B3F-1000 is a reasonable prototype choice, but internal-pull-up-only contact reliability should be qualified in the intended environment and over life.
|
||||
|
||||
For a conservative silver-contact design within the stated rated load, consider **one external 2.2 kΩ pull-up to 3.3 V per button** (about 1.5 mA while pressed); verify supply/resistor tolerances and GPIO limits before adopting. This recommendation was subsequently adopted in the A0 carrier schematic; see the integration decision above. If low current is important, the datasheet also lists **B3F-1002-G** with gold contacts in the same p. 4 dimensional group, rated 100 µA–50 mA at 3–24 VDC, but with a different operating force (1.76 ±0.49 N); weak internal pull-ups are not automatically within that rated range either. It has not been selected or stock-checked here.
|
||||
The original A0 recommendation was 2.2 kΩ (about 1.5 mA); the purchased-part update uses **one external 2 kΩ 1% pull-up to 3.3 V per button** (nominal 1.65 mA while pressed); verify supply/resistor tolerances and GPIO limits before adopting. This recommendation was subsequently adopted in the A0 carrier schematic; see the integration decision above. If low current is important, the datasheet also lists **B3F-1002-G** with gold contacts in the same p. 4 dimensional group, rated 100 µA–50 mA at 3–24 VDC, but with a different operating force (1.76 ±0.49 N); weak internal pull-ups are not automatically within that rated range either. It has not been selected or stock-checked here.
|
||||
|
||||
Retain firmware debouncing; a mechanical switch is not bounce-free. The device is unsealed (IP00), and the datasheet prohibits washing.
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# DE-9 mechanical reference — pinned Adafruit 5988 STEP
|
||||
|
||||
> **Current status (2026-09-22):** [User acceptance and group alignment](user-acceptance.md) supersedes the earlier pending user-fit, USB-clearance, male-map, procurement and mounting-hardware items below. Original research/evidence remains historical; acceptance is not independent qualification.
|
||||
|
||||
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Provisional carrier module footprints
|
||||
|
||||
> **Current status (2026-09-22):** [User acceptance and group alignment](user-acceptance.md) supersedes the earlier pending user-fit, USB-clearance, male-map, procurement and mounting-hardware items below. Original research/evidence remains historical; acceptance is not independent qualification.
|
||||
|
||||
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Compact two-layer PCB — A1 draft
|
||||
|
||||
> **Current status (2026-09-22):** [User acceptance and group alignment](user-acceptance.md) supersedes the earlier pending user-fit, USB-clearance, male-map, procurement and mounting-hardware items below. Original research/evidence remains historical; acceptance is not independent qualification.
|
||||
|
||||
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||
|
||||
@@ -49,9 +51,9 @@ component body corners.
|
||||
|---|---|---|
|
||||
| U1 | (8, 23) | ESP32 left header pin 1; antenna toward top-left cutaway, USB toward bottom edge |
|
||||
| U2 | (43, 26.670) | MAX3243 header pin 1; bare module PCB front exactly flush with carrier top edge |
|
||||
| DS1 | (43, 36) | OLED module upper-left PCB corner; display faces up |
|
||||
| R1 / R2 / R3 | (43 / 54 / 65, 34) | Axial pull-ups in the gap between RS-232 and OLED |
|
||||
| SW1 / SW2 / SW3 | (43 / 55 / 67, 68) | Back, select, next below display; switch pad 1 origins |
|
||||
| DS1 | (43.970, 36) | OLED module upper-left PCB corner; display faces up |
|
||||
| R1 / R2 / R3 | (42.160 / 53.160 / 64.160, 32.6685) | Axial pull-ups in the gap between RS-232 and OLED |
|
||||
| SW1 / SW2 / SW3 | (41.720 / 53.720 / 65.720, 68) | Back, select, next below display; switch pad 1 origins |
|
||||
| H1 | (3.5, 22.5) | Proposed case mounting hole below antenna notch |
|
||||
| H2 | (3.5, 75) | Proposed case mounting hole near bottom-left edge |
|
||||
| H3 | (78, 4) | Proposed case mounting hole |
|
||||
@@ -61,12 +63,12 @@ Outline vertices: (36,0), (82,0), (82,80), (0,80), (0,19), (36,19), closed.
|
||||
The previous A0 board is retained in `../../../../hardware/PCB/validation/pcb-a0-before-reposition.kicad_pcb`.
|
||||
The board area is **58.76 cm²**; its bounding rectangle is 65.6 cm².
|
||||
|
||||
H1–H4 are proposed **Ø3.2 mm NPTH M3 clearance holes**, not a previously agreed
|
||||
case specification. They are marked board-only and excluded from the electrical
|
||||
H1–H4 are user-approved **Ø3.2 mm NPTH M3 clearance holes**. The user selected
|
||||
10 mm brass standoffs into heat inserts; see the current acceptance record. They are marked board-only and excluded from the electrical
|
||||
BOM. The OLED and RS-232 module mounting holes are additional, separate holes.
|
||||
Screw heads, spacers, heat-set inserts, tool access and underside lead clearance
|
||||
must be checked against the final case and assembly heights. No insert dimensions
|
||||
or module stand-off heights are assumed.
|
||||
must be checked against the final case and assembly heights. Exact insert dimensions remain unspecified. The user intends/reports 10 mm
|
||||
assembled module elevation, not independently measured; see user-acceptance.md.
|
||||
|
||||
## Routing and electrical status
|
||||
|
||||
@@ -74,8 +76,8 @@ The PCB contains actual copper, not just ratsnest placement:
|
||||
|
||||
- All **38 required physical-pad spanning connections** across **17 connected
|
||||
nets** routed, including both duplicated pads of each switch contact.
|
||||
- **565 segments**, signal width **0.25 mm**, 3.3 V and GND width **0.50 mm**.
|
||||
- **22 through vias**, **0.70 mm copper / 0.30 mm drill**.
|
||||
- **938 segments** after incremental group-alignment repair, signal width **0.25 mm**, 3.3 V and GND width **0.50 mm**.
|
||||
- **41 through vias** after repair, **0.70 mm copper / 0.30 mm drill**.
|
||||
- **No copper pours**; ground is explicitly routed. This is an initial routing
|
||||
solution, not a reviewed return-current/EMI or power-integrity design.
|
||||
- Proposed minimum different-net clearance **0.25 mm** and copper-edge clearance
|
||||
@@ -107,8 +109,7 @@ Missing RF clearance cannot be signed off from the current footprint alone.
|
||||
|
||||
### RS-232
|
||||
|
||||
U2 uses the new **`Carrier:MAX3243_Reference_Provisional`** footprint **on the PCB
|
||||
only**. Its bare-module outline/header/holes are adapted from published female
|
||||
U2 uses **`Carrier:MAX3243_Reference_Provisional`** in the schematic and PCB. Its bare-module outline/header/holes are adapted from published female
|
||||
5988 CAD for the intended male 6253 module. The user explicitly confirms identical
|
||||
male/female external dimensions. The linked Adafruit STEP has now been measured
|
||||
with OpenCascade; its nominal metal geometry is used on that basis. Male header
|
||||
@@ -143,12 +144,9 @@ the carrier edge. No wall thickness, cable-backshell clearance or sealing scheme
|
||||
has been assumed. The model's anchor legs extend **2.230 mm below the module
|
||||
underside**; select a stand-off/header stack or clearance solution before assembly.
|
||||
|
||||
The schematic footprint remains intentionally blank, preserving its caution
|
||||
and avoiding edits to the schematic that was open in KiCad. Consequently there
|
||||
is **one known schematic-parity warning**: U2 footprint differs from the empty
|
||||
schematic assignment. This is not excluded/suppressed, and is not a net mismatch.
|
||||
Once the male footprint is qualified, explicitly assign it in the schematic
|
||||
before relying on Update PCB from Schematic to manage U2's footprint.
|
||||
The user has now confirmed male pinout/header/footprint equivalence and reviewed
|
||||
connections pin by pin. U2 is assigned in both schematic and PCB; validators
|
||||
require **zero schematic parity warnings**, without exclusions.
|
||||
|
||||
### OLED and controls
|
||||
|
||||
@@ -264,8 +262,8 @@ Run from the repository root with the system Python containing KiCad bindings:
|
||||
The first script exports a **fresh saved-schematic netlist** and checks the actual
|
||||
PCB pad assignments, component linkage, dimensions, layer count, nominal thickness,
|
||||
mounting holes, track/via sizes and KiCad DRC. It never rewrites the PCB or project.
|
||||
It reports the single expected U2 parity warning explicitly, and fails on any
|
||||
other parity issue, DRC violation or unconnected item. Reports are retained in
|
||||
It requires zero parity issues, DRC violations and unconnected items, including
|
||||
the retained user-confirmed U2 assignment. Reports are retained in
|
||||
`../../../../hardware/PCB/validation/pcb-parity-drc.json` and `../../../../hardware/PCB/validation/pcb-validation-summary.json`.
|
||||
|
||||
The second independently checks continuous copper/hole/edge geometry and original
|
||||
@@ -291,7 +289,8 @@ Measurement-integration validation with KiCad **10.0.6**:
|
||||
- PCB, project and schematic retain their existing content except the authorized
|
||||
board graphics. No firmware/assets, reroute, upload, erase or hardware test.
|
||||
|
||||
Current results:
|
||||
Historical measurement-integration results (current alignment results are in
|
||||
[user-acceptance.md](user-acceptance.md)):
|
||||
|
||||
- **0 physical DRC errors/warnings; 0 unconnected items.**
|
||||
- All saved-schematic pad nets match the PCB; **1 expected U2 footprint parity warning**.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# MAX3243 carrier — PROVISIONAL female CAD reference / VERIFY MALE
|
||||
|
||||
> **Current status (2026-09-22):** [User acceptance and group alignment](user-acceptance.md) supersedes the earlier pending user-fit, USB-clearance, male-map, procurement and mounting-hardware items below. Original research/evidence remains historical; acceptance is not independent qualification.
|
||||
|
||||
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# Carrier group alignment and user acceptance — 2026-09-22
|
||||
|
||||
This is the current acceptance record. It supersedes pending-user-confirmation
|
||||
items in the earlier research and A0/A1 notes; it does not replace their original
|
||||
manufacturer evidence or claim new hardware tests, fabrication approval or certification.
|
||||
|
||||
## Saved placement
|
||||
|
||||
Coordinates below are **carrier-local mm**, X right/Y down. Add (100,100) for
|
||||
KiCad coordinates. All footprint rotations remain zero. Body centres, not
|
||||
header/pad origins, define the common vertical axis **X=56.970 mm**.
|
||||
|
||||
| Item | Old footprint origin | New footprint origin | Movement |
|
||||
|---|---|---|---|
|
||||
| U2 RS-232 | (43,26.670) | unchanged | none |
|
||||
| DS1 OLED | (43,36) | (43.970,36) | +0.970 X |
|
||||
| R1 | (43,34) | (42.160,32.6685) | −0.840 X, −1.3315 Y |
|
||||
| R2 | (54,34) | (53.160,32.6685) | −0.840 X, −1.3315 Y |
|
||||
| R3 | (65,34) | (64.160,32.6685) | −0.840 X, −1.3315 Y |
|
||||
| SW1 | (43,68) | (41.720,68) | −1.280 X |
|
||||
| SW2 | (55,68) | (53.720,68) | −1.280 X |
|
||||
| SW3 | (67,68) | (65.720,68) | −1.280 X |
|
||||
|
||||
U1 remains (8,23); carrier outline, four carrier mounting holes, RF reserve and
|
||||
confirmed USB geometry are unchanged. Resistors remain a horizontal group with
|
||||
11 mm pitch; buttons remain a horizontal group with 12 mm pitch. Neither group
|
||||
was rearranged into a vertical stack.
|
||||
|
||||
The published RS-232 bare PCB spans X=41.095…72.845, Y=0…29.337, including
|
||||
its rounded rear corners beyond the header row. Its centre X=56.970 is retained
|
||||
to minimize module movement and preserve connector/case registration. OLED PCB
|
||||
bounds are X=43.970…69.970, Y=36…62. The OLED's offset display window is not
|
||||
used as the module centre.
|
||||
|
||||
The nominal resistor bodies are 6.3 × 2.5 mm, centred 3.81 mm right of pad 1.
|
||||
Their group spans X=42.820…71.120, Y=31.4185…33.9185. The 6.663 mm gap between
|
||||
module boards is therefore split into **2.0815 mm clearance / 2.5 mm body /
|
||||
2.0815 mm clearance**. Adjacent resistor bodies have 4.7 mm horizontal clearance.
|
||||
These are nominal planar CAD clearances, not new tolerance or height measurements.
|
||||
|
||||
Omron B3F-1000 nominal bodies are 6 × 6 mm with centres 3.25 mm right and
|
||||
2.25 mm below their pad-1 origins. Group body bounds X=41.970…71.970,
|
||||
Y=67.250…73.250; OLED-to-button body gap **5.250 mm**, adjacent button body
|
||||
gaps **6 mm**. Footprint courtyard and lead envelopes are wider than the bodies;
|
||||
the alignment audit uses the actual F.Fab bodies, not a pad-origin average.
|
||||
|
||||
## Closed by explicit user acceptance/report
|
||||
|
||||
- All component dimensions and physical fit match visually; USB placement and
|
||||
clearance are correct. This is user acceptance, not an independently performed
|
||||
new print-fit or hardware test by the agent.
|
||||
- Female headers are described as 8.5 mm and standard male headers as adding
|
||||
2.5 mm. User intends/reports **10 mm assembled elevation**. The arithmetic sum
|
||||
is 11 mm, but mating height cannot be inferred by simply adding those descriptions.
|
||||
**10 mm is not independently measured or precision-verified.** User reports
|
||||
RS-232 mounting prongs clear the carrier at the intended assembly height.
|
||||
- Four Ø3.2 mm M3 carrier holes approved; **10 mm brass standoffs into heat
|
||||
inserts**, M3 daughterboard screws, and **M2 display hardware** selected.
|
||||
This does not establish DE-9 cable-retention thread type, exact screw lengths,
|
||||
insert dimensions or the final printed case wall/cutout.
|
||||
- **Omron B3F-1000 switches purchased.** Existing standard KiCad footprint is
|
||||
retained; documented duplicate-pad mapping is unchanged.
|
||||
- **2 kΩ, 1% metal-film resistors purchased.** R1–R3 values updated in schematic,
|
||||
PCB and BOM; nominal DIN0207 6.3 × 2.5 mm body / 7.62 mm lead pitch retained
|
||||
under the visual-fit confirmation. Exact resistor MPN and purchased wattage
|
||||
were not supplied. Design requirement remains at least 0.125 W. At nominal
|
||||
3.3 V, each pressed external pull-up draws 1.65 mA and dissipates 5.445 mW;
|
||||
these calculations do not quantify regulator margin or GPIO internal pull-ups.
|
||||
- Male Adafruit module has identical reference pinout/header mapping/footprint
|
||||
per user, who reviewed module connections pin by pin. Existing schematic U2
|
||||
assignment `Carrier:MAX3243_Reference_Provisional` is preserved; clean parity
|
||||
is required, not waived. Original female CAD provenance is unchanged.
|
||||
- Nonisolated USB power arrangement explicitly accepted and reported already
|
||||
tested by user. **Successful operation does not establish backfeed safety**, in
|
||||
particular with simultaneous powered hosts. No isolation/backfeed protection
|
||||
was added or claimed.
|
||||
- Existing 3.3 V supply successfully powers the modules per user. This is operating
|
||||
evidence, not measured regulator current/thermal/transient headroom.
|
||||
- User confirms backside OLED I²C pull-ups and operation without extras. **Their
|
||||
resistance and termination rail remain unmeasured.** No values/rail are invented
|
||||
and no external I²C pull-ups added.
|
||||
- Current RF reserve and power/ground routing accepted based on visual and
|
||||
operational experience. This is not manufacturer RF qualification, measured
|
||||
signal/power integrity, regulator-margin proof, EMC testing or certification.
|
||||
|
||||
## Verification and remaining limits
|
||||
|
||||
Incremental routing starts from `hardware/PCB/validation/pcb-before-group-alignment.kicad_pcb`.
|
||||
It removes copper obstructed by moved pads, reconnects retained routes and trims
|
||||
KiCad-reported dangling stubs. **476/587 original copper items remain exactly
|
||||
unchanged**; 111 original items removed. No whole-board regeneration or route
|
||||
reset was used. `pcb-group-migration.json` records the removed UUIDs and repairs;
|
||||
`validate_group_alignment.py` checks the full parsed non-copper delta, retained
|
||||
copper UUID/content, body centrelines/gaps, and unchanged project settings.
|
||||
Original A0 and measured-U1 snapshots/reports are retained, not rebaselined.
|
||||
|
||||
Run from repository root:
|
||||
|
||||
```sh
|
||||
/usr/bin/python3 -B hardware/PCB/validate_schematic.py
|
||||
/usr/bin/python3 -B hardware/PCB/validate_pcb_draft.py
|
||||
/usr/bin/python3 -B hardware/PCB/validate_group_alignment.py
|
||||
/usr/bin/python3 -B hardware/PCB/validation/pcb-routing-check.py
|
||||
/usr/bin/python3 -B hardware/PCB/validate_u1_update.py
|
||||
/usr/bin/python3 -B hardware/PCB/validate_footprints.py
|
||||
/usr/bin/python3 -B hardware/PCB/reference/db9-cad/validate.py
|
||||
```
|
||||
|
||||
Validation completed with KiCad 10.0.6: schematic ERC **0 errors/warnings**;
|
||||
PCB DRC **0 physical violations, 0 unconnected items, 0 parity issues**. All
|
||||
17 connected nets, 9 electrical components, 44 U1 pins and 15 firmware GPIO
|
||||
contracts checked. Current routing: **938 segments, 41 vias**; unchanged width
|
||||
rules (0.25 mm signal / 0.50 mm power and ground), 0.70/0.30 mm vias. Independent
|
||||
minimum different-net/copper-to-NPTH gap **0.252762 mm**, minimum board-edge gap
|
||||
**1.900 mm**; RF reserve remains clear. Exact group/preservation and U1/silk
|
||||
regressions, two footprint tests and DB9 CAD/source checks passed. KiCad bindings
|
||||
emit known startup enum-property assertions but all checks complete successfully.
|
||||
|
||||
DB9 projections and 94 device-silkscreen items refreshed. Placement PDF (1:1),
|
||||
PCB SVG/PNG/3D and schematic PDF/SVG/PNG regenerated; PNG previews visually
|
||||
reviewed. The 3D view still lacks complete module models and is not assembly
|
||||
qualification. No removed PCB presentation/draft markers were reintroduced.
|
||||
|
||||
Current acceptance closes the user-review gates above. Still not independently
|
||||
qualified: regulator margin, USB backfeed safety, pull-up values/rail, RF/EMC,
|
||||
complete tolerance/height/case/actuator envelopes, finished-hole fabrication
|
||||
process and certification. Missing module courtyards remain a known model limit;
|
||||
DRC does not validate complete assembled-module or case fit. No new hardware
|
||||
tests, firmware changes, Gerbers, fabrication order or manufacturing approval.
|
||||
@@ -10,11 +10,26 @@ See **[pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md)** f
|
||||
mechanical assumptions and validation. A review drawing is `pcb-placement-draft.pdf`.
|
||||
|
||||
The schematic contains the complete ESP32 development-board interface, MAX3243
|
||||
breakout logic interface, I²C OLED, three Omron B3F-1000 buttons and three 2.2 kΩ
|
||||
breakout logic interface, I²C OLED, three Omron B3F-1000 buttons and three 2 kΩ
|
||||
button pull-ups. USB connectors, regulator, reset/boot circuit, DE-9 and MAX3243
|
||||
charge pump are already on their respective modules and are not duplicated on
|
||||
the carrier. All 3.3 V and ground connections are explicit named nets.
|
||||
|
||||
## Current user acceptance and placement
|
||||
|
||||
See [user-acceptance.md](../../docs/devel/hardware/PCB/user-acceptance.md) for the exact group placements, nominal body clearances and evidence limits. Common body centreline is carrier-local X=56.970 mm; resistor bodies are centred in the RS-232/OLED gap with 2.0815 mm nominal clearance to each PCB. U1, U2, outline, carrier holes and USB placement are unchanged. R1–R3 are purchased 2 kΩ 1% metal-film parts; Omron B3F-1000 switches are also purchased.
|
||||
|
||||
The user accepts visual fit/USB clearance, male module mapping, M3 carrier/daughterboard and M2 display hardware, current RF reserve and power/ground routing. Intended/reported assembled module elevation is 10 mm, not an independently verified sum of the 8.5 mm female and described 2.5 mm male contributions. USB nonisolation is accepted; backfeed safety, regulator margin and RF qualification are not established. OLED backside pull-ups are confirmed by user, but their values/rail are unmeasured. Earlier pending-status language below is historical where superseded by this record.
|
||||
|
||||
PCB presentation uses **A3 landscape (420 × 297 mm)** instead of A4; the
|
||||
carrier stays at absolute X=100…182, Y=100…180 mm. No origin, footprint,
|
||||
routing or zone was moved. The 1:1 placement PDF includes the worksheet/title
|
||||
block, clear of the PCB; `validation/pcb-placement-page.png` previews the full
|
||||
page. Print at actual size on A3 for the 1:1 fit check, not “fit to A4”.
|
||||
`update_presentation.py` removes the accepted USB/geometry/pinout/assembly
|
||||
pending labels with lock/concurrent-edit guards and an exact-content audit.
|
||||
RF qualification and connector/case-cutout cautions remain.
|
||||
|
||||
## Files and use
|
||||
|
||||
| File | Purpose |
|
||||
@@ -76,12 +91,11 @@ modify footprints. The generated KiCad files are the editable design artifacts.
|
||||
matching board markings. This is photo/label evidence, not a continuity test:
|
||||
verify actual pin-to-function mapping before release, especially on clones.
|
||||
- DS1 pins 1–4 are GND, VCC, SCL, SDA, front/display view with header at top.
|
||||
- U2 pin numbering follows published Adafruit **5988 female** CAD as a provisional
|
||||
reference for the selected **6253 male** module. Its footprint is deliberately
|
||||
**unassigned in the schematic** until the actual male header and mechanics are
|
||||
verified. The PCB draft assigns `Carrier:MAX3243_Reference_Provisional` on the
|
||||
PCB only, causing one documented footprint-parity warning. Its reference nets
|
||||
are routed for review, not male-board qualification. See [adafruit-research.md](../../docs/devel/hardware/PCB/adafruit-research.md)
|
||||
- U2 pin numbering follows published Adafruit **5988 female** CAD for the selected
|
||||
**6253 male** module. The user confirmed the same pinout, header mapping and
|
||||
footprint as the reference. Both schematic and PCB now assign
|
||||
`Carrier:MAX3243_Reference_Provisional`; clean footprint parity is required.
|
||||
The identifier is unchanged; this assignment is not hardware or fabrication qualification. See [adafruit-research.md](../../docs/devel/hardware/PCB/adafruit-research.md)
|
||||
and [pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md); this is not a generic DE-9 footprint.
|
||||
- No-connect crosses mean unused on the carrier, not disconnected internally.
|
||||
Native USB GPIO19/20, UART0 GPIO43/44, memory-conflicting GPIO35–37 and the 5 V
|
||||
@@ -92,8 +106,8 @@ modify footprints. The generated KiCad files are the editable design artifacts.
|
||||
bidirectional pad; firmware **must use open-drain mode** for OFF. ERC does not
|
||||
enforce that configuration or model the breakout's internal OFF pull-up.
|
||||
- Selected buttons are Omron B3F-1000 with their verified KiCad THT footprint.
|
||||
Added R1–R3, **2.2 kΩ, 1%, at least 0.125 W**, from each button input to 3.3 V.
|
||||
They provide nominal 1.5 mA contact current / 4.95 mW dissipation while pressed,
|
||||
Added R1–R3, **2 kΩ, 1%, at least 0.125 W**, from each button input to 3.3 V.
|
||||
They provide nominal 1.65 mA contact current / 5.445 mW dissipation while pressed,
|
||||
rather than relying on the internal pull-up for the silver-contact rated load.
|
||||
Firmware's internal pull-ups and debouncing remain enabled; no firmware changes
|
||||
are required. Final supply tolerance/load budget still needs checking.
|
||||
@@ -118,9 +132,9 @@ the user. This does **not** validate connector drills, male RS-232 geometry,
|
||||
electrical operation, power budget, procurement or fabrication.
|
||||
|
||||
The routed PCB draft has **0 physical DRC violations and 0 unconnected items**;
|
||||
a fresh netlist-to-PCB check passes. One deliberate U2 footprint-parity warning
|
||||
remains (PCB reference footprint versus blank schematic assignment). No added
|
||||
DRC exclusions hide this warning. Missing courtyards are ignored by default DRC
|
||||
a fresh netlist-to-PCB check passes with **zero schematic-parity issues**,
|
||||
including the user-confirmed U2 footprint assignment. No DRC exclusions were
|
||||
added. Missing courtyards are ignored by default DRC
|
||||
and remain a manual release check. Run `/usr/bin/python3 -B
|
||||
hardware/PCB/validate_pcb_draft.py` from the repository root; see
|
||||
[pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md) for the complete verification scope and remaining gates.
|
||||
@@ -133,10 +147,10 @@ Repository sources: `../../docs/wiring.md`, `../../src/board_pins.h`,
|
||||
|
||||
| Assembly | Repository identification | Footprint status |
|
||||
|---|---|---|
|
||||
| ESP32 development board | User confirmed HW-678 V0.0.0 markings and S3-N16R8 shield, two USB-C ports | Two 22-pin rows; nominal 2.54 mm pitch and 25.40 mm row spacing supported by simultaneous breadboard fit. Mechanical outline registration and socket/drill selection remain pending. |
|
||||
| RS-232 module | Adafruit MAX3243 full-pinout breakout; male is product 6253, female is 5988 | Eagle pin map/PCB geometry and STEP metal dimensions extracted. User confirms identical male/female external dimensions; actual fit and male electrical map remain release checks. Reference footprint used on PCB only. |
|
||||
| ESP32 development board | User confirmed HW-678 V0.0.0 markings and S3-N16R8 shield, two USB-C ports | Two 22-pin rows; nominal 2.54 mm pitch and 25.40 mm row spacing supported by simultaneous breadboard fit. Measured planar outline and visual fit accepted by user; finished-hole process remains unqualified. |
|
||||
| RS-232 module | Adafruit MAX3243 full-pinout breakout; male is product 6253, female is 5988 | Eagle pin map/PCB geometry and STEP metal dimensions extracted. User confirms identical male pinout/header/footprint, visual fit and pin-by-pin review. Reference footprint assigned in schematic and PCB. |
|
||||
| Display | 128×64 yellow/blue I²C OLED, address 0x3c; Amazon ASIN B0F13SSSFB | 26 mm square board, 22.50 mm hole spacing, Ø2 mm holes and centred header 1.50 mm from top edge are user-fit-verified. Local footprint assigned; header pad/drill selection remains provisional. |
|
||||
| Three buttons | Omron B3F-1000; previous, select, next; normally open to ground, active low | Datasheet and `Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x` pad mapping verified. External 2.2 kΩ pull-ups included. Live distributor stock remains unverified. |
|
||||
| Three buttons | Omron B3F-1000; previous, select, next; normally open to ground, active low | Datasheet and `Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x` pad mapping verified. Purchased per user; external purchased 2 kΩ 1% metal-film pull-ups included. |
|
||||
|
||||
Adafruit manufacturer CAD starting point:
|
||||
https://learn.adafruit.com/adafruit-rs-232-full-pinout-level-shifter-breakout/downloads
|
||||
@@ -147,8 +161,9 @@ The original files and CC BY-SA 3.0 notices are retained in `reference/`; they
|
||||
remain under their upstream license. Omron reference documents retain their
|
||||
manufacturer notices. The CAD-parts repository's STEP and MIT license are separately
|
||||
retained under `reference/db9-cad/`. External metal geometry is used for the male
|
||||
module on the user's explicit equivalence confirmation; this does not validate
|
||||
its electrical map, connector threads, tolerances, assembly fit or case cutout.
|
||||
module on the user's explicit equivalence confirmation. The user separately
|
||||
confirmed electrical mapping and visual fit; connector threads, tolerances and
|
||||
final case cutout are not independently qualified.
|
||||
|
||||
## Verified logical connections for the existing firmware profile
|
||||
|
||||
@@ -181,11 +196,11 @@ The wiring guide warns that compatible ESP32 boards may have different layouts.
|
||||
- Keep UART0 / GPIO43 and GPIO44 available through the onboard USB-UART bridge.
|
||||
- Reserve GPIO19 and GPIO20 for onboard native USB.
|
||||
- Existing firmware profile powers RS-232 Vin and OLED VCC from 3.3 V, not 5 V.
|
||||
- Confirm the exact development board's regulator capacity and USB power-path
|
||||
behavior before finalizing the power design, especially with both USB ports attached.
|
||||
- Display pull-up presence, resistance and termination rail must be checked on
|
||||
the actual module. Do not select external pull-up values without this information.
|
||||
- Firmware enables internal button pull-ups; this carrier additionally uses 2.2 kΩ
|
||||
- User reports successful 3.3 V module operation and accepts nonisolated USB power.
|
||||
Regulator margin and USB backfeed safety remain unverified, especially with two powered hosts.
|
||||
- User confirms backside OLED I²C pull-ups and operation without extras.
|
||||
Resistance and termination rail are unmeasured; no external values are invented.
|
||||
- Firmware enables internal button pull-ups; this carrier additionally uses 2 kΩ
|
||||
external pull-ups for the selected buttons' contact-current requirement.
|
||||
- RS-232 OFF uses the breakout's onboard pull-up; GPIO9 releases it to enable.
|
||||
- The breakout is not galvanically isolated; signal grounds are common.
|
||||
@@ -198,17 +213,18 @@ The wiring guide warns that compatible ESP32 boards may have different layouts.
|
||||
- OLED purchase: https://www.amazon.de/dp/B0F13SSSFB — I²C, not SPI.
|
||||
- Both Amazon URLs returned an access-check page on inspection; the ASINs alone
|
||||
do not establish a verified manufacturer drawing or footprint.
|
||||
- Use the male Adafruit breakout. User reports male/female board dimensions match;
|
||||
verify the selected male board against its manufacturer CAD.
|
||||
- Use the male Adafruit breakout. User confirms dimensions, pinout/header/footprint
|
||||
equivalence and completed a pin-by-pin module connection review.
|
||||
- Carrier for complete modules: soldered module headers for final assembly,
|
||||
sockets for beta testing without a case. Header pitch, row spacing, drill size
|
||||
and socket clearance must be validated against selected hardware.
|
||||
- Power only through the ESP32 development board's USB connectors, typically a
|
||||
powerbank or the serial peer's USB port, with common ground. Do not introduce
|
||||
another carrier power input or assume simultaneous USB supplies are isolated.
|
||||
- Select readily available THT buttons; the existing random switches need not be reused.
|
||||
- Case will be 3D-printed after PCB finalization. Include mounting provisions in
|
||||
the layout, but screw size, hole diameter and insert bosses are not yet selected.
|
||||
- User purchased Omron B3F-1000 THT buttons and 2 kΩ 1% metal-film resistors.
|
||||
- Case will be 3D-printed after PCB finalization. User approves M3 carrier holes,
|
||||
10 mm brass standoffs into heat inserts, M3 daughterboard screws and M2 display
|
||||
hardware. Exact insert/boss dimensions and screw lengths are not supplied.
|
||||
|
||||
## User-supplied mechanical evidence
|
||||
|
||||
@@ -233,7 +249,7 @@ The wiring guide warns that compatible ESP32 boards may have different layouts.
|
||||
- Both USB-C metal shells are **8.87 mm wide**, with opening faces flush with the
|
||||
main PCB bottom edge. Left shell's outside left edge is **4.16 mm** from the PCB
|
||||
left edge; right shell's outside right edge is **3.66 mm** from the PCB right edge.
|
||||
Shell depth/height and mating-plug clearance remain unmeasured.
|
||||
User accepts USB placement and mating-plug clearance visually; shell depth/height remain unmeasured.
|
||||
- The library and routed board's embedded U1 now record these bounds on F.Fab;
|
||||
USB mouths are width spans only, not complete connector outlines. Measured
|
||||
board-local silkscreen, 1:1 placement PDF and SVG/PNG/3D previews are refreshed. See
|
||||
@@ -271,20 +287,19 @@ The wiring guide warns that compatible ESP32 boards may have different layouts.
|
||||
Crosshairs denote header centres, not specified pad or drill diameters. This
|
||||
template is not a KiCad footprint or a fabrication file.
|
||||
|
||||
## Remaining mechanical/electrical checks
|
||||
## Current acceptance checklist and evidence limits
|
||||
|
||||
1. OLED outline, mounting-hole placement, revised header position and display
|
||||
outline are user fit-verified. Select carrier header/socket pads and drills
|
||||
from the actual connector drawings.
|
||||
2. ESP32 planar body/antenna bounds and USB mouth positions are now recorded in
|
||||
the library and embedded board footprint. Verify a 1:1 fit; complete module heights, USB depth/height, mating-plug clearances and
|
||||
assembly courtyards. Measured antenna bounds do not establish an RF keepout.
|
||||
3. Verify power-path schematic/regulator capacity and OLED pull-up implementation.
|
||||
Photos alone do not establish those electrical properties.
|
||||
- [x] Component dimensions and visual physical fit accepted by user, including USB placement/clearance and RS-232 prong clearance.
|
||||
- [x] Male Adafruit pinout/header/footprint equivalence and pin-by-pin module connection review confirmed by user; existing U2 schematic assignment retained with clean parity.
|
||||
- [x] M3 carrier holes, 10 mm brass standoffs into heat inserts, M3 daughterboard screws and M2 display hardware approved.
|
||||
- [x] Omron B3F-1000 and 2 kΩ 1% metal-film resistors purchased per user.
|
||||
- [x] Existing 3.3 V supply operation and OLED onboard pull-ups reported; no extra I²C pull-ups needed in reported operation.
|
||||
- [x] Nonisolated USB power, existing RF reserve and power/ground routing accepted by user.
|
||||
- [ ] Independently verified regulator margin, USB backfeed safety, OLED pull-up resistance/rail and manufacturer RF/EMC qualification are not available.
|
||||
- [ ] Detailed fastener lengths/insert dimensions, final case cutout, tolerance/height envelopes and fabrication-process qualification remain outside this acceptance.
|
||||
|
||||
The native schematic, local libraries and routed two-layer PCB draft are present.
|
||||
Before fabrication, finish the above checks, qualify the male RS-232 module map
|
||||
and footprint, and confirm procurement. The carrier now proposes an 82 × 80 mm
|
||||
stepped outline and four Ø3.2 mm M3 mounting holes; case hardware is not yet fixed.
|
||||
The PCB's RF reserve is a draft design allocation, not a verified module antenna
|
||||
keepout. Module/USB/DE-9 clearances must still be established from actual hardware.
|
||||
The intended/reported 10 mm assembled elevation is not independently measured;
|
||||
8.5 + 2.5 = 11 mm is not evidence of actual mated height. User acceptance closes
|
||||
the corresponding review gates, not these factual evidence limits. Missing
|
||||
module courtyards still limit automated assembly checks. No new hardware tests,
|
||||
firmware changes, Gerbers, order or certification are implied.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
References,Quantity,Description,Manufacturer,MPN or identification,Footprint,Status
|
||||
U1,1,ESP32-S3 N16R8 development board,User supplied,HW-678 V0.0.0 / ASIN B0F3XMYYQY,Carrier:HW678_2x22_Provisional,Header pitch/spacing confirmed; connector drill fit and module envelope pending
|
||||
U2,1,MAX3243 full-pinout RS-232 breakout male,Adafruit,6253,Carrier:MAX3243_Reference_Provisional,PCB-only reference footprint; schematic assignment blank; male header/geometry verification required
|
||||
DS1,1,128x64 yellow-blue I2C OLED 26mm PCB,User supplied,ASIN B0F13SSSFB,Carrier:OLED_26mm_I2C_Provisional,Module geometry user-fit-verified; carrier header drill fit pending
|
||||
SW1 SW2 SW3,3,Normally-open 6mm THT tactile switch,Omron,B3F-1000,Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x,Datasheet/footprint checked; stock unverified
|
||||
R1 R2 R3,3,2.2k ohm 1 percent axial resistor at least 0.125W,TBD,TBD,Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal,Design value selected; exact MPN and body/lead fit pending
|
||||
H1 H2 H3 H4,4,Proposed M3 case mounting locations with 3.2mm NPTH,TBD,TBD,MountingHole:MountingHole_3.2mm_M3,Board-only hole locations; screws and insert/standoff dimensions not selected
|
||||
Assembly hardware,TBD,Module headers and optional beta-test sockets plus mounting hardware,TBD,TBD,,Not yet selected; not included in electrical component count
|
||||
U1,1,ESP32-S3 N16R8 development board,User supplied,HW-678 V0.0.0 / ASIN B0F3XMYYQY,Carrier:HW678_2x22_Provisional,Dimensions and visual fit including USB placement/clearance user accepted
|
||||
U2,1,MAX3243 full-pinout RS-232 breakout male,Adafruit,6253,Carrier:MAX3243_Reference_Provisional,Assigned in schematic and PCB; male pinout/header/footprint equivalence and pin-by-pin review user confirmed
|
||||
DS1,1,128x64 yellow-blue I2C OLED 26mm PCB,User supplied,ASIN B0F13SSSFB,Carrier:OLED_26mm_I2C_Provisional,Module geometry and visual fit user confirmed; onboard I2C pull-ups present but values/rail unmeasured
|
||||
SW1 SW2 SW3,3,Normally-open 6mm THT tactile switch,Omron,B3F-1000,Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x,User reports purchased; datasheet/footprint mapping checked
|
||||
R1 R2 R3,3,2k ohm 1 percent metal-film axial resistor,TBD,User purchased; exact MPN and wattage unspecified,Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal,Visual dimensions accepted; retained nominal 6.3x2.5mm body and 7.62mm pitch; design rating at least 0.125W
|
||||
H1 H2 H3 H4,4,M3 carrier mounting locations with 3.2mm NPTH,User selected,M3 screws and 10mm brass standoffs into heat inserts,MountingHole:MountingHole_3.2mm_M3,User approved; board-only holes excluded from electrical component count
|
||||
Module mounting hardware,TBD,M3 daughterboard screws and M2 OLED hardware,User selected,Exact insert and screw lengths unspecified,,User approved; case wall and detailed fastener specifications not defined
|
||||
Module headers,TBD,8.5mm female headers and standard male headers,User selected,Male described as adding 2.5mm,,User intended/reported assembled elevation 10mm; not independently measured; arithmetic sum 11mm is not verified mating height; RS232 prongs clear per user
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Export a 1:1 placement PDF with measured HW678 geometry, without editing CAD.
|
||||
|
||||
Export the saved board's verified measured U1 graphics. Only review captions
|
||||
change on a temporary plotting board; no library overlay hides stale CAD.
|
||||
Export the saved board's verified measured U1 graphics and A3 worksheet.
|
||||
No library overlay or temporary caption hides stale CAD.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
@@ -21,18 +21,16 @@ def export():
|
||||
original = PCB.read_bytes()
|
||||
board = p.LoadBoard(str(PCB))
|
||||
verify(board)
|
||||
for drawing in board.GetDrawings():
|
||||
if isinstance(drawing, p.PCB_TEXT):
|
||||
if drawing.GetText() == "Nominal RS-232 metal verified; ESP32 USB / antenna / assembly clearance pending":
|
||||
drawing.SetText("Measured ESP32 outline / USB mouths; RF / plug / assembly clearance pending")
|
||||
board.GetTitleBlock().SetComment(0, "Measured planar outlines; verify RF / plug / assembly clearance")
|
||||
from update_presentation import expected_tree
|
||||
from validate_u1_update import parse, canonical
|
||||
assert canonical(expected_tree(parse(PCB))) == canonical(parse(PCB)), "Update PCB presentation first"
|
||||
with tempfile.TemporaryDirectory(prefix="carrier-placement-") as temporary:
|
||||
draft = Path(temporary) / PCB.name
|
||||
result = Path(temporary) / PDF.name
|
||||
if not p.SaveBoard(str(draft), board, True):
|
||||
raise RuntimeError("Cannot save temporary plotting board")
|
||||
subprocess.run([
|
||||
"kicad-cli", "pcb", "export", "pdf", "--mode-single",
|
||||
"kicad-cli", "pcb", "export", "pdf", "--mode-single", "--include-border-title",
|
||||
"--layers", "F.Fab,F.SilkS,Dwgs.User,Edge.Cuts",
|
||||
"--sketch-pads-on-fab-layers", "--black-and-white", "--scale", "1",
|
||||
"--output", str(result), str(draft),
|
||||
|
||||
@@ -22,13 +22,13 @@ OUTLINE = [(36,0), (82,0), (82,80), (0,80), (0,19), (36,19)]
|
||||
PLACEMENT = {
|
||||
"U1": ("Carrier:HW678_2x22_Provisional", 8, 23),
|
||||
"U2": ("Carrier:MAX3243_Reference_Provisional", 43, 26.670),
|
||||
"DS1": ("Carrier:OLED_26mm_I2C_Provisional", 43, 36),
|
||||
"R1": ("Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal", 43, 34),
|
||||
"R2": ("Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal", 54, 34),
|
||||
"R3": ("Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal", 65, 34),
|
||||
"SW1": ("Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x", 43, 68),
|
||||
"SW2": ("Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x", 55, 68),
|
||||
"SW3": ("Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x", 67, 68),
|
||||
"DS1": ("Carrier:OLED_26mm_I2C_Provisional", 43.97, 36),
|
||||
"R1": ("Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal", 42.16, 32.6685),
|
||||
"R2": ("Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal", 53.16, 32.6685),
|
||||
"R3": ("Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal", 64.16, 32.6685),
|
||||
"SW1": ("Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x", 41.72, 68),
|
||||
"SW2": ("Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x", 53.72, 68),
|
||||
"SW3": ("Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x", 65.72, 68),
|
||||
}
|
||||
MOUNTS = [(3.5,22.5), (3.5,75), (78,4), (78,76)]
|
||||
# Deliberate allocation, NOT a measured antenna envelope or an RF guarantee.
|
||||
@@ -116,7 +116,7 @@ def main():
|
||||
title=board.GetTitleBlock()
|
||||
title.SetTitle("Serial Swiss Army Knife - 82x80 mm carrier")
|
||||
title.SetRevision("A1")
|
||||
title.SetComment(0,"USB / antenna geometry pending; verify module pinout / assembly")
|
||||
title.SetComment(0,"")
|
||||
pad_nets={}
|
||||
for source_net in source.findall("nets/net"):
|
||||
net=p.NETINFO_ITEM(board,source_net.get("name"))
|
||||
@@ -125,7 +125,6 @@ def main():
|
||||
pad_nets[node.get("ref"),node.get("pin")]=(net,node)
|
||||
for ref,(lib_id,x,y) in PLACEMENT.items():
|
||||
comp=components[ref]
|
||||
if ref != "U2":
|
||||
assert comp.findtext("footprint")==lib_id,(ref,"Schematic footprint changed")
|
||||
fp=load(lib_id)
|
||||
fp.SetReference(ref)
|
||||
@@ -145,7 +144,7 @@ def main():
|
||||
elif ref=="U2":
|
||||
fp.Reference().SetPosition(xy(56.97,14.670))
|
||||
elif ref=="DS1":
|
||||
fp.Reference().SetPosition(xy(56,49))
|
||||
fp.Reference().SetPosition(xy(56.97,49))
|
||||
elif ref.startswith("SW"):
|
||||
fp.Reference().SetPosition(xy(x+3.25,y-2))
|
||||
else:
|
||||
@@ -188,7 +187,7 @@ def main():
|
||||
text(board,"RF RESERVE",20.5,26,p.Dwgs_User,1.2)
|
||||
text(board,"VERIFY ANTENNA",20.5,29,p.Dwgs_User,1)
|
||||
text(board,"ESP32 HW678",20.7,48)
|
||||
text(board,"USB ACCESS - VERIFY",18,83,p.Dwgs_User,1)
|
||||
|
||||
text(board,"DE-9 OUT / NOMINAL MALE",56.97,-1.830,p.Dwgs_User,1)
|
||||
text(board,"MAX3243 REF",56.97,17.670)
|
||||
text(board,"26mm OLED",56,52)
|
||||
@@ -196,11 +195,15 @@ def main():
|
||||
text(board,label,x,75, size=0.9)
|
||||
|
||||
text(board,"82 x 80 mm / 2 copper layers",41,-5,p.Dwgs_User,1.5)
|
||||
text(board,"Nominal RS-232 metal verified; ESP32 USB / antenna / assembly clearance pending",41,-2.5,p.Dwgs_User,0.9)
|
||||
from update_presentation import CAPTION
|
||||
text(board,CAPTION,41,-2.5,p.Dwgs_User,0.9)
|
||||
board.BuildConnectivity()
|
||||
assert p.SaveBoard(str(PCB),board,True)
|
||||
# Separate process avoids repeated-load SWIG lifetime issues in pcbnew.
|
||||
import sys
|
||||
subprocess.run([sys.executable, "-B", str(ROOT/"update_presentation.py")], check=True)
|
||||
print("Created",PCB,"with 9 circuit footprints, 4 proposed M3 mounts, 2 copper layers.")
|
||||
print("Placement draft only. U2 nominal male external metal dimensions user-confirmed; pin map / assembly still require qualification.")
|
||||
print("Placement draft only. User-confirmed fit and pin map; RF/electrical/fabrication qualification not established.")
|
||||
|
||||
if __name__=="__main__":
|
||||
main()
|
||||
|
||||
@@ -111,6 +111,8 @@ def note(text,x,y,size=1.27):
|
||||
|
||||
def place(name,ref,value,x,y,nets,extra=None):
|
||||
pins,hw,hh,_,footprint,description = SPECS[name]
|
||||
if ref == "U2":
|
||||
footprint = "Carrier:MAX3243_Reference_Provisional"
|
||||
instance = [f'(symbol (lib_id {q("Carrier:"+name)}) (at {n(x)} {n(y)} 0) (unit 1) (in_bom yes) (on_board yes) (dnp no) (uuid {q(uid(ref))})',
|
||||
prop("Reference",ref,x,y-hh-5.08),prop("Value",value,x,y-hh-2.54),prop("Footprint",footprint,x,y,True),prop("Datasheet","",x,y,True)]
|
||||
for k,v in (extra or {}).items():
|
||||
@@ -140,12 +142,12 @@ for number,name,*_ in MCU:
|
||||
mcu_nets[number]=GPIO_NET[int(name[4:])]
|
||||
place("HW678_N16R8","U1","HW678 / S3-N16R8",95.25,109.22,mcu_nets,{"Status":"Header geometry nominal; outline/antenna clearance pending","Pin_numbering":"Left 1-22; right 23-44; both top-to-bottom, antenna up"})
|
||||
rs_nets=dict(zip(map(str,range(1,13)),["+3V3","GND","RS_DCD","RS_RX","RS_TX","RS_DTR","RS_DSR","RS_RTS","RS_CTS","RS_RI","RS_VALID","RS_OFF_N"]))
|
||||
place("MAX3243_Module_Reference","U2","Adafruit MAX3243 / male 6253",228.6,96.52,rs_nets,{"Status":"VERIFY male header numbering; footprint intentionally unassigned","Datasheet_source":"../../docs/devel/hardware/PCB/adafruit-research.md; reference female 5988 CAD"})
|
||||
place("MAX3243_Module_Reference","U2","Adafruit MAX3243 / male 6253",228.6,96.52,rs_nets,{"Status":"User confirmed male pinout/header/footprint and pin-by-pin connections","Datasheet_source":"../../docs/devel/hardware/PCB/adafruit-research.md; reference female 5988 CAD"})
|
||||
place("OLED_I2C_26mm","DS1","128x64 I2C OLED / 0x3C",228.6,152.4,{"1":"GND","2":"+3V3","3":"OLED_SCL","4":"OLED_SDA"},{"Status":"Module outline/header/display user fit-verified; pad/drill fit provisional"})
|
||||
for index,(net,function) in enumerate((("BTN_PREVIOUS","Previous / back"),("BTN_SELECT","Select / confirm"),("BTN_NEXT","Next")),1):
|
||||
y=88.9+(index-1)*35.56
|
||||
place("R",f"R{index}","2.2k 1%",327.66,y,{"1":"+3V3","2":net},{"Rating":"At least 0.125 W; exact resistor MPN pending"})
|
||||
place("SW_NO",f"SW{index}","B3F-1000",327.66,y+15.24,{"1":net,"2":"GND"},{"Manufacturer":"Omron","MPN":"B3F-1000","Function":function,"Procurement":"Live stock not verified"})
|
||||
place("R",f"R{index}","2k 1%",327.66,y,{"1":"+3V3","2":net},{"Rating":"Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"})
|
||||
place("SW_NO",f"SW{index}","B3F-1000",327.66,y+15.24,{"1":net,"2":"GND"},{"Manufacturer":"Omron","MPN":"B3F-1000","Function":function,"Procurement":"User reports purchased"})
|
||||
note(function,302.26,y+21.59,1.016)
|
||||
|
||||
note("MODULE CARRIER - INITIAL SCHEMATIC",15.24,15.24,2.54)
|
||||
@@ -153,14 +155,14 @@ note("Native KiCad 10 schematic. Named wire labels join matching nets on this sh
|
||||
note("U1: complete development board",53.34,57.15,1.778)
|
||||
note("Antenna up / USB down: left row 1-22, right row 23-44.\nNC marks mean unused on carrier, not unused inside module.",53.34,64.77,1.016)
|
||||
note("U2: complete RS-232 breakout",177.8,57.15,1.778)
|
||||
note("Male 6253 selected. Numbers from published 5988 female CAD.\nVERIFY male header before routing; no footprint assigned.\nDE-9 and charge pump are already on the module.",177.8,64.77,1.016)
|
||||
note("Male 6253 selected. Numbers from published 5988 female CAD.\nMale mapping user-reviewed; reference footprint assigned.\nDE-9 and charge pump are already on the module.",177.8,64.77,1.016)
|
||||
note("Controls: active-low, firmware-debounced",292.1,57.15,1.778)
|
||||
note("External 2.2k pull-ups: ~1.5 mA pressed at 3.3 V.\nChosen for B3F-1000 rated contact load; internal\npull-ups may remain enabled. Exact resistor MPN pending.",292.1,64.77,1.016)
|
||||
note("External 2k 1% pull-ups: ~1.65 mA at 3.3 V.\nChosen for B3F-1000 rated contact load; internal\npull-ups may remain enabled. Exact resistor MPN pending.",292.1,64.77,1.016)
|
||||
note("DS1: front view GND / VCC / SCL / SDA",177.8,128.27,1.524)
|
||||
note("Power at 3.3 V only. Verify on-module I2C pull-ups;\nexternal values intentionally not guessed.",177.8,165.1,1.016)
|
||||
note("POWER / RECOVERY\nPower only through U1's existing USB ports. No carrier 5 V input.\nU1 regulator supplies +3V3; GND pins are common on the module.\nPin 1 +3V3 and pin 22 GND model the onboard supply for ERC.\nGPIO19/20 native USB and GPIO43/44 UART0 remain onboard.\nVerify USB power isolation before attaching two powered hosts.\nKeep both USB connectors and reset/boot buttons accessible.",15.24,185.42)
|
||||
note("Power at 3.3 V only. Onboard I2C pull-ups user-confirmed;\nresistance and rail unmeasured; no external pull-ups.",177.8,165.1,1.016)
|
||||
note("POWER / RECOVERY\nPower only through U1's existing USB ports. No carrier 5 V input.\nU1 regulator supplies +3V3; GND pins are common on the module.\nPin 1 +3V3 and pin 22 GND model the onboard supply for ERC.\nGPIO19/20 native USB and GPIO43/44 UART0 remain onboard.\nUSB nonisolated accepted; backfeed safety not verified.\nKeep both USB connectors and reset/boot buttons accessible.",15.24,185.42)
|
||||
note("RS-232 / ELECTRICAL LIMITS\nTX/RX and modem labels are at the ESP32 logic side.\nOFF_N is active low; GPIO9 must remain open-drain.\nBreakout provides its OFF pull-up. No galvanic isolation.\nNo carrier connection to raw RS-232 voltages.\nCarrier GND, USB GND and RS-232 signal ground are common.\nNever power OLED or MAX3243 logic from 5 V.",152.4,185.42)
|
||||
note("BEFORE PCB RELEASE\nVerify male breakout pin order/footprint.\nConfirm header/socket finished-hole requirements.\nComplete module courtyards and antenna clearance.\nVerify 3.3 V budget, OLED pull-ups and USB power path.\nChoose mounting hardware and confirm button stock.\nERC is not hardware or fabrication approval.",292.1,185.42)
|
||||
note("USER ACCEPTANCE / EVIDENCE LIMITS\nModule mapping and visual fit accepted by user.\nB3F-1000 and 2k 1% metal-film resistors purchased.\nM3 carrier/module hardware; M2 display hardware.\nRF/power routing accepted, not qualified.\nRegulator margin and USB backfeed safety unverified.\nERC is not hardware or fabrication approval.",292.1,185.42)
|
||||
items.append(')')
|
||||
ROOT.joinpath(PROJECT+".kicad_sch").write_text('\n'.join(items)+'\n')
|
||||
ROOT.joinpath("validation/expected-nets.json").write_text(json.dumps(expected,indent=2,sort_keys=True)+'\n')
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/python3
|
||||
"""One-shot body-centred placement with incremental copper repair, not regeneration.
|
||||
|
||||
Writes a candidate only. Review its DRC before promoting it to the native board.
|
||||
Requires the retained pre-alignment snapshot and no editor locks.
|
||||
"""
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import pcbnew as p
|
||||
from shapely.geometry import Point, LineString, box
|
||||
import route_pcb_draft as r
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
BASE = ROOT/'validation/pcb-before-group-alignment.kicad_pcb'
|
||||
MOVES = {'DS1': ((143,136),(143.97,136)),
|
||||
**{f'R{i+1}': ((143+11*i,134),(142.16+11*i,132.6685)) for i in range(3)},
|
||||
**{f'SW{i+1}': ((143+12*i,168),(141.72+12*i,168)) for i in range(3)}}
|
||||
|
||||
|
||||
def unlocked():
|
||||
assert not list(ROOT.glob('*.lck')), 'KiCad editor lock exists'
|
||||
assert not list(ROOT.glob('.~lock.*')), 'Document editor lock exists'
|
||||
|
||||
|
||||
def geom(item):
|
||||
if isinstance(item,p.PAD):
|
||||
if item.GetShape()==p.PAD_SHAPE_CIRCLE:
|
||||
return Point(r.pos(item)).buffer(p.ToMM(item.GetSize().x)/2,quad_segs=32)
|
||||
bb=item.GetBoundingBox()
|
||||
return box(p.ToMM(bb.GetX()),p.ToMM(bb.GetY()),p.ToMM(bb.GetRight()),p.ToMM(bb.GetBottom()))
|
||||
if isinstance(item,p.PCB_VIA):
|
||||
return Point(r.pos(item)).buffer(.35,quad_segs=32)
|
||||
return LineString([xy(item.GetStart()),xy(item.GetEnd())]).buffer(p.ToMM(item.GetWidth())/2,quad_segs=32)
|
||||
|
||||
|
||||
def xy(v):
|
||||
return (p.ToMM(v.x),p.ToMM(v.y))
|
||||
|
||||
|
||||
def layers(item):
|
||||
return set(r.LAYERS) if isinstance(item,(p.PAD,p.PCB_VIA)) else {item.GetLayer()}
|
||||
|
||||
|
||||
def groups(board, net):
|
||||
items=[a for f in board.GetFootprints() for a in f.Pads() if a.GetNetCode()==net]
|
||||
items += [t for t in board.GetTracks() if t.GetNetCode()==net]
|
||||
shapes=[geom(i) for i in items]
|
||||
parents=list(range(len(items)))
|
||||
def root(i):
|
||||
while parents[i]!=i:
|
||||
i=parents[i]
|
||||
return i
|
||||
for i,a in enumerate(items):
|
||||
for j in range(i):
|
||||
if layers(a)&layers(items[j]) and shapes[i].distance(shapes[j])<1e-6:
|
||||
parents[root(i)]=root(j)
|
||||
result={}
|
||||
for i,a in enumerate(items):
|
||||
result.setdefault(root(i),[]).append(a)
|
||||
return list(result.values())
|
||||
|
||||
|
||||
def anchors(group):
|
||||
result=[]
|
||||
for a in group:
|
||||
points=[r.pos(a)] if isinstance(a,(p.PAD,p.PCB_VIA)) else [xy(a.GetStart()),xy(a.GetEnd())]
|
||||
result.extend((point,layers(a)) for point in points)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
unlocked()
|
||||
before=r.PCB.read_bytes()
|
||||
assert before==BASE.read_bytes(), 'Input differs from preserved baseline'
|
||||
protected={name:(ROOT/name).read_bytes() for name in ('serial-carrier.kicad_pro','serial-carrier.kicad_prl','serial-carrier.kicad_sch')}
|
||||
board=p.LoadBoard(str(r.PCB))
|
||||
fps={f.GetReference():f for f in board.GetFootprints()}
|
||||
for ref,(old,new) in MOVES.items():
|
||||
assert r.pos(fps[ref])==old
|
||||
fps[ref].SetPosition(r.point(new))
|
||||
if ref.startswith('R'):
|
||||
fps[ref].SetValue('2k 1%')
|
||||
pads=[a for f in board.GetFootprints() for a in f.Pads()]
|
||||
removed=[]
|
||||
# Only remove copper made illegal by a moved pad/hole. Everything else is
|
||||
# initially retained, including all unrelated nets and manual routing.
|
||||
moved_pads=[a for ref in MOVES for a in fps[ref].Pads()]
|
||||
for t in list(board.GetTracks()):
|
||||
if any((not a.GetNetCode() or a.GetNetCode()!=t.GetNetCode()) and
|
||||
geom(t).distance(geom(a))<.25-1e-6 for a in moved_pads):
|
||||
removed.append(t.m_Uuid.AsString())
|
||||
board.Delete(t)
|
||||
|
||||
repaired=[]
|
||||
deadline=time.monotonic()+600
|
||||
for ref,(old,new) in MOVES.items():
|
||||
for pad in fps[ref].Pads():
|
||||
if not pad.GetNumber(): continue
|
||||
target=r.pos(pad)
|
||||
start=(round(target[0]+old[0]-new[0],6),round(target[1]+old[1]-new[1],6))
|
||||
if geom(pad).covers(Point(start)): continue
|
||||
attached=[t for t in board.GetTracks() if t.GetNetCode()==pad.GetNetCode() and start in (xy(t.GetStart()),xy(t.GetEnd()))]
|
||||
if not attached: continue
|
||||
width=.5 if pad.GetNetname() in ('/+3V3','/GND') else .25
|
||||
blocked,via=r.masks(board,pads,pad.GetNetCode(),width)
|
||||
allowed=set.union(*(layers(t) for t in attached))
|
||||
for z,layer in enumerate(r.LAYERS):
|
||||
if layer not in allowed: blocked[r.node(start,z)]=True
|
||||
path,_=r.astar(blocked,via,start,target,deadline,500000)
|
||||
assert path, ('Cannot extend moved pad',ref,pad.GetNumber())
|
||||
r.add_path(board,path,start,target,pad.GetNetCode(),width)
|
||||
repaired.append(dict(net=pad.GetNetname(),start=start,end=target))
|
||||
nets=sorted({a.GetNetCode() for a in pads if a.GetNumber() and a.GetNetCode()}, key=lambda n: (board.FindNet(n).GetNetname() in ('/+3V3','/GND'), not board.FindNet(n).GetNetname().startswith('/OLED'), n))
|
||||
for net in nets:
|
||||
while len(parts:=groups(board,net))>1:
|
||||
options=[]
|
||||
for i,aa in enumerate(parts):
|
||||
for bb in parts[i+1:]:
|
||||
for a,la in anchors(aa):
|
||||
for b,lb in anchors(bb):
|
||||
options.append(((a[0]-b[0])**2+(a[1]-b[1])**2,a,la,b,lb))
|
||||
options.sort(key=lambda v:v[0])
|
||||
name=parts[0][0].GetNetname()
|
||||
width=.5 if name in ('/+3V3','/GND') else .25
|
||||
blocked,via=r.masks(board,pads,net,width)
|
||||
success=False
|
||||
for _,a,la,b,lb in options[:400]:
|
||||
mask=blocked.copy()
|
||||
for z,layer in enumerate(r.LAYERS):
|
||||
if layer not in la: mask[r.node(a,z)]=True
|
||||
if layer not in lb: mask[r.node(b,z)]=True
|
||||
path,_=r.astar(mask,via,a,b,deadline,250000)
|
||||
if path:
|
||||
r.add_path(board,path,a,b,net,width)
|
||||
repaired.append(dict(net=name,start=a,end=b))
|
||||
print(repaired[-1],flush=True)
|
||||
success=True
|
||||
break
|
||||
assert time.monotonic()<deadline, 'Bounded routing deadline reached'
|
||||
assert success, ('Cannot reconnect retained copper',name)
|
||||
unlocked()
|
||||
assert r.PCB.read_bytes()==before
|
||||
assert all((ROOT/n).read_bytes()==data for n,data in protected.items())
|
||||
out=ROOT/'validation/pcb-group-candidate.kicad_pcb'
|
||||
# Let KiCad identify true dangling stubs after repair. Do not prune valid
|
||||
# retained routes before reconnecting the moved pads.
|
||||
from add_device_silkscreen import add_outlines
|
||||
drc=ROOT/'validation/pcb-group-candidate-drc.json'
|
||||
for attempt in range(20):
|
||||
add_outlines(board)
|
||||
assert p.SaveBoard(str(out),board,True)
|
||||
subprocess.run(['kicad-cli','pcb','drc','--format','json','--all-track-errors','--severity-all','-o',str(drc),str(out)],check=True,timeout=60)
|
||||
results=json.loads(drc.read_text())
|
||||
ids={i['uuid'] for v in results['violations'] if v['type']=='track_dangling' for i in v['items']}
|
||||
if not ids:
|
||||
assert not results['unconnected_items'], results['unconnected_items']
|
||||
assert all(v['type']=='lib_footprint_issues' for v in results['violations']), results['violations']
|
||||
break
|
||||
for t in list(board.GetTracks()):
|
||||
if t.m_Uuid.AsString() in ids:
|
||||
removed.append(t.m_Uuid.AsString())
|
||||
board.Delete(t)
|
||||
else:
|
||||
raise AssertionError('Dangling-stub cleanup did not converge')
|
||||
report=dict(baseline_sha256=hashlib.sha256(before).hexdigest(),moves_absolute_mm=MOVES,
|
||||
removed_track_uuids=removed,repaired_connections=repaired,
|
||||
retained_tracks=sum(t.m_Uuid.AsString() not in removed for t in p.LoadBoard(str(BASE)).GetTracks()),
|
||||
protected_sha256={n:hashlib.sha256(data).hexdigest() for n,data in protected.items()})
|
||||
(ROOT/'validation/pcb-group-migration.json').write_text(json.dumps(report,indent=2)+'\n')
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
@@ -15,9 +15,10 @@ def remove_labels(board):
|
||||
title = board.GetTitleBlock()
|
||||
assert title.GetTitle() in (TITLE, TITLE + " draft")
|
||||
assert title.GetRevision() in ("A1", "A1 DRAFT")
|
||||
assert title.GetComment(0) in (COMMENT, "NOT FOR FABRICATION - " + COMMENT)
|
||||
assert title.GetComment(0) in ("", COMMENT, "NOT FOR FABRICATION - " + COMMENT)
|
||||
title.SetTitle(TITLE)
|
||||
title.SetRevision("A1")
|
||||
if title.GetComment(0):
|
||||
title.SetComment(0, COMMENT)
|
||||
for drawing in list(board.GetDrawings()):
|
||||
if isinstance(drawing, p.PCB_TEXT) and drawing.GetText() == SILK:
|
||||
|
||||
@@ -234,9 +234,9 @@
|
||||
(symbol (lib_id "Carrier:MAX3243_Module_Reference") (at 228.6 96.52 0) (unit 1) (in_bom yes) (on_board yes) (dnp no) (uuid "1127195e-e393-5d1d-9119-b25d10556144")
|
||||
(property "Reference" "U2" (at 228.6 73.66 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "Adafruit MAX3243 / male 6253" (at 228.6 76.2 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Footprint" "" (at 228.6 96.52 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Footprint" "Carrier:MAX3243_Reference_Provisional" (at 228.6 96.52 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 228.6 96.52 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Status" "VERIFY male header numbering; footprint intentionally unassigned" (at 228.6 96.52 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Status" "User confirmed male pinout/header/footprint and pin-by-pin connections" (at 228.6 96.52 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet_source" "adafruit-research.md; reference female 5988 CAD" (at 228.6 96.52 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "14d1ea7b-f53a-592f-9305-545890b94099"))
|
||||
(pin "2" (uuid "b2027da1-66f1-5c08-b29b-b63e30a160ff"))
|
||||
@@ -296,10 +296,10 @@
|
||||
(label "OLED_SDA" (at 200.66 156.21 0) (effects (font (size 1.016 1.016)) (justify left bottom)) (uuid "dc86b9ed-3904-50d0-8423-c02004518d88"))
|
||||
(symbol (lib_id "Carrier:R") (at 327.66 88.9 0) (unit 1) (in_bom yes) (on_board yes) (dnp no) (uuid "defb78eb-632f-5f9d-a59d-dffbb1d31dec")
|
||||
(property "Reference" "R1" (at 327.66 82.804 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "2.2k 1%" (at 327.66 85.344 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "2k 1%" (at 327.66 85.344 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Footprint" "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal" (at 327.66 88.9 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 327.66 88.9 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Rating" "At least 0.125 W; exact resistor MPN pending" (at 327.66 88.9 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Rating" "Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified" (at 327.66 88.9 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "79a88116-1a52-5fcb-855e-da54defa9400"))
|
||||
(pin "2" (uuid "b56a5773-68f5-5c6d-936a-8289c11714de"))
|
||||
(instances (project "serial-carrier" (path "/c86e0d56-4290-5f2b-b790-14926c8cdeea" (reference "R1") (unit 1)))))
|
||||
@@ -315,7 +315,7 @@
|
||||
(property "Manufacturer" "Omron" (at 327.66 104.14 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "MPN" "B3F-1000" (at 327.66 104.14 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Function" "Previous / back" (at 327.66 104.14 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Procurement" "Live stock not verified" (at 327.66 104.14 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Procurement" "User reports purchased" (at 327.66 104.14 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "0e490c78-3450-5a5b-b7d3-d55c32de988b"))
|
||||
(pin "2" (uuid "6bbc82d1-8cfb-5fd5-832c-8df2775a89c9"))
|
||||
(instances (project "serial-carrier" (path "/c86e0d56-4290-5f2b-b790-14926c8cdeea" (reference "SW1") (unit 1)))))
|
||||
@@ -326,10 +326,10 @@
|
||||
(text "Previous / back" (at 302.26 110.49 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "d84df288-be52-5de4-899d-bf29ba78f4f2"))
|
||||
(symbol (lib_id "Carrier:R") (at 327.66 124.46 0) (unit 1) (in_bom yes) (on_board yes) (dnp no) (uuid "405055c0-35ab-5334-ad4d-30d45c5ef6f2")
|
||||
(property "Reference" "R2" (at 327.66 118.364 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "2.2k 1%" (at 327.66 120.904 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "2k 1%" (at 327.66 120.904 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Footprint" "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal" (at 327.66 124.46 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 327.66 124.46 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Rating" "At least 0.125 W; exact resistor MPN pending" (at 327.66 124.46 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Rating" "Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified" (at 327.66 124.46 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "c23dbef2-d61d-56fa-a158-bb9912ef48ac"))
|
||||
(pin "2" (uuid "f60eb205-60d1-5d8f-ac84-ca04f66e7106"))
|
||||
(instances (project "serial-carrier" (path "/c86e0d56-4290-5f2b-b790-14926c8cdeea" (reference "R2") (unit 1)))))
|
||||
@@ -345,7 +345,7 @@
|
||||
(property "Manufacturer" "Omron" (at 327.66 139.7 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "MPN" "B3F-1000" (at 327.66 139.7 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Function" "Select / confirm" (at 327.66 139.7 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Procurement" "Live stock not verified" (at 327.66 139.7 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Procurement" "User reports purchased" (at 327.66 139.7 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "19bafbc6-666f-5e53-a4ef-265057c6dc8a"))
|
||||
(pin "2" (uuid "9e3f47ba-92ac-5503-9d87-6b5590a70ccb"))
|
||||
(instances (project "serial-carrier" (path "/c86e0d56-4290-5f2b-b790-14926c8cdeea" (reference "SW2") (unit 1)))))
|
||||
@@ -356,10 +356,10 @@
|
||||
(text "Select / confirm" (at 302.26 146.05 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "c28f2639-f392-552b-858e-b031aabc8c0a"))
|
||||
(symbol (lib_id "Carrier:R") (at 327.66 160.02 0) (unit 1) (in_bom yes) (on_board yes) (dnp no) (uuid "78f9a74f-5d91-5687-b50e-68176333df23")
|
||||
(property "Reference" "R3" (at 327.66 153.924 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "2.2k 1%" (at 327.66 156.464 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Value" "2k 1%" (at 327.66 156.464 0) (effects (font (size 1.27 1.27)) ))
|
||||
(property "Footprint" "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal" (at 327.66 160.02 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Datasheet" "" (at 327.66 160.02 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Rating" "At least 0.125 W; exact resistor MPN pending" (at 327.66 160.02 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Rating" "Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified" (at 327.66 160.02 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "d871ee2e-bc82-5f23-a436-7b7ab74b73a9"))
|
||||
(pin "2" (uuid "96528b84-ba49-5f38-8445-b6ca2cadc93b"))
|
||||
(instances (project "serial-carrier" (path "/c86e0d56-4290-5f2b-b790-14926c8cdeea" (reference "R3") (unit 1)))))
|
||||
@@ -375,7 +375,7 @@
|
||||
(property "Manufacturer" "Omron" (at 327.66 175.26 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "MPN" "B3F-1000" (at 327.66 175.26 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Function" "Next" (at 327.66 175.26 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Procurement" "Live stock not verified" (at 327.66 175.26 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(property "Procurement" "User reports purchased" (at 327.66 175.26 0) (effects (font (size 1.27 1.27)) (hide yes)))
|
||||
(pin "1" (uuid "85beb3b7-e16f-5450-ba94-5711245a7b25"))
|
||||
(pin "2" (uuid "300c4196-eb91-5e58-a9aa-c84b8f7c4219"))
|
||||
(instances (project "serial-carrier" (path "/c86e0d56-4290-5f2b-b790-14926c8cdeea" (reference "SW3") (unit 1)))))
|
||||
@@ -389,12 +389,12 @@
|
||||
(text "U1: complete development board" (at 53.34 57.15 0) (effects (font (size 1.778 1.778)) (justify left top)) (uuid "320a8f4e-ff8b-5100-845b-dfe249b39a2e"))
|
||||
(text "Antenna up / USB down: left row 1-22, right row 23-44.\nNC marks mean unused on carrier, not unused inside module." (at 53.34 64.77 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "649df1fa-48c0-580d-aeb8-d019ffd0b61d"))
|
||||
(text "U2: complete RS-232 breakout" (at 177.8 57.15 0) (effects (font (size 1.778 1.778)) (justify left top)) (uuid "5226007c-1904-547d-8ddb-008d14b83d51"))
|
||||
(text "Male 6253 selected. Numbers from published 5988 female CAD.\nVERIFY male header before routing; no footprint assigned.\nDE-9 and charge pump are already on the module." (at 177.8 64.77 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "9b62e7ca-695a-51af-bbbe-e7ee4ee45c08"))
|
||||
(text "Male 6253 selected. Numbers from published 5988 female CAD.\nMale mapping user-reviewed; reference footprint assigned.\nDE-9 and charge pump are already on the module." (at 177.8 64.77 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "9b62e7ca-695a-51af-bbbe-e7ee4ee45c08"))
|
||||
(text "Controls: active-low, firmware-debounced" (at 292.1 57.15 0) (effects (font (size 1.778 1.778)) (justify left top)) (uuid "9d91985c-7375-5177-9a5e-8280e3bcaff6"))
|
||||
(text "External 2.2k pull-ups: ~1.5 mA pressed at 3.3 V.\nChosen for B3F-1000 rated contact load; internal\npull-ups may remain enabled. Exact resistor MPN pending." (at 292.1 64.77 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "b2dd72f5-86fc-5ce1-b09a-67a6fe9404d4"))
|
||||
(text "External 2k 1% pull-ups: ~1.65 mA at 3.3 V.\nChosen for B3F-1000 rated contact load; internal\npull-ups may remain enabled. Exact resistor MPN pending." (at 292.1 64.77 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "b2dd72f5-86fc-5ce1-b09a-67a6fe9404d4"))
|
||||
(text "DS1: front view GND / VCC / SCL / SDA" (at 177.8 128.27 0) (effects (font (size 1.524 1.524)) (justify left top)) (uuid "7b57748d-38ca-5712-97f9-bf6dab6dafaf"))
|
||||
(text "Power at 3.3 V only. Verify on-module I2C pull-ups;\nexternal values intentionally not guessed." (at 177.8 165.1 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "fb23ec11-40f8-56c7-b78b-0da2d40e4b72"))
|
||||
(text "POWER / RECOVERY\nPower only through U1's existing USB ports. No carrier 5 V input.\nU1 regulator supplies +3V3; GND pins are common on the module.\nPin 1 +3V3 and pin 22 GND model the onboard supply for ERC.\nGPIO19/20 native USB and GPIO43/44 UART0 remain onboard.\nVerify USB power isolation before attaching two powered hosts.\nKeep both USB connectors and reset/boot buttons accessible." (at 15.24 185.42 0) (effects (font (size 1.27 1.27)) (justify left top)) (uuid "cadf6b77-e888-5f04-b4f6-3873114844ca"))
|
||||
(text "Power at 3.3 V only. Onboard I2C pull-ups user-confirmed;\nresistance and rail unmeasured; no external pull-ups." (at 177.8 165.1 0) (effects (font (size 1.016 1.016)) (justify left top)) (uuid "fb23ec11-40f8-56c7-b78b-0da2d40e4b72"))
|
||||
(text "POWER / RECOVERY\nPower only through U1's existing USB ports. No carrier 5 V input.\nU1 regulator supplies +3V3; GND pins are common on the module.\nPin 1 +3V3 and pin 22 GND model the onboard supply for ERC.\nGPIO19/20 native USB and GPIO43/44 UART0 remain onboard.\nUSB nonisolated accepted; backfeed safety not verified.\nKeep both USB connectors and reset/boot buttons accessible." (at 15.24 185.42 0) (effects (font (size 1.27 1.27)) (justify left top)) (uuid "cadf6b77-e888-5f04-b4f6-3873114844ca"))
|
||||
(text "RS-232 / ELECTRICAL LIMITS\nTX/RX and modem labels are at the ESP32 logic side.\nOFF_N is active low; GPIO9 must remain open-drain.\nBreakout provides its OFF pull-up. No galvanic isolation.\nNo carrier connection to raw RS-232 voltages.\nCarrier GND, USB GND and RS-232 signal ground are common.\nNever power OLED or MAX3243 logic from 5 V." (at 152.4 185.42 0) (effects (font (size 1.27 1.27)) (justify left top)) (uuid "0f7da388-3edb-5cc3-9937-13492397a221"))
|
||||
(text "BEFORE PCB RELEASE\nVerify male breakout pin order/footprint.\nConfirm header/socket finished-hole requirements.\nComplete module courtyards and antenna clearance.\nVerify 3.3 V budget, OLED pull-ups and USB power path.\nChoose mounting hardware and confirm button stock.\nERC is not hardware or fabrication approval." (at 292.1 185.42 0) (effects (font (size 1.27 1.27)) (justify left top)) (uuid "1df2bd9e-fbff-5047-92a9-09f33d7cd7ac"))
|
||||
(text "USER ACCEPTANCE / EVIDENCE LIMITS\nModule mapping and visual fit accepted by user.\nB3F-1000 and 2k 1% metal-film resistors purchased.\nM3 carrier/module hardware; M2 display hardware.\nRF/power routing accepted, not qualified.\nRegulator margin and USB backfeed safety unverified.\nERC is not hardware or fabrication approval." (at 292.1 185.42 0) (effects (font (size 1.27 1.27)) (justify left top)) (uuid "1df2bd9e-fbff-5047-92a9-09f33d7cd7ac"))
|
||||
)
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Guarded accepted-fit label cleanup and A3 worksheet; never move board items."""
|
||||
import copy
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import pcbnew as p
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
PCB = ROOT / "serial-carrier.kicad_pcb"
|
||||
COMMENT = "USB / antenna geometry pending; verify module pinout / assembly"
|
||||
USB = "USB ACCESS - VERIFY"
|
||||
OLD_CAPTION = "Nominal RS-232 metal verified; ESP32 USB / antenna / assembly clearance pending"
|
||||
CAPTION = "Nominal RS-232 metal verified; RF qualification not established"
|
||||
|
||||
|
||||
def expected_tree(tree):
|
||||
result = copy.deepcopy(tree)
|
||||
for item in list(result):
|
||||
if not isinstance(item, list):
|
||||
continue
|
||||
if item[0] == "paper":
|
||||
assert item in (["paper", '"A4"'], ["paper", '"A3"'])
|
||||
item[1] = '"A3"'
|
||||
elif item[0] == "title_block":
|
||||
for field in list(item[1:]):
|
||||
if field == ["comment", "1", '"' + COMMENT + '"']:
|
||||
item.remove(field)
|
||||
elif item[0] == "gr_text":
|
||||
if item[1] == '"' + USB + '"':
|
||||
assert ['layer', '"Dwgs.User"'] in item
|
||||
result.remove(item)
|
||||
elif item[1] == '"' + OLD_CAPTION + '"':
|
||||
assert ['layer', '"Dwgs.User"'] in item
|
||||
item[1] = '"' + CAPTION + '"'
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
from validate_u1_update import parse, canonical
|
||||
lock = ROOT / "~serial-carrier.kicad_pcb.lck"
|
||||
if lock.exists():
|
||||
raise RuntimeError("Close PCB Editor before changing presentation")
|
||||
original = PCB.read_bytes()
|
||||
protected = {path: path.read_bytes() for path in ROOT.glob("serial-carrier.kicad_pr*")}
|
||||
expected = expected_tree(parse(PCB))
|
||||
with tempfile.TemporaryDirectory(prefix="carrier-presentation-", dir=ROOT) as temporary:
|
||||
candidate = Path(temporary) / PCB.name
|
||||
# PAGE_INFO is opaque in this pcbnew binding. Load the exact paper-only
|
||||
# substitution, then use the native saver for all label changes.
|
||||
text = original.decode()
|
||||
assert text.count('(paper "A4")') + text.count('(paper "A3")') == 1
|
||||
candidate.write_text(text.replace('(paper "A4")', '(paper "A3")'))
|
||||
board = p.LoadBoard(str(candidate))
|
||||
title = board.GetTitleBlock()
|
||||
assert title.GetComment(0) in (COMMENT, "")
|
||||
title.SetComment(0, "")
|
||||
for drawing in list(board.GetDrawings()):
|
||||
if isinstance(drawing, p.PCB_TEXT):
|
||||
if drawing.GetText() == USB:
|
||||
assert drawing.GetLayer() == p.Dwgs_User
|
||||
board.Remove(drawing)
|
||||
elif drawing.GetText() == OLD_CAPTION:
|
||||
drawing.SetText(CAPTION)
|
||||
assert p.SaveBoard(str(candidate), board, True)
|
||||
assert canonical(parse(candidate)) == canonical(expected), "Unexpected physical/content change"
|
||||
assert canonical(expected_tree(parse(candidate))) == canonical(expected), "Not idempotent"
|
||||
if lock.exists() or PCB.read_bytes() != original:
|
||||
raise RuntimeError("PCB changed or reopened; refusing overwrite")
|
||||
assert all(path.read_bytes() == data for path, data in protected.items()), "Settings changed"
|
||||
candidate.replace(PCB)
|
||||
print("A3 worksheet / accepted-fit labels updated; exact preservation and idempotence PASS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python3
|
||||
"""Exact authorized placement/value delta, retained copper and body geometry audit."""
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import uuid
|
||||
import pcbnew as p
|
||||
from validate_u1_update import parse, canonical, ident
|
||||
from add_device_silkscreen import NS
|
||||
from refine_group_placement import ROOT, BASE, MOVES
|
||||
|
||||
METADATA = {
|
||||
**{f'R{i}': ('Rating', 'Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified') for i in range(1,4)},
|
||||
**{f'SW{i}': ('Procurement', 'User reports purchased') for i in range(1,4)},
|
||||
'U2': ('Status', 'User confirmed male pinout/header/footprint and pin-by-pin connections'),
|
||||
}
|
||||
|
||||
|
||||
def expected_identity(board):
|
||||
"""Apply only the seven authorized placements and three resistor values."""
|
||||
fps={f.GetReference():f for f in board.GetFootprints()}
|
||||
for ref,(old,new) in MOVES.items():
|
||||
fps[ref].SetPosition(p.VECTOR2I(p.FromMM(new[0]),p.FromMM(new[1])))
|
||||
if ref.startswith('R'): fps[ref].SetValue('2k 1%')
|
||||
return board
|
||||
|
||||
|
||||
def audit():
|
||||
assert hashlib.sha256(BASE.read_bytes()).hexdigest()=='b9e020939fff42b5c7a3161ed89af9dc3c1b9c016268311b3db7ab407ed67192'
|
||||
old=parse(BASE)
|
||||
new=parse(ROOT/'serial-carrier.kicad_pcb')
|
||||
from update_presentation import expected_tree
|
||||
wanted=expected_tree(old)
|
||||
for f in wanted:
|
||||
if not isinstance(f,list) or f[0]!='footprint': continue
|
||||
ref=next(v[2].strip('"') for v in f if isinstance(v,list) and v[:2]==['property','"Reference"'])
|
||||
if ref in METADATA:
|
||||
key,value=METADATA[ref]
|
||||
next(v for v in f if isinstance(v,list) and v[:2]==['property',json.dumps(key)])[2]=json.dumps(value)
|
||||
if ref in MOVES:
|
||||
at=next(v for v in f if isinstance(v,list) and v[0]=='at')
|
||||
at[1:3]=[format(x,'.10g') for x in MOVES[ref][1]]
|
||||
if ref.startswith('R'):
|
||||
next(v for v in f if isinstance(v,list) and v[:2]==['property','"Value"'])[2]='"2k 1%"'
|
||||
silk={str(uuid.uuid5(NS,str(i))) for i in range(4096)}
|
||||
def without_copper(tree):
|
||||
return [v for v in tree if not isinstance(v,list) or v[0] not in ('segment','via')]
|
||||
assert canonical(without_copper(wanted),silk)==canonical(without_copper(new),silk), 'Unexpected non-copper edit'
|
||||
before={ident(v):v for v in old if isinstance(v,list) and v[0] in ('segment','via')}
|
||||
after={ident(v):v for v in new if isinstance(v,list) and v[0] in ('segment','via')}
|
||||
migration=json.loads((ROOT/'validation/pcb-group-migration.json').read_text())
|
||||
removed=set(before)-set(after)
|
||||
assert removed==set(before)&set(migration['removed_track_uuids'])
|
||||
assert all(before[k]==after[k] for k in before.keys()&after.keys()), 'Retained copper changed'
|
||||
assert len(before.keys()&after.keys())==476
|
||||
for name in ('serial-carrier.kicad_pro','serial-carrier.kicad_prl'):
|
||||
assert hashlib.sha256((ROOT/name).read_bytes()).hexdigest()==migration['protected_sha256'][name]
|
||||
board=p.LoadBoard(str(ROOT/'serial-carrier.kicad_pcb'))
|
||||
fps={f.GetReference():f for f in board.GetFootprints()}
|
||||
def xy(v): return p.ToMM(v.x),p.ToMM(v.y)
|
||||
# Published U2 F.Fab outline includes the bare PCB beyond the header row.
|
||||
u2pts=[xy(v) for g in fps['U2'].GraphicalItems() if isinstance(g,p.PCB_SHAPE) and g.GetLayer()==p.F_Fab for v in (g.GetStart(),g.GetEnd())]
|
||||
axis=(min(x for x,y in u2pts)+max(x for x,y in u2pts))/2
|
||||
bottom=max(y for x,y in u2pts)
|
||||
oled=next(g for g in fps['DS1'].GraphicalItems() if isinstance(g,p.PCB_SHAPE) and g.GetLayer()==p.F_Fab and g.GetStart()==fps['DS1'].GetPosition())
|
||||
assert abs((xy(oled.GetStart())[0]+xy(oled.GetEnd())[0])/2-axis)<1e-6
|
||||
resistor_boxes=[g for i in range(1,4) for g in fps[f'R{i}'].GraphicalItems() if isinstance(g,p.PCB_SHAPE) and g.GetLayer()==p.F_Fab and g.GetShape()==p.SHAPE_T_RECT]
|
||||
assert len(resistor_boxes)==3
|
||||
left=min(xy(g.GetStart())[0] for g in resistor_boxes)
|
||||
right=max(xy(g.GetEnd())[0] for g in resistor_boxes)
|
||||
assert abs((left+right)/2-axis)<1e-6
|
||||
topgap=min(xy(g.GetStart())[1] for g in resistor_boxes)-bottom
|
||||
bottomgap=xy(oled.GetStart())[1]-max(xy(g.GetEnd())[1] for g in resistor_boxes)
|
||||
assert abs(topgap-2.0815)<1e-6 and abs(bottomgap-topgap)<1e-6
|
||||
switchpts=[xy(v) for i in range(1,4) for g in fps[f'SW{i}'].GraphicalItems() if isinstance(g,p.PCB_SHAPE) and g.GetLayer()==p.F_Fab for v in (g.GetStart(),g.GetEnd())]
|
||||
assert abs((min(x for x,y in switchpts)+max(x for x,y in switchpts))/2-axis)<1e-6
|
||||
switchgap=min(y for x,y in switchpts)-xy(oled.GetEnd())[1]
|
||||
assert abs(switchgap-5.25)<1e-6
|
||||
result=dict(common_axis_absolute_x_mm=axis,resistor_to_rs232_body_gap_mm=round(topgap,6),resistor_to_oled_body_gap_mm=round(bottomgap,6),oled_to_button_body_gap_mm=switchgap,
|
||||
original_copper_items=len(before),retained_copper_items=476,removed_original_copper_items=len(removed),new_copper_items=len(after.keys()-before.keys()),
|
||||
exact_unrelated_content_preserved=True,project_settings_preserved=True,baseline_sha256=migration['baseline_sha256'],board_sha256=hashlib.sha256((ROOT/'serial-carrier.kicad_pcb').read_bytes()).hexdigest())
|
||||
(ROOT/'validation/pcb-group-alignment.json').write_text(json.dumps(result,indent=2)+'\n')
|
||||
return result
|
||||
|
||||
if __name__=='__main__': print(json.dumps(audit(),indent=2))
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read-only PCB checks and fresh KiCad reports; does not regenerate or save PCB.
|
||||
|
||||
Requires KiCad's pcbnew binding in system Python. The known U2 footprint parity
|
||||
warning is reported explicitly, not hidden or added to KiCad's exclusions.
|
||||
Requires KiCad's pcbnew binding in system Python. Schematic footprint parity
|
||||
must be clean, including the user-confirmed U2 reference footprint assignment.
|
||||
"""
|
||||
import hashlib
|
||||
import json
|
||||
@@ -23,6 +23,13 @@ run("kicad-cli","sch","export","netlist","--format","kicadxml","-o",str(OUT/"pcb
|
||||
run("kicad-cli","pcb","drc","--format","json","--all-track-errors","--severity-all","--schematic-parity","-o",str(OUT/"pcb-parity-drc.json"),str(PCB))
|
||||
board=p.LoadBoard(str(PCB))
|
||||
verify(board)
|
||||
from update_presentation import expected_tree, CAPTION
|
||||
from validate_u1_update import parse, canonical
|
||||
tree=parse(PCB)
|
||||
assert ['paper', '"A3"'] in tree, "A3 worksheet required to clear the title block"
|
||||
assert canonical(expected_tree(tree)) == canonical(tree), "Stale presentation labels"
|
||||
assert board.GetTitleBlock().GetComment(0) == ""
|
||||
assert any(isinstance(g,p.PCB_TEXT) and g.GetText()==CAPTION for g in board.GetDrawings())
|
||||
source=ET.parse(OUT/"pcb-source-netlist.xml").getroot()
|
||||
comps={c.get("ref"):c for c in source.findall("components/comp")}
|
||||
footprints={f.GetReference():f for f in board.GetFootprints()}
|
||||
@@ -38,9 +45,9 @@ expected_edges={frozenset((a,b)) for a,b in zip(expected_outline,expected_outlin
|
||||
actual_edges={frozenset((round(p.ToMM(v.x)-100,5),round(p.ToMM(v.y)-100,5))
|
||||
for v in (d.GetStart(),d.GetEnd())) for d in outline}
|
||||
assert actual_edges==expected_edges
|
||||
placements={"U1":(8,23),"U2":(43,26.670),"DS1":(43,36),
|
||||
"R1":(43,34),"R2":(54,34),"R3":(65,34),
|
||||
"SW1":(43,68),"SW2":(55,68),"SW3":(67,68),
|
||||
placements={"U1":(8,23),"U2":(43,26.670),"DS1":(43.97,36),
|
||||
"R1":(42.16,32.6685),"R2":(53.16,32.6685),"R3":(64.16,32.6685),
|
||||
"SW1":(41.72,68),"SW2":(53.72,68),"SW3":(65.72,68),
|
||||
"H1":(3.5,22.5),"H2":(3.5,75),"H3":(78,4),"H4":(78,76)}
|
||||
for ref,(x,y) in placements.items():
|
||||
position=footprints[ref].GetPosition()
|
||||
@@ -72,7 +79,7 @@ for ref,comp in comps.items():
|
||||
assert fp.GetPath().AsString().endswith("/"+comp.findtext("tstamps")),ref
|
||||
wanted=comp.findtext("footprint")
|
||||
if ref=="U2":
|
||||
assert not wanted
|
||||
assert wanted=="Carrier:MAX3243_Reference_Provisional"
|
||||
assert fp.GetFPIDAsString()=="Carrier:MAX3243_Reference_Provisional"
|
||||
else:
|
||||
assert fp.GetFPIDAsString()==wanted,ref
|
||||
@@ -102,8 +109,7 @@ report=json.loads((OUT/"pcb-parity-drc.json").read_text())
|
||||
assert not report["violations"],report["violations"]
|
||||
assert not report["unconnected_items"],report["unconnected_items"]
|
||||
parity=report["schematic_parity"]
|
||||
assert len(parity)==1 and parity[0]["type"]=="footprint_symbol_mismatch",parity
|
||||
assert len(parity[0]["items"])==1 and parity[0]["items"][0]["uuid"]==footprints["U2"].m_Uuid.AsString()
|
||||
assert not parity,parity
|
||||
summary={
|
||||
"carrier_bounds_mm":[82,80],
|
||||
"outline_local_mm":expected_outline,
|
||||
@@ -120,12 +126,14 @@ summary={
|
||||
"physical_DRC_violations":0,
|
||||
"unconnected_items":0,
|
||||
"schematic_pad_nets_match":True,
|
||||
"known_parity_warning":"U2 reference footprint assigned on PCB only; schematic footprint intentionally blank; nominal male external metal dimensions user-confirmed",
|
||||
"schematic_parity_violations":0,
|
||||
"project_settings_sha256":hashlib.sha256((ROOT/'serial-carrier.kicad_pro').read_bytes()).hexdigest(),
|
||||
"ignored_DRC_checks":report["ignored_checks"],
|
||||
"not_validated":["male module pin map/header fit", "ESP32 measured-planar 1:1 fit and complete assembly envelope/heights", "header/socket fit", "RF performance and antenna clearance", "power/return-path integrity", "USB power path", "OLED pull-ups", "fabrication process", "hardware operation"],
|
||||
"user_accepted":["module dimensions/visual fit and USB clearance", "male pinout/header/footprint and pin-by-pin review", "M3 carrier/module and M2 display hardware", "RF keepout and power/ground routing"],
|
||||
"not_independently_verified":["reported 10 mm assembled module elevation", "regulator margin", "USB backfeed safety", "OLED onboard pull-up resistance/rail", "manufacturer RF qualification", "fabrication process/certification"],
|
||||
"new_hardware_tests":False,
|
||||
}
|
||||
(OUT/"pcb-validation-summary.json").write_text(json.dumps(summary,indent=2)+"\n")
|
||||
print("PCB draft verified: 82 x 80 mm; 2 copper layers; 0 physical DRC violations; 0 unrouted.")
|
||||
print("All schematic pad nets match. One expected U2 footprint parity warning remains visible.")
|
||||
print("All schematic pad nets and footprint assignments match. Zero schematic parity violations.")
|
||||
print("Not fabrication-ready; see ../../docs/devel/hardware/PCB/pcb-draft-notes.md and ignored checks in the DRC report.")
|
||||
|
||||
@@ -57,7 +57,7 @@ for i, net in enumerate(("BTN_PREVIOUS", "BTN_SELECT", "BTN_NEXT"), 1):
|
||||
assert len(nets[net]) == 3
|
||||
assert (f"SW{i}", "1") in members[net] and (f"R{i}", "2") in members[net]
|
||||
assert components[f"SW{i}"].findtext("value") == "B3F-1000"
|
||||
assert components[f"R{i}"].findtext("value") == "2.2k 1%"
|
||||
assert components[f"R{i}"].findtext("value") == "2k 1%"
|
||||
assert members["+3V3"] == sorted([("U1","1"),("U1","2"),("U2","1"),("DS1","2"),("R1","1"),("R2","1"),("R3","1")])
|
||||
assert members["GND"] == sorted([("U1","22"),("U1","23"),("U1","43"),("U1","44"),("U2","2"),("DS1","1"),("SW1","2"),("SW2","2"),("SW3","2")])
|
||||
active = {name for name in nets if not name.startswith("unconnected-")}
|
||||
@@ -70,8 +70,8 @@ assert sorted(int(node.get("pin")) for nodes in nets.values() for node in nodes
|
||||
# USB/UART0, 5 V and PSRAM-conflicting pins must never be carrier nets.
|
||||
for name in active:
|
||||
assert not any(node.get("ref") == "U1" and node.get("pin") in ("21","24","25","33","34","35","41","42") for node in nets[name])
|
||||
# U2's physical male footprint must stay explicitly unresolved in this draft.
|
||||
assert not components["U2"].findtext("footprint")
|
||||
# User confirmed the male module shares the reference pinout/header/footprint.
|
||||
assert components["U2"].findtext("footprint") == "Carrier:MAX3243_Reference_Provisional"
|
||||
expected = json.loads((OUT / "expected-nets.json").read_text())
|
||||
assert set(expected) == active
|
||||
for net, nodes in expected.items():
|
||||
@@ -87,8 +87,9 @@ summary = "\n".join([
|
||||
"15 firmware GPIO definitions independently checked against exported pin functions.",
|
||||
"All 44 MCU header pins accounted for; USB/UART0/5V/PSRAM pins unused on carrier.",
|
||||
"PDF and SVG exported successfully.",
|
||||
"Not validated: male-module physical mapping, power budget/path, OLED pull-ups,",
|
||||
"selected connector fit, PCB DRC/layout, procurement, or assembled hardware.",
|
||||
"User accepted module mapping/fit and reports purchased switches/resistors.",
|
||||
"No new hardware tests; regulator margin, USB backfeed safety, RF performance",
|
||||
"and fabrication qualification are not established by this schematic check.",
|
||||
]) + "\n"
|
||||
(OUT / "summary.txt").write_text(summary)
|
||||
print(summary)
|
||||
|
||||
@@ -59,7 +59,11 @@ def main():
|
||||
u1_ids = {str(uuid.uuid5(U1_NS,str(i))) for i in range(6)}
|
||||
# The original baseline retains historical draft labels. Permit only the
|
||||
# exact requested title/revision/comment edits and removal of the silk label.
|
||||
assert canonical(expected_tree(parse(BASE)), silk_ids) == canonical(parse(PCB), silk_ids | u1_ids), "Change outside owned U1 additions / device silk / exact presentation labels"
|
||||
# Keep proving the original integration against its original evidence, then
|
||||
# audit the explicitly authorized subsequent placement/routing delta.
|
||||
from validate_group_alignment import audit, BASE as GROUP_BASE
|
||||
assert canonical(expected_tree(parse(BASE)), silk_ids) == canonical(parse(GROUP_BASE), silk_ids | u1_ids), "Historical U1 integration changed"
|
||||
audit()
|
||||
generated = [g for g in board.GetDrawings() if g.m_Uuid.AsString() in silk_ids]
|
||||
assert not any(isinstance(g,p.PCB_TEXT) and g.GetText()=="HEADER GUIDES" for g in board.GetDrawings())
|
||||
carrier = Polygon([(136,100),(182,100),(182,180),(100,180),(100,119),(136,119)])
|
||||
@@ -92,7 +96,7 @@ def main():
|
||||
assert PCB.read_bytes() == before
|
||||
assert not (ROOT/"~serial-carrier.kicad_pcb.lck").exists()
|
||||
summary = dict(measured_library_matches_embedded_u1=True,
|
||||
existing_uuids_pads_nets_placement_routing_settings_and_unrelated_graphics_preserved=True,
|
||||
historical_u1_integration_and_subsequent_authorized_group_delta_verified=True,
|
||||
presentation_label_cleanup=True,
|
||||
silk_refresh_idempotent=True, stale_geometry_negative_control=True,
|
||||
silk_clipped_to_carrier=True, usb_mouth_markers=2,
|
||||
@@ -100,7 +104,7 @@ def main():
|
||||
baseline_sha256=hashlib.sha256(BASE.read_bytes()).hexdigest(),
|
||||
board_sha256=hashlib.sha256(before).hexdigest(),
|
||||
project_sha256=hashlib.sha256((ROOT/"serial-carrier.kicad_pro").read_bytes()).hexdigest())
|
||||
(ROOT/"validation/pcb-u1-update.json").write_text(json.dumps(summary,indent=2)+"\n")
|
||||
(ROOT/"validation/pcb-u1-group-regression.json").write_text(json.dumps(summary,indent=2)+"\n")
|
||||
print(json.dumps(summary,indent=2))
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schemas.kicad.org/erc.v1.json",
|
||||
"coordinate_units": "mm",
|
||||
"date": "2026-09-20T20:14:47",
|
||||
"date": "2026-09-22T09:55:18",
|
||||
"ignored_checks": [
|
||||
{
|
||||
"description": "Global label only appears once in the schematic",
|
||||
|
||||
|
Before Width: | Height: | Size: 231 KiB After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 347 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"common_axis_absolute_x_mm": 156.97,
|
||||
"resistor_to_rs232_body_gap_mm": 2.0815,
|
||||
"resistor_to_oled_body_gap_mm": 2.0815,
|
||||
"oled_to_button_body_gap_mm": 5.25,
|
||||
"original_copper_items": 587,
|
||||
"retained_copper_items": 476,
|
||||
"removed_original_copper_items": 111,
|
||||
"new_copper_items": 503,
|
||||
"exact_unrelated_content_preserved": true,
|
||||
"project_settings_preserved": true,
|
||||
"baseline_sha256": "b9e020939fff42b5c7a3161ed89af9dc3c1b9c016268311b3db7ab407ed67192",
|
||||
"board_sha256": "fa95d5747c6e455b2c5effd11d11091b1f95df015dbe73c99b14092487a034c5"
|
||||
}
|
||||
@@ -0,0 +1,639 @@
|
||||
{
|
||||
"baseline_sha256": "b9e020939fff42b5c7a3161ed89af9dc3c1b9c016268311b3db7ab407ed67192",
|
||||
"moves_absolute_mm": {
|
||||
"DS1": [
|
||||
[
|
||||
143,
|
||||
136
|
||||
],
|
||||
[
|
||||
143.97,
|
||||
136
|
||||
]
|
||||
],
|
||||
"R1": [
|
||||
[
|
||||
143,
|
||||
134
|
||||
],
|
||||
[
|
||||
142.16,
|
||||
132.6685
|
||||
]
|
||||
],
|
||||
"R2": [
|
||||
[
|
||||
154,
|
||||
134
|
||||
],
|
||||
[
|
||||
153.16,
|
||||
132.6685
|
||||
]
|
||||
],
|
||||
"R3": [
|
||||
[
|
||||
165,
|
||||
134
|
||||
],
|
||||
[
|
||||
164.16,
|
||||
132.6685
|
||||
]
|
||||
],
|
||||
"SW1": [
|
||||
[
|
||||
143,
|
||||
168
|
||||
],
|
||||
[
|
||||
141.72,
|
||||
168
|
||||
]
|
||||
],
|
||||
"SW2": [
|
||||
[
|
||||
155,
|
||||
168
|
||||
],
|
||||
[
|
||||
153.72,
|
||||
168
|
||||
]
|
||||
],
|
||||
"SW3": [
|
||||
[
|
||||
167,
|
||||
168
|
||||
],
|
||||
[
|
||||
165.72,
|
||||
168
|
||||
]
|
||||
]
|
||||
},
|
||||
"removed_track_uuids": [
|
||||
"38b14c4a-1117-48f1-84c2-69df406efd45",
|
||||
"0436d2d5-7e3c-4343-a104-b27263f50378",
|
||||
"0cd2f1ba-dd20-43d0-88bf-b78d25caa7f2",
|
||||
"0df43fc8-4848-4075-9ee2-84440d3d4c52",
|
||||
"114df41e-6104-445f-b52b-b623673294c3",
|
||||
"2f38905d-78e9-4853-b3d0-50b257179e7b",
|
||||
"5b48705f-d547-4a77-84fa-6e4149256b50",
|
||||
"68657908-8a9e-41e0-832d-7e05a448acea",
|
||||
"6b91cd8a-246d-4cfc-9398-242d2912fc37",
|
||||
"88c50c34-fa4a-4fb9-89c7-fb8e40fcaad2",
|
||||
"9e80e1c0-4aaa-4ebc-9689-497f9e2d3e92",
|
||||
"b88df4ea-9af3-402e-b7ca-59ce0b2065bb",
|
||||
"e1a92e9c-97e5-4400-b977-42536a8d2f2d",
|
||||
"073a79bf-6f12-4c76-ba49-fbbfb977d470",
|
||||
"18bac46c-c87a-4575-b8af-1934ea6110c4",
|
||||
"20378a0c-b433-42ba-a643-c5af22ce09ed",
|
||||
"28595a1b-947e-4fdd-be3e-d725cdabbcd4",
|
||||
"49bb5dc7-5ca7-4d9c-8bb0-79ddb15d928f",
|
||||
"79379f9c-e912-4b19-af4c-1638bb948703",
|
||||
"97355a54-7801-4a02-8b6f-e1d31d364035",
|
||||
"a2b2baa5-e50d-449a-ab05-53aaaf15c06c",
|
||||
"da453fcc-84cc-470b-bc5a-49aa69700fa2",
|
||||
"eab2ad52-ceaa-4fcb-ba9d-336bbc8745de",
|
||||
"27bfb939-d170-4d17-9aab-8d68ea6995ca",
|
||||
"6bb98a8c-ace7-48cb-8836-47b54273ebc3",
|
||||
"8f0d756a-6380-4e82-b65c-17d2f12cf462",
|
||||
"eb39c8eb-56cd-4331-b798-e769bfd19c05",
|
||||
"16c19532-1b7d-4251-a3f2-07d08811abe4",
|
||||
"45f61cb8-ad1d-4ca5-bb0d-3d26df7a61eb",
|
||||
"4dc2388f-7f09-4efc-ae8d-5593cfd0efc5",
|
||||
"527da12c-1892-4783-8e1e-bbb23491cf13",
|
||||
"643622f8-985f-41ef-9f60-97b85df04a4b",
|
||||
"7201c8b8-a924-4a78-8718-8639e1b84375",
|
||||
"82d24494-1863-4798-8dab-d6acaa6af1ef",
|
||||
"9d886175-0a22-4800-bacf-d027a2594149",
|
||||
"b87e55c6-667a-4320-b42e-5311956f3869",
|
||||
"ca3aebf5-89f6-4d95-920c-7f751b4e7227",
|
||||
"d43c4432-114e-4488-b457-1015c677dd58",
|
||||
"d5c6f4dc-918d-453a-a950-6fa7ff814a53",
|
||||
"db004298-a5ea-4295-a340-99861f12affa",
|
||||
"e058474e-eea8-44c5-9566-227ab00f9713",
|
||||
"ebf07a5b-1b77-4da4-9009-b5456894c1a6",
|
||||
"0348920f-ac8a-4881-925c-2379c1c042a3",
|
||||
"1376e497-a31b-44ba-8756-a0c3909ae497",
|
||||
"1ae9db6c-00ce-498f-a69a-4917829179a3",
|
||||
"3385b064-faba-489a-ab8f-e2efd7bc6078",
|
||||
"41b7e5dc-e873-4e78-add1-4a8aef2cf0f2",
|
||||
"4a9ea5ca-9548-464c-90aa-d5a11171700b",
|
||||
"53084fac-b12d-4262-8e8a-326bb1598e1d",
|
||||
"6278694c-ab84-4d18-966d-cdd87569b736",
|
||||
"70ecf107-6be8-4c81-944d-b9195d0e907b",
|
||||
"7d370000-a728-42ad-8fb3-07bd56f55834",
|
||||
"8a26d3a6-0e4e-47ac-914a-4e2ac03e80b8",
|
||||
"a4794925-fe72-46c4-bea5-8cdf2ead8bbe",
|
||||
"c8f89618-ed14-47ff-8e60-c489062cce99",
|
||||
"d42d944c-5031-40e7-bab4-578d2a05e07c",
|
||||
"fc51bdbd-326c-4620-b799-b1fcbfd0abfa",
|
||||
"08eda9a1-b0e4-4ed1-8d7d-cec706f0162c",
|
||||
"56f6e44c-d9f4-4144-8950-869e365e09d5",
|
||||
"575729f7-8e91-43fc-ba36-47bd1927aebb",
|
||||
"58b8f7f8-efff-41d9-a224-6c87461ab440",
|
||||
"847e753c-6cc0-4300-8d7c-a3aba8914103",
|
||||
"8b3de05f-9c1d-4e9f-82ab-64f1d17763e7",
|
||||
"c0c4c166-7646-41f9-b005-9fe5c7ded558",
|
||||
"c972b3de-08dc-4ad9-a684-3ff40523f29a",
|
||||
"e0902a82-2700-41ac-b7e3-328984d82703",
|
||||
"efb34c99-e6f3-4563-b01e-7d740f4bb0d1",
|
||||
"5a6d5bb5-ab3e-4f95-9afc-8e9b5e1651b3",
|
||||
"679beec8-cb5f-4c3c-9fab-d7d370b03f50",
|
||||
"70fb66f6-deb5-4699-bb7d-effa571a4758",
|
||||
"736e4133-a876-4d18-83ad-2a0649b0a9bf",
|
||||
"785f6aab-ebe2-4476-b49f-a75d98b64c44",
|
||||
"7a2820fc-35e9-408e-8e6d-1fa62e0f3416",
|
||||
"870bf561-8eb8-4dbe-a63b-64a573c40a28",
|
||||
"b519d784-ca35-488d-b0d2-5f4c344e7519",
|
||||
"0225087c-1bcb-4bdc-ac0b-11fe1bff8a27",
|
||||
"0237718c-7284-4cf8-8958-2e7e2e829e16",
|
||||
"18a95172-f9ce-43f8-b9dd-ab56b48eaaa8",
|
||||
"1ca667c6-9ebc-4ed5-9aeb-90c176a57a9f",
|
||||
"241e7690-286a-467b-b825-70d989d4cb66",
|
||||
"2a05aea8-421b-4c13-a82e-d49ae2a0c82e",
|
||||
"2d7c81b1-9664-4486-bb9e-46db89cc5b02",
|
||||
"310d85fb-84d9-440e-80a3-b2824fcd4d15",
|
||||
"3fbbbb13-12d9-42e8-9b0a-7c40cbeef8a5",
|
||||
"4260b587-cd60-4ca0-8a41-71e2e49f46a4",
|
||||
"52415179-03b2-49b7-9e57-1fd6965ef4f4",
|
||||
"7239518a-93d0-46dc-9114-703f779efb33",
|
||||
"7bbda78d-630f-429b-816c-5b3db4ef39d0",
|
||||
"7df7c029-9651-4323-bd67-afbf86fe336b",
|
||||
"7e68296a-607b-446e-91e0-4fd4e30f18c9",
|
||||
"a88c6fe8-c782-4db0-9c1c-afc4a1bc3f4b",
|
||||
"c058ecbb-3e79-457e-a16c-77c319e0107c",
|
||||
"ce63f9cd-80a2-422e-9d7b-251b8f54bbf7",
|
||||
"d1d00ff3-b284-45e3-836a-0f5cfb666142",
|
||||
"da267940-8d82-4b44-9d44-b28614c250cf",
|
||||
"dbfbcc12-4305-4cd7-ba73-6f6dade960e4",
|
||||
"e125d718-5926-473f-b25b-b9419465b0b1",
|
||||
"ec4f2ef8-ca69-4520-aee7-6b12e42625b1",
|
||||
"eca47b24-175d-4f12-9300-8770ec274a4d",
|
||||
"08f34dfb-69ce-4ce7-aeb3-9b04878939bd",
|
||||
"75e60ab2-4a98-4ae8-8155-068279b73dd6",
|
||||
"98485ed4-b71e-46b1-aebe-781d40674d9f",
|
||||
"9cbac9b7-9f29-4742-9e3b-8817a22ad4af",
|
||||
"ab28a160-1194-460a-bcd9-cb165a51a47e",
|
||||
"bac74aef-8326-4288-ac02-51ae39830b3d",
|
||||
"d8112662-ca8b-4828-8ad4-34d71ca66db7",
|
||||
"55f54e2b-ba8d-4c59-97e8-2e3c26ec1d98",
|
||||
"a9579dde-c548-4e39-a3a9-6de3be621548",
|
||||
"30c97d70-ce85-48b4-b576-22926efcb4ae",
|
||||
"2ca96e04-1f1e-4466-b4ed-78484f82acd2",
|
||||
"f120e834-5db9-4ce4-9e4b-3b2b0757fa3f",
|
||||
"15c5caea-afd8-4f13-ad41-65aa162be77b",
|
||||
"64cefc0c-db46-4fe9-bba7-c43c8901e416",
|
||||
"0806ce46-3896-4c68-ab6b-e07f137928b3"
|
||||
],
|
||||
"repaired_connections": [
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
152.19,
|
||||
137.5
|
||||
],
|
||||
"end": [
|
||||
153.16,
|
||||
137.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
154.73,
|
||||
137.5
|
||||
],
|
||||
"end": [
|
||||
155.7,
|
||||
137.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/OLED_SCL",
|
||||
"start": [
|
||||
157.27,
|
||||
137.5
|
||||
],
|
||||
"end": [
|
||||
158.24,
|
||||
137.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/OLED_SDA",
|
||||
"start": [
|
||||
159.81,
|
||||
137.5
|
||||
],
|
||||
"end": [
|
||||
160.78,
|
||||
137.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
143.0,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
142.16,
|
||||
132.6685
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_PREVIOUS",
|
||||
"start": [
|
||||
150.62,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
149.78,
|
||||
132.6685
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
154.0,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
153.16,
|
||||
132.6685
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_SELECT",
|
||||
"start": [
|
||||
161.62,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
160.78,
|
||||
132.6685
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
165.0,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
164.16,
|
||||
132.6685
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_NEXT",
|
||||
"start": [
|
||||
172.62,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
171.78,
|
||||
132.6685
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_PREVIOUS",
|
||||
"start": [
|
||||
143.0,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
141.72,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_PREVIOUS",
|
||||
"start": [
|
||||
149.5,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
148.22,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
143.0,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
141.72,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
149.5,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
148.22,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_SELECT",
|
||||
"start": [
|
||||
155.0,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
153.72,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_SELECT",
|
||||
"start": [
|
||||
161.5,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
160.22,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
155.0,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
153.72,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
161.5,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
160.22,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_NEXT",
|
||||
"start": [
|
||||
167.0,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
165.72,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_NEXT",
|
||||
"start": [
|
||||
173.5,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
172.22,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
167.0,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
165.72,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
173.5,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
172.22,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/OLED_SDA",
|
||||
"start": [
|
||||
135.75,
|
||||
161.55
|
||||
],
|
||||
"end": [
|
||||
159.8,
|
||||
137.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/OLED_SCL",
|
||||
"start": [
|
||||
134.9,
|
||||
159.85
|
||||
],
|
||||
"end": [
|
||||
157.25,
|
||||
137.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_SELECT",
|
||||
"start": [
|
||||
161.5,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
161.5,
|
||||
134.1
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_DCD",
|
||||
"start": [
|
||||
133.1,
|
||||
131.9
|
||||
],
|
||||
"end": [
|
||||
148.08,
|
||||
126.67
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_RI",
|
||||
"start": [
|
||||
155.25,
|
||||
138.7
|
||||
],
|
||||
"end": [
|
||||
165.86,
|
||||
126.67
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_DTR",
|
||||
"start": [
|
||||
148.6,
|
||||
133.1
|
||||
],
|
||||
"end": [
|
||||
155.7,
|
||||
126.67
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_CTS",
|
||||
"start": [
|
||||
153.45,
|
||||
138.75
|
||||
],
|
||||
"end": [
|
||||
154.75,
|
||||
135.65
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_RX",
|
||||
"start": [
|
||||
135.75,
|
||||
139.5
|
||||
],
|
||||
"end": [
|
||||
144.45,
|
||||
132.8
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_VALID",
|
||||
"start": [
|
||||
157.75,
|
||||
138.7
|
||||
],
|
||||
"end": [
|
||||
165.9,
|
||||
127.95
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/RS_OFF_N",
|
||||
"start": [
|
||||
160.3,
|
||||
138.7
|
||||
],
|
||||
"end": [
|
||||
162.4,
|
||||
136.75
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_PREVIOUS",
|
||||
"start": [
|
||||
148.22,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
149.5,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/BTN_NEXT",
|
||||
"start": [
|
||||
172.22,
|
||||
168.0
|
||||
],
|
||||
"end": [
|
||||
173.5,
|
||||
168.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
148.22,
|
||||
172.5
|
||||
],
|
||||
"end": [
|
||||
149.5,
|
||||
172.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
145.54,
|
||||
126.67
|
||||
],
|
||||
"end": [
|
||||
150.25,
|
||||
135.8
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/GND",
|
||||
"start": [
|
||||
149.5,
|
||||
170.0
|
||||
],
|
||||
"end": [
|
||||
148.1,
|
||||
156.55
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
143.0,
|
||||
133.5
|
||||
],
|
||||
"end": [
|
||||
143.0,
|
||||
134.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
154.0,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
156.35,
|
||||
136.4
|
||||
]
|
||||
},
|
||||
{
|
||||
"net": "/+3V3",
|
||||
"start": [
|
||||
148.7,
|
||||
134.0
|
||||
],
|
||||
"end": [
|
||||
152.65,
|
||||
132.65
|
||||
]
|
||||
}
|
||||
],
|
||||
"retained_tracks": 476,
|
||||
"protected_sha256": {
|
||||
"serial-carrier.kicad_pro": "9ad5b992785ebfe01fa7ea4e1aa391a2c7d7f1f2c3b1c13136173c301ecb6516",
|
||||
"serial-carrier.kicad_prl": "f7c452b3be0ad43b4557f4d9c24a38124cd88268edfc2c9bbd59e3f2429e3772",
|
||||
"serial-carrier.kicad_sch": "621acfd9ed8e025a90442e5f82f73d7d5e3228d7a48459a0fa4b0c46e763912a"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
||||
"coordinate_units": "mm",
|
||||
"date": "2026-09-21T22:52:26",
|
||||
"date": "2026-09-22T10:24:36",
|
||||
"ignored_checks": [
|
||||
{
|
||||
"description": "Footprint has no courtyard defined",
|
||||
@@ -30,23 +30,7 @@
|
||||
"exclusion"
|
||||
],
|
||||
"kicad_version": "10.0.6",
|
||||
"schematic_parity": [
|
||||
{
|
||||
"description": "Carrier:MAX3243_Reference_Provisional stimmt nicht mit dem Footprint des Symbols () überein.",
|
||||
"items": [
|
||||
{
|
||||
"description": "Footprint U2",
|
||||
"pos": {
|
||||
"x": 143.0,
|
||||
"y": 126.67
|
||||
},
|
||||
"uuid": "812387f6-6524-459e-8102-fe9c55122668"
|
||||
}
|
||||
],
|
||||
"severity": "warning",
|
||||
"type": "footprint_symbol_mismatch"
|
||||
}
|
||||
],
|
||||
"schematic_parity": [],
|
||||
"source": "serial-carrier.kicad_pcb",
|
||||
"unconnected_items": [],
|
||||
"violations": []
|
||||
|
||||
|
After Width: | Height: | Size: 49 KiB |
@@ -1,39 +1,39 @@
|
||||
{
|
||||
"immutable_geometry_nets_pads_linkage_preserved": true,
|
||||
"board_sha256": "94e941625b3b9fc9a08b9f2d532eb35003ea8da41d456a1c67269b9651200e05",
|
||||
"independent_min_different_net_or_npth_clearance_mm": 0.2705441169852849,
|
||||
"geometry_nets_pads_linkage_match_authorized_group_moves_and_values": true,
|
||||
"board_sha256": "fa95d5747c6e455b2c5effd11d11091b1f95df015dbe73c99b14092487a034c5",
|
||||
"independent_min_different_net_or_npth_clearance_mm": 0.2527621919573916,
|
||||
"closest_items": [
|
||||
"U1.38:4b8eabef-8493-437d-bd02-cb9ff4afd7cd",
|
||||
"d95f45cb-7fa4-4203-a786-0a069425e2b8"
|
||||
"DS1.4:91a4dcef-1b36-4e3d-a15c-c4578077c27b",
|
||||
"265214f3-776f-4707-be6e-993749f3c8a7"
|
||||
],
|
||||
"min_copper_or_npth_edge_gap_mm": 1.9,
|
||||
"min_rf_gap_mm": 0.025000000000005684,
|
||||
"min_via_to_other_drilled_hole_gap_mm": 0.8205441169852848,
|
||||
"min_via_to_other_drilled_hole_gap_mm": 0.47246725698856695,
|
||||
"track_width_counts": {
|
||||
"0.25": 451,
|
||||
"0.5": 114
|
||||
"0.25": 642,
|
||||
"0.5": 296
|
||||
},
|
||||
"routed_length_mm_by_net": {
|
||||
"/BTN_SELECT": 88.94832552628598,
|
||||
"/GND": 141.68412246223386,
|
||||
"/+3V3": 90.29604731966161,
|
||||
"/RS_DCD": 43.068490178879834,
|
||||
"/BTN_SELECT": 93.73257585313998,
|
||||
"/GND": 167.13877028476702,
|
||||
"/+3V3": 110.38340361450781,
|
||||
"/RS_DCD": 43.09677445013352,
|
||||
"/RS_DSR": 55.09398706495997,
|
||||
"/RS_RI": 67.91373841554615,
|
||||
"/RS_DTR": 52.53487761643567,
|
||||
"/RS_RI": 75.93696129246699,
|
||||
"/RS_DTR": 53.18208599609399,
|
||||
"/RS_RTS": 60.056714985368814,
|
||||
"/RS_CTS": 63.65082753507114,
|
||||
"/RS_CTS": 81.83108365614024,
|
||||
"/RS_TX": 53.135260491559045,
|
||||
"/RS_RX": 51.637429252872906,
|
||||
"/RS_VALID": 72.10630197626116,
|
||||
"/RS_OFF_N": 78.04100118378895,
|
||||
"/BTN_PREVIOUS": 78.83370879187974,
|
||||
"/OLED_SDA": 62.65168524184884,
|
||||
"/OLED_SCL": 61.1779295330028,
|
||||
"/BTN_NEXT": 101.24898628384834
|
||||
"/RS_RX": 52.38890111544552,
|
||||
"/RS_VALID": 73.53056575207658,
|
||||
"/RS_OFF_N": 78.70963033389047,
|
||||
"/BTN_PREVIOUS": 84.48232420012336,
|
||||
"/OLED_SDA": 63.88671049501825,
|
||||
"/OLED_SCL": 62.3836654642909,
|
||||
"/BTN_NEXT": 106.89760169209195
|
||||
},
|
||||
"tracks": 565,
|
||||
"vias": 22,
|
||||
"tracks": 938,
|
||||
"vias": 41,
|
||||
"copper_zones": 0,
|
||||
"drc_freshly_run": true,
|
||||
"drc_violations": 0,
|
||||
|
||||
@@ -21,7 +21,11 @@ r = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(r)
|
||||
board = p.LoadBoard(str(r.PCB))
|
||||
original = p.LoadBoard(str(ROOT / 'validation/pcb-routing-input.kicad_pcb'))
|
||||
assert r.identity(board) == r.identity(original), 'immutable source geometry/linkage changed'
|
||||
import sys
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from validate_group_alignment import expected_identity, audit
|
||||
audit()
|
||||
assert r.identity(board) == r.identity(expected_identity(original)), 'source geometry/linkage differs beyond authorized group moves/values'
|
||||
items, holes = [], []
|
||||
for f in board.GetFootprints():
|
||||
for pad in f.Pads():
|
||||
@@ -106,7 +110,7 @@ subprocess.run(['kicad-cli', 'pcb', 'drc', '--format', 'json', '--all-track-erro
|
||||
assert r.PCB.read_bytes() == before, 'board changed during audit'
|
||||
drc = json.loads(ROOT.joinpath('validation/pcb-routing-drc.json').read_text())
|
||||
assert not drc['violations'] and not drc['unconnected_items']
|
||||
report = dict(immutable_geometry_nets_pads_linkage_preserved=True,
|
||||
report = dict(geometry_nets_pads_linkage_match_authorized_group_moves_and_values=True,
|
||||
board_sha256=hashlib.sha256(before).hexdigest(),
|
||||
independent_min_different_net_or_npth_clearance_mm=min_clearance,
|
||||
closest_items=closest, min_copper_or_npth_edge_gap_mm=min_edge,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
||||
"coordinate_units": "mm",
|
||||
"date": "2026-09-21T22:33:53",
|
||||
"date": "2026-09-22T10:21:48",
|
||||
"ignored_checks": [
|
||||
{
|
||||
"description": "Footprint has no courtyard defined",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<export version="E">
|
||||
<design>
|
||||
<source>/home/mscholz/Repos/ESP32_serial_swiss_army_knife/hardware/PCB/serial-carrier.kicad_sch</source>
|
||||
<date>2026-09-21T22:52:19</date>
|
||||
<date>2026-09-22T10:24:34</date>
|
||||
<tool>Eeschema 10.0.6-1.fc44</tool>
|
||||
<sheet number="1" name="/" tstamps="/">
|
||||
<title_block>
|
||||
@@ -51,16 +51,16 @@
|
||||
</units>
|
||||
</comp>
|
||||
<comp ref="R1">
|
||||
<value>2.2k 1%</value>
|
||||
<value>2k 1%</value>
|
||||
<footprint>Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</footprint>
|
||||
<fields>
|
||||
<field name="Rating">At least 0.125 W; exact resistor MPN pending</field>
|
||||
<field name="Rating">Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified</field>
|
||||
<field name="Footprint">Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="R" description="Resistor, axial DIN0207 footprint; exact MPN pending"/>
|
||||
<property name="Rating" value="At least 0.125 W; exact resistor MPN pending"/>
|
||||
<property name="Rating" value="Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -75,16 +75,16 @@
|
||||
</units>
|
||||
</comp>
|
||||
<comp ref="R2">
|
||||
<value>2.2k 1%</value>
|
||||
<value>2k 1%</value>
|
||||
<footprint>Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</footprint>
|
||||
<fields>
|
||||
<field name="Rating">At least 0.125 W; exact resistor MPN pending</field>
|
||||
<field name="Rating">Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified</field>
|
||||
<field name="Footprint">Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="R" description="Resistor, axial DIN0207 footprint; exact MPN pending"/>
|
||||
<property name="Rating" value="At least 0.125 W; exact resistor MPN pending"/>
|
||||
<property name="Rating" value="Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -99,16 +99,16 @@
|
||||
</units>
|
||||
</comp>
|
||||
<comp ref="R3">
|
||||
<value>2.2k 1%</value>
|
||||
<value>2k 1%</value>
|
||||
<footprint>Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</footprint>
|
||||
<fields>
|
||||
<field name="Rating">At least 0.125 W; exact resistor MPN pending</field>
|
||||
<field name="Rating">Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified</field>
|
||||
<field name="Footprint">Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="R" description="Resistor, axial DIN0207 footprint; exact MPN pending"/>
|
||||
<property name="Rating" value="At least 0.125 W; exact resistor MPN pending"/>
|
||||
<property name="Rating" value="Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -129,7 +129,7 @@
|
||||
<field name="Manufacturer">Omron</field>
|
||||
<field name="MPN">B3F-1000</field>
|
||||
<field name="Function">Previous / back</field>
|
||||
<field name="Procurement">Live stock not verified</field>
|
||||
<field name="Procurement">User reports purchased</field>
|
||||
<field name="Footprint">Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
@@ -138,7 +138,7 @@
|
||||
<property name="Manufacturer" value="Omron"/>
|
||||
<property name="MPN" value="B3F-1000"/>
|
||||
<property name="Function" value="Previous / back"/>
|
||||
<property name="Procurement" value="Live stock not verified"/>
|
||||
<property name="Procurement" value="User reports purchased"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -159,7 +159,7 @@
|
||||
<field name="Manufacturer">Omron</field>
|
||||
<field name="MPN">B3F-1000</field>
|
||||
<field name="Function">Select / confirm</field>
|
||||
<field name="Procurement">Live stock not verified</field>
|
||||
<field name="Procurement">User reports purchased</field>
|
||||
<field name="Footprint">Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
@@ -168,7 +168,7 @@
|
||||
<property name="Manufacturer" value="Omron"/>
|
||||
<property name="MPN" value="B3F-1000"/>
|
||||
<property name="Function" value="Select / confirm"/>
|
||||
<property name="Procurement" value="Live stock not verified"/>
|
||||
<property name="Procurement" value="User reports purchased"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -189,7 +189,7 @@
|
||||
<field name="Manufacturer">Omron</field>
|
||||
<field name="MPN">B3F-1000</field>
|
||||
<field name="Function">Next</field>
|
||||
<field name="Procurement">Live stock not verified</field>
|
||||
<field name="Procurement">User reports purchased</field>
|
||||
<field name="Footprint">Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
@@ -198,7 +198,7 @@
|
||||
<property name="Manufacturer" value="Omron"/>
|
||||
<property name="MPN" value="B3F-1000"/>
|
||||
<property name="Function" value="Next"/>
|
||||
<property name="Procurement" value="Live stock not verified"/>
|
||||
<property name="Procurement" value="User reports purchased"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -282,15 +282,16 @@
|
||||
</comp>
|
||||
<comp ref="U2">
|
||||
<value>Adafruit MAX3243 / male 6253</value>
|
||||
<footprint>Carrier:MAX3243_Reference_Provisional</footprint>
|
||||
<fields>
|
||||
<field name="Status">VERIFY male header numbering; footprint intentionally unassigned</field>
|
||||
<field name="Status">User confirmed male pinout/header/footprint and pin-by-pin connections</field>
|
||||
<field name="Datasheet_source">adafruit-research.md; reference female 5988 CAD</field>
|
||||
<field name="Footprint"/>
|
||||
<field name="Footprint">Carrier:MAX3243_Reference_Provisional</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="MAX3243_Module_Reference" description="Adafruit male 6253 intended; physical numbering from published female 5988 CAD, VERIFY MALE"/>
|
||||
<property name="Status" value="VERIFY male header numbering; footprint intentionally unassigned"/>
|
||||
<property name="Status" value="User confirmed male pinout/header/footprint and pin-by-pin connections"/>
|
||||
<property name="Datasheet_source" value="adafruit-research.md; reference female 5988 CAD"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"measured_library_matches_embedded_u1": true,
|
||||
"historical_u1_integration_and_subsequent_authorized_group_delta_verified": true,
|
||||
"presentation_label_cleanup": true,
|
||||
"silk_refresh_idempotent": true,
|
||||
"stale_geometry_negative_control": true,
|
||||
"silk_clipped_to_carrier": true,
|
||||
"usb_mouth_markers": 2,
|
||||
"generated_silk_items": 94,
|
||||
"baseline_sha256": "53e36538c0669604457b9e1a9ff1b1e20422970e8f9f356088c4581868ad5045",
|
||||
"board_sha256": "fa95d5747c6e455b2c5effd11d11091b1f95df015dbe73c99b14092487a034c5",
|
||||
"project_sha256": "9ad5b992785ebfe01fa7ea4e1aa391a2c7d7f1f2c3b1c13136173c301ecb6516"
|
||||
}
|
||||
@@ -39,31 +39,31 @@
|
||||
26.67
|
||||
],
|
||||
"DS1": [
|
||||
43,
|
||||
43.97,
|
||||
36
|
||||
],
|
||||
"R1": [
|
||||
43,
|
||||
34
|
||||
42.16,
|
||||
32.6685
|
||||
],
|
||||
"R2": [
|
||||
54,
|
||||
34
|
||||
53.16,
|
||||
32.6685
|
||||
],
|
||||
"R3": [
|
||||
65,
|
||||
34
|
||||
64.16,
|
||||
32.6685
|
||||
],
|
||||
"SW1": [
|
||||
43,
|
||||
41.72,
|
||||
68
|
||||
],
|
||||
"SW2": [
|
||||
55,
|
||||
53.72,
|
||||
68
|
||||
],
|
||||
"SW3": [
|
||||
67,
|
||||
65.72,
|
||||
68
|
||||
],
|
||||
"H1": [
|
||||
@@ -95,8 +95,8 @@
|
||||
"hex_mouth_overhang": 2.87,
|
||||
"flange_front_inset": 1.93
|
||||
},
|
||||
"tracks": 565,
|
||||
"vias": 22,
|
||||
"tracks": 938,
|
||||
"vias": 41,
|
||||
"copper_layers": 2,
|
||||
"draft_thickness_mm": 1.6,
|
||||
"electrical_footprints": 9,
|
||||
@@ -104,7 +104,7 @@
|
||||
"physical_DRC_violations": 0,
|
||||
"unconnected_items": 0,
|
||||
"schematic_pad_nets_match": true,
|
||||
"known_parity_warning": "U2 reference footprint assigned on PCB only; schematic footprint intentionally blank; nominal male external metal dimensions user-confirmed",
|
||||
"schematic_parity_violations": 0,
|
||||
"project_settings_sha256": "9ad5b992785ebfe01fa7ea4e1aa391a2c7d7f1f2c3b1c13136173c301ecb6516",
|
||||
"ignored_DRC_checks": [
|
||||
{
|
||||
@@ -128,15 +128,19 @@
|
||||
"key": "footprint_type_mismatch"
|
||||
}
|
||||
],
|
||||
"not_validated": [
|
||||
"male module pin map/header fit",
|
||||
"ESP32 measured-planar 1:1 fit and complete assembly envelope/heights",
|
||||
"header/socket fit",
|
||||
"RF performance and antenna clearance",
|
||||
"power/return-path integrity",
|
||||
"USB power path",
|
||||
"OLED pull-ups",
|
||||
"fabrication process",
|
||||
"hardware operation"
|
||||
]
|
||||
"user_accepted": [
|
||||
"module dimensions/visual fit and USB clearance",
|
||||
"male pinout/header/footprint and pin-by-pin review",
|
||||
"M3 carrier/module and M2 display hardware",
|
||||
"RF keepout and power/ground routing"
|
||||
],
|
||||
"not_independently_verified": [
|
||||
"reported 10 mm assembled module elevation",
|
||||
"regulator margin",
|
||||
"USB backfeed safety",
|
||||
"OLED onboard pull-up resistance/rail",
|
||||
"manufacturer RF qualification",
|
||||
"fabrication process/certification"
|
||||
],
|
||||
"new_hardware_tests": false
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
@@ -2,7 +2,7 @@
|
||||
<export version="E">
|
||||
<design>
|
||||
<source>/home/mscholz/Repos/ESP32_serial_swiss_army_knife/hardware/PCB/serial-carrier.kicad_sch</source>
|
||||
<date>2026-09-20T20:14:45</date>
|
||||
<date>2026-09-22T09:55:17</date>
|
||||
<tool>Eeschema 10.0.6-1.fc44</tool>
|
||||
<sheet number="1" name="/" tstamps="/">
|
||||
<title_block>
|
||||
@@ -51,16 +51,16 @@
|
||||
</units>
|
||||
</comp>
|
||||
<comp ref="R1">
|
||||
<value>2.2k 1%</value>
|
||||
<value>2k 1%</value>
|
||||
<footprint>Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</footprint>
|
||||
<fields>
|
||||
<field name="Rating">At least 0.125 W; exact resistor MPN pending</field>
|
||||
<field name="Rating">Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified</field>
|
||||
<field name="Footprint">Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="R" description="Resistor, axial DIN0207 footprint; exact MPN pending"/>
|
||||
<property name="Rating" value="At least 0.125 W; exact resistor MPN pending"/>
|
||||
<property name="Rating" value="Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -75,16 +75,16 @@
|
||||
</units>
|
||||
</comp>
|
||||
<comp ref="R2">
|
||||
<value>2.2k 1%</value>
|
||||
<value>2k 1%</value>
|
||||
<footprint>Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</footprint>
|
||||
<fields>
|
||||
<field name="Rating">At least 0.125 W; exact resistor MPN pending</field>
|
||||
<field name="Rating">Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified</field>
|
||||
<field name="Footprint">Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="R" description="Resistor, axial DIN0207 footprint; exact MPN pending"/>
|
||||
<property name="Rating" value="At least 0.125 W; exact resistor MPN pending"/>
|
||||
<property name="Rating" value="Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -99,16 +99,16 @@
|
||||
</units>
|
||||
</comp>
|
||||
<comp ref="R3">
|
||||
<value>2.2k 1%</value>
|
||||
<value>2k 1%</value>
|
||||
<footprint>Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</footprint>
|
||||
<fields>
|
||||
<field name="Rating">At least 0.125 W; exact resistor MPN pending</field>
|
||||
<field name="Rating">Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified</field>
|
||||
<field name="Footprint">Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="R" description="Resistor, axial DIN0207 footprint; exact MPN pending"/>
|
||||
<property name="Rating" value="At least 0.125 W; exact resistor MPN pending"/>
|
||||
<property name="Rating" value="Purchased metal-film 1%; design at least 0.125 W; actual MPN/wattage unspecified"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -129,7 +129,7 @@
|
||||
<field name="Manufacturer">Omron</field>
|
||||
<field name="MPN">B3F-1000</field>
|
||||
<field name="Function">Previous / back</field>
|
||||
<field name="Procurement">Live stock not verified</field>
|
||||
<field name="Procurement">User reports purchased</field>
|
||||
<field name="Footprint">Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
@@ -138,7 +138,7 @@
|
||||
<property name="Manufacturer" value="Omron"/>
|
||||
<property name="MPN" value="B3F-1000"/>
|
||||
<property name="Function" value="Previous / back"/>
|
||||
<property name="Procurement" value="Live stock not verified"/>
|
||||
<property name="Procurement" value="User reports purchased"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -159,7 +159,7 @@
|
||||
<field name="Manufacturer">Omron</field>
|
||||
<field name="MPN">B3F-1000</field>
|
||||
<field name="Function">Select / confirm</field>
|
||||
<field name="Procurement">Live stock not verified</field>
|
||||
<field name="Procurement">User reports purchased</field>
|
||||
<field name="Footprint">Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
@@ -168,7 +168,7 @@
|
||||
<property name="Manufacturer" value="Omron"/>
|
||||
<property name="MPN" value="B3F-1000"/>
|
||||
<property name="Function" value="Select / confirm"/>
|
||||
<property name="Procurement" value="Live stock not verified"/>
|
||||
<property name="Procurement" value="User reports purchased"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -189,7 +189,7 @@
|
||||
<field name="Manufacturer">Omron</field>
|
||||
<field name="MPN">B3F-1000</field>
|
||||
<field name="Function">Next</field>
|
||||
<field name="Procurement">Live stock not verified</field>
|
||||
<field name="Procurement">User reports purchased</field>
|
||||
<field name="Footprint">Button_Switch_THT:SW_TH_Tactile_Omron_B3F-100x</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
@@ -198,7 +198,7 @@
|
||||
<property name="Manufacturer" value="Omron"/>
|
||||
<property name="MPN" value="B3F-1000"/>
|
||||
<property name="Function" value="Next"/>
|
||||
<property name="Procurement" value="Live stock not verified"/>
|
||||
<property name="Procurement" value="User reports purchased"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
<sheetpath names="/" tstamps="/"/>
|
||||
@@ -282,15 +282,16 @@
|
||||
</comp>
|
||||
<comp ref="U2">
|
||||
<value>Adafruit MAX3243 / male 6253</value>
|
||||
<footprint>Carrier:MAX3243_Reference_Provisional</footprint>
|
||||
<fields>
|
||||
<field name="Status">VERIFY male header numbering; footprint intentionally unassigned</field>
|
||||
<field name="Status">User confirmed male pinout/header/footprint and pin-by-pin connections</field>
|
||||
<field name="Datasheet_source">adafruit-research.md; reference female 5988 CAD</field>
|
||||
<field name="Footprint"/>
|
||||
<field name="Footprint">Carrier:MAX3243_Reference_Provisional</field>
|
||||
<field name="Datasheet"/>
|
||||
<field name="Description"/>
|
||||
</fields>
|
||||
<libsource lib="Carrier" part="MAX3243_Module_Reference" description="Adafruit male 6253 intended; physical numbering from published female 5988 CAD, VERIFY MALE"/>
|
||||
<property name="Status" value="VERIFY male header numbering; footprint intentionally unassigned"/>
|
||||
<property name="Status" value="User confirmed male pinout/header/footprint and pin-by-pin connections"/>
|
||||
<property name="Datasheet_source" value="adafruit-research.md; reference female 5988 CAD"/>
|
||||
<property name="Sheetname" value="serial-carrier"/>
|
||||
<property name="Sheetfile" value="serial-carrier.kicad_sch"/>
|
||||
@@ -448,7 +449,7 @@
|
||||
</library>
|
||||
</libraries>
|
||||
<nets>
|
||||
<net code="1" name="/+3V3" class="Default">
|
||||
<net code="1" name="/+3V3" class="Power">
|
||||
<node ref="DS1" pin="2" pinfunction="VCC_2" pintype="power_in"/>
|
||||
<node ref="R1" pin="1" pintype="passive"/>
|
||||
<node ref="R2" pin="1" pintype="passive"/>
|
||||
@@ -472,7 +473,7 @@
|
||||
<node ref="SW2" pin="1" pintype="passive"/>
|
||||
<node ref="U1" pin="19" pinfunction="GPIO13_19" pintype="input"/>
|
||||
</net>
|
||||
<net code="5" name="/GND" class="Default">
|
||||
<net code="5" name="/GND" class="Power">
|
||||
<node ref="DS1" pin="1" pinfunction="GND_1" pintype="power_in"/>
|
||||
<node ref="SW1" pin="2" pintype="passive"/>
|
||||
<node ref="SW2" pin="2" pintype="passive"/>
|
||||
|
||||
@@ -4,5 +4,6 @@ KiCad ERC: zero reported errors/warnings; no project exclusions added.
|
||||
15 firmware GPIO definitions independently checked against exported pin functions.
|
||||
All 44 MCU header pins accounted for; USB/UART0/5V/PSRAM pins unused on carrier.
|
||||
PDF and SVG exported successfully.
|
||||
Not validated: male-module physical mapping, power budget/path, OLED pull-ups,
|
||||
selected connector fit, PCB DRC/layout, procurement, or assembled hardware.
|
||||
User accepted module mapping/fit and reports purchased switches/resistors.
|
||||
No new hardware tests; regulator margin, USB backfeed safety, RF performance
|
||||
and fabrication qualification are not established by this schematic check.
|
||||
|
||||