Integrate DB9 mounting into enclosure lid
This commit is contained in:
+29
-23
@@ -12,14 +12,13 @@ Open `enclosure.scad` in OpenSCAD (tested here with **2021.01**). Set `part` to:
|
||||
|---|---|
|
||||
| `assembly` / `exploded` | Assembled / exploded reference preview; **not printable combined parts** |
|
||||
| `base` | Floor, carrier insert bosses, daughterboard insert bosses, side assembly bosses |
|
||||
| `lid` | Roof, skirt, screw head wells, button guides, DB9 panel attachment hangers, raised labels |
|
||||
| `panel` | Separate externally installed DB9 retaining panel, oriented flat for printing |
|
||||
| `lid` | Roof, skirt, screw head wells, button guides, integral bottom-open DB9 retaining panel and ribs, raised labels |
|
||||
| `plunger` / `plungers` | One / three identical captive button actuators |
|
||||
|
||||
| `labels` | Raised text alone for a second material or separately attached lettering |
|
||||
| `layout` | Spaced parts for inspection; orient/support each part separately in the slicer |
|
||||
|
||||
`show_references` displays simplified hardware only in **F5 preview** (`$preview` and `%` background geometry); it is excluded from STL/F6 geometry. The reference boxes are not complete component models. Orange/brown stock DB9 post envelopes deliberately reveal the conflict with a panel fitted without replacing/reseating the posts. Gold dummies represent **two M3 and four M2 male/female brass standoffs above the carrier**, with lower studs and blind upper bores; their OD, stud length and bore depth are provisional. The red RF overlay and enclosure RF-reserve restriction have been removed at the user's request. Actual antenna geometry remains; no PCB reserve/copper/layout was changed and no Wi-Fi performance guarantee is implied.
|
||||
`show_references` displays simplified hardware only in **F5 preview** (`$preview` and `%` background geometry); it is excluded from STL/F6 geometry. The reference boxes are not complete component models. Orange/brown DB9 post-body envelopes are shown reseated **outside** the integral panel; they illustrate unverified hardware, not guaranteed stock-thread or cable fit. Gold dummies represent **two M3 and four M2 male/female brass standoffs above the carrier**, with lower studs and blind upper bores; their OD, stud length and bore depth are provisional. The red RF overlay and enclosure RF-reserve restriction have been removed at the user's request. Actual antenna geometry remains; no PCB reserve/copper/layout was changed and no Wi-Fi performance guarantee is implied.
|
||||
|
||||
All dimensions are **mm**. Parameter/source coordinates are carrier-local, X right and Y down toward USB; add `(100,100)` for KiCad coordinates. The rendered model maps `Y_world = 80 - Y_carrier`, so the DB9 is above the USB end in top view. Text is separately transformed to remain readable from the USB end.
|
||||
|
||||
@@ -55,14 +54,14 @@ The user's intended/reported module elevation is **10 mm**. The descriptions “
|
||||
|
||||
**Insert pockets are examples, not validated fits:** M3 Ø4.2 × 4 deep; M2 Ø3.2 × 3 deep, 0.3 entry flare. These numbers do not specify insert OD/length, thread engagement or thermal process. Choose the actual insert family and set diameter/depth/boss OD using its supplier recommendations and a material-specific test coupon first. Install inserts before electronics. Default carrier pockets leave 3.4 mm solid material to the outside bottom (1 mm boss web plus 2.4 floor).
|
||||
|
||||
Default insert count with all options enabled: **12 M3** (4 carrier, 4 case, 2 RS-232, 2 panel hangers) and **4 M2** (OLED). The last two M3 pockets face outward horizontally in the lid; install with the lid removed. These M3s are **not** the connector cable-lock threads.
|
||||
Default insert count with all options enabled: **10 M3** (4 carrier, 4 case, 2 RS-232) and **4 M2** (OLED), all in the base. **No DB9 panel insert hangers, panel-to-lid screws or separate printable plate remain.** Connector screw posts use their own verified thread, not an inferred M3 designation.
|
||||
|
||||
Screw-length selection is by measured engagement, not a fixed BOM promise:
|
||||
|
||||
- Carrier screws: **1.6 mm** carrier thickness + washers + engagement into the base insert. Keep the head and shoulder clear of nearby components/copper.
|
||||
- Case screw under-head plane Z34.6 to insert mouth Z15 is **19.6**, plus chosen engagement (e.g. 3 gives 22.6); do not blindly use an overlong 25 mm screw in a 4 mm pocket.
|
||||
- Lower brass studs first traverse **1.6 mm carrier**, then engage the base inserts. `brass_m3_stud_l` / `brass_m2_stud_l` default to **4 mm reference lengths**, not measured hardware: tips at Z5.0 give **2.4 mm engagement**, leaving 1.6 mm to the M3 pocket bottom / 0.6 mm to the M2 pocket bottom. Actual insert usable threads may differ from pocket depth: measure before selecting. Upper daughterboard screws traverse **1.57 RS-232 / 1.6 OLED**, plus washers and chosen engagement into the **top female**, not the base. Preview female depths are 4/3 mm, also unknown. Verify studs/screws pass the nominal 3/2 mm holes without force; these are not generous M3/M2 clearance holes.
|
||||
- Panel-to-lid screws traverse **2 mm** panel plus selected M3 engagement. Round/pan head clearance is open from outside; use small heads/washers which do not foul cable hardware.
|
||||
- Connector posts traverse **2.4 mm** integral panel plus any washers/gap before engaging the connector flange. See the thread-reach and cable-mating gates below; these are not panel-to-lid screws.
|
||||
|
||||
## Cable interfaces
|
||||
|
||||
@@ -78,19 +77,25 @@ Signage says **USB**, not a guessed left/right native-USB vs USB-UART mapping. B
|
||||
|
||||

|
||||
|
||||
The STEP source registers the flange front at **Y1.930**, stock lock mouths **Y−2.870**, shell nose **Y−3.870**, with module front at Y0. The flange is **30.8 × 12.55**. Shell maximum root/lip envelope is **16.878038 × 9.1**; the panel hole is an intentionally rectangular **18.078038 × 10.3** envelope with 0.6 margin per side, not a reconstructed D-profile.
|
||||

