/* a box divided on layers, 4 long screws at the corners to keep it together, alignment pins between layers * top, big hole for the screen and small holes for air * board, holds the board and the switch / button / usb / sd holes on the sides; the co2 sensor goes here as well * bottom, for battery and pm sensor */ use // all measurements are in mm // thickness of bottom / top / side walls WALL_THICKNESS=1; /* [Screws] */ // diameter of the screws; I'm using M4 screws SCREW_DIAM=4; // extra space between screws and box edges SCREW_PAD=0; // length of the bolt's stem, avoids half-empty holes in the bottom BOLT_STEM_Z=24.7; /* [Particle sensor] */ // width (connector side - inlet side) PM_X=40.8; // height PM_Y=40.8; // depth PM_Z=12.5; // height of the window for the connector PM_CONN_Y=15.8; // depth of the window for the connector, from the top of the sensor PM_CONN_DEPTH=7.7; /* [Battery] */ // width (longest dimension) BATT_X=55; // height BATT_Y=33.5; // depth (shortest dimension) BATT_Z=10.8; /* [CO2 sensor] */ // width (connector side - other side) CO_X=28.5; // height CO_Y=24.1; // depth CO_Z=8; // height of the window for the connector CO_CONN_Y=10; // diameter of top holes to let air into the CO2 sensor CO_HOLE_DIAM=2; // distance between hole centres CO_HOLE_DIST=4; /* [Cable wells] */ // width of the space for the battery & pm cables BOT_CABLE_WELL_X=15; // width of the space between CO2 sensor & main board MID_CABLE_WELL_X=5; /* [Main board] */ // width (longest dimension) BOARD_X=66.5; // height BOARD_Y=36.8; // distance between display face and bottom of PCB BOARD_SINK=2.2; // width offset of left mounting hole BOARD_PIN_OFF_X=2.35; // width distance between left and right mounting hole BOARD_PIN_DIST_X=61.43; // height offset of bottom mounting hole BOARD_PIN_OFF_Y=2.15; // height distance between bottom and top mounting hole BOARD_PIN_DIST_Y=32.5; // diameter of support pillars BOARD_SUPPORT_DIAM=3; // diameter of mounting holes BOARD_SUPPORT_PIN_DIAM=2; /* [Display] */ // width (longest dimension) DISPLAY_WIN_X=51; // height DISPLAY_WIN_Y=26; // width offset of view window DISPLAY_X_OFF=2.5; // height offset of view window DISPLAY_Y_OFF=6.1; /* [USB hole] */ // width USB_MICRO_B_X=12; // depth USB_MICRO_B_Z=4.75; // width distance between left edge of board to middle of USB socket USB_MICRO_B_OFF_X=42.7; /* [SD space] */ // width (bit wider than the middle dimension of the SD card) SD_X=12.5; // height (how much the card protrudes from the PCB) SD_Y=9.5; // depth (distance between bottom of PCB and bottom of card) SD_Z=3.5; // width distance between left edge of board to middle of SD card SD_OFF_X=54; /* [On/Off switch hole] */ // width SWITCH_X=9; // height (how much the switch protrudes) SWITCH_Y=2; // depth (distance between bottom of PCB and bottom of switch) SWITCH_Z=4; // width distance between left edge of board to middle of switch SWITCH_OFF_X=42.5; /* [Reset button hole] */ // width RESET_X=2.5; // depth (distance between bottom of PCB and bottom of hole) RESET_Z=2; // width distance between left edge of board to middle of button RESET_OFF_X=51; /* [Other button] */ // width BUTTON_X=5; // depth (distance between bottom of PCB and bottom of hole) BUTTON_Z=2.5; // width distance between left edge of board to middle of button BUTTON_OFF_X=58; module __Customizer_Limit__ () {} // we use SWITCH_Y to offset the PCB from the edge, so the height of // the pillars that partially cover the switch+button holes has to be // the same PILLAR_Y=SWITCH_Y; // fudge factor: the pillars need to be a bit thinner than the holes // they go into PILLAR_SHRINK=0.05; // top layer is 2 walls thick, just because TOP_Z=WALL_THICKNESS*2; // middle layer needs to hold the CO2 sensor on a wall; the PCB is much thinner MIDDLE_Z=WALL_THICKNESS + CO_Z; // bottom layers needs to hold the PM sensor and the battery BOTTOM_Z=WALL_THICKNESS + max(PM_Z,BATT_Z); // width of the whole thing: 2 walls, plus wider of the middle & bottom layers TOTAL_X=2*WALL_THICKNESS + max( PM_X + BOT_CABLE_WELL_X + BATT_X, CO_X + MID_CABLE_WELL_X + BOARD_X ); // height of the whole thing: 2 walls, plus highest of the middle & bottom layers TOTAL_Y=2*(WALL_THICKNESS + SCREW_DIAM + SCREW_PAD) + max(PM_Y,CO_Y,BOARD_Y,BATT_Y); // nearly all objects are centered in X&Y, and most are sitting on Z=0 // just like a centered cube, but sitting at Z=0 module box(v) { translate([0,0,v[2]/2]) cube(v,center=true); } /* SCREWS */ module screw_hole() { cylinder(h=200,d=SCREW_DIAM,center=true,$fs=0.1); } // this is nearly copied from threads.scad: its ScrewHole subtracts // the hole from its children, I need a thing that I can subtract // multiple times from a single object, threads.scad doesn't expose // that module myScrew(outer_diam, height, position=[0,0,0], rotation=[0,0,0], pitch=0, tooth_angle=30, tolerance=0.4, tooth_height=0) { extra_height = 0.001 * height; translate(position) rotate(rotation) translate([0, 0, -extra_height/2]) ScrewThread(1.01*outer_diam + 1.25*tolerance, height + extra_height, pitch, tooth_angle, tolerance, tooth_height=tooth_height); } module screw_hole_threaded() { H=BOLT_STEM_Z-TOP_Z-MIDDLE_Z; myScrew( outer_diam=SCREW_DIAM, height=H, position=[0,0,BOTTOM_Z-H] ); } module place_screw_holes() { OFF=WALL_THICKNESS + SCREW_DIAM/2 + SCREW_PAD; translate([ TOTAL_X/2 - OFF, TOTAL_Y/2 - OFF, 0]) children(); translate([ -(TOTAL_X/2 - OFF), TOTAL_Y/2 - OFF, 0]) children(); translate([ TOTAL_X/2 - OFF, -(TOTAL_Y/2 - OFF), 0]) children(); translate([ -(TOTAL_X/2 - OFF), -(TOTAL_Y/2 - OFF), 0]) children(); } module screw_holes() { place_screw_holes() screw_hole(); } module screw_holes_threaded() { place_screw_holes() screw_hole_threaded(); } /* all the various modules */ module pm_sensor() { box([PM_X,PM_Y,PM_Z]); } module bottom_cable_well() { box([BOT_CABLE_WELL_X,PM_CONN_Y,PM_CONN_DEPTH]); } module battery() { box([BATT_X,BATT_Y,BATT_Z]); } module co_sensor() { box([CO_X,CO_Y,CO_Z]); } module co_hole() { cylinder(h=200,d=CO_HOLE_DIAM,center=true,$fs=0.2); } module co_holes() { translate([-CO_X/2,-CO_Y/2,0]) for( x=[CO_HOLE_DIAM:CO_HOLE_DIST:CO_X], y=[CO_HOLE_DIAM:CO_HOLE_DIST:CO_Y] ) translate([x,y,0]) co_hole(); } module middle_cable_well() { box([ 20, // doesn't matter, wells at both sides are much wider CO_CONN_Y, CO_Z, ]); } module board_well() { cube([ BOARD_X, BOARD_Y, 200], center=true); } module board_support() { HEIGHT=MIDDLE_Z - BOARD_SINK; EXTRA=BOARD_SUPPORT_DIAM*2; translate([0,0,HEIGHT/2]) union() { cylinder( h=HEIGHT, d=BOARD_SUPPORT_DIAM, center=true,$fs=0.1 ); translate([EXTRA/2, EXTRA/2 - BOARD_SUPPORT_DIAM/2, 0]) cube([EXTRA,EXTRA,HEIGHT],center=true); translate([EXTRA/2 - BOARD_SUPPORT_DIAM/2, EXTRA/2, 0]) cube([EXTRA,EXTRA,HEIGHT],center=true); translate([0,0,BOARD_SINK/2]) cylinder( h=HEIGHT+BOARD_SINK, d=BOARD_SUPPORT_PIN_DIAM, center=true,$fs=0.1 ); } } module board_supports() { translate([ -BOARD_X/2 + BOARD_PIN_OFF_X, -BOARD_Y/2 + BOARD_PIN_OFF_Y,0]) { translate([ 0, 0, 0]) rotate([0,0,180]) board_support(); translate([ BOARD_PIN_DIST_X, 0, 0]) rotate([0,0,-90]) board_support(); translate([ 0, BOARD_PIN_DIST_Y, 0]) rotate([0,0,90]) board_support(); translate([ BOARD_PIN_DIST_X, BOARD_PIN_DIST_Y, 0]) board_support(); } } module usb_well() { translate([0,0,-USB_MICRO_B_Z]) box([USB_MICRO_B_X, 30, 30]); } module sd_well() { EXTRA=10; translate([0,SD_Y/2-EXTRA/2,-SD_Z]) box([SD_X, SD_Y+EXTRA, 30]); } module switch_well() { translate([0,0,-SWITCH_Z]) box([SWITCH_X, 30, 30]); } module switch_pillar() { H=BOARD_SINK+TOP_Z/2; translate([0,0,-H/2]) cube([SWITCH_X-PILLAR_SHRINK, PILLAR_Y, H], center=true); } module reset_well() { translate([0,0,-RESET_Z]) box([RESET_X, 30, 30]); } module reset_pillar() { H=BOARD_SINK+TOP_Z/2; translate([0,0,-H/2]) cube([RESET_X-PILLAR_SHRINK, PILLAR_Y, H], center=true); } module button_well() { translate([0,0,-BUTTON_Z]) box([BUTTON_X, 30, 30]); } module button_pillar() { H=BOARD_SINK+TOP_Z/2; translate([0,0,-H/2]) cube([BUTTON_X-PILLAR_SHRINK, PILLAR_Y, H], center=true); } // these have matching outside_pillars for the top piece, filling them // down to the right depth; top piece needs to be printed upside-down module outside_wells() { translate([-BOARD_X/2,0,0]) { translate([0,BOARD_Y/2,0]) { translate([USB_MICRO_B_OFF_X, 0,0]) usb_well(); translate([SD_OFF_X, 0,0]) sd_well(); // not actually facing outside }; translate([0,-BOARD_Y/2,0]) { translate([SWITCH_OFF_X, 0,0]) switch_well(); translate([RESET_OFF_X, 0,0]) reset_well(); translate([BUTTON_OFF_X, 0,0]) button_well(); }; } } module outside_pillars() { translate([-BOARD_X/2,0,TOP_Z/2]) { translate([0,-TOTAL_Y/2+PILLAR_Y/2,0]) { translate([SWITCH_OFF_X, 0,0]) switch_pillar(); translate([RESET_OFF_X, 0,0]) reset_pillar(); translate([BUTTON_OFF_X, 0,0]) button_pillar(); }; } } module display_hole() { cube([DISPLAY_WIN_X, DISPLAY_WIN_Y, 30], center=true); } /* The Layers */ module bottom() { PM_X_OFF=(TOTAL_X - PM_X)/2; CABLE_X_OFF= PM_X_OFF - PM_X/2 - BOT_CABLE_WELL_X/2; // touching the PM pocket CABLE_Y_OFF=-PM_Y/2 + PM_CONN_Y/2; CABLE_Z_OFF=BOTTOM_Z - PM_CONN_DEPTH; BATT_X_OFF=CABLE_X_OFF - BOT_CABLE_WELL_X/2 - BATT_X/2; BATT_Z_OFF=BOTTOM_Z - BATT_Z; difference() { box([TOTAL_X, TOTAL_Y, BOTTOM_Z]); screw_holes_threaded(); translate([PM_X_OFF, 0, WALL_THICKNESS]) pm_sensor(); translate([CABLE_X_OFF, CABLE_Y_OFF, CABLE_Z_OFF]) bottom_cable_well(); translate([BATT_X_OFF, 0, BATT_Z_OFF]) battery(); } } // used by middle and top BOARD_X_OFF= (TOTAL_X-BOARD_X)/2 - CO_X - MID_CABLE_WELL_X - WALL_THICKNESS; BOARD_Y_OFF= (-TOTAL_Y+BOARD_Y)/2 + SWITCH_Y; CO_X_OFF=(TOTAL_X - CO_X)/2 - WALL_THICKNESS; module middle() { CO_Z_OFF=MIDDLE_Z - CO_Z; CABLE_X_OFF= CO_X_OFF - CO_X/2 - MID_CABLE_WELL_X/2; BOARD_Z_OFF=MIDDLE_Z - BOARD_SINK; union() { difference() { box([TOTAL_X, TOTAL_Y, MIDDLE_Z]); screw_holes(); translate([CO_X_OFF, 0, CO_Z_OFF]) co_sensor(); translate([CABLE_X_OFF, 0, CO_Z_OFF]) middle_cable_well(); translate([BOARD_X_OFF, BOARD_Y_OFF, 0]) { translate([0,0, 0]) board_well(); translate([0,0, BOARD_Z_OFF]) outside_wells(); } } intersection() { translate([BOARD_X_OFF, BOARD_Y_OFF, 0]) board_supports(); box([TOTAL_X, TOTAL_Y, MIDDLE_Z]); } } } module top() { DISP_X_OFF=BOARD_X_OFF - (BOARD_X-DISPLAY_WIN_X)/2 + DISPLAY_X_OFF; DISP_Y_OFF=BOARD_Y_OFF - (BOARD_Y-DISPLAY_WIN_Y)/2 + DISPLAY_Y_OFF; union() { difference() { box([TOTAL_X, TOTAL_Y, TOP_Z]); screw_holes(); translate([CO_X_OFF, 0, 0]) co_holes(); translate([DISP_X_OFF, DISP_Y_OFF, 0]) display_hole(); } translate([BOARD_X_OFF, 0, 0]) outside_pillars(); } } /* render! */ translate([0, TOTAL_Y * 1.2, 0]) bottom(); //translate([0, 0, -BOTTOM_Z-2]) bottom(); middle(); translate([0, -TOTAL_Y * 1.2, 0]) rotate([180,0,0]) top(); //translate([0, 0, +MIDDLE_Z+2]) top();