Advance PCB carrier to A1 layout
Reposition the ESP32 and RS-232 reference within the notched outline, preserve the A0 snapshot, and add second-pass two-layer routing evidence. Refresh validation reports and draft previews while keeping fabrication status provisional.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 252 KiB After Width: | Height: | Size: 283 KiB |
+6057
-4667
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 175 KiB |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
||||||
"coordinate_units": "mm",
|
"coordinate_units": "mm",
|
||||||
"date": "2026-09-20T23:07:29",
|
"date": "2026-09-21T11:39:10",
|
||||||
"ignored_checks": [
|
"ignored_checks": [
|
||||||
{
|
{
|
||||||
"description": "Footprint has no courtyard defined",
|
"description": "Footprint has no courtyard defined",
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"description": "Footprint U2",
|
"description": "Footprint U2",
|
||||||
"pos": {
|
"pos": {
|
||||||
"x": 143.0,
|
"x": 143.0,
|
||||||
"y": 130.0
|
"y": 126.67
|
||||||
},
|
},
|
||||||
"uuid": "812387f6-6524-459e-8102-fe9c55122668"
|
"uuid": "812387f6-6524-459e-8102-fe9c55122668"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
|||||||
|
/builddir/build/BUILD/kicad-10.0.6-build/kicad-10.0.6/include/properties/property.h(607): assert "m_choices.GetCount() > 0" failed in PROPERTY_ENUM(): No enum choices defined
|
||||||
|
/builddir/build/BUILD/kicad-10.0.6-build/kicad-10.0.6/include/properties/property.h(607): assert "m_choices.GetCount() > 0" failed in PROPERTY_ENUM(): No enum choices defined
|
||||||
|
/builddir/build/BUILD/kicad-10.0.6-build/kicad-10.0.6/include/properties/property.h(607): assert "m_choices.GetCount() > 0" failed in PROPERTY_ENUM(): No enum choices defined
|
||||||
|
{"net": "/+3V3", "start": "U1.1", "end": "U1.2", "start_mm": [108.0, 123.0], "end_mm": [108.0, 125.54], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 52}
|
||||||
|
{"net": "/+3V3", "start": "R2.1", "end": "DS1.2", "start_mm": [154.0, 134.0], "end_mm": [154.73, 137.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 911}
|
||||||
|
{"net": "/BTN_NEXT", "start": "SW3.1", "end": "SW3.1", "start_mm": [167.0, 168.0], "end_mm": [173.5, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/BTN_PREVIOUS", "start": "SW1.1", "end": "SW1.1", "start_mm": [143.0, 168.0], "end_mm": [149.5, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/BTN_SELECT", "start": "SW2.1", "end": "SW2.1", "start_mm": [155.0, 168.0], "end_mm": [161.5, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/+3V3", "start": "U2.1", "end": "R1.1", "start_mm": [143.0, 126.67], "end_mm": [143.0, 134.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 148}
|
||||||
|
{"net": "/+3V3", "start": "DS1.2", "end": "R3.1", "start_mm": [154.73, 137.5], "end_mm": [165.0, 134.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 5139}
|
||||||
|
{"net": "/+3V3", "start": "R1.1", "end": "R2.1", "start_mm": [143.0, 134.0], "end_mm": [154.0, 134.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 14153}
|
||||||
|
{"net": "/BTN_SELECT", "start": "SW2.1", "end": "R2.2", "start_mm": [161.5, 168.0], "end_mm": [161.62, 134.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 2039}
|
||||||
|
{"net": "/BTN_NEXT", "start": "SW3.1", "end": "R3.2", "start_mm": [173.5, 168.0], "end_mm": [172.62, 134.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 12615}
|
||||||
|
{"net": "/BTN_PREVIOUS", "start": "SW1.1", "end": "R1.2", "start_mm": [149.5, 168.0], "end_mm": [150.62, 134.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 15179}
|
||||||
|
{"net": "/+3V3", "start": "U1.2", "end": "U2.1", "start_mm": [108.0, 125.54], "end_mm": [143.0, 126.67], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 297043}
|
||||||
|
{"net": "/BTN_PREVIOUS", "start": "U1.16", "end": "SW1.1", "start_mm": [108.0, 161.1], "end_mm": [143.0, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 70393}
|
||||||
|
{"net": "/RS_DCD", "start": "U1.4", "end": "U2.3", "start_mm": [108.0, 130.62], "end_mm": [148.08, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 54772}
|
||||||
|
{"net": "/BTN_SELECT", "start": "SW2.1", "end": "U1.19", "start_mm": [155.0, 168.0], "end_mm": [108.0, 168.72], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 36364}
|
||||||
|
{"net": "/RS_RX", "start": "U1.11", "end": "U2.4", "start_mm": [108.0, 148.4], "end_mm": [150.62, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 119946}
|
||||||
|
{"net": "/RS_TX", "start": "U1.10", "end": "U2.5", "start_mm": [108.0, 145.86], "end_mm": [153.16, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 297203}
|
||||||
|
{"net": "/RS_DTR", "start": "U1.7", "end": "U2.6", "start_mm": [108.0, 138.24], "end_mm": [155.7, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 217446}
|
||||||
|
{"net": "/RS_DSR", "start": "U1.5", "end": "U2.7", "start_mm": [108.0, 133.16], "end_mm": [158.24, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 177204}
|
||||||
|
{"net": "/RS_RTS", "start": "U1.8", "end": "U2.8", "start_mm": [108.0, 140.78], "end_mm": [160.78, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 268999}
|
||||||
|
{"net": "/OLED_SCL", "start": "U1.18", "end": "DS1.3", "start_mm": [108.0, 166.18], "end_mm": [157.27, 137.5], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 453162}
|
||||||
|
{"net": "/RS_CTS", "start": "U1.9", "end": "U2.9", "start_mm": [108.0, 143.32], "end_mm": [163.32, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 336768}
|
||||||
|
{"net": "/OLED_SDA", "start": "U1.17", "end": "DS1.4", "start_mm": [108.0, 163.64], "end_mm": [159.81, 137.5], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 574321}
|
||||||
|
{"net": "/RS_RI", "start": "U1.6", "end": "U2.10", "start_mm": [108.0, 135.7], "end_mm": [165.86, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 221877}
|
||||||
|
{"net": "/BTN_NEXT", "start": "U1.20", "end": "SW3.1", "start_mm": [108.0, 171.26], "end_mm": [167.0, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 69853}
|
||||||
|
{"net": "/RS_VALID", "start": "U1.12", "end": "U2.11", "start_mm": [108.0, 150.94], "end_mm": [168.4, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 344679}
|
||||||
|
{"net": "/RS_OFF_N", "start": "U1.15", "end": "U2.12", "start_mm": [108.0, 158.56], "end_mm": [170.94, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 690965}
|
||||||
|
{"net": "/GND", "start": "U1.43", "end": "U1.44", "start_mm": [133.4, 173.8], "end_mm": [133.4, 176.34], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 52}
|
||||||
|
{"net": "/GND", "start": "SW2.2", "end": "SW1.2", "start_mm": [155.0, 172.5], "end_mm": [149.5, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 111}
|
||||||
|
{"net": "/GND", "start": "SW2.2", "end": "SW3.2", "start_mm": [161.5, 172.5], "end_mm": [167.0, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 111}
|
||||||
|
{"net": "/GND", "start": "SW2.2", "end": "SW2.2", "start_mm": [155.0, 172.5], "end_mm": [161.5, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/GND", "start": "SW3.2", "end": "SW3.2", "start_mm": [167.0, 172.5], "end_mm": [173.5, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/GND", "start": "SW1.2", "end": "SW1.2", "start_mm": [149.5, 172.5], "end_mm": [143.0, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/GND", "start": "SW1.2", "end": "U1.43", "start_mm": [143.0, 172.5], "end_mm": [133.4, 173.8], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 2466}
|
||||||
|
{"net": "/GND", "start": "U2.2", "end": "U1.23", "start_mm": [145.54, 126.67], "end_mm": [133.4, 123.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 7318}
|
||||||
|
{"net": "/GND", "start": "DS1.1", "end": "U2.2", "start_mm": [152.19, 137.5], "end_mm": [145.54, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": true, "expansions": 2378118}
|
||||||
|
{"net": "/GND", "start": "U1.44", "end": "U1.22", "start_mm": [133.4, 176.34], "end_mm": [108.0, 176.34], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 509}
|
||||||
|
{"net": "/GND", "start": "SW1.2", "end": "DS1.1", "start_mm": [149.5, 172.5], "end_mm": [152.19, 137.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 19033}
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'PNG file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'JPEG file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'TIFF file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'GIF file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'PNM file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'PCX file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'IFF file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'Windows icon file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'Windows cursor file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'Windows animated cursor file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'TGA file'
|
||||||
|
10:59:13: Debug: Adding duplicate image handler for 'XPM file'
|
||||||
|
{
|
||||||
|
"draft_only": true,
|
||||||
|
"saved": true,
|
||||||
|
"grid_mm": 0.05,
|
||||||
|
"clearance_mm": 0.25,
|
||||||
|
"safety_margin_mm": 0.02,
|
||||||
|
"via_diameter_mm": 0.7,
|
||||||
|
"via_drill_mm": 0.3,
|
||||||
|
"elapsed_seconds": 31.17737436400057,
|
||||||
|
"routed_tree_edges": 38,
|
||||||
|
"total_tree_edges": 38,
|
||||||
|
"tracks": 674,
|
||||||
|
"vias": 16,
|
||||||
|
"immutable_identity_sha256": "783ce7e212880e0836c93e1782d39bbfbc505fae856d9720ee6c50f5ba8f6275",
|
||||||
|
"input_sha256": "df62a5aeb64a6d871a477a8c9fe94c100210d5b239a0617558907d9d3d9a6007",
|
||||||
|
"validation": "KiCad DRC must be run separately; tree-edge counts are not DRC connectivity"
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/builddir/build/BUILD/kicad-10.0.6-build/kicad-10.0.6/include/properties/property.h(607): assert "m_choices.GetCount() > 0" failed in PROPERTY_ENUM(): No enum choices defined
|
||||||
|
/builddir/build/BUILD/kicad-10.0.6-build/kicad-10.0.6/include/properties/property.h(607): assert "m_choices.GetCount() > 0" failed in PROPERTY_ENUM(): No enum choices defined
|
||||||
|
/builddir/build/BUILD/kicad-10.0.6-build/kicad-10.0.6/include/properties/property.h(607): assert "m_choices.GetCount() > 0" failed in PROPERTY_ENUM(): No enum choices defined
|
||||||
|
{"net": "/GND", "start": "U1.43", "end": "U1.44", "start_mm": [133.4, 173.8], "end_mm": [133.4, 176.34], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 52}
|
||||||
|
{"net": "/+3V3", "start": "U1.1", "end": "U1.2", "start_mm": [108.0, 123.0], "end_mm": [108.0, 125.54], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 52}
|
||||||
|
{"net": "/+3V3", "start": "R2.1", "end": "DS1.2", "start_mm": [154.0, 134.0], "end_mm": [154.73, 137.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 911}
|
||||||
|
{"net": "/GND", "start": "SW2.2", "end": "SW1.2", "start_mm": [155.0, 172.5], "end_mm": [149.5, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 111}
|
||||||
|
{"net": "/GND", "start": "SW2.2", "end": "SW3.2", "start_mm": [161.5, 172.5], "end_mm": [167.0, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 111}
|
||||||
|
{"net": "/BTN_NEXT", "start": "SW3.1", "end": "SW3.1", "start_mm": [167.0, 168.0], "end_mm": [173.5, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/BTN_PREVIOUS", "start": "SW1.1", "end": "SW1.1", "start_mm": [143.0, 168.0], "end_mm": [149.5, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/BTN_SELECT", "start": "SW2.1", "end": "SW2.1", "start_mm": [155.0, 168.0], "end_mm": [161.5, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/GND", "start": "SW2.2", "end": "SW2.2", "start_mm": [155.0, 172.5], "end_mm": [161.5, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/GND", "start": "SW3.2", "end": "SW3.2", "start_mm": [167.0, 172.5], "end_mm": [173.5, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/GND", "start": "SW1.2", "end": "SW1.2", "start_mm": [149.5, 172.5], "end_mm": [143.0, 172.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 131}
|
||||||
|
{"net": "/+3V3", "start": "U2.1", "end": "R1.1", "start_mm": [143.0, 126.67], "end_mm": [143.0, 134.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 148}
|
||||||
|
{"net": "/GND", "start": "SW1.2", "end": "U1.43", "start_mm": [143.0, 172.5], "end_mm": [133.4, 173.8], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 4535}
|
||||||
|
{"net": "/+3V3", "start": "DS1.2", "end": "R3.1", "start_mm": [154.73, 137.5], "end_mm": [165.0, 134.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 5139}
|
||||||
|
{"net": "/+3V3", "start": "R1.1", "end": "R2.1", "start_mm": [143.0, 134.0], "end_mm": [154.0, 134.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 14153}
|
||||||
|
{"net": "/GND", "start": "U2.2", "end": "U1.23", "start_mm": [145.54, 126.67], "end_mm": [133.4, 123.0], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 9372}
|
||||||
|
{"net": "/GND", "start": "DS1.1", "end": "U2.2", "start_mm": [152.19, 137.5], "end_mm": [145.54, 126.67], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 8327}
|
||||||
|
{"net": "/BTN_SELECT", "start": "SW2.1", "end": "R2.2", "start_mm": [161.5, 168.0], "end_mm": [161.62, 134.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 2039}
|
||||||
|
{"net": "/BTN_NEXT", "start": "SW3.1", "end": "R3.2", "start_mm": [173.5, 168.0], "end_mm": [172.62, 134.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 12615}
|
||||||
|
{"net": "/BTN_PREVIOUS", "start": "SW1.1", "end": "R1.2", "start_mm": [149.5, 168.0], "end_mm": [150.62, 134.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 15179}
|
||||||
|
{"net": "/+3V3", "start": "U1.2", "end": "U2.1", "start_mm": [108.0, 125.54], "end_mm": [143.0, 126.67], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 332527}
|
||||||
|
{"net": "/BTN_PREVIOUS", "start": "U1.16", "end": "SW1.1", "start_mm": [108.0, 161.1], "end_mm": [143.0, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 70393}
|
||||||
|
{"net": "/RS_DCD", "start": "U1.4", "end": "U2.3", "start_mm": [108.0, 130.62], "end_mm": [148.08, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 21855}
|
||||||
|
{"net": "/BTN_SELECT", "start": "SW2.1", "end": "U1.19", "start_mm": [155.0, 168.0], "end_mm": [108.0, 168.72], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 36364}
|
||||||
|
{"net": "/RS_RX", "start": "U1.11", "end": "U2.4", "start_mm": [108.0, 148.4], "end_mm": [150.62, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 123675}
|
||||||
|
{"net": "/RS_TX", "start": "U1.10", "end": "U2.5", "start_mm": [108.0, 145.86], "end_mm": [153.16, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 397389}
|
||||||
|
{"net": "/RS_DTR", "start": "U1.7", "end": "U2.6", "start_mm": [108.0, 138.24], "end_mm": [155.7, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 294174}
|
||||||
|
{"net": "/RS_DSR", "start": "U1.5", "end": "U2.7", "start_mm": [108.0, 133.16], "end_mm": [158.24, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 294332}
|
||||||
|
{"net": "/RS_RTS", "start": "U1.8", "end": "U2.8", "start_mm": [108.0, 140.78], "end_mm": [160.78, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 378833}
|
||||||
|
{"net": "/OLED_SCL", "start": "U1.18", "end": "DS1.3", "start_mm": [108.0, 166.18], "end_mm": [157.27, 137.5], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 442107}
|
||||||
|
{"net": "/RS_CTS", "start": "U1.9", "end": "U2.9", "start_mm": [108.0, 143.32], "end_mm": [163.32, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 409990}
|
||||||
|
{"net": "/OLED_SDA", "start": "U1.17", "end": "DS1.4", "start_mm": [108.0, 163.64], "end_mm": [159.81, 137.5], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 567654}
|
||||||
|
{"net": "/RS_RI", "start": "U1.6", "end": "U2.10", "start_mm": [108.0, 135.7], "end_mm": [165.86, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 436874}
|
||||||
|
{"net": "/BTN_NEXT", "start": "U1.20", "end": "SW3.1", "start_mm": [108.0, 171.26], "end_mm": [167.0, 168.0], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 273975}
|
||||||
|
{"net": "/RS_VALID", "start": "U1.12", "end": "U2.11", "start_mm": [108.0, 150.94], "end_mm": [168.4, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 425742}
|
||||||
|
{"net": "/RS_OFF_N", "start": "U1.15", "end": "U2.12", "start_mm": [108.0, 158.56], "end_mm": [170.94, 126.67], "routed": true, "width_mm": 0.25, "power_fallback": false, "expansions": 645804}
|
||||||
|
{"net": "/GND", "start": "U1.44", "end": "U1.22", "start_mm": [133.4, 176.34], "end_mm": [108.0, 176.34], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 509}
|
||||||
|
{"net": "/GND", "start": "SW1.2", "end": "DS1.1", "start_mm": [149.5, 172.5], "end_mm": [152.19, 137.5], "routed": true, "width_mm": 0.5, "power_fallback": false, "expansions": 617019}
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'PNG file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'JPEG file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'TIFF file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'GIF file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'PNM file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'PCX file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'IFF file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'Windows icon file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'Windows cursor file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'Windows animated cursor file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'TGA file'
|
||||||
|
11:12:00: Debug: Adding duplicate image handler for 'XPM file'
|
||||||
|
{
|
||||||
|
"draft_only": true,
|
||||||
|
"saved": true,
|
||||||
|
"grid_mm": 0.05,
|
||||||
|
"clearance_mm": 0.25,
|
||||||
|
"safety_margin_mm": 0.02,
|
||||||
|
"via_diameter_mm": 0.7,
|
||||||
|
"via_drill_mm": 0.3,
|
||||||
|
"elapsed_seconds": 32.44472043699898,
|
||||||
|
"routed_tree_edges": 38,
|
||||||
|
"total_tree_edges": 38,
|
||||||
|
"tracks": 565,
|
||||||
|
"vias": 22,
|
||||||
|
"immutable_identity_sha256": "783ce7e212880e0836c93e1782d39bbfbc505fae856d9720ee6c50f5ba8f6275",
|
||||||
|
"input_sha256": "0ea7f0abff371eacc81262dc949f768cd42128f389ea4af9facee5034368d884",
|
||||||
|
"validation": "KiCad DRC must be run separately; tree-edge counts are not DRC connectivity"
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
||||||
"coordinate_units": "mm",
|
"coordinate_units": "mm",
|
||||||
"date": "2026-09-20T22:50:33",
|
"date": "2026-09-21T11:16:44",
|
||||||
"ignored_checks": [
|
"ignored_checks": [
|
||||||
{
|
{
|
||||||
"description": "Footprint has no courtyard defined",
|
"description": "Footprint has no courtyard defined",
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
{
|
{
|
||||||
"immutable_geometry_nets_pads_linkage_preserved": true,
|
"immutable_geometry_nets_pads_linkage_preserved": true,
|
||||||
"board_sha256": "03b69c4084dba328b11e2373c219e8fe462bcb3add934cf9f527214b21bd9cb6",
|
"board_sha256": "53e36538c0669604457b9e1a9ff1b1e20422970e8f9f356088c4581868ad5045",
|
||||||
"independent_min_different_net_or_npth_clearance_mm": 0.2749999999999886,
|
"independent_min_different_net_or_npth_clearance_mm": 0.2705441169852849,
|
||||||
"closest_items": [
|
"closest_items": [
|
||||||
"R2.2:c118f27f-8ad4-4abe-b9c0-a30a22fe60c9",
|
"U1.38:4b8eabef-8493-437d-bd02-cb9ff4afd7cd",
|
||||||
"99fb402a-445b-4eba-aedf-76b7f0cad042"
|
"d95f45cb-7fa4-4203-a786-0a069425e2b8"
|
||||||
],
|
],
|
||||||
"min_copper_or_npth_edge_gap_mm": 0.5250000000000057,
|
"min_copper_or_npth_edge_gap_mm": 1.9,
|
||||||
"min_rf_gap_mm": 0.025000000000005684,
|
"min_rf_gap_mm": 0.025000000000005684,
|
||||||
"min_via_to_other_drilled_hole_gap_mm": 0.8401754250991242,
|
"min_via_to_other_drilled_hole_gap_mm": 0.8205441169852848,
|
||||||
"track_width_counts": {
|
"track_width_counts": {
|
||||||
"0.25": 425,
|
"0.25": 451,
|
||||||
"0.5": 144
|
"0.5": 114
|
||||||
},
|
},
|
||||||
"routed_length_mm_by_net": {
|
"routed_length_mm_by_net": {
|
||||||
"/BTN_SELECT": 95.16152954766495,
|
"/BTN_SELECT": 88.94832552628598,
|
||||||
"/GND": 152.2615364335217,
|
"/GND": 141.68412246223386,
|
||||||
"/+3V3": 106.94219089103528,
|
"/+3V3": 90.29604731966161,
|
||||||
"/RS_DCD": 47.76152984055992,
|
"/RS_DCD": 43.068490178879834,
|
||||||
"/RS_DSR": 56.835284377834164,
|
"/RS_DSR": 55.09398706495997,
|
||||||
"/RS_RI": 66.02000635033798,
|
"/RS_RI": 67.91373841554615,
|
||||||
"/RS_DTR": 52.162796381301995,
|
"/RS_DTR": 52.53487761643567,
|
||||||
"/RS_RTS": 56.23655121147421,
|
"/RS_RTS": 60.056714985368814,
|
||||||
"/RS_CTS": 58.073809511675655,
|
"/RS_CTS": 63.65082753507114,
|
||||||
"/RS_TX": 47.51030692030297,
|
"/RS_TX": 53.135260491559045,
|
||||||
"/RS_RX": 43.90406233625865,
|
"/RS_RX": 51.637429252872906,
|
||||||
"/RS_VALID": 64.36573922778403,
|
"/RS_VALID": 72.10630197626116,
|
||||||
"/RS_OFF_N": 67.05000417516253,
|
"/RS_OFF_N": 78.04100118378895,
|
||||||
"/BTN_PREVIOUS": 86.95076506672746,
|
"/BTN_PREVIOUS": 78.83370879187974,
|
||||||
"/OLED_SDA": 54.781626385188304,
|
"/OLED_SDA": 62.65168524184884,
|
||||||
"/OLED_SCL": 53.30787155502609,
|
"/OLED_SCL": 61.1779295330028,
|
||||||
"/BTN_NEXT": 106.4266552277327
|
"/BTN_NEXT": 101.24898628384834
|
||||||
},
|
},
|
||||||
"tracks": 569,
|
"tracks": 565,
|
||||||
"vias": 20,
|
"vias": 22,
|
||||||
"copper_zones": 0,
|
"copper_zones": 0,
|
||||||
"drc_freshly_run": true,
|
"drc_freshly_run": true,
|
||||||
"drc_violations": 0,
|
"drc_violations": 0,
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ for i, a in enumerate(items):
|
|||||||
if distance < min_clearance:
|
if distance < min_clearance:
|
||||||
min_clearance, closest = distance, (a[0], b[0])
|
min_clearance, closest = distance, (a[0], b[0])
|
||||||
assert min_clearance >= 0.25 - 1e-6, (min_clearance, closest)
|
assert min_clearance >= 0.25 - 1e-6, (min_clearance, closest)
|
||||||
outline = Polygon([(100,100),(182,100),(182,180),(136,180),(136,161),(100,161)])
|
outline = Polygon([(136,100),(182,100),(182,180),(100,180),(100,119),(136,119)])
|
||||||
rf = box(110, 100, 131, 113)
|
rf = box(110, 119, 131, 132)
|
||||||
min_edge, min_rf = float('inf'), float('inf')
|
min_edge, min_rf = float('inf'), float('inf')
|
||||||
for label, net, layers, geom, radius in items:
|
for label, net, layers, geom, radius in items:
|
||||||
assert outline.covers(geom), label
|
assert outline.covers(geom), label
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
"$schema": "https://schemas.kicad.org/drc.v1.json",
|
||||||
"coordinate_units": "mm",
|
"coordinate_units": "mm",
|
||||||
"date": "2026-09-20T23:07:33",
|
"date": "2026-09-21T11:39:13",
|
||||||
"ignored_checks": [
|
"ignored_checks": [
|
||||||
{
|
{
|
||||||
"description": "Footprint has no courtyard defined",
|
"description": "Footprint has no courtyard defined",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"a0_snapshot_unchanged": true,
|
||||||
|
"a1_unrouted_snapshot_unchanged": true,
|
||||||
|
"protected_files_unchanged": [
|
||||||
|
"serial-carrier.kicad_pro",
|
||||||
|
"serial-carrier.kicad_sch",
|
||||||
|
"serial-carrier.kicad_prl",
|
||||||
|
"fp-lib-table",
|
||||||
|
"sym-lib-table",
|
||||||
|
"Carrier.kicad_sym",
|
||||||
|
"Carrier.pretty/OLED_26mm_I2C_Provisional.kicad_mod",
|
||||||
|
"Carrier.pretty/HW678_2x22_Provisional.kicad_mod",
|
||||||
|
"Carrier.pretty/MAX3243_Reference_Provisional.kicad_mod"
|
||||||
|
],
|
||||||
|
"intentional_documentation_updates": [
|
||||||
|
"README.md",
|
||||||
|
"pcb-draft-notes.md"
|
||||||
|
],
|
||||||
|
"cad_projection_graphics": 7,
|
||||||
|
"footprints_identical_except_authorized_placement": true,
|
||||||
|
"footprint_count": 13,
|
||||||
|
"unrelated_board_graphics_preserved": 5,
|
||||||
|
"non_owned_board_graphic_uuids_preserved": true,
|
||||||
|
"refreshed_device_silkscreen_items": 91,
|
||||||
|
"pcb_editor_lock_absent": true,
|
||||||
|
"final_board_sha256": "53e36538c0669604457b9e1a9ff1b1e20422970e8f9f356088c4581868ad5045"
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
"""Compare A1 against the untouched A0 snapshot, including unrelated graphics."""
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
ROOT=Path(__file__).resolve().parent.parent
|
||||||
|
OUT=ROOT/'validation'
|
||||||
|
|
||||||
|
|
||||||
|
def parse(path):
|
||||||
|
tokens=iter(re.findall(r'"(?:\\.|[^"\\])*"|[()]|[^\s()]+',path.read_text()))
|
||||||
|
def group():
|
||||||
|
result=[]
|
||||||
|
for token in tokens:
|
||||||
|
if token==')':
|
||||||
|
return result
|
||||||
|
result.append(group() if token=='(' else token)
|
||||||
|
raise ValueError('unterminated S-expression')
|
||||||
|
assert next(tokens)=='('
|
||||||
|
return group()
|
||||||
|
|
||||||
|
|
||||||
|
def child(item,key):
|
||||||
|
return next(v for v in item if isinstance(v,list) and v[0]==key)
|
||||||
|
|
||||||
|
|
||||||
|
def ident(item):
|
||||||
|
return child(item,'uuid')[1].strip('"')
|
||||||
|
|
||||||
|
|
||||||
|
def sha(path):
|
||||||
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
report=json.loads(OUT.joinpath('pcb-routing-reposition.json').read_text())
|
||||||
|
a0=OUT/'pcb-a0-before-reposition.kicad_pcb'
|
||||||
|
a1=ROOT/'serial-carrier.kicad_pcb'
|
||||||
|
assert sha(a0)==report['a0_snapshot_sha256'], 'A0 snapshot changed'
|
||||||
|
assert sha(OUT/'pcb-routing-input.kicad_pcb')==report['a1_unrouted_sha256'], 'A1 routing input changed'
|
||||||
|
# These two handoff documents are intentionally updated during A1 integration.
|
||||||
|
protected={name:digest for name,digest in report['protected_file_sha256'].items()
|
||||||
|
if name not in ('README.md','pcb-draft-notes.md')}
|
||||||
|
for name,digest in protected.items():
|
||||||
|
assert sha(ROOT/name)==digest, 'protected file changed: '+name
|
||||||
|
old,new=parse(a0),parse(a1)
|
||||||
|
old_fp={ident(x):x for x in old if isinstance(x,list) and x[0]=='footprint'}
|
||||||
|
new_fp={ident(x):x for x in new if isinstance(x,list) and x[0]=='footprint'}
|
||||||
|
assert set(old_fp)==set(new_fp)
|
||||||
|
for key,fp in old_fp.items():
|
||||||
|
# Footprint geometry, fields, UUIDs and connectivity must be identical;
|
||||||
|
# only the top-level placement may differ, and only for the four moves.
|
||||||
|
ref=next(v[2].strip('"') for v in fp if isinstance(v,list) and v[:2]==['property','"Reference"'])
|
||||||
|
cleaned=lambda f:[v for v in f if not isinstance(v,list) or v[0]!='at']
|
||||||
|
assert cleaned(fp)==cleaned(new_fp[key]), 'footprint content changed: '+ref
|
||||||
|
if ref not in report['moves_local_mm']:
|
||||||
|
assert child(fp,'at')==child(new_fp[key],'at'),ref
|
||||||
|
else:
|
||||||
|
expected=report['moves_local_mm'][ref][1]
|
||||||
|
assert [round(float(v)-100,6) for v in child(new_fp[key],'at')[1:3]]==expected,ref
|
||||||
|
ns=uuid.UUID('67964aa4-c3a5-45d8-9b54-250fb68e7c41')
|
||||||
|
owned={str(uuid.uuid5(ns,str(i))) for i in range(4096)}
|
||||||
|
cad_ns=uuid.UUID('ba7274a8-c35d-46bc-908d-cd86708fc6c4')
|
||||||
|
cad_owned={str(uuid.uuid5(cad_ns,str(i))) for i in range(7)}
|
||||||
|
changed_texts=set(report['changed_board_text_uuids'])
|
||||||
|
for item in old:
|
||||||
|
if isinstance(item,list) and item[0]=='gr_text':
|
||||||
|
value=json.loads(item[1])
|
||||||
|
if value=='82 x 80 mm / 2 copper layers' or value.startswith('DE-9 OUT /'):
|
||||||
|
changed_texts.add(ident(item))
|
||||||
|
|
||||||
|
def drawings(tree):
|
||||||
|
return {ident(x):x for x in tree if isinstance(x,list) and
|
||||||
|
(x[0].startswith('gr_') or x[0] in ('dimension','target','image'))}
|
||||||
|
|
||||||
|
old_gr,new_gr=drawings(old),drawings(new)
|
||||||
|
edge_ids={key for key,value in old_gr.items() if child(value,'layer')[1]=='"Edge.Cuts"'}
|
||||||
|
assert cad_owned <= set(new_gr), 'missing CAD projection graphics'
|
||||||
|
assert all(child(new_gr[key],'layer')[1]=='"Dwgs.User"' for key in cad_owned)
|
||||||
|
excluded=owned|cad_owned|changed_texts|edge_ids
|
||||||
|
unchanged_old={key:value for key,value in old_gr.items() if key not in excluded}
|
||||||
|
unchanged_new={key:value for key,value in new_gr.items() if key not in excluded}
|
||||||
|
assert unchanged_old==unchanged_new, 'unrelated board graphics changed'
|
||||||
|
assert set(old_gr)-owned==set(new_gr)-owned-cad_owned, 'non-owned board graphic UUID set changed'
|
||||||
|
assert [x for x in old if isinstance(x,list) and x[0]=='net']==[x for x in new if isinstance(x,list) and x[0]=='net']
|
||||||
|
assert not (ROOT/'~serial-carrier.kicad_pcb.lck').exists(), 'PCB reopened before handoff'
|
||||||
|
result=dict(a0_snapshot_unchanged=True,a1_unrouted_snapshot_unchanged=True,
|
||||||
|
protected_files_unchanged=list(protected),
|
||||||
|
intentional_documentation_updates=['README.md','pcb-draft-notes.md'],
|
||||||
|
cad_projection_graphics=len(cad_owned),
|
||||||
|
footprints_identical_except_authorized_placement=True,
|
||||||
|
footprint_count=len(old_fp),unrelated_board_graphics_preserved=len(unchanged_old),
|
||||||
|
non_owned_board_graphic_uuids_preserved=True,
|
||||||
|
refreshed_device_silkscreen_items=len(set(new_gr)&owned),
|
||||||
|
pcb_editor_lock_absent=True,final_board_sha256=sha(a1))
|
||||||
|
OUT.joinpath('pcb-routing-preservation.json').write_text(json.dumps(result,indent=2)+'\n')
|
||||||
|
print(json.dumps(result,indent=2))
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"revision": "A1",
|
||||||
|
"a0_snapshot_sha256": "25669051086d6a5315650580ead8ce847f42ae07b8d08af5d76f0a813131ff18",
|
||||||
|
"a1_unrouted_sha256": "df62a5aeb64a6d871a477a8c9fe94c100210d5b239a0617558907d9d3d9a6007",
|
||||||
|
"outline_local_mm": [
|
||||||
|
[
|
||||||
|
36,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
82,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
82,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
19
|
||||||
|
],
|
||||||
|
[
|
||||||
|
36,
|
||||||
|
19
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"moves_local_mm": {
|
||||||
|
"U1": [
|
||||||
|
[
|
||||||
|
8,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
[
|
||||||
|
8,
|
||||||
|
23
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"U2": [
|
||||||
|
[
|
||||||
|
43,
|
||||||
|
30
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43,
|
||||||
|
26.67
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"H1": [
|
||||||
|
[
|
||||||
|
3.5,
|
||||||
|
3.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.5,
|
||||||
|
22.5
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"H2": [
|
||||||
|
[
|
||||||
|
3.5,
|
||||||
|
56
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.5,
|
||||||
|
75
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changed_board_text_uuids": [
|
||||||
|
"e8228b54-6114-48a6-80ef-7f0a68175b1d",
|
||||||
|
"ce061da3-831a-4fb1-9676-9177435d649e",
|
||||||
|
"f45876c7-f270-4231-bfa4-b69ded09f3a4",
|
||||||
|
"c695eb94-004e-4a56-801a-0ca6797a46b2",
|
||||||
|
"94303a67-2a65-4a16-9499-2b409a657ba6",
|
||||||
|
"fa700fde-d4f3-437c-9b51-4322d0fd68e0",
|
||||||
|
"de46e0e9-8a3a-42d0-b1c7-8915a9856836"
|
||||||
|
],
|
||||||
|
"normalized_footprints_preserved": true,
|
||||||
|
"nets_preserved": true,
|
||||||
|
"protected_file_sha256": {
|
||||||
|
"serial-carrier.kicad_pro": "9ad5b992785ebfe01fa7ea4e1aa391a2c7d7f1f2c3b1c13136173c301ecb6516",
|
||||||
|
"serial-carrier.kicad_sch": "1876ef30aae16d9eb4a421efea7c090c50cddd16a59a9bfe866ed5c802e2571c",
|
||||||
|
"serial-carrier.kicad_prl": "b448331403441aabec6e4718d3c82f4fac4073fa9430762d6bb0b5b34c16def9",
|
||||||
|
"README.md": "d3339535217e2c2e214a620e7826878d2ae635eec288041f57012966e95765ca",
|
||||||
|
"pcb-draft-notes.md": "7be3fa469867b98530a84a61fc978b89d853a1652963a8d24373a64f367f8360",
|
||||||
|
"fp-lib-table": "a56dc436d1442de03c7953b426a90444dce078d57b24f70f1675d0beb19acd4e",
|
||||||
|
"sym-lib-table": "f3520b095a4f6238fb7a46dd5c8ab460a8e842dc085a53d2c7f912609015b5ae",
|
||||||
|
"Carrier.kicad_sym": "ba642459312b5b5cb5b5021d0b573c427c3ab93a2cde51941f018bbcba07716a",
|
||||||
|
"Carrier.pretty/OLED_26mm_I2C_Provisional.kicad_mod": "ab2aa7cb0e84d31f18e2493088830964b04764f44873dfb0e30a4c7faa43ef37",
|
||||||
|
"Carrier.pretty/HW678_2x22_Provisional.kicad_mod": "87a32630a388ebd1646ca04a1152e29833735a62e2fca980eaa187c7ca386e20",
|
||||||
|
"Carrier.pretty/MAX3243_Reference_Provisional.kicad_mod": "90c7c0b24007a7e8cb6815641363880fe420fe60e76ea7670c83c217a6d47ecd"
|
||||||
|
},
|
||||||
|
"remaining_tracks": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
"""One-shot A0 -> A1 placement migration of the existing board, never regeneration.
|
||||||
|
|
||||||
|
Requires --apply and the untouched pcb-a0-before-reposition.kicad_pcb snapshot.
|
||||||
|
Never replaces the snapshot or accepts an already edited/migrated input.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pcbnew as p
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from route_pcb_draft import identity
|
||||||
|
|
||||||
|
PCB = ROOT / 'serial-carrier.kicad_pcb'
|
||||||
|
BACKUP = ROOT / 'validation/pcb-a0-before-reposition.kicad_pcb'
|
||||||
|
LOCK = ROOT / '~serial-carrier.kicad_pcb.lck'
|
||||||
|
OLD = [(0,0),(82,0),(82,80),(36,80),(36,61),(0,61)]
|
||||||
|
NEW = [(36,0),(82,0),(82,80),(0,80),(0,19),(36,19)]
|
||||||
|
MOVES = {'U1': ((8,4),(8,23)), 'U2': ((43,30),(43,26.670)),
|
||||||
|
'H1': ((3.5,3.5),(3.5,22.5)), 'H2': ((3.5,56),(3.5,75))}
|
||||||
|
TEXT_MOVES = {'RF RESERVE':19, 'VERIFY ANTENNA':19, 'ESP32 HW678':19,
|
||||||
|
'USB ACCESS - VERIFY':19, 'DE-9 OUT / VERIFY MALE':-3.330,
|
||||||
|
'MAX3243 REF':-3.330}
|
||||||
|
|
||||||
|
|
||||||
|
def vec(x,y):
|
||||||
|
return p.VECTOR2I(p.FromMM(x),p.FromMM(y))
|
||||||
|
|
||||||
|
|
||||||
|
def local(v):
|
||||||
|
return (round(p.ToMM(v.x)-100,6),round(p.ToMM(v.y)-100,6))
|
||||||
|
|
||||||
|
|
||||||
|
def sha(path):
|
||||||
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def normalized_footprints(board):
|
||||||
|
# Normalize placement only, retaining UUIDs, local pad geometry and linkage.
|
||||||
|
entries = identity(board)[1]
|
||||||
|
normalized = []
|
||||||
|
for entry in entries:
|
||||||
|
fields = list(entry)
|
||||||
|
x,y = fields[5]
|
||||||
|
fields[5] = (0,0)
|
||||||
|
pads = []
|
||||||
|
for pad in fields[-1]:
|
||||||
|
values = list(pad)
|
||||||
|
values[3] = (round(values[3][0]-x,6),round(values[3][1]-y,6))
|
||||||
|
pads.append(tuple(values))
|
||||||
|
fields[-1] = pads
|
||||||
|
normalized.append(tuple(fields))
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser=argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--apply',action='store_true')
|
||||||
|
args=parser.parse_args()
|
||||||
|
if not args.apply:
|
||||||
|
parser.error('explicit --apply required; only accepts the original A0 snapshot')
|
||||||
|
assert not LOCK.exists(), 'PCB Editor lock exists'
|
||||||
|
assert BACKUP.exists(), 'make the one-time A0 snapshot first'
|
||||||
|
before=PCB.read_bytes()
|
||||||
|
assert before==BACKUP.read_bytes(), 'current board differs from A0 snapshot; refusing migration'
|
||||||
|
protected=[ROOT/name for name in ('serial-carrier.kicad_pro','serial-carrier.kicad_sch',
|
||||||
|
'serial-carrier.kicad_prl','README.md','pcb-draft-notes.md','fp-lib-table',
|
||||||
|
'sym-lib-table','Carrier.kicad_sym')]
|
||||||
|
protected+=list((ROOT/'Carrier.pretty').glob('*'))
|
||||||
|
hashes={str(path.relative_to(ROOT)):sha(path) for path in protected if path.is_file()}
|
||||||
|
board=p.LoadBoard(str(PCB))
|
||||||
|
immutable=normalized_footprints(board)
|
||||||
|
net_table=identity(board)[-1]
|
||||||
|
footprints={f.GetReference():f for f in board.GetFootprints()}
|
||||||
|
for ref,(old,new) in MOVES.items():
|
||||||
|
fp=footprints[ref]
|
||||||
|
assert local(fp.GetPosition())==old,(ref,local(fp.GetPosition()))
|
||||||
|
fp.SetPosition(vec(new[0]+100,new[1]+100))
|
||||||
|
for track in list(board.GetTracks()):
|
||||||
|
board.Delete(track)
|
||||||
|
edges=[d for d in board.GetDrawings() if d.GetLayer()==p.Edge_Cuts]
|
||||||
|
assert len(edges)==6
|
||||||
|
for i,(a,b) in enumerate(zip(OLD,OLD[1:]+OLD[:1])):
|
||||||
|
edge=next(e for e in edges if frozenset((local(e.GetStart()),local(e.GetEnd())))==frozenset((a,b)))
|
||||||
|
aa,bb=NEW[i],NEW[(i+1)%6]
|
||||||
|
edge.SetStart(vec(aa[0]+100,aa[1]+100))
|
||||||
|
edge.SetEnd(vec(bb[0]+100,bb[1]+100))
|
||||||
|
assert len(board.Zones())==1 and board.Zones()[0].GetIsRuleArea()
|
||||||
|
zone=board.Zones()[0]
|
||||||
|
contour=zone.Outline().COutline(0)
|
||||||
|
assert {local(contour.CPoint(i)) for i in range(contour.PointCount())}=={(10,0),(31,0),(31,13),(10,13)}
|
||||||
|
zone.Move(vec(0,19))
|
||||||
|
changed_texts=[]
|
||||||
|
for text,dy in TEXT_MOVES.items():
|
||||||
|
found=[d for d in board.GetDrawings() if isinstance(d,p.PCB_TEXT) and d.GetText()==text]
|
||||||
|
assert len(found)==1,(text,len(found))
|
||||||
|
drawing=found[0]
|
||||||
|
drawing.Move(vec(0,dy))
|
||||||
|
if text=='DE-9 OUT / VERIFY MALE':
|
||||||
|
drawing.SetText('DE-9 OUT / NOMINAL MALE')
|
||||||
|
changed_texts.append(drawing.m_Uuid.AsString())
|
||||||
|
for d in board.GetDrawings():
|
||||||
|
if isinstance(d,p.PCB_TEXT) and d.GetText()=='Carrier envelope only; module overhang and connector clearance not qualified':
|
||||||
|
d.SetText('Nominal RS-232 metal verified; ESP32 USB / antenna / assembly clearance pending')
|
||||||
|
changed_texts.append(d.m_Uuid.AsString())
|
||||||
|
title=board.GetTitleBlock()
|
||||||
|
title.SetRevision('A1 DRAFT')
|
||||||
|
title.SetComment(0,'NOT FOR FABRICATION - USB / antenna geometry pending; verify module pinout / assembly')
|
||||||
|
assert normalized_footprints(board)==immutable
|
||||||
|
assert identity(board)[-1]==net_table
|
||||||
|
candidate=ROOT/'validation/pcb-routing-reposition-candidate.kicad_pcb'
|
||||||
|
assert p.SaveBoard(str(candidate),board,True)
|
||||||
|
saved=p.LoadBoard(str(candidate))
|
||||||
|
assert normalized_footprints(saved)==immutable
|
||||||
|
assert identity(saved)==identity(board)
|
||||||
|
assert not LOCK.exists() and PCB.read_bytes()==before
|
||||||
|
assert all(sha(ROOT/name)==digest for name,digest in hashes.items()), 'protected input changed'
|
||||||
|
candidate.replace(PCB)
|
||||||
|
report=dict(revision='A1',a0_snapshot_sha256=sha(BACKUP),a1_unrouted_sha256=sha(PCB),
|
||||||
|
outline_local_mm=NEW,moves_local_mm=MOVES,changed_board_text_uuids=changed_texts,
|
||||||
|
normalized_footprints_preserved=True,nets_preserved=True,
|
||||||
|
protected_file_sha256=hashes,remaining_tracks=len(saved.GetTracks()))
|
||||||
|
ROOT.joinpath('validation/pcb-routing-reposition.json').write_text(json.dumps(report,indent=2)+'\n')
|
||||||
|
print(json.dumps(report,indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
main()
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,118 @@
|
|||||||
# Two-layer draft routing result
|
# A1 top-left-notch / bottom-USB routing — current result
|
||||||
|
|
||||||
|
Date: 2026-09-21. Actual revised board: `../serial-carrier.kicad_pcb`.
|
||||||
|
|
||||||
|
**38/38 connections across 17 multi-pad nets; 0 physical DRC violations,
|
||||||
|
0 unconnected items; exactly 1 expected U2 footprint parity warning.**
|
||||||
|
The warning is the board-only `Carrier:MAX3243_Reference_Provisional` assignment
|
||||||
|
versus the intentionally blank schematic footprint, not a net mismatch.
|
||||||
|
|
||||||
|
## Geometry and preservation
|
||||||
|
|
||||||
|
Coordinates below are local to absolute origin (100,100) mm; all dimensions mm.
|
||||||
|
|
||||||
|
- Carrier: 82 x 80, outline `[(36,0),(82,0),(82,80),(0,80),(0,19),(36,19)]`.
|
||||||
|
- U1 origin: (8,23), formerly (8,4). Header end-pad centres now Y=76.340;
|
||||||
|
USB guide Y=83 is 3 mm below the bottom edge. No ESP32 body was invented.
|
||||||
|
- U2 origin: (43,26.670), formerly (43,30). Actual reference F.Fab bare-PCB
|
||||||
|
front edge is absolute Y=100.000, flush with carrier local Y=0.
|
||||||
|
- Nominal male metal external dimensions are user-confirmed: shell overhang
|
||||||
|
3.870, hex-mouth overhang 2.870, flange front 1.930 behind the carrier edge.
|
||||||
|
With this placement their local Y positions are -3.870, -2.870 and +1.930.
|
||||||
|
Integration added seven board-local Dwgs.User graphics for the CAD metal
|
||||||
|
projections and screw-lock axes.
|
||||||
|
- H1: (3.5,22.5), H2: (3.5,75). H3/H4 unchanged at (78,4)/(78,76).
|
||||||
|
- OLED, resistors and switches unchanged.
|
||||||
|
- RF reserve: local X=10..31 / Y=19..32, absolute X=110..131 / Y=119..132,
|
||||||
|
both layers; no tracks, vias or fills inside it. Boundary remains provisional.
|
||||||
|
|
||||||
|
The existing A0 board was mutated, **not regenerated**. The one-time
|
||||||
|
`pcb-a0-before-reposition.kicad_pcb` backup remains byte-for-byte intact.
|
||||||
|
`pcb-routing-input.kicad_pcb` is now the A1 unrouted audit input, not A0.
|
||||||
|
`pcb-routing-preservation.py` verified all 13 complete serialized footprints
|
||||||
|
identical except the four authorized top-level placements, including schematic
|
||||||
|
fields, pad/net definitions, electrical UUID paths and all footprint graphics.
|
||||||
|
Non-owned board-graphic UUIDs and unrelated graphics are preserved. ESP/RS/RF
|
||||||
|
annotations moved with placement; the post-routing silkscreen refresh replaced
|
||||||
|
91 owned graphics. Its edge clipping avoids silk across U2's flush front edge.
|
||||||
|
|
||||||
|
All board saves used `SaveBoard(..., True)`. Project `.pro`, `.prl`, schematic,
|
||||||
|
symbol/footprint libraries and library tables retain their original hashes.
|
||||||
|
README and pcb-draft-notes were intentionally updated during integration. The generator's constants/text were updated but it was **not
|
||||||
|
run**; its automatic project/netclass writes were removed. PCB Editor lock was
|
||||||
|
absent at migration, routing, silkscreen refresh and final preservation check.
|
||||||
|
|
||||||
|
## Routing and independent measurements
|
||||||
|
|
||||||
|
- F.Cu/B.Cu; **565 segments, 22 through vias**.
|
||||||
|
- **451 signal segments at 0.25 mm; 114 +3V3/GND segments at 0.5 mm**.
|
||||||
|
- Vias: 0.7 mm copper / 0.3 mm drill. No copper pours needed.
|
||||||
|
- Continuous measured minimum different-net/NPTH clearance: **0.270544 mm**.
|
||||||
|
- Minimum copper/NPTH-to-edge gap: **1.900 mm**.
|
||||||
|
- Minimum copper/NPTH-to-RF-reservation gap: **0.025 mm**.
|
||||||
|
- Minimum via-drill edge to other drilled-hole edge: **0.820544 mm**.
|
||||||
|
|
||||||
|
Two bounded passes, each with 240 seconds / 1,200,000 expansions per search:
|
||||||
|
|
||||||
|
1. 31.18 seconds, 38/38, 674 segments / 16 vias, physical DRC clean. One GND
|
||||||
|
connection required the router's 0.25 mm fallback; strict width validation
|
||||||
|
correctly failed. Retained as `pcb-routing-a1-pass1.kicad_pcb` for evidence.
|
||||||
|
2. 32.44 seconds, 38/38, 565 segments / 22 vias. Short GND connections (<=15 mm)
|
||||||
|
now route before longer signal fanout; long GND links remain last. All power
|
||||||
|
and ground now use 0.5 mm; no fallback. This is the saved current result.
|
||||||
|
|
||||||
|
The grid remains 0.05 mm, physical clearance 0.25 mm plus 0.02 mm routing margin.
|
||||||
|
No rip-up/unbounded retry or external network router was used.
|
||||||
|
|
||||||
|
## Validation / rerun commands
|
||||||
|
|
||||||
|
From `hardware/PCB`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/usr/bin/python3 -B validate_pcb_draft.py
|
||||||
|
/usr/bin/python3 -B validation/pcb-routing-check.py
|
||||||
|
/usr/bin/python3 -B validation/pcb-routing-preservation.py
|
||||||
|
kicad-cli pcb drc --format json --all-track-errors --severity-all --exit-code-violations -o validation/pcb-routing-all-drc.json serial-carrier.kicad_pcb
|
||||||
|
```
|
||||||
|
|
||||||
|
All passed on A1. Validators perform fresh KiCad DRC, fresh schematic-netlist
|
||||||
|
comparison, geometry checks, continuous copper-distance checks, and routing
|
||||||
|
save-refusal tests. DRC was read-only to preserve project settings; no copper
|
||||||
|
zones exist to refill. Existing ignored DRC checks were not changed; see reports.
|
||||||
|
The preservation audit includes the intentional documentation updates and seven
|
||||||
|
CAD projection graphics while retaining electrical and project preservation checks.
|
||||||
|
|
||||||
|
Rerouting remains explicitly destructive to existing/manual copper and requires:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/usr/bin/python3 -B route_pcb_draft.py --route --overwrite --replace-routes --seconds 240 --max-expansions 1200000
|
||||||
|
/usr/bin/python3 -B add_device_silkscreen.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Then rerun the validators. The one-shot `pcb-routing-reposition.py --apply`
|
||||||
|
refuses anything except the original A0 snapshot and must **not** be rerun on A1.
|
||||||
|
|
||||||
|
Current evidence: `pcb-routing-a1-pass2.log`, `pcb-routing-run.json`,
|
||||||
|
`pcb-routing-drc.json`, `pcb-routing-all-drc.json`, `pcb-parity-drc.json`,
|
||||||
|
`pcb-validation-summary.json`, `pcb-routing-check.json`,
|
||||||
|
`pcb-routing-reposition.json`, and `pcb-routing-preservation.json`.
|
||||||
|
The current post-integration board SHA-256 is recorded in
|
||||||
|
`pcb-routing-check.json`; the CAD projection additions do not change copper.
|
||||||
|
|
||||||
|
**Still a draft, not fabrication-ready.** ESP32 geometry/antenna/USB access,
|
||||||
|
module pinout/header fit, full assembly/height clearances, power integrity and
|
||||||
|
hardware operation remain unqualified. Nominal RS-232 metal dimensions alone
|
||||||
|
are not assembly qualification. Placement PDF, SVG/PNG and carrier-only 3D
|
||||||
|
preview were refreshed after CAD envelope integration. No Gerbers generated.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Historical A0 routing result — superseded by A1 above
|
||||||
|
|
||||||
|
The following records the earlier A0 routing/integration. Its dimensions, counts,
|
||||||
|
measurements and no-save reproduction do not describe current A1. A0 `pass1`,
|
||||||
|
`pass2`, `pass3`, `baseline` and `dry-run` files are historical; use the A1 and
|
||||||
|
current reports listed above.
|
||||||
|
|
||||||
Date: 2026-09-20. KiCad 10.0.6; system `/usr/bin/python3`.
|
Date: 2026-09-20. KiCad 10.0.6; system `/usr/bin/python3`.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<export version="E">
|
<export version="E">
|
||||||
<design>
|
<design>
|
||||||
<source>/home/mscholz/Repos/ESP32_serial_swiss_army_knife/hardware/PCB/serial-carrier.kicad_sch</source>
|
<source>/home/mscholz/Repos/ESP32_serial_swiss_army_knife/hardware/PCB/serial-carrier.kicad_sch</source>
|
||||||
<date>2026-09-20T23:07:27</date>
|
<date>2026-09-21T11:39:08</date>
|
||||||
<tool>Eeschema 10.0.6-1.fc44</tool>
|
<tool>Eeschema 10.0.6-1.fc44</tool>
|
||||||
<sheet number="1" name="/" tstamps="/">
|
<sheet number="1" name="/" tstamps="/">
|
||||||
<title_block>
|
<title_block>
|
||||||
|
|||||||
@@ -3,6 +3,100 @@
|
|||||||
82,
|
82,
|
||||||
80
|
80
|
||||||
],
|
],
|
||||||
|
"outline_local_mm": [
|
||||||
|
[
|
||||||
|
36,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
82,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
82,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
19
|
||||||
|
],
|
||||||
|
[
|
||||||
|
36,
|
||||||
|
19
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"placements_local_mm": {
|
||||||
|
"U1": [
|
||||||
|
8,
|
||||||
|
23
|
||||||
|
],
|
||||||
|
"U2": [
|
||||||
|
43,
|
||||||
|
26.67
|
||||||
|
],
|
||||||
|
"DS1": [
|
||||||
|
43,
|
||||||
|
36
|
||||||
|
],
|
||||||
|
"R1": [
|
||||||
|
43,
|
||||||
|
34
|
||||||
|
],
|
||||||
|
"R2": [
|
||||||
|
54,
|
||||||
|
34
|
||||||
|
],
|
||||||
|
"R3": [
|
||||||
|
65,
|
||||||
|
34
|
||||||
|
],
|
||||||
|
"SW1": [
|
||||||
|
43,
|
||||||
|
68
|
||||||
|
],
|
||||||
|
"SW2": [
|
||||||
|
55,
|
||||||
|
68
|
||||||
|
],
|
||||||
|
"SW3": [
|
||||||
|
67,
|
||||||
|
68
|
||||||
|
],
|
||||||
|
"H1": [
|
||||||
|
3.5,
|
||||||
|
22.5
|
||||||
|
],
|
||||||
|
"H2": [
|
||||||
|
3.5,
|
||||||
|
75
|
||||||
|
],
|
||||||
|
"H3": [
|
||||||
|
78,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"H4": [
|
||||||
|
78,
|
||||||
|
76
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rf_reserve_absolute_mm": [
|
||||||
|
110,
|
||||||
|
119,
|
||||||
|
131,
|
||||||
|
132
|
||||||
|
],
|
||||||
|
"u2_bare_pcb_front_local_y_mm": 0.0,
|
||||||
|
"nominal_user_confirmed_rs232_metal_mm": {
|
||||||
|
"shell_overhang": 3.87,
|
||||||
|
"hex_mouth_overhang": 2.87,
|
||||||
|
"flange_front_inset": 1.93
|
||||||
|
},
|
||||||
|
"tracks": 565,
|
||||||
|
"vias": 22,
|
||||||
"copper_layers": 2,
|
"copper_layers": 2,
|
||||||
"draft_thickness_mm": 1.6,
|
"draft_thickness_mm": 1.6,
|
||||||
"electrical_footprints": 9,
|
"electrical_footprints": 9,
|
||||||
@@ -10,7 +104,8 @@
|
|||||||
"physical_DRC_violations": 0,
|
"physical_DRC_violations": 0,
|
||||||
"unconnected_items": 0,
|
"unconnected_items": 0,
|
||||||
"schematic_pad_nets_match": true,
|
"schematic_pad_nets_match": true,
|
||||||
"known_parity_warning": "U2 provisional female-reference footprint assigned on PCB only; schematic footprint intentionally blank",
|
"known_parity_warning": "U2 reference footprint assigned on PCB only; schematic footprint intentionally blank; nominal male external metal dimensions user-confirmed",
|
||||||
|
"project_settings_sha256": "9ad5b992785ebfe01fa7ea4e1aa391a2c7d7f1f2c3b1c13136173c301ecb6516",
|
||||||
"ignored_DRC_checks": [
|
"ignored_DRC_checks": [
|
||||||
{
|
{
|
||||||
"description": "Footprint has no courtyard defined",
|
"description": "Footprint has no courtyard defined",
|
||||||
@@ -34,8 +129,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"not_validated": [
|
"not_validated": [
|
||||||
"male module pin map/fit",
|
"male module pin map/header fit",
|
||||||
"assembly envelope and heights",
|
"ESP32 geometry and complete assembly envelope/heights",
|
||||||
"header/socket fit",
|
"header/socket fit",
|
||||||
"RF performance and antenna clearance",
|
"RF performance and antenna clearance",
|
||||||
"power/return-path integrity",
|
"power/return-path integrity",
|
||||||
|
|||||||
Reference in New Issue
Block a user