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:
2026-09-21 12:32:01 +02:00
parent c0f7a1ad51
commit 7d76a41540
21 changed files with 26918 additions and 5227 deletions
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

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 175 KiB

+2 -2
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schemas.kicad.org/drc.v1.json",
"coordinate_units": "mm",
"date": "2026-09-20T23:07:29",
"date": "2026-09-21T11:39:10",
"ignored_checks": [
{
"description": "Footprint has no courtyard defined",
@@ -38,7 +38,7 @@
"description": "Footprint U2",
"pos": {
"x": 143.0,
"y": 130.0
"y": 126.67
},
"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",
"coordinate_units": "mm",
"date": "2026-09-20T22:50:33",
"date": "2026-09-21T11:16:44",
"ignored_checks": [
{
"description": "Footprint has no courtyard defined",
+27 -27
View File
@@ -1,39 +1,39 @@
{
"immutable_geometry_nets_pads_linkage_preserved": true,
"board_sha256": "03b69c4084dba328b11e2373c219e8fe462bcb3add934cf9f527214b21bd9cb6",
"independent_min_different_net_or_npth_clearance_mm": 0.2749999999999886,
"board_sha256": "53e36538c0669604457b9e1a9ff1b1e20422970e8f9f356088c4581868ad5045",
"independent_min_different_net_or_npth_clearance_mm": 0.2705441169852849,
"closest_items": [
"R2.2:c118f27f-8ad4-4abe-b9c0-a30a22fe60c9",
"99fb402a-445b-4eba-aedf-76b7f0cad042"
"U1.38:4b8eabef-8493-437d-bd02-cb9ff4afd7cd",
"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_via_to_other_drilled_hole_gap_mm": 0.8401754250991242,
"min_via_to_other_drilled_hole_gap_mm": 0.8205441169852848,
"track_width_counts": {
"0.25": 425,
"0.5": 144
"0.25": 451,
"0.5": 114
},
"routed_length_mm_by_net": {
"/BTN_SELECT": 95.16152954766495,
"/GND": 152.2615364335217,
"/+3V3": 106.94219089103528,
"/RS_DCD": 47.76152984055992,
"/RS_DSR": 56.835284377834164,
"/RS_RI": 66.02000635033798,
"/RS_DTR": 52.162796381301995,
"/RS_RTS": 56.23655121147421,
"/RS_CTS": 58.073809511675655,
"/RS_TX": 47.51030692030297,
"/RS_RX": 43.90406233625865,
"/RS_VALID": 64.36573922778403,
"/RS_OFF_N": 67.05000417516253,
"/BTN_PREVIOUS": 86.95076506672746,
"/OLED_SDA": 54.781626385188304,
"/OLED_SCL": 53.30787155502609,
"/BTN_NEXT": 106.4266552277327
"/BTN_SELECT": 88.94832552628598,
"/GND": 141.68412246223386,
"/+3V3": 90.29604731966161,
"/RS_DCD": 43.068490178879834,
"/RS_DSR": 55.09398706495997,
"/RS_RI": 67.91373841554615,
"/RS_DTR": 52.53487761643567,
"/RS_RTS": 60.056714985368814,
"/RS_CTS": 63.65082753507114,
"/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
},
"tracks": 569,
"vias": 20,
"tracks": 565,
"vias": 22,
"copper_zones": 0,
"drc_freshly_run": true,
"drc_violations": 0,
+2 -2
View File
@@ -70,8 +70,8 @@ for i, a in enumerate(items):
if distance < min_clearance:
min_clearance, closest = distance, (a[0], b[0])
assert min_clearance >= 0.25 - 1e-6, (min_clearance, closest)
outline = Polygon([(100,100),(182,100),(182,180),(136,180),(136,161),(100,161)])
rf = box(110, 100, 131, 113)
outline = Polygon([(136,100),(182,100),(182,180),(100,180),(100,119),(136,119)])
rf = box(110, 119, 131, 132)
min_edge, min_rf = float('inf'), float('inf')
for label, net, layers, geom, radius in items:
assert outline.covers(geom), label
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schemas.kicad.org/drc.v1.json",
"coordinate_units": "mm",
"date": "2026-09-20T23:07:33",
"date": "2026-09-21T11:39:13",
"ignored_checks": [
{
"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
+115 -1
View File
@@ -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`.
@@ -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-20T23:07:27</date>
<date>2026-09-21T11:39:08</date>
<tool>Eeschema 10.0.6-1.fc44</tool>
<sheet number="1" name="/" tstamps="/">
<title_block>
@@ -3,6 +3,100 @@
82,
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,
"draft_thickness_mm": 1.6,
"electrical_footprints": 9,
@@ -10,7 +104,8 @@
"physical_DRC_violations": 0,
"unconnected_items": 0,
"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": [
{
"description": "Footprint has no courtyard defined",
@@ -34,8 +129,8 @@
}
],
"not_validated": [
"male module pin map/fit",
"assembly envelope and heights",
"male module pin map/header fit",
"ESP32 geometry and complete assembly envelope/heights",
"header/socket fit",
"RF performance and antenna clearance",
"power/return-path integrity",