|
||||
|
||||
The **separate 46 × 13.68 × 2 panel** sits at **Y−0.070…1.930**, above the module PCB at Z21.07…34.75. The module PCB extends under/forward of it; the panel must not be extended down through that PCB. A **48 mm wide** main-wall service recess is open at X33…81, Z18.4…35, making the connector and panel screws externally reachable. Shell nose remains 0.47 beyond the ordinary outer wall plane. Check your cable hood against this recess; no universal connector/cable compatibility is claimed.
|
||||
The STEP registers flange front **Y1.930**, original post mouths **Y−2.870**, shell nose **Y−3.870**, with module front Y0. Flange bounds are **X41.57…72.37**, Z20.57262024…33.12262024, thickness 0.4. Shell maximum root/lip envelope is **16.878038 × 9.1**; the design uses a conservative rectangular clearance, not a reconstructed D-profile.
|
||||
|
||||
There are **three independent fastener systems**:
|
||||
**Integral cover mounting:** a **48 mm nominal wide × 2.4 mm axial-thickness** panel spans X33…81 (0.02 mm union overlap at each wall), Y−0.470…1.930, Z21.07…35 and joins the roof directly. Two **6 mm nominal wide full-height ribs**, X33…39 and X75…81 (outer union overlap 0.02 mm), run forward to the outer wall plane Y−3.4. They provide broad roof load paths rather than small insert posts, with 2.57/2.63 mm lateral clearance to the flange. The ribs are forward of the connector anchors (nearest anchor Y5.03; panel rear Y1.93 gives 3.10 mm separation). Panel bottom remains **0.5 mm above the module PCB top**; neither wings nor ribs extend through the PCB. Load/print strength still needs testing.
|
||||
|
||||
1. **Connector flange/cable lock axes X44.47 / 69.47, spacing 25**, parallel to Y, Z26.84762024. Panel holes default to Ø3.6 clearance, adjustable with `external_fastener_clearance`. The source models Ø3 cylindrical bores/Ø3.4 mouth chamfers, **no threads**. This does not establish M3, 4-40, pitch, removable posts, or usable engagement.
|
||||
2. **Panel-to-lid M3 screws X38 / 76**, spacing 38, same height, into forward-facing insert hangers. These carry panel loads to the case and can be serviced externally.
|
||||
3. **Module-to-carrier M3 screws**, Z axes, **24.13 spacing**, as above. Connector anchor/prong holes are not used as mounting screws.
|
||||
The shell slot is **18.078038 mm wide**, X47.930981…66.009019, **open through the panel bottom**, with top Z31.99762024 (0.6 above shell top). There is no lower crossbar to trap the connector. The upper rail is **3.00238 mm high**, continuous with the roof. Two **closed Ø3.6 through holes** retain screw posts on **X44.47 / 69.47, Z26.84762024, 25 mm spacing**, parallel to Y. The minimum slot-to-hole ligament is **1.660981 mm**. `external_fastener_clearance` is clearance only, **not a thread designation**. The module mounting holes remain a separate **24.13 mm** Z-axis system; anchor/prong holes are not new mounting points.
|
||||
|
||||
**Required unresolved hardware selection:** confirm the actual connector posts can be removed/reseated, determine their real thread and required engagement, and fit suitable panel-mount replacement jackscrews (or a verified equivalent) that sandwich this panel against the flange **while preserving the cable's mating screw receptacles and nose reach**. Stock 4.8 mm posts occupy the panel volume; simply passing ordinary screws into their front bores does not clamp the panel to the flange and consumes the cable locks. **Do not do that. Do not assume an M3 screw fits.** `panel_t`, `panel_flange_gap` and clearance are adjustable for verified hardware; keeping the replacement posts too far forward can prevent full cable mating even if threads engage.
|
||||
The outer-wall service recess is **X33…81**, open down to the lid seam (**Z15.25**) so no lid crossbar sweeps over the shell. The former 1 mm side slits are closed by extending the integral panel/ribs to join the wall boundaries. A **base-only stepped upstand** fills the lower opening: shoulders span X33.25…80.75 and rise from Z15 to **Z20.82**; the **17.578038 mm-wide central tongue** rises to **Z21.69762024**, leaving **0.6 mm below the STEP shell minimum Z22.29762024**. The steps interfit without an undercut, retaining **0.25 mm** horizontal edge/tongue seams and the vertical shoulder-to-panel seam. The upstand is **2.9 mm deep**, Y−3.4…−0.5: it stays **0.5 mm forward of the PCB edge Y0** and 5.53 mm forward of the nearest anchor Y5.03, rather than filling through the PCB/underside. This closes unwanted openings, not the required manufacturing/cable clearances, and is not a sealed enclosure. Forward ribs narrow the unobstructed central cable approach to **X39…75 (36 mm)**. Shell nose stays **0.47 mm beyond the ordinary outer wall** and **3.4 mm beyond the panel front**. Full cable-hood, tool and mating clearance must be checked against that recess, not assumed from nose protrusion alone.
|
||||
|
||||
The detachable panel is intentional: an integral panel with closed shell hole could not be lowered vertically over the installed horizontal connector. Lower the lid first, then introduce the panel along the mating axis from outside and fasten it. `db_panel=false` removes lid hangers and assembled panel for a temporary access-only configuration — **not mechanically equivalent to external flange retention**. If the stock connector cannot accept suitable panel hardware, this retention concept must be revised, not declared validated.
|
||||
**Installation path, not a horizontal-slide claim:** remove both connector screw posts and unplug *all* cables. Hold the lid at its final XY registration and lower it straight down. The shell enters the bottom-open slot; the closed side holes sweep beside the shell, where the removed posts used to obstruct them. The panel remains forward of the flange and above the PCB for the entire descent. The USB shells end at Y77.705, inboard of the rear inner wall Y81 by **3.295 mm**, so a closed USB bay does not require a horizontal slide with cables removed. Downward button guides stay on their final XY axes, stop above switch tops, and case bosses approach their corresponding base bosses without a lateral move. The nominal continuous 60 mm sweep is tested in `validate.py --installation`, including a negative control that detects unremoved posts. This checks selected envelopes, not unmodeled components or manufacturing tolerances.
|
||||
|
||||
**Hardware gate — do not skip:** remove stock posts only if the real connector permits it without damaging captive nuts/anchors. Screw their male studs **from outside through the cover holes into the connector**, leaving post bodies outside and female cable-lock receptacles available. Actual thread/pitch, removability, stud length, usable female depth and clamp torque are **unmeasured**; STEP Ø3 bores/Ø3.4 chamfers prove neither M3 nor 4-40. Ordinary screws inserted into the original front bores do not implement this clamp.
|
||||
|
||||
For under-shoulder stud length `L`, washer thickness `w`, and flange gap `g`, available engagement is **`L − panel_t − w − g`**: default **`L − 2.4 − w`**. To retain the previous engagement, studs must provide **2.4 mm more reach** (plus any washer/gap). Reusing stock posts is conditional; verified longer-stud replacements or compatible extensions may be required, and must neither bottom nor interfere internally. Do not order a guessed thread. Default `panel_flange_gap=0` registers direct flange contact; any positive gap needs a measured, supported clamp stack, not tightening across air and bending the PCB.
|
||||
|
||||
Reseating a same-length **4.8 mm** post body puts its mouth at **Y−5.270**, **1.4 mm forward of the shell nose**, instead of 1 mm behind it. This is a significant **full-mating risk**, not validated by thread engagement. A verified shorter shoulder/body, recessed cable hardware or another compatible panel-mount post geometry may be necessary; retaining the original mouth plane would require a **2.4 mm body length** at the default panel face, whose female thread depth is not established. Keep full shell mating and functional cable locks as acceptance limits. If no compatible hardware exists, revise the cover/hardware design rather than force it. `panel_t`, `panel_flange_gap`, `external_fastener_clearance` and `panel_rib_w` expose the relevant choices; do not thin structural webs just to make stock threads reach.
|
||||
|
||||
`db_panel=false` omits the integral panel/ribs and base upstand for temporary access only, **not equivalent connector retention**. The obsolete `panel` export mode and `panel-default.stl` are removed; there is no duplicate printable plate.
|
||||
|
||||
## Display, buttons and antenna
|
||||
|
||||
@@ -104,13 +109,13 @@ The detachable panel is intentional: an integral panel with closed shell hole co
|
||||
|
||||
1. Measure insert family, actual assembled stack and connector hardware before committing to a full print. Inspect the rendered assembly against physical parts. Print cutout/insert/guide coupons or slicer-cut samples first.
|
||||
2. Print the base floor down. Indicative starting point only: 0.4 mm nozzle, 0.2 mm layers, 4 perimeters, 4–5 top/bottom layers, 25–40% infill with local solid regions around inserts. PETG is a reasonable prototype option; PLA is for fit checks away from heat. Determine material/thermal suitability yourself. No sealing, flame or temperature rating is established.
|
||||
3. Lid can be printed roof-up with supports beneath the roof, guides, panel hangers and cable openings, avoiding supports in bores where practical. Alternatively print roof-down with `emboss=false`, attach separately printed text, or use a proper multi-material setup. **Integrated raised text prevents a flat roof-down first layer**; do not ignore this 0.6 mm offset. No support-free claim for the supplied embossed lid.
|
||||
4. Print panel flat in its supplied orientation. **No plastic spacers are printed**; the brass dummies are preview-only purchased hardware. Print plungers with brim and support under their low retaining flange, or choose a slicer orientation that yields smooth, strong shafts. Deburr guides without opening them so far that stems tilt/jam. Test all three independently.
|
||||
5. Heat-set inserts with a controlled tool to the selected supplier process. Panel hanger inserts install horizontally from the front. Remove strings/debris; inspect for splits, exposed insert metal and screw bottoming.
|
||||
3. Lid can be printed roof-up with supports beneath the roof, guides, integral panel/ribs and cable openings, avoiding supports in bores where practical. Alternatively print roof-down with `emboss=false`, attach separately printed text, or use a proper multi-material setup. **Integrated raised text prevents a flat roof-down first layer**; do not ignore this 0.6 mm offset. No support-free claim for the supplied embossed lid.
|
||||
4. There is **no separate DB9 plate** to print. **No plastic spacers are printed**; the brass dummies are preview-only purchased hardware. Print plungers with brim and support under their low retaining flange, or choose a slicer orientation that yields smooth, strong shafts. Deburr guides without opening them so far that stems tilt/jam. Test all three independently.
|
||||
5. Heat-set inserts with a controlled tool to the selected supplier process. All inserts are in the base; none are used for the DB9 panel. Remove strings/debris; inspect for splits, exposed insert metal and screw bottoming.
|
||||
6. Seat the carrier directly on all base support bosses and secure its four M3 mounting holes. Install the two M3 and four M2 **10 mm brass standoffs above the carrier**, with lower studs through the corresponding existing carrier holes into inserts. Seat the RS-232/OLED on them and fasten with short screws into the top females; seat the ESP32 on its headers. Check that brass body height matches the header's seated height, neither bending boards nor lifting connectors; check DB9 prongs and all underside solder joints against the 5 mm floor clearance.
|
||||
7. Insert plungers from under the lid. Lower lid onto the base with DB9 panel **off**. Verify actuation/return before fastening the four case screws. Check screw heads fit the wells and screws engage without bottoming.
|
||||
8. After validating/removing the stock connector posts as required, slide the panel in from outside. Fit verified replacement connector jackscrews to the flange and the two independent M3 panel-to-lid screws. Do not use assembly screws to pull a misaligned flange/PCB into position. Test full USB/DB9 mating and cable locks; perform a restrained cable-load test before trusting retention.
|
||||
9. To remove the lid, disconnect cables and remove panel/connector retention hardware first. The full electronics assembly should remain independently secured by carrier/daughterboard fasteners.
|
||||
7. Disconnect all cables and remove the two verified-removable DB9 screw posts **before lowering the lid**. Insert plungers from under the lid and hold with removable tape. Align the cover in its final XY position and lower vertically: shell enters the bottom-open slot, panel wings clear the PCB, guides align over buttons. Do not slide horizontally, tilt around the USB end or force the shell. Verify free seating and button return; start the four case screws loosely.
|
||||
8. Insert verified stock or replacement post studs from outside through the two closed cover holes into the connector. Check clamp contact, engagement and bottoming independently; do not use them to pull a misaligned flange/PCB into position. Tighten case/post hardware cautiously to verified values. Test full USB/DB9 mating and cable locks, particularly the changed post-mouth reach; perform a restrained cable-load test before trusting retention.
|
||||
9. To remove the cover, unplug every cable, unscrew both connector posts completely out of the cover holes and remove the four case screws; lift vertically, retaining the plungers. The electronics remain secured by their independent carrier/daughterboard fasteners.
|
||||
|
||||
Raised labels are positive **0.6 mm emboss**, not engraving. Single-colour is the default; roof-up printing allows a filament change just above the roof for contrasting text (check actual sliced layers). For a separate material export `labels`, and export the lid with `emboss=false`. In a multi-object slicer, preserve XY origin and translate the flat text object up **22.13 mm** relative to the default lid export (its initial 0.02 overlaps the roof). Do not auto-centre individual letters; STL carries neither colour nor assembly transforms. Changes to stack require recalculating that offset: `top_z - eps - split_z - seam_clearance`.
|
||||
|
||||
@@ -121,18 +126,19 @@ Run from `hardware/case`; no network or added dependencies are needed. Shell com
|
||||
```sh
|
||||
QT_QPA_PLATFORM=offscreen openscad -o base-default.stl -D 'part="base"' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o lid-default.stl -D 'part="lid"' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o panel-default.stl -D 'part="panel"' enclosure.scad
|
||||
|
||||
QT_QPA_PLATFORM=offscreen openscad -o plungers-default.stl -D 'part="plungers"' enclosure.scad
|
||||
|
||||
python3 validate.py
|
||||
/usr/bin/python3 validate.py --source
|
||||
/usr/bin/python3 -B validate.py --source --installation --variants
|
||||
QT_QPA_PLATFORM=offscreen openscad -o assembly-preview.png --imgsize=1200,1000 --viewall --autocenter --projection=o --camera=0,0,0,55,0,25,200 -D 'part="exploded"' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o db9-preview.png --imgsize=1200,900 --viewall --autocenter --projection=o --camera=0,0,0,80,0,180,200 -D 'part="assembly"' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o lid-detail.png --render --imgsize=1200,900 --viewall --autocenter --projection=o --camera=0,0,0,100,0,155,200 -D 'part="lid"' enclosure.scad
|
||||
```
|
||||
|
||||
`validate.py` uses Python's standard library and installed OpenSCAD for evaluated datum, stud-engagement and nozzle-web fixtures, plus ASCII STL checks; `--source` additionally uses the **already installed system KiCad `pcbnew` binding**, read-only. It verifies current outline/holes/placements, measured U1 USB mouth graphics and STEP-derived lock/shell data against case constants. It is a drift tripwire, not a full source parser for every SCAD expression. It checks each STL has two oppositely directed uses of every triangle edge, nonzero triangle area, the expected connected component count (1 base, 1 lid, 1 panel, 3 plungers), positive total signed volume and minimum Z≈0. It does **not** prove vertex-manifoldness, absence of self-intersections, full component collision clearance or physical fit.
|
||||
`validate.py` uses Python's standard library and installed OpenSCAD for evaluated datum, stud-engagement and nozzle-web fixtures, plus ASCII STL checks; `--source` additionally uses the **already installed system KiCad `pcbnew` binding**, read-only. It verifies current outline/holes/placements, measured U1 USB mouth graphics and STEP-derived lock/shell data against case constants. It is a drift tripwire, not a full source parser for every SCAD expression. It checks each STL has two oppositely directed uses of every triangle edge, nonzero triangle area, the expected connected component count (1 base, 1 lid, 3 plungers), positive total signed volume and minimum Z≈0. It does **not** prove vertex-manifoldness, absence of self-intersections, full component collision clearance or physical fit.
|
||||
|
||||
Executed for this corrected design: all four default CGAL exports completed with `Simple: yes`; native-source registration, evaluated geometry fixtures and the mesh checks passed; exploded and connector-side PNGs rendered and were visually inspected. `spacers-default.stl` and the `spacers` mode/layout objects were removed. The base is 10 mm shorter; lid/panel/plunger print-local dimensions stay unchanged because their assembly datums all descend together. All four exports were regenerated. KiCad printed its existing enum-property assertions but completed the read-only checks. No slicer, physical print, screw-fit, cable-load, hardware/firmware, thermal or RF tests were performed. Additional geometry regression results, if any, are recorded in [checks.md](checks.md).
|
||||
Validation results and bounded parameter/sweep tests are recorded in [checks.md](checks.md). The remaining exports are base, integral lid and three plungers; the corrected 37.4 mm shell / 7.4–9–19 mm stack is unchanged. No slicer, physical print, screw-fit, cable-load, hardware/firmware, thermal or RF acceptance is implied.
|
||||
|
||||
## Evidence and remaining measurements
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 79 KiB |
+1049
-769
File diff suppressed because it is too large
Load Diff
+59
-26
@@ -1,46 +1,79 @@
|
||||
# Corrected enclosure checks — 2026-09-26
|
||||
# Integral-cover connector mounting checks — 2026-09-26
|
||||
|
||||
This supersedes the initial under-carrier-brass stack. It is a CAD validation record, **not physical acceptance**. Tools already installed: OpenSCAD 2021.01, system Python 3 and KiCad `pcbnew`. No packages downloaded/installed. PCB hardware files were read only.
|
||||
This supersedes the separate DB9 panel/insert-hanger design. **CAD evidence, not physical acceptance.** Installed OpenSCAD 2021.01 and system Python/KiCad were used without downloads. PCB files were read only; changes are confined to the case and agent current-state record.
|
||||
|
||||
## Default design
|
||||
## Geometry and outputs
|
||||
|
||||
Commands are in [README.md](README.md). All four printable part exports completed within bounded 180-second command timeouts, with OpenSCAD CGAL reporting `Simple: yes`:
|
||||
Shell remains **106 × 86.8 × 37.4 mm**; carrier underside/top **Z7.4/9**, brass bodies 10 mm above carrier, module underside **Z19**, roof underside **Z35**. RF overlay remains removed; PCB layout/copper/reserve unchanged.
|
||||
|
||||
| Export | Connected triangle components | Triangle count |
|
||||
- Integral panel: nominal X33…81 (0.02 mm union overlap into each wall), Y−0.47…1.93, Z21.07…35, **2.4 mm clamp thickness**. Direct roof connection, two **6 mm nominal** forward ribs (inner edges unchanged) outside the flange; no DB9 heat inserts or independent attachment screws.
|
||||
- Closed **Ø3.6** post-clearance holes, **25 mm** spacing, axis Z26.84762024. No M3/4-40 inference.
|
||||
- Bottom-open **18.078038 mm** shell slot; top Z31.99762024. Minimum hole-to-slot web **1.660981 mm**; top rail **3.00238 mm**. Panel bottom clears module PCB by 0.5 mm; anchors begin 3.10 mm behind the panel rear. Flange registration comes from extracted STEP faces, not the module mounting-hole pitch.
|
||||
- Former 1 mm side slits closed by panel/rib extensions into wall boundaries. Base shoulders: X33.25…80.75, top Z20.82; central tongue width 17.578038, top Z21.69762024 (0.6 below STEP shell). Upstand Y−3.4…−0.5, depth 2.9: PCB-edge clearance 0.5, anchor separation 5.53. Shoulder/edge/tongue seams 0.25. No undercut, no change to 36 mm cable approach width.
|
||||
- Front-wall service recess extends down to lid seam. USB opening stays unchanged; installation is vertical with all cables and both connector posts removed, not a horizontal slide under button guides.
|
||||
- Deleted `panel-default.stl`, panel mode and layout object. `db_panel=false` is access-only. Base insert count is **10 M3 + 4 M2**.
|
||||
|
||||
Affected base/lid exports regenerated; unchanged plungers retained and revalidated under bounded 180-second command timeouts; CGAL reported `Simple: yes`:
|
||||
|
||||
| Export | Connected triangle components | Triangles |
|
||||
|---|---:|---:|
|
||||
| `base-default.stl` | 1 | 8364 |
|
||||
| `lid-default.stl` | 1 | 13080 |
|
||||
| `panel-default.stl` | 1 | 816 |
|
||||
| `base-default.stl` | 1 | 8404 |
|
||||
| `lid-default.stl` | 1 | 12712 |
|
||||
| `plungers-default.stl` | 3 | 1716 |
|
||||
|
||||
The shell is **106 × 86.8 × 37.4 mm** (roof), 10 mm lower than the initial mistaken stack. Carrier underside/top are **Z7.4/9**, module underside **Z19**, roof underside **Z35**. The base top descends to Z15. The lid, panel, plungers, USB bay, DB9 hangers and display all descend with their parent datums; their relative fit remains unchanged. Print-local lid/panel/plunger nominal dimensions therefore remain unchanged, although all exports were regenerated. Gold hardware references replace printed spacers; no under-carrier brass, printable spacer mode, layout spacers or active spacer STL remains. The obsolete initial spacer STL had no unstaged user modifications before removal.
|
||||
Base and lid geometry changed only at the connector closure; plunger geometry is unchanged. The lid is one connected printable part, not overlapping separate exports. Three PNGs (`assembly-preview.png`, `db9-preview.png`, `lid-detail.png`) regenerated headlessly under 120-second timeouts and visually reviewed. The bare-lid view exposes the closed post holes, open-bottom shell slot, forward ribs and downward guides; the assembled view shows reseated post-body references outside the panel. Simplified background references are not production component CAD.
|
||||
|
||||
`/usr/bin/python3 validate.py --source` passed:
|
||||
## Source, expressions and mesh regression
|
||||
|
||||
- Actual SCAD-evaluated default stack, including roof, split, button/guide, DB9 axis/panel bottom, USB bay and plunger length.
|
||||
- Independent `carrier_boss_h=7` fixture moves assembly Z by 2 without changing plunger length; `module_gap=12, lid_above_carrier=28` raises modules/roof/ports and lengthens plungers without moving the carrier or button contact.
|
||||
- Reference 4 mm studs cross the 1.6 mm carrier and give 2.4 mm nominal engagement. Negative fixtures reject an M3 stud that cannot reach past the carrier, an M2 stud that bottoms in the pocket, and a 0.8 mm shell wall.
|
||||
- Analytical 0.4 mm nozzle checks: shell/floor/roof at least three nozzle widths; insert entry and guide webs at least two. The limiting default panel-hanger entry web is 0.8 mm, M3 daughter entry web 1.1 mm, M2 daughter entry web 1.1 mm and guide radial web 1.75 mm. These selected web checks are not slicer coverage or proof that all text/features print reliably.
|
||||
- Native PCB outline vertex set, 1.6 mm thickness, U1/U2/DS1 registration and orientation.
|
||||
- Actual carrier/daughterboard NPTH centres **and diameters** (carrier 3.2, RS-232 3, OLED 2), no U1 NPTH mounting holes, and button body centres. The ESP32 remains header-supported; no mounting holes were invented.
|
||||
- Saved measured U1 USB mouth graphic centres/widths and mouth Y; extracted STEP shell bounds and front-lock axes (25 mm, not 24.13 mm module holes).
|
||||
- Every exported triangle has nonzero area, every edge has exactly two oppositely wound uses, positive total signed volume, expected connected component counts, bottom Z≈0 and default maximum bounds (including the shortened base) to detect stale exports. These are **edge-manifold checks**, not an exhaustive mesh-intersection/vertex-manifold proof.
|
||||
Run from this directory:
|
||||
|
||||
KiCad emitted its known enum-property assertions but completed. It did not save or regenerate the board.
|
||||
```sh
|
||||
/usr/bin/python3 -B validate.py --source --installation --variants
|
||||
```
|
||||
|
||||
PNG rendering succeeded headlessly with `QT_QPA_PLATFORM=offscreen`, within a bounded 120-second command timeout. Both `assembly-preview.png` and `db9-preview.png` were visually inspected: lower base, gold supports above carrier, simplified ESP32 header supports, separated lid/base, button tips, raised labels, USB bay and externally approachable DB9 panel. No red RF overlay remains. The actual projecting antenna rectangle is retained; the enclosure no longer reserves the PCB RF region. PCB copper/reserve/layout/schematic are unchanged and no Wi-Fi performance guarantee is implied. Hardware envelopes are incomplete; stock connector-post references still intentionally overlap the panel to expose the required replacement/reseating. Visual review does not certify clearance.
|
||||
PASS:
|
||||
|
||||
- Native saved PCB outline, thickness, placements/orientations, carrier/module NPTH centres **and diameters**, no invented ESP32 mounting holes, button centres and measured USB mouth graphics.
|
||||
- Extracted STEP flange plane/lateral bounds, shell size and true 25 mm lock axes. Read-only KiCad emitted its known enum-property assertions but completed.
|
||||
- Actual SCAD-evaluated stack/connector expressions; independent raised-carrier and taller-module fixtures; stud engagement; obsolete panel/spacer rejection.
|
||||
- Negative assertions for insufficient stud reach, bottoming studs, thin shell/panel, negative flange gap, panel entering the PCB plane, oversized lock holes consuming slot ligaments, ribs entering flange lateral bounds, insufficient sliding seam/shell clearance and inverted closure steps.
|
||||
- Every STL triangle nondegenerate, every edge used twice with opposite winding, expected component counts, positive total volume, print-bed Z and expected bounds. These are **edge-manifold checks**, not exhaustive vertex-manifold or self-intersection proof.
|
||||
|
||||
## Continuous installation sweep
|
||||
|
||||
`--installation` renders CGAL intersections of the **actual lid** with rectangular hardware envelopes swept continuously through **60 mm of relative vertical travel**, from below to the final position. A remote 1 mm³ sentinel makes an empty intersection exportable; a passing result has only that cube, one component and exactly 1 mm³ volume.
|
||||
|
||||
Included envelopes: maximum shell root/lip, flange, STEP anchor-bracket bounds, RS-232/ESP32/OLED boards, antenna, provisional OLED glass and USB shells, full button-body/actuator bounding boxes and conservative square base-boss bounds. Flange contact is inset 0.01 mm solely to avoid a coincident CGAL face; physical tolerances are not inferred. No cable envelopes are included because **cables must be disconnected**. Plungers move with the lid and contact the switches at final seating; their return/retention remains an independent physical gate.
|
||||
|
||||
- **PASS default**: posts removed, zero modeled swept interference.
|
||||
- **PASS variant**: `carrier_boss_h=7`, `module_gap=12`, `lid_above_carrier=28`, `panel_t=3`.
|
||||
- **PASS clearance variant**: `seam_clearance=0.4`, `shell_margin=0.8`, `end_clearance=1.5`.
|
||||
- Each test also intersects the actual base/lid at seating, sweeps both upstand steps separately downward 60 mm against the lid, and sweeps electronics envelopes upward 60 mm against the new base material (equivalent to vertical electronics insertion). Only the sentinel remains. Existing conservative base-boss envelopes remain included.
|
||||
- An initial full-base Minkowski sweep exceeded the outer 240-second command timeout after default passed. Replacing it with a whole-upstand hull introduced false diagonal interference; the final per-step exact sweep avoids that invented material and completes within the bounded runs.
|
||||
- **PASS negative control**: restoring original post-body envelopes causes detected interference, demonstrating that leaving posts installed is not an allowed assembly path.
|
||||
|
||||
The first thicker-panel sweep exposed a thin residual inner-wall strip where the panel front lay forward of the original wall's inner face. The recess now clears at least the full wall thickness, independently of panel depth; the corrected fixture passes. The recess starts at the lid seam; the new forward-only base upstand occupies its lower portion.
|
||||
|
||||
These are selected conservative nominal envelopes, not a full STEP solid-motion/real-component qualification. Unknown socket bodies, component details, wires, tolerances and actual USB/glass heights remain outside the proof.
|
||||
|
||||
## Parameter CGAL smoke tests
|
||||
|
||||
Temporary STL files were rendered and checked for one closed consistently wound edge-manifold component:
|
||||
Rendered to temporary files (180-second limit per render), then checked with `validate.mesh_check(path, 1)` for a single closed, consistently wound edge-manifold component:
|
||||
|
||||
```sh
|
||||
QT_QPA_PLATFORM=offscreen openscad -o /tmp/case-correction-variant-lid.stl -D 'part="lid"' -D 'module_gap=12' -D 'lid_above_carrier=28' -D 'carrier_boss_h=7' -D 'emboss=false' -D 'db_panel=false' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o /tmp/case-correction-variant-base.stl -D 'part="base"' -D 'daughter_supports=false' -D 'side_extra=14' -D 'm3_insert_d=4.4' -D 'panel_mount_boss_d=7' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o /tmp/case-integral-variant-lid.stl -D 'part="lid"' -D 'carrier_boss_h=7' -D 'module_gap=12' -D 'lid_above_carrier=28' -D 'panel_t=3' -D 'emboss=false' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o /tmp/case-integral-access-lid.stl -D 'part="lid"' -D 'db_panel=false' -D 'emboss=false' enclosure.scad
|
||||
QT_QPA_PLATFORM=offscreen openscad -o /tmp/case-integral-variant-base.stl -D 'part="base"' -D 'daughter_supports=false' -D 'side_extra=14' -D 'm3_insert_d=4.4' enclosure.scad
|
||||
```
|
||||
|
||||
Both reported `Simple: yes`. The first is a 41.4 mm roof-height configuration, lid export 3848 triangles. The second is a 110 mm-wide base, 4972 triangles. These test parameter propagation; **neither alternative is a selected or physically validated stack**. Arbitrary parameter combinations are not guaranteed valid; review every regenerated assembly.
|
||||
The repeatable `--variants` suite now covers these three configurations plus a raised/thicker-panel **base**. All four PASS CGAL and one-component edge-manifold checks. These exercise a thicker integral panel with raised stack, access-only lid and wider base/alternate insert pocket. They are not selected or physically validated configurations. Arbitrary parameter combinations are not guaranteed valid. `git diff --check` also passed.
|
||||
|
||||
## Explicit limits
|
||||
## Hardware gates — unresolved, not hidden by CAD success
|
||||
|
||||
No physical print, slicer analysis, insert installation, board assembly, connector/thread fit, plunger return/overload test, cable-load test, RF/thermal check or firmware test was performed. No full solid-interference solver was used. In particular, 5 mm carrier-to-floor clearance must be checked against actual solder/prongs, and conductive brass shoulders/insert faces against real lands/traces. Stud lengths, upper female depths, insert usable threads, USB Z/overmoulds, display height and DB9 hardware are still provisional as detailed in the README. The DB9 retention concept remains conditional on verified compatible panel jackscrews; stock posts are not claimed to fit the panel.
|
||||
1. Verify posts really unscrew and determine **actual thread/pitch**, under-shoulder stud length, female depth, required engagement, clamp torque and compatibility with any captive connector hardware. No thread is established by the STEP bore.
|
||||
2. Default panel consumes **2.4 mm additional reach**: engagement = stud length − 2.4 − washers (− any flange gap). Stock studs may be too short. Verified longer-stud posts/extensions may be needed; do not bottom them or use ordinary screws in the cable-lock mouths.
|
||||
3. A same-length 4.8 mm body reseated outside the panel moves its mouth from Y−2.870 to **Y−5.270**, now **1.4 mm ahead of the shell nose**. Full mating may fail despite adequate thread engagement. A compatible shorter-body/panel-mount solution must be measured; a nominal 2.4 mm body would restore the old mouth plane but is **not a qualified available part or sufficient female-thread-depth claim**.
|
||||
4. Preserve full cable mating/locking and external nose protrusion (0.47 mm beyond ordinary wall, 3.4 mm beyond panel); verify actual hood and wrench access inside the **36 mm** central space between ribs. Revise geometry/hardware if necessary rather than forcing a fit.
|
||||
5. Existing gates remain: insert selection, underside solder/prong and conductive shoulder clearances, header/brass stack agreement, USB envelopes, glass height, button preload/return/overload, material/print orientation and cable-load strength.
|
||||
|
||||
No physical print, slicer analysis, insert installation, connector/thread measurement, full cable mating, load, RF, thermal, firmware or device test was performed. The stronger integral load path is designed and modeled, **not mechanically load-qualified**.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
+108
-29
@@ -1,7 +1,7 @@
|
||||
// Provisional enclosure, mm. See README.md before printing or ordering hardware.
|
||||
// Saved carrier coordinates: X right, Y toward USB, Z above base floor.
|
||||
// Display in conventional top view via world(): X right, cable/DB9 end up.
|
||||
part = "assembly"; // assembly, exploded, base, lid, panel, plunger, plungers, labels, layout, check
|
||||
part = "assembly"; // assembly, exploded, base, lid, plunger, plungers, labels, layout, check
|
||||
show_references = true; // preview only: never included in STL
|
||||
emboss = true;
|
||||
$fn = 48;
|
||||
@@ -60,11 +60,10 @@ usb_reference_h = 3.5; // preview estimate ONLY
|
||||
|
||||
/* [DB9 panel: replacement jackscrews must be verified] */
|
||||
db_panel = true; // false leaves service opening; no case-to-flange retention
|
||||
panel_t = 2;
|
||||
panel_t = 2.4; // axial clamp thickness; consumes this much additional stud reach
|
||||
panel_flange_gap = 0; // nominal flange contact; tune after physical measurement
|
||||
external_fastener_clearance = 3.6; // NOT a thread designation
|
||||
panel_mount_x = [38,76]; // separate M3 panel-to-lid screws, NOT connector locks
|
||||
panel_mount_boss_d = 6.4;
|
||||
panel_rib_w = 5; // broad forward ribs outside the flange, not insert hangers
|
||||
shell_margin = 0.6; // per edge on STEP maximum shell envelope
|
||||
panel_bottom_above_module = 0.5;
|
||||
|
||||
@@ -111,6 +110,12 @@ flange_y = 1.930;
|
||||
panel_rear_y = flange_y-panel_flange_gap;
|
||||
panel_front_y = panel_rear_y-panel_t;
|
||||
panel_low_z = rs_top+panel_bottom_above_module;
|
||||
recess_left = 33;
|
||||
recess_right = 81;
|
||||
shell_slot_w = 16.878038+2*shell_margin;
|
||||
upstand_top = db_axis_z-9.1/2-shell_margin;
|
||||
upstand_shoulder = panel_low_z-seam_clearance;
|
||||
upstand_rear = min(-0.5,panel_front_y);
|
||||
button_top = carrier_top+button_h;
|
||||
guide_low_z = button_top+guide_bottom_above_button;
|
||||
guide_high_z = lid_z;
|
||||
@@ -120,7 +125,7 @@ plunger_length = top_z+plunger_above_lid-button_top;
|
||||
assert(side_extra >= case_boss_d+2, "Widen side rails for case bosses");
|
||||
assert(carrier_boss_h > max(m3_insert_depth,m2_insert_depth), "Leave material under inserts");
|
||||
assert(min(wall,floor_t,lid_t) >= 3*nozzle_d, "Shell needs at least three nozzle widths");
|
||||
assert((min(carrier_boss_d,case_boss_d,daughter_m3_boss_d,panel_mount_boss_d)
|
||||
assert((min(carrier_boss_d,case_boss_d,daughter_m3_boss_d)
|
||||
-m3_insert_d-2*insert_lead)/2 >= 2*nozzle_d, "M3 entry web too thin");
|
||||
assert((daughter_m2_boss_d-m2_insert_d-2*insert_lead)/2 >= 2*nozzle_d, "M2 entry web too thin");
|
||||
assert((guide_od-plunger_d-plunger_clearance)/2 >= 2*nozzle_d, "Guide web too thin");
|
||||
@@ -132,7 +137,19 @@ assert(module_gap > max(brass_m3_female_depth,brass_m2_female_depth)+1);
|
||||
assert(m3_insert_d+2 < min(carrier_boss_d,case_boss_d,daughter_m3_boss_d));
|
||||
assert(m2_insert_d+2 < daughter_m2_boss_d);
|
||||
assert(lid_z > rs_top+12.55262024+0.8, "Lid intersects DB9 flange");
|
||||
assert(panel_low_z >= rs_top+0.5, "Panel must stay above module PCB during lowering");
|
||||
assert(panel_low_z < db_axis_z-external_fastener_clearance/2-0.8);
|
||||
assert(panel_t >= 3*nozzle_d && panel_rib_w >= 3*nozzle_d);
|
||||
assert(seam_clearance >= 0.2 && seam_clearance <= 0.5, "Keep printable sliding seams");
|
||||
assert(shell_margin >= 0.5, "Retain shell/cable clearance");
|
||||
assert(upstand_rear-ymin >= 3*nozzle_d, "Upstand wall too thin");
|
||||
assert(upstand_shoulder > split_z && upstand_top > upstand_shoulder,
|
||||
"Stepped closure must rise above base and below shell");
|
||||
assert(upstand_rear <= -0.5, "Do not enter projecting module PCB/underside");
|
||||
assert(panel_flange_gap >= 0, "Panel cannot cross flange plane");
|
||||
assert(34+panel_rib_w < 41.57 && 80-panel_rib_w > 72.37, "Ribs must clear flange sides");
|
||||
assert(12.5-16.878038/2-shell_margin-external_fastener_clearance/2 >= 3*nozzle_d,
|
||||
"Closed lock holes need a solid web to the open shell slot");
|
||||
assert(panel_front_y > -2.870, "Panel would obscure stock cable-lock mouth plane");
|
||||
assert(guide_high_z > guide_low_z+4);
|
||||
assert(flange_bottom_z+plunger_flange_t < guide_low_z, "Plunger flange trapped below guide");
|
||||
@@ -145,7 +162,13 @@ echo("DB9 lock axis Z, split, lid underside",db_axis_z,split_z,lid_z);
|
||||
echo("CHECK stack",[carrier_bottom,carrier_top,module_bottom,rs_top,lid_z,top_z,
|
||||
split_z,button_top,guide_low_z,db_axis_z,panel_low_z,
|
||||
esp_top-usb_below_module_top,esp_top+usb_above_module_top,plunger_length]);
|
||||
echo("CHECK closure",[recess_left,recess_right,upstand_rear,upstand_shoulder,
|
||||
upstand_top,shell_slot_w-2*seam_clearance,seam_clearance]);
|
||||
echo("CHECK engagement",[brass_m3_stud_l-carrier_t,brass_m2_stud_l-carrier_t]);
|
||||
echo("CHECK connector",[panel_front_y,panel_rear_y,panel_t,panel_low_z,
|
||||
db_axis_z+9.1/2+shell_margin,16.878038+2*shell_margin,
|
||||
lock_x[1]-lock_x[0],external_fastener_clearance,panel_rib_w,
|
||||
12.5-16.878038/2-shell_margin-external_fastener_clearance/2]);
|
||||
|
||||
// Reflect carrier Y for a conventional top view; text compensates separately.
|
||||
module world() { translate([0,80,0]) rotate([0,0,0]) mirror([0,1,0]) children(); }
|
||||
@@ -176,13 +199,25 @@ module daughter_bosses() {
|
||||
module cable_cuts() {
|
||||
// Whole DB9 service recess clears PCB overhang, flange and wrench approach.
|
||||
// Panel lives inward of this wall and ABOVE the module PCB, never through it.
|
||||
box(33,ymin-1,module_bottom-0.6,48,panel_front_y-ymin+1,lid_z-module_bottom+0.6);
|
||||
box(33,ymin-1,split_z+seam_clearance-eps,48,
|
||||
max(panel_front_y,-end_clearance)-ymin+1+eps,
|
||||
lid_z-split_z-seam_clearance+eps);
|
||||
// Wide joined bay admits overmoulds to the inboard USB mouths. No thin web.
|
||||
box(usb_centres[0]-(8.87+usb_extra_width)/2,usb_mouth_y-1,
|
||||
esp_top-usb_below_module_top,
|
||||
usb_centres[1]-usb_centres[0]+8.87+usb_extra_width,
|
||||
ymax-usb_mouth_y+2,usb_below_module_top+usb_above_module_top);
|
||||
}
|
||||
module connector_upstand(travel=0) {
|
||||
// Forward of the PCB edge, including its unknown underside components.
|
||||
// Shoulders meet the ribs/panel with a seam; tongue closes the open shell slot.
|
||||
box(recess_left+seam_clearance,ymin,split_z-eps-travel,
|
||||
recess_right-recess_left-2*seam_clearance,upstand_rear-ymin,
|
||||
upstand_shoulder-split_z+eps+travel);
|
||||
box(module_cx-shell_slot_w/2+seam_clearance,ymin,upstand_shoulder-eps-travel,
|
||||
shell_slot_w-2*seam_clearance,upstand_rear-ymin,
|
||||
upstand_top-upstand_shoulder+eps+travel);
|
||||
}
|
||||
module base() {
|
||||
difference() {
|
||||
union() {
|
||||
@@ -205,27 +240,28 @@ module base() {
|
||||
}
|
||||
cable_cuts();
|
||||
}
|
||||
if(db_panel) connector_upstand();
|
||||
}
|
||||
module panel() {
|
||||
module connector_panel() {
|
||||
difference() {
|
||||
// Separate externally fitted part: a closed shell hole cannot lower over DB9.
|
||||
box(34,panel_front_y,panel_low_z,46,panel_t,lid_z-panel_low_z-seam_clearance);
|
||||
// Bottom-open slot clears the shell during straight vertical lid installation.
|
||||
// Solid wings and top rail meet the roof; nothing crosses the PCB plane.
|
||||
box(recess_left-eps,panel_front_y,panel_low_z,
|
||||
recess_right-recess_left+2*eps,panel_t,lid_z-panel_low_z+eps);
|
||||
box(module_cx-16.878038/2-shell_margin,panel_front_y-eps,
|
||||
db_axis_z-9.1/2-shell_margin,16.878038+2*shell_margin,
|
||||
panel_t+2*eps,9.1+2*shell_margin);
|
||||
panel_low_z-eps,16.878038+2*shell_margin,
|
||||
panel_t+2*eps,db_axis_z+9.1/2+shell_margin-panel_low_z+eps);
|
||||
for(x=lock_x) ybore(x,panel_front_y-eps,db_axis_z,
|
||||
external_fastener_clearance,panel_t+2*eps);
|
||||
for(x=panel_mount_x) ybore(x,panel_front_y-eps,db_axis_z,m3_clearance,panel_t+2*eps);
|
||||
|
||||
}
|
||||
}
|
||||
module panel_hangers() {
|
||||
for(x=panel_mount_x) difference() {
|
||||
box(x-panel_mount_boss_d/2,panel_rear_y,db_axis_z-panel_mount_boss_d/2,
|
||||
panel_mount_boss_d,m3_insert_depth+2,lid_z-db_axis_z+panel_mount_boss_d/2+eps);
|
||||
ybore(x,panel_rear_y-eps,db_axis_z,m3_insert_d,m3_insert_depth+eps);
|
||||
translate([x,panel_rear_y-eps,db_axis_z]) rotate([-90,0,0])
|
||||
cylinder(d1=m3_insert_d+2*insert_lead,d2=m3_insert_d,h=insert_lead+eps);
|
||||
}
|
||||
module connector_ribs() {
|
||||
// Forward of flange/anchors, outside flange X bounds; full-height roof load path.
|
||||
box(recess_left-eps,ymin,panel_low_z,34+panel_rib_w-recess_left+eps,
|
||||
panel_rear_y-ymin,lid_z-panel_low_z+eps);
|
||||
box(80-panel_rib_w,ymin,panel_low_z,recess_right-(80-panel_rib_w)+eps,
|
||||
panel_rear_y-ymin,lid_z-panel_low_z+eps);
|
||||
}
|
||||
module guide(p) {
|
||||
difference() {
|
||||
@@ -246,7 +282,7 @@ module lid_body() {
|
||||
side_extra/2,4,lid_z-split_z-seam_clearance+eps);
|
||||
}
|
||||
for(p=button_centres) guide(p);
|
||||
if(db_panel) panel_hangers();
|
||||
|
||||
}
|
||||
// Measured glass/display boundary plus margin, not a guessed active area.
|
||||
box(44.97-display_margin,40.5-display_margin,lid_z-eps,
|
||||
@@ -271,7 +307,11 @@ module labels() {
|
||||
label_at(module_cx,35,"DISPLAY",2.4);
|
||||
for(i=[0:2]) label_at(button_centres[i][0],77,["BACK","SELECT","NEXT"][i],2.1);
|
||||
}
|
||||
module lid() { lid_body(); if(emboss) labels(); }
|
||||
module lid() {
|
||||
lid_body();
|
||||
if(db_panel) { connector_panel(); connector_ribs(); }
|
||||
if(emboss) labels();
|
||||
}
|
||||
module plunger() {
|
||||
// Local bottom at zero, prints vertically; brim recommended for slender stem.
|
||||
cylinder(d=plunger_d,h=plunger_length);
|
||||
@@ -324,9 +364,9 @@ module references() {
|
||||
box(41.57,1.930,rs_top+0.00262024,30.8,0.4,12.55);
|
||||
box(module_cx-16.878038/2,-3.870,db_axis_z-9.1/2,16.878038,5.8,9.1);
|
||||
}
|
||||
// Stock hex envelopes conflict with panel intentionally: must be replaced/reseated.
|
||||
// Reseated 4.8 mm post-body reference ONLY; stud/thread and cable reach unverified.
|
||||
color([0.8,0.5,0.1,0.65]) for(x=lock_x)
|
||||
ybore(x,-2.870,db_axis_z,5.311622,4.8);
|
||||
ybore(x,(db_panel?panel_front_y:flange_y)-4.8,db_axis_z,5.311622,4.8);
|
||||
color("gray") for(p=button_centres) {
|
||||
box(p[0]-3,p[1]-3,carrier_top,6,6,3.4);
|
||||
zcyl(p,carrier_top+3.4,3.5,button_h-3.4);
|
||||
@@ -337,16 +377,53 @@ module references() {
|
||||
}
|
||||
}
|
||||
module assembled(explode=0) {
|
||||
color("slategray") base();
|
||||
color([0.8,0.85,0.9,0.7]) translate([0,0,explode]) lid();
|
||||
if(db_panel) color("slategray") translate([0,-explode/2,0]) panel();
|
||||
color("slategray") render(convexity=10) base();
|
||||
color([0.8,0.85,0.9,0.7]) translate([0,0,explode]) render(convexity=10) lid();
|
||||
|
||||
color("orange") for(p=button_centres) translate([p[0],p[1],button_top+explode]) plunger();
|
||||
if($preview && show_references) %references();
|
||||
}
|
||||
// Conservative rectangular envelopes swept downward in the lid frame: equivalent
|
||||
// to lowering the lid vertically 60 mm onto fixed electronics, posts REMOVED.
|
||||
// Contact faces inset 0.01 only to avoid a coincident flange face in CGAL.
|
||||
module installation_envelopes(stock_posts=false,travel=60,upward=false) {
|
||||
module swept(x,y,z,w,d,h) { box(x,y,upward?z:z-travel,w,d,h+travel); }
|
||||
swept(module_cx-16.878038/2,-3.870,db_axis_z-9.1/2,16.878038,5.8,9.1);
|
||||
swept(41.57,flange_y+0.01,rs_top+0.00262024,30.8,0.39,12.55);
|
||||
// Connector anchor brackets: STEP solids 13/14, not invented mounting holes.
|
||||
for(x=[41.97,66.97]) swept(x,5.03,module_bottom-2.23,5,6.4,12.35262024);
|
||||
swept(41.095,0,module_bottom,31.75,29.337,rs232_t);
|
||||
swept(6.635,21.635,module_bottom,28.09,56.07,esp_t);
|
||||
swept(12.045,14.745,module_bottom,17.63,6.89,esp_t);
|
||||
swept(43.97,36,module_bottom,26,26,oled_t);
|
||||
swept(44.97,40.5,oled_top,24.5,16.5,2);
|
||||
for(x=usb_centres) swept(x-8.87/2,usb_mouth_y-usb_reference_depth,esp_top,
|
||||
8.87,usb_reference_depth,usb_reference_h);
|
||||
for(p=button_centres) swept(p[0]-3,p[1]-3,carrier_top,6,6,button_h);
|
||||
for(p=case_holes) swept(p[0]-case_boss_d/2,p[1]-case_boss_d/2,
|
||||
floor_t,case_boss_d,case_boss_d,split_z-floor_t);
|
||||
if(stock_posts) for(x=lock_x)
|
||||
swept(x-5.311622/2,-2.870,db_axis_z-2.3,5.311622,4.8,4.6);
|
||||
}
|
||||
module installation_check(stock_posts=false) {
|
||||
// Sentinel permits exporting an otherwise empty intersection; expected volume 1.
|
||||
cube(1);
|
||||
translate([0,0,80]) {
|
||||
intersection() { lid(); installation_envelopes(stock_posts); }
|
||||
intersection() { lid(); base(); }
|
||||
// Sweep each step separately: a whole-part hull invents diagonal material.
|
||||
if(db_panel) intersection() { lid(); connector_upstand(60); }
|
||||
// New material must also clear electronics while boards lower into the base.
|
||||
if(db_panel) intersection() {
|
||||
connector_upstand();
|
||||
installation_envelopes(false,60,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Each printable export is on Z=0, without hardware reference geometry.
|
||||
if(part=="base") world() base();
|
||||
else if(part=="lid") translate([0,0,-split_z-seam_clearance]) world() lid();
|
||||
else if(part=="panel") rotate([90,0,0]) translate([-34,-panel_front_y,-panel_low_z]) panel();
|
||||
|
||||
else if(part=="plunger") plunger();
|
||||
else if(part=="plungers") plungers();
|
||||
|
||||
@@ -356,8 +433,10 @@ else if(part=="layout") {
|
||||
translate([xmax-xmin+12,0,-split_z-seam_clearance]) world() lid();
|
||||
translate([0,-22,0]) plungers();
|
||||
|
||||
translate([60,-20,0]) rotate([90,0,0]) translate([-34,-panel_front_y,-panel_low_z]) panel();
|
||||
|
||||
}
|
||||
else if(part=="installation-check") installation_check();
|
||||
else if(part=="installation-blocked") installation_check(true);
|
||||
else if(part=="check") {} // datums/assertions only, used by validate.py
|
||||
else if(part=="assembly") world() assembled();
|
||||
else if(part=="exploded") world() assembled(28);
|
||||
|
||||
+5977
-8553
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
File diff suppressed because it is too large
Load Diff
@@ -3261,20 +3261,6 @@ solid OpenSCAD_Model
|
||||
vertex 1.53073 -3.69552 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.65934 -0.751845 0
|
||||
outer loop
|
||||
vertex 2.43505 -3.17341 0.5
|
||||
vertex 2.82843 -2.82843 1.7
|
||||
vertex 2.43505 -3.17341 1.7
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.65934 -0.751845 0
|
||||
outer loop
|
||||
vertex 2.82843 -2.82843 1.7
|
||||
vertex 2.43505 -3.17341 0.5
|
||||
vertex 2.82843 -2.82843 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.896871 -0.442291 0
|
||||
outer loop
|
||||
vertex -3.4641 -2 0.5
|
||||
@@ -3317,6 +3303,20 @@ solid OpenSCAD_Model
|
||||
vertex -1.53073 -3.69552 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.65934 -0.751845 0
|
||||
outer loop
|
||||
vertex 2.43505 -3.17341 0.5
|
||||
vertex 2.82843 -2.82843 1.7
|
||||
vertex 2.43505 -3.17341 1.7
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.65934 -0.751845 0
|
||||
outer loop
|
||||
vertex 2.82843 -2.82843 1.7
|
||||
vertex 2.43505 -3.17341 0.5
|
||||
vertex 2.82843 -2.82843 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.831471 -0.555569 0
|
||||
outer loop
|
||||
vertex -3.17341 -2.43505 0.5
|
||||
@@ -7265,20 +7265,6 @@ solid OpenSCAD_Model
|
||||
vertex 13.5307 -3.69552 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 14.435 -3.17341 0.5
|
||||
vertex 14.8284 -2.82843 1.7
|
||||
vertex 14.435 -3.17341 1.7
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 14.8284 -2.82843 1.7
|
||||
vertex 14.435 -3.17341 0.5
|
||||
vertex 14.8284 -2.82843 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.896871 -0.442291 0
|
||||
outer loop
|
||||
vertex 8.5359 -2 0.5
|
||||
@@ -7321,6 +7307,20 @@ solid OpenSCAD_Model
|
||||
vertex 10.4693 -3.69552 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 14.435 -3.17341 0.5
|
||||
vertex 14.8284 -2.82843 1.7
|
||||
vertex 14.435 -3.17341 1.7
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 14.8284 -2.82843 1.7
|
||||
vertex 14.435 -3.17341 0.5
|
||||
vertex 14.8284 -2.82843 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.831471 -0.555569 0
|
||||
outer loop
|
||||
vertex 8.82659 -2.43505 0.5
|
||||
@@ -11269,20 +11269,6 @@ solid OpenSCAD_Model
|
||||
vertex 25.5307 -3.69552 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 26.435 -3.17341 0.5
|
||||
vertex 26.8284 -2.82843 1.7
|
||||
vertex 26.435 -3.17341 1.7
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 26.8284 -2.82843 1.7
|
||||
vertex 26.435 -3.17341 0.5
|
||||
vertex 26.8284 -2.82843 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.896887 -0.44226 0
|
||||
outer loop
|
||||
vertex 20.5359 -2 0.5
|
||||
@@ -11325,6 +11311,20 @@ solid OpenSCAD_Model
|
||||
vertex 22.4693 -3.69552 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 26.435 -3.17341 0.5
|
||||
vertex 26.8284 -2.82843 1.7
|
||||
vertex 26.435 -3.17341 1.7
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.659321 -0.751861 0
|
||||
outer loop
|
||||
vertex 26.8284 -2.82843 1.7
|
||||
vertex 26.435 -3.17341 0.5
|
||||
vertex 26.8284 -2.82843 0.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.831462 -0.555582 0
|
||||
outer loop
|
||||
vertex 20.8266 -2.43505 0.5
|
||||
|
||||
@@ -78,6 +78,11 @@ def source_check():
|
||||
cad = json.loads((PCB / "reference/db9-cad/measurements.json").read_text())
|
||||
near([axis["location"][0]+43-1.905 for axis in cad["front_lock_axes"]], literal("lock_x"))
|
||||
near(cad["shell_outer_including_root_and_lip"]["dimensions"], [16.878038, 5.8, 9.1])
|
||||
shell = cad["shell_outer_including_root_and_lip"]["bounds"]
|
||||
near([shell[2], 29.337-shell[4]], [3.29762024, -3.87])
|
||||
flange = cad["front_flange_exposed_faces"]["bounds"]
|
||||
near([29.337-flange[4]], [literal("flange_y")])
|
||||
near([flange[0]+41.095, flange[3]+41.095], [41.57, 72.37])
|
||||
for axis in cad["front_lock_axes"]:
|
||||
near([axis["location"][2]-1.57], [6.27762024])
|
||||
print("PASS native PCB outline, thickness, placements, NPTH centres, buttons, USB mouths, STEP lock/shell data")
|
||||
@@ -127,6 +132,8 @@ def mesh_check(path, expected_components):
|
||||
|
||||
def geometry_check():
|
||||
"""Evaluate actual SCAD expressions, including negative stud/web fixtures."""
|
||||
assert 'panel_hangers' not in SCAD and 'panel_mount_' not in SCAD
|
||||
assert 'part=="panel"' not in SCAD and not (ROOT/"panel-default.stl").exists()
|
||||
assert 'module spacers(' not in SCAD and 'part=="spacers"' not in SCAD
|
||||
assert not (ROOT/"spacers-default.stl").exists(), "Obsolete printable spacers must not ship"
|
||||
assert 'brass_standoff_h' not in SCAD, "No brass height below carrier"
|
||||
@@ -149,35 +156,103 @@ def geometry_check():
|
||||
match = re.search(r'ECHO: "CHECK '+label+r'", (\[[^\n]+\])', output)
|
||||
assert match, output
|
||||
return ast.literal_eval(match.group(1))
|
||||
return datum("stack"), datum("engagement")
|
||||
stack, engagement = run()
|
||||
closure = datum("closure")
|
||||
assert closure[2] <= -0.5 and closure[3] < closure[4]
|
||||
if not defines:
|
||||
near(closure, [33,81,-0.5,20.82,21.6976,17.578,0.25])
|
||||
return datum("stack"), datum("engagement"), datum("connector")
|
||||
stack, engagement, connector = run()
|
||||
near(connector, [-0.47, 1.93, 2.4, 21.07, 31.9976, 18.078, 25, 3.6, 5, 1.66098])
|
||||
# Independent expected defaults, not values copied from exported meshes.
|
||||
near(stack, [7.4, 9, 19, 20.57, 35, 37.4, 15, 13.3, 15.6,
|
||||
26.8476, 21.07, 19.6, 26.6, 25.6])
|
||||
near(engagement, [2.4, 2.4])
|
||||
raised, _ = run(["carrier_boss_h=7"])
|
||||
raised, _, _ = run(["carrier_boss_h=7"])
|
||||
near(raised, [value+(0 if i == 13 else 2) for i, value in enumerate(stack)])
|
||||
taller, _ = run(["module_gap=12", "lid_above_carrier=28"])
|
||||
taller, _, _ = run(["module_gap=12", "lid_above_carrier=28"])
|
||||
near(taller, [value+(2 if i in [2,3,4,5,9,10,11,12,13] else 0)
|
||||
for i, value in enumerate(stack)])
|
||||
run(["brass_m3_stud_l=1.6"], valid=False)
|
||||
run(["brass_m2_stud_l=5"], valid=False)
|
||||
run(["wall=0.8"], valid=False)
|
||||
run(["panel_t=0.8"], valid=False)
|
||||
run(["panel_flange_gap=-0.1"], valid=False)
|
||||
run(["panel_bottom_above_module=-0.1"], valid=False)
|
||||
run(["external_fastener_clearance=5"], valid=False)
|
||||
run(["panel_rib_w=8"], valid=False)
|
||||
run(["seam_clearance=0"], valid=False)
|
||||
run(["shell_margin=0.1"], valid=False)
|
||||
run(["panel_bottom_above_module=2"], valid=False)
|
||||
print("PASS actual SCAD default/raised-carrier/taller-module datums, stud engagement and nozzle-web rejection fixtures")
|
||||
|
||||
|
||||
def installation_check():
|
||||
"""CGAL intersection of actual lid with continuous vertical envelope sweeps."""
|
||||
with tempfile.TemporaryDirectory(prefix="case-install-") as temp:
|
||||
for mode, defines in [
|
||||
("installation-check", []),
|
||||
("installation-check", ["carrier_boss_h=7", "module_gap=12", "lid_above_carrier=28", "panel_t=3"]),
|
||||
("installation-check", ["seam_clearance=0.4", "shell_margin=0.8", "end_clearance=1.5"]),
|
||||
("installation-blocked", []),
|
||||
]:
|
||||
path = Path(temp)/"sweep.stl"
|
||||
command = ["openscad", "-o", str(path), "-D", f'part="{mode}"', "-D", "emboss=false"]
|
||||
for define in defines:
|
||||
command.extend(["-D", define])
|
||||
result = subprocess.run(command+[str(ROOT/"enclosure.scad")],
|
||||
env={**os.environ, "QT_QPA_PLATFORM": "offscreen"},
|
||||
capture_output=True, text=True, timeout=180)
|
||||
assert result.returncode == 0 and "ERROR:" not in result.stderr, result.stderr
|
||||
if mode == "installation-check":
|
||||
report = mesh_check(path, 1)
|
||||
near([report["volume_mm3"]], [1])
|
||||
else:
|
||||
# Negative control: unremoved post envelopes MUST hit the panel.
|
||||
text = path.read_text()
|
||||
zs = [float(z) for z in re.findall(r"\bvertex\s+[-+\deE.]+\s+[-+\deE.]+\s+([-+\deE.]+)", text)]
|
||||
assert max(zs) > 80, "Fixture failed to detect installed posts"
|
||||
print("PASS continuous vertical installation sweep", mode, defines)
|
||||
|
||||
|
||||
def variant_check():
|
||||
fixtures = [
|
||||
('lid', ['carrier_boss_h=7', 'module_gap=12', 'lid_above_carrier=28', 'panel_t=3']),
|
||||
('base', ['carrier_boss_h=7', 'module_gap=12', 'lid_above_carrier=28', 'panel_t=3']),
|
||||
('lid', ['db_panel=false']),
|
||||
('base', ['daughter_supports=false', 'side_extra=14', 'm3_insert_d=4.4']),
|
||||
]
|
||||
with tempfile.TemporaryDirectory(prefix='case-variant-') as temp:
|
||||
for part, defines in fixtures:
|
||||
path = Path(temp)/'variant.stl'
|
||||
command = ['openscad', '-o', str(path), '-D', f'part="{part}"', '-D', 'emboss=false']
|
||||
for define in defines:
|
||||
command.extend(['-D', define])
|
||||
result = subprocess.run(command+[str(ROOT/'enclosure.scad')],
|
||||
env={**os.environ, 'QT_QPA_PLATFORM': 'offscreen'},
|
||||
capture_output=True, text=True, timeout=180)
|
||||
assert result.returncode == 0 and 'ERROR:' not in result.stderr, result.stderr
|
||||
mesh_check(path, 1)
|
||||
print('PASS parameter CGAL mesh', part, defines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--source", action="store_true", help="also check saved KiCad/STEP registration")
|
||||
parser.add_argument("--installation", action="store_true", help="CGAL continuous vertical sweep and blocked-post control")
|
||||
parser.add_argument('--variants', action='store_true', help='CGAL parameter mesh smoke tests')
|
||||
args = parser.parse_args()
|
||||
if args.variants:
|
||||
variant_check()
|
||||
if args.installation:
|
||||
installation_check()
|
||||
geometry_check()
|
||||
if args.source:
|
||||
source_check()
|
||||
reports = [mesh_check(ROOT / name, n) for name, n in [
|
||||
("base-default.stl", 1), ("lid-default.stl", 1), ("panel-default.stl", 1),
|
||||
("base-default.stl", 1), ("lid-default.stl", 1),
|
||||
("plungers-default.stl", 3)]]
|
||||
for report, high in zip(reports, [[94,83.4,15], [94,83.4,22.75],
|
||||
[46,0,2], [28,4,25.6]]):
|
||||
for report, high in zip(reports, [[94,83.4,21.6976], [94,83.4,22.75],
|
||||
[28,4,25.6]]):
|
||||
near(report["bounds_mm"][1], high) # catch stale exports of the old stack
|
||||
print(json.dumps(reports, indent=2))
|
||||
print("PASS edge-manifold topology, winding, component counts, positive total volumes and print-bed Z")
|
||||
|
||||
Reference in New Issue
Block a user