Devesh36/pocket-audio-player

A portable, rechargeable microSD audio player built around an RP2040, SPI flash, USB‑C charging/power management, LCD/button controls, PCM5102A I²S DAC, and TPA6132A headphone amplifier in a 3D‑printed enclosure.

Version
1.0.12
License
unset
Stars
0

scripts/check-enclosure.tsx

import { renderToJscadPlan } from "jscad-fiber/headless"
import { executeJscadOperations, resolveReferencePlanes } from "jscad-planner"
import modeling from "@jscad/modeling"
import { CaseBase, CaseLid, ButtonPlunger, controlButtons, mountingHoles, RingButton, ringAngles, caseDimensions, batteryDimensions } from "../enclosure"
import { OledShape, oledMounts, oledDimensions } from "../oled-shape"
import {BatteryPlusLead,BatteryMinusLead} from "../mechanical-shapes"
const m=modeling as any
const solid=(jsx:any)=>executeJscadOperations(m,resolveReferencePlanes(renderToJscadPlan(jsx)).geometry)
const base=solid(<CaseBase />),lid=solid(<CaseLid />)
const volumes:any={baseLidOverlap:m.measurements.measureVolume(m.booleans.intersect(base,lid))}
const pcb=m.primitives.cuboid({size:[60,90,1.6]})
const oledLocal=solid(<OledShape />)
const oled=m.transforms.translate([0,-7,0.8],oledLocal)
volumes.oledLidOverlap=m.measurements.measureVolume(m.booleans.intersect(oled,lid))
volumes.oledBaseOverlap=m.measurements.measureVolume(m.booleans.intersect(oled,base))
volumes.oledPcbOverlap=m.measurements.measureVolume(m.booleans.intersect(oled,pcb))
volumes.pcbBaseOverlap=m.measurements.measureVolume(m.booleans.intersect(pcb,base))
volumes.pcbLidOverlap=m.measurements.measureVolume(m.booleans.intersect(pcb,lid))
const moving:any[]=[]
for(const [name] of controlButtons){
 const p=solid(name==='SELECT'?<ButtonPlunger />:<RingButton angle={ringAngles[name as keyof typeof ringAngles]} />)
 moving.push([name,p])
 const pressed=m.transforms.translate([0,0,-0.45],p)
 volumes[`${name}PressedLidOverlap`]=m.measurements.measureVolume(m.booleans.intersect(pressed,lid))
 volumes[`${name}PressedBaseOverlap`]=m.measurements.measureVolume(m.booleans.intersect(pressed,base))
 volumes[`${name}LidOverlap`]=m.measurements.measureVolume(m.booleans.intersect(p,lid))
 volumes[`${name}BaseOverlap`]=m.measurements.measureVolume(m.booleans.intersect(p,base))
}
for(let i=0;i<moving.length;i++)for(let j=i+1;j<moving.length;j++)volumes[`${moving[i][0]}${moving[j][0]}Overlap`]=m.measurements.measureVolume(m.booleans.intersect(moving[i][1],moving[j][1]))
// Purchased protected 500 mAh cell envelope, below the lower PCB half.
const battery=m.primitives.cuboid({size:[29,36,4.75],center:[...batteryDimensions.center]})
volumes.batteryBaseOverlap=m.measurements.measureVolume(m.booleans.intersect(base,battery))
volumes.batteryPcbOverlap=m.measurements.measureVolume(m.booleans.intersect(pcb,battery))
for(const [name,jsx] of [['plus',<BatteryPlusLead/>],['minus',<BatteryMinusLead/>]] as const){const wire=solid(jsx);volumes[`${name}WirePcbOverlap`]=m.measurements.measureVolume(m.booleans.intersect(wire,pcb));volumes[`${name}WireBaseOverlap`]=m.measurements.measureVolume(m.booleans.intersect(wire,base));volumes[`${name}WireLidOverlap`]=m.measurements.measureVolume(m.booleans.intersect(wire,lid));for(const [button,p] of moving)volumes[`${name}WirePressed${button}Overlap`]=m.measurements.measureVolume(m.booleans.intersect(wire,m.transforms.translate([0,0,-0.45],p)))}
const batteryPlug=m.primitives.cuboid({size:[6.5,5,8],center:[-22,-16.45,4.8]});volumes.batteryPlugLidOverlap=m.measurements.measureVolume(m.booleans.intersect(batteryPlug,lid));for(const [name,p] of moving)volumes[`batteryPlugPressed${name}Overlap`]=m.measurements.measureVolume(m.booleans.intersect(batteryPlug,m.transforms.translate([0,0,-0.45],p)));
const lcdPlug=m.primitives.cuboid({size:[13.25,4.25,7.3],center:[0,-7,4.45]});volumes.lcdPlugLidOverlap=m.measurements.measureVolume(m.booleans.intersect(lcdPlug,lid));volumes.lcdPlugScreenOverlap=m.measurements.measureVolume(m.booleans.intersect(lcdPlug,oled));for(const [name,p] of moving)volumes[`lcdPlugPressed${name}Overlap`]=m.measurements.measureVolume(m.booleans.intersect(lcdPlug,m.transforms.translate([0,0,-0.45],p)));
if(Object.values(volumes).some((v:any)=>Math.abs(v)>1e-5))throw new Error(JSON.stringify(volumes))
const c=await Bun.file('dist/index/circuit.json').json()
const pcbHoles=c.filter((c:any)=>c.type==='pcb_hole'&&Math.abs(c.hole_diameter-2.7)<1e-5)
for(const [x,y] of mountingHoles)if(!pcbHoles.some((h:any)=>Math.abs(h.x-x)<1e-5&&Math.abs(h.y-y)<1e-5))throw new Error('Missing PCB mounting hole')
const result={checks:'passed',intersectionVolumesMm3:volumes,mountingHoles,oledMounts,oledDimensions,bodySizeMm:[caseDimensions.width,caseDimensions.height,caseDimensions.lidTopZ-caseDimensions.floorZ],caseDimensions,pcbClearancePerSideMm:0.5,lipClearancePerSideMm:0.3,buttonGuideRadialClearanceMm:0.3,buttonRestGapMm:0.25,checkedButtonTravelMm:0.45,batteryDimensions,notes:'Digital geometry checks use the Waveshare 1.9-inch manufacturer CAD and a conservative module/socket envelope. Purchased hardware, the crimp harness, screws and printing tolerances still require physical fit testing.'}
await Bun.write('dist/enclosure/fit-check.json',JSON.stringify(result,null,2));console.log(JSON.stringify(result,null,2))