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.
@@ -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,8 +125,7 @@ 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")
|
||||
assert comp.findtext("footprint")==lib_id,(ref,"Schematic footprint changed")
|
||||
fp=load(lib_id)
|
||||
fp.SetReference(ref)
|
||||
fp.SetValue(comp.findtext("value"))
|
||||
@@ -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,10 +15,11 @@ 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")
|
||||
title.SetComment(0, COMMENT)
|
||||
if title.GetComment(0):
|
||||
title.SetComment(0, COMMENT)
|
||||
for drawing in list(board.GetDrawings()):
|
||||
if isinstance(drawing, p.PCB_TEXT) and drawing.GetText() == SILK:
|
||||
assert drawing.GetLayer() == p.F_SilkS
|
||||
|
||||
@@ -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.
|
||||
|
||||