|
|
|
@@ -19,13 +19,14 @@ show_references = true;
|
|
|
|
|
/* [Stack] */
|
|
|
|
|
top_profile = "generic"; // [generic,gps,oled128x64]
|
|
|
|
|
wing_count = 1; // [1:1:8]
|
|
|
|
|
wing_pitch = 11; // [7:0.1:16]
|
|
|
|
|
wing_pitch = 12.25; // [7:0.1:16]
|
|
|
|
|
stack_height_override = 0; // [0:0.1:100]
|
|
|
|
|
generic_component_height = 3; // [0:0.1:30]
|
|
|
|
|
generic_top_gap = 2; // [0.5:0.1:10]
|
|
|
|
|
gps_antenna_height = 4;
|
|
|
|
|
gps_antenna_height = 6;
|
|
|
|
|
gps_top_gap = 5;
|
|
|
|
|
oled_component_height = 2.4;
|
|
|
|
|
oled_display_height = 1.5;
|
|
|
|
|
oled_face_gap = 0.8;
|
|
|
|
|
|
|
|
|
|
/* [OLED controls] */
|
|
|
|
@@ -47,10 +48,15 @@ battery_diameter = 18;
|
|
|
|
|
battery_radial_clearance = 0.6;
|
|
|
|
|
battery_end_clearance = 1;
|
|
|
|
|
battery_partition_thickness = 1.8;
|
|
|
|
|
battery_board_clearance = 3;
|
|
|
|
|
rocker_cutout_size = [12.9, 19.4];
|
|
|
|
|
rocker_corner_radius = 0.8;
|
|
|
|
|
rocker_body_size = [15, 21, 21];
|
|
|
|
|
rocker_artwork_gap = 2;
|
|
|
|
|
rocker_partition_offset = 2;
|
|
|
|
|
rocker_tab_panel_thickness = 2;
|
|
|
|
|
rocker_tab_land = 2;
|
|
|
|
|
rocker_tab_taper_length = 0.6;
|
|
|
|
|
|
|
|
|
|
/* [Tripod mount] */
|
|
|
|
|
tripod_insert_enabled = false;
|
|
|
|
@@ -77,10 +83,11 @@ corner_radius = 4;
|
|
|
|
|
board_bottom_clearance = 5.4;
|
|
|
|
|
board_side_clearance = 7;
|
|
|
|
|
board_rear_clearance = 4;
|
|
|
|
|
usb_board_wall_gap = 1.2;
|
|
|
|
|
usb_board_wall_gap = 2;
|
|
|
|
|
lid_lip_depth = 2.4;
|
|
|
|
|
lid_lip_thickness = 1.2;
|
|
|
|
|
fit_clearance = 0.35;
|
|
|
|
|
cutout_min_web = 1.2;
|
|
|
|
|
hole_compensation = 0.2;
|
|
|
|
|
lid_screw_head_diameter = 5.2;
|
|
|
|
|
lid_screw_head_height = 2.5;
|
|
|
|
@@ -97,10 +104,15 @@ lid_art_margin = 8.5;
|
|
|
|
|
logo_file = "assets/NME-fixed.svg";
|
|
|
|
|
|
|
|
|
|
/* [USB-C opening] */
|
|
|
|
|
usb_opening_width = 12.5;
|
|
|
|
|
usb_opening_height = 7;
|
|
|
|
|
usb_opening_enabled = true;
|
|
|
|
|
usb_guide_enabled = true;
|
|
|
|
|
usb_connector_overhang = 2;
|
|
|
|
|
usb_guide_wall_thickness = 1.2;
|
|
|
|
|
usb_guide_end_gap = 1;
|
|
|
|
|
usb_opening_width = 13.5;
|
|
|
|
|
usb_opening_height = 8;
|
|
|
|
|
usb_opening_radius = 1.5;
|
|
|
|
|
usb_vertical_offset = 0;
|
|
|
|
|
usb_vertical_offset = 0.5;
|
|
|
|
|
|
|
|
|
|
/* [Custom wall cutouts] */
|
|
|
|
|
// Entry format: [face, shape, u, z, width, height, radius]
|
|
|
|
@@ -156,6 +168,20 @@ board_origin_y = wall_thickness + board_side_clearance
|
|
|
|
|
+ combined_tripod_board_gutter;
|
|
|
|
|
board_bottom_z = effective_floor + effective_board_clearance;
|
|
|
|
|
board_top_z = board_bottom_z + board_thickness;
|
|
|
|
|
usb_center_u = board_origin_y + usb_center_y;
|
|
|
|
|
usb_center_z = board_top_z + 1.6 + usb_vertical_offset;
|
|
|
|
|
usb_inner_width = usb_opening_width + hole_compensation;
|
|
|
|
|
usb_inner_height = usb_opening_height + hole_compensation;
|
|
|
|
|
usb_guide_length = max(
|
|
|
|
|
0,
|
|
|
|
|
usb_board_wall_gap - usb_connector_overhang - usb_guide_end_gap
|
|
|
|
|
);
|
|
|
|
|
usb_guide_active = usb_opening_enabled && usb_guide_enabled
|
|
|
|
|
&& usb_guide_length > 0;
|
|
|
|
|
usb_reserved_width = usb_inner_width
|
|
|
|
|
+ (usb_guide_active ? 2 * usb_guide_wall_thickness : 0);
|
|
|
|
|
usb_reserved_height = usb_inner_height
|
|
|
|
|
+ (usb_guide_active ? 2 * usb_guide_wall_thickness : 0);
|
|
|
|
|
|
|
|
|
|
computed_top_pcb_z = board_top_z + wing_count * wing_pitch;
|
|
|
|
|
top_pcb_z = stack_height_override > 0
|
|
|
|
@@ -170,7 +196,7 @@ profile_top_gap =
|
|
|
|
|
top_profile == "oled128x64" ? oled_face_gap :
|
|
|
|
|
generic_top_gap;
|
|
|
|
|
|
|
|
|
|
partition_y = board_origin_y + feather_width + 1;
|
|
|
|
|
partition_y = board_origin_y + feather_width + battery_board_clearance;
|
|
|
|
|
battery_center_y = partition_y + battery_partition_thickness
|
|
|
|
|
+ battery_radial_clearance + battery_diameter / 2;
|
|
|
|
|
battery_start_x = front_lid_boss_center + lid_boss_r
|
|
|
|
@@ -191,10 +217,14 @@ case_width_battery = battery_center_y + battery_diameter / 2
|
|
|
|
|
case_width = battery_enabled ? max(case_width_plain, case_width_battery)
|
|
|
|
|
: case_width_plain;
|
|
|
|
|
|
|
|
|
|
rocker_center_x = case_length - wall_thickness - fit_clearance
|
|
|
|
|
rocker_installed_center_x = case_length - wall_thickness - fit_clearance
|
|
|
|
|
- lid_lip_thickness - fit_clearance - rocker_body_size[0] / 2;
|
|
|
|
|
rocker_center_y = front_lid_boss_center + lid_boss_r + fit_clearance
|
|
|
|
|
+ rocker_body_size[1] / 2;
|
|
|
|
|
rocker_installed_center_y = front_lid_boss_center + lid_boss_r + fit_clearance
|
|
|
|
|
+ rocker_body_size[1] / 2 + rocker_partition_offset;
|
|
|
|
|
// The printable lid is flipped across Y when installed, so its local cutout
|
|
|
|
|
// coordinate must be mirrored from the assembled switch location.
|
|
|
|
|
rocker_lid_center_x = rocker_installed_center_x;
|
|
|
|
|
rocker_lid_center_y = case_width - rocker_installed_center_y;
|
|
|
|
|
|
|
|
|
|
lid_artwork_enabled = lid_label != "" || lid_logo_enabled;
|
|
|
|
|
lid_art_rotation =
|
|
|
|
@@ -202,7 +232,7 @@ lid_art_rotation =
|
|
|
|
|
lid_art_orientation == "usb_front" ? 90 :
|
|
|
|
|
lid_art_orientation == "usb_right" ? 180 : 270;
|
|
|
|
|
lid_art_safe_max_x = battery_enabled
|
|
|
|
|
? rocker_center_x - rocker_cutout_size[0] / 2 - rocker_artwork_gap
|
|
|
|
|
? rocker_lid_center_x - rocker_cutout_size[0] / 2 - rocker_artwork_gap
|
|
|
|
|
: case_length - lid_art_margin;
|
|
|
|
|
lid_art_physical_size = [
|
|
|
|
|
lid_art_safe_max_x - lid_art_margin,
|
|
|
|
@@ -273,6 +303,34 @@ lid_boss_xy = [
|
|
|
|
|
case_width - wall_thickness - lid_boss_r + lid_boss_wall_overlap
|
|
|
|
|
]
|
|
|
|
|
];
|
|
|
|
|
rocker_boss_clearance = min([
|
|
|
|
|
for (p = lid_boss_xy)
|
|
|
|
|
sqrt(
|
|
|
|
|
pow(max(
|
|
|
|
|
abs(p[0] - rocker_installed_center_x)
|
|
|
|
|
- rocker_body_size[0] / 2,
|
|
|
|
|
0
|
|
|
|
|
), 2)
|
|
|
|
|
+ pow(max(
|
|
|
|
|
abs(p[1] - rocker_installed_center_y)
|
|
|
|
|
- rocker_body_size[1] / 2,
|
|
|
|
|
0
|
|
|
|
|
), 2)
|
|
|
|
|
) - lid_boss_r
|
|
|
|
|
]);
|
|
|
|
|
rocker_relief_half_size = [
|
|
|
|
|
rocker_cutout_size[0] / 2,
|
|
|
|
|
rocker_cutout_size[1] / 2 + rocker_tab_land + rocker_tab_taper_length
|
|
|
|
|
];
|
|
|
|
|
rocker_relief_boss_clearance = min([
|
|
|
|
|
for (p = lid_boss_xy)
|
|
|
|
|
sqrt(
|
|
|
|
|
pow(max(abs(p[0] - rocker_lid_center_x)
|
|
|
|
|
- rocker_relief_half_size[0], 0), 2)
|
|
|
|
|
+ pow(max(abs(p[1] - rocker_lid_center_y)
|
|
|
|
|
- rocker_relief_half_size[1], 0), 2)
|
|
|
|
|
) - lid_boss_r
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
assert(wing_count >= 1, "wing_count must be at least one");
|
|
|
|
|
assert(top_profile == "generic" || top_profile == "gps"
|
|
|
|
@@ -280,6 +338,14 @@ assert(top_profile == "generic" || top_profile == "gps"
|
|
|
|
|
assert(wall_thickness >= 1.2, "wall_thickness is too small for 0.6 mm FDM");
|
|
|
|
|
assert(battery_partition_thickness >= 1.2,
|
|
|
|
|
"battery_partition_thickness is too small for 0.6 mm FDM");
|
|
|
|
|
assert(battery_board_clearance > 0,
|
|
|
|
|
"battery_board_clearance must be positive");
|
|
|
|
|
assert(rocker_tab_panel_thickness >= 1.2,
|
|
|
|
|
"rocker tab panel needs at least two 0.6 mm lines");
|
|
|
|
|
assert(rocker_tab_panel_thickness < lid_thickness,
|
|
|
|
|
"rocker tab panel must be thinner than the lid");
|
|
|
|
|
assert(rocker_tab_land > 0 && rocker_tab_taper_length > 0,
|
|
|
|
|
"rocker tab relief land and taper must be positive");
|
|
|
|
|
assert(lid_lip_thickness >= 1.2, "lid lip needs at least two 0.6 mm lines");
|
|
|
|
|
assert(lid_art_orientation == "usb_left"
|
|
|
|
|
|| lid_art_orientation == "usb_front"
|
|
|
|
@@ -302,6 +368,15 @@ assert(m25_insert_outer_diameter >= m25_insert_hole_diameter,
|
|
|
|
|
assert(!tripod_insert_enabled
|
|
|
|
|
|| tripod_boss_diameter > tripod_insert_hole_diameter + 3,
|
|
|
|
|
"Tripod insert boss does not have enough radial material");
|
|
|
|
|
assert(usb_opening_enabled || !usb_guide_enabled,
|
|
|
|
|
"USB guide requires the USB opening");
|
|
|
|
|
assert(!usb_opening_enabled
|
|
|
|
|
|| usb_center_z - usb_reserved_height / 2 >= effective_floor,
|
|
|
|
|
"USB opening or guide intersects the case floor");
|
|
|
|
|
assert(!usb_opening_enabled
|
|
|
|
|
|| usb_center_z + usb_reserved_height / 2
|
|
|
|
|
<= body_height - lid_lip_depth,
|
|
|
|
|
"USB opening or guide intersects the lid lip");
|
|
|
|
|
assert(!tripod_insert_enabled
|
|
|
|
|
|| tripod_insert_outer_diameter >= tripod_insert_hole_diameter,
|
|
|
|
|
"Tripod insert entry must not be smaller than its pilot");
|
|
|
|
@@ -311,18 +386,73 @@ assert(!battery_enabled || !tripod_insert_enabled
|
|
|
|
|
|| tripod_partition_clearance >= 1,
|
|
|
|
|
"Tripod insert entry is too close to the battery partition");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| rocker_center_x - rocker_body_size[0] / 2
|
|
|
|
|
|| rocker_installed_center_x - rocker_body_size[0] / 2
|
|
|
|
|
>= board_origin_x + feather_length + fit_clearance,
|
|
|
|
|
"Rocker body overlaps the Feather PCB");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| rocker_center_y + rocker_body_size[1] / 2
|
|
|
|
|
|| rocker_installed_center_y + rocker_body_size[1] / 2
|
|
|
|
|
<= partition_y - fit_clearance,
|
|
|
|
|
"Rocker body overlaps the battery partition");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| rocker_installed_center_x - rocker_body_size[0] / 2
|
|
|
|
|
>= wall_thickness + lid_lip_thickness + fit_clearance,
|
|
|
|
|
"Rocker body overlaps the front lid lip");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| rocker_installed_center_x + rocker_body_size[0] / 2
|
|
|
|
|
<= case_length - wall_thickness - lid_lip_thickness - fit_clearance,
|
|
|
|
|
"Rocker body overlaps the rear lid lip");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| rocker_installed_center_y - rocker_body_size[1] / 2
|
|
|
|
|
>= wall_thickness + lid_lip_thickness + fit_clearance,
|
|
|
|
|
"Rocker body overlaps the Feather-side lid lip");
|
|
|
|
|
assert(!battery_enabled || rocker_boss_clearance >= fit_clearance - eps,
|
|
|
|
|
"Rocker body overlaps a lid boss");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| rocker_relief_boss_clearance >= fit_clearance - eps,
|
|
|
|
|
"Rocker tab relief overlaps a lid boss scallop");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| abs(
|
|
|
|
|
case_width - rocker_lid_center_y - rocker_installed_center_y
|
|
|
|
|
) < eps,
|
|
|
|
|
"Rocker lid cutout does not mirror to the installed switch location");
|
|
|
|
|
assert(!battery_enabled
|
|
|
|
|
|| body_height - rocker_body_size[2] >= effective_floor,
|
|
|
|
|
"Rocker body intersects the case floor");
|
|
|
|
|
|
|
|
|
|
function clamp(v, lo, hi) = min(max(v, lo), hi);
|
|
|
|
|
function cutout_height(entry) = len(entry) > 5 ? entry[5] : entry[4];
|
|
|
|
|
function cutouts_separated(first, second, web) =
|
|
|
|
|
abs(first[2] - second[2])
|
|
|
|
|
>= (first[4] + second[4]) / 2 + web
|
|
|
|
|
|| abs(first[3] - second[3])
|
|
|
|
|
>= (cutout_height(first) + cutout_height(second)) / 2 + web;
|
|
|
|
|
function cutout_clear_of_usb(entry, web) =
|
|
|
|
|
abs(entry[2] - usb_center_u)
|
|
|
|
|
>= (entry[4] + usb_reserved_width) / 2 + web
|
|
|
|
|
|| abs(entry[3] - usb_center_z)
|
|
|
|
|
>= (cutout_height(entry) + usb_reserved_height) / 2 + web;
|
|
|
|
|
|
|
|
|
|
module validate_cutout_spacing() {
|
|
|
|
|
if (len(extra_cutouts) > 1)
|
|
|
|
|
for (first = [0 : len(extra_cutouts) - 2],
|
|
|
|
|
second = [first + 1 : len(extra_cutouts) - 1])
|
|
|
|
|
if (extra_cutouts[first][0] == extra_cutouts[second][0])
|
|
|
|
|
assert(
|
|
|
|
|
cutouts_separated(
|
|
|
|
|
extra_cutouts[first],
|
|
|
|
|
extra_cutouts[second],
|
|
|
|
|
cutout_min_web
|
|
|
|
|
),
|
|
|
|
|
"Custom cutouts are too close on the same wall"
|
|
|
|
|
);
|
|
|
|
|
if (usb_opening_enabled)
|
|
|
|
|
for (entry = extra_cutouts)
|
|
|
|
|
if (entry[0] == "front")
|
|
|
|
|
assert(
|
|
|
|
|
cutout_clear_of_usb(entry, cutout_min_web),
|
|
|
|
|
"Front cutout is too close to the USB opening or guide"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module rounded_rect_2d(size, radius) {
|
|
|
|
|
r = clamp(radius, 0.01, min(size[0], size[1]) / 2 - 0.01);
|
|
|
|
@@ -406,6 +536,40 @@ module face_circle_cutout(face, u, z, diameter) {
|
|
|
|
|
rotate([-90, 0, 0]) cylinder(h = depth, d = diameter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module x_rounded_prism(x, length, u, z, width, height, radius) {
|
|
|
|
|
r = clamp(radius, 0.01, min(width, height) / 2 - 0.01);
|
|
|
|
|
hull()
|
|
|
|
|
for (yy = [u - width / 2 + r, u + width / 2 - r],
|
|
|
|
|
zz = [z - height / 2 + r, z + height / 2 - r])
|
|
|
|
|
translate([x, yy, zz])
|
|
|
|
|
rotate([0, 90, 0]) cylinder(h = length, r = r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module usb_guide_shell() {
|
|
|
|
|
outer_width = usb_inner_width + 2 * usb_guide_wall_thickness;
|
|
|
|
|
outer_height = usb_inner_height + 2 * usb_guide_wall_thickness;
|
|
|
|
|
difference() {
|
|
|
|
|
x_rounded_prism(
|
|
|
|
|
wall_thickness - eps,
|
|
|
|
|
usb_guide_length + 2 * eps,
|
|
|
|
|
usb_center_u,
|
|
|
|
|
usb_center_z,
|
|
|
|
|
outer_width,
|
|
|
|
|
outer_height,
|
|
|
|
|
usb_opening_radius + usb_guide_wall_thickness
|
|
|
|
|
);
|
|
|
|
|
x_rounded_prism(
|
|
|
|
|
wall_thickness - 2 * eps,
|
|
|
|
|
usb_guide_length + 4 * eps,
|
|
|
|
|
usb_center_u,
|
|
|
|
|
usb_center_z,
|
|
|
|
|
usb_inner_width,
|
|
|
|
|
usb_inner_height,
|
|
|
|
|
usb_opening_radius
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module custom_wall_cutout(entry) {
|
|
|
|
|
face = entry[0];
|
|
|
|
|
shape = entry[1];
|
|
|
|
@@ -554,6 +718,8 @@ module body_positive() {
|
|
|
|
|
if (tripod_insert_enabled)
|
|
|
|
|
translate([tripod_center_x, tripod_center_y, 0])
|
|
|
|
|
cylinder(h = effective_floor, d = tripod_boss_diameter);
|
|
|
|
|
if (usb_guide_active)
|
|
|
|
|
usb_guide_shell();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -579,15 +745,15 @@ module body_cutouts() {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// USB-C cable opening. Connector center is from the official ESP32 V2 PCB.
|
|
|
|
|
usb_center_z = board_top_z + 1.6 + usb_vertical_offset;
|
|
|
|
|
face_rounded_cutout(
|
|
|
|
|
"front",
|
|
|
|
|
board_origin_y + usb_center_y,
|
|
|
|
|
usb_center_z,
|
|
|
|
|
usb_opening_width + hole_compensation,
|
|
|
|
|
usb_opening_height + hole_compensation,
|
|
|
|
|
usb_opening_radius
|
|
|
|
|
);
|
|
|
|
|
if (usb_opening_enabled)
|
|
|
|
|
face_rounded_cutout(
|
|
|
|
|
"front",
|
|
|
|
|
usb_center_u,
|
|
|
|
|
usb_center_z,
|
|
|
|
|
usb_inner_width,
|
|
|
|
|
usb_inner_height,
|
|
|
|
|
usb_opening_radius
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (tripod_insert_enabled) {
|
|
|
|
|
translate([tripod_center_x, tripod_center_y, 0]) {
|
|
|
|
@@ -711,13 +877,18 @@ module lid_cutouts() {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (battery_enabled)
|
|
|
|
|
translate([rocker_center_x, rocker_center_y, -eps])
|
|
|
|
|
if (battery_enabled) {
|
|
|
|
|
translate([rocker_lid_center_x, rocker_lid_center_y, -eps])
|
|
|
|
|
centered_rounded_prism(
|
|
|
|
|
rocker_cutout_size,
|
|
|
|
|
lid_thickness + lid_lip_depth + 2 * eps,
|
|
|
|
|
rocker_corner_radius
|
|
|
|
|
);
|
|
|
|
|
rocker_tab_reliefs(
|
|
|
|
|
[rocker_lid_center_x, rocker_lid_center_y],
|
|
|
|
|
rocker_cutout_size
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (top_profile == "oled128x64") {
|
|
|
|
|
beveled_oled_window();
|
|
|
|
@@ -728,6 +899,41 @@ module lid_cutouts() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Thin the horizontal top/bottom edges of the upright switch opening where the
|
|
|
|
|
// spring tabs latch. Each full-width land overlaps the rounded opening by its
|
|
|
|
|
// corner radius, then extends outward before tapering to the surrounding lid.
|
|
|
|
|
module rocker_tab_reliefs(center, opening_size) {
|
|
|
|
|
relief_depth = lid_thickness - rocker_tab_panel_thickness;
|
|
|
|
|
opening_overlap = rocker_corner_radius;
|
|
|
|
|
strip = 0.02;
|
|
|
|
|
for (side = [-1, 1]) {
|
|
|
|
|
edge_y = center[1] + side * opening_size[1] / 2;
|
|
|
|
|
translate([
|
|
|
|
|
center[0] - opening_size[0] / 2,
|
|
|
|
|
edge_y - (side < 0 ? rocker_tab_land : opening_overlap),
|
|
|
|
|
rocker_tab_panel_thickness
|
|
|
|
|
])
|
|
|
|
|
cube([opening_size[0], rocker_tab_land + opening_overlap,
|
|
|
|
|
relief_depth + eps]);
|
|
|
|
|
|
|
|
|
|
hull() {
|
|
|
|
|
translate([
|
|
|
|
|
center[0] - opening_size[0] / 2,
|
|
|
|
|
edge_y + side * rocker_tab_land - (side < 0 ? strip : 0),
|
|
|
|
|
rocker_tab_panel_thickness
|
|
|
|
|
])
|
|
|
|
|
cube([opening_size[0], strip, relief_depth + eps]);
|
|
|
|
|
translate([
|
|
|
|
|
center[0] - opening_size[0] / 2,
|
|
|
|
|
edge_y + side * (rocker_tab_land + rocker_tab_taper_length)
|
|
|
|
|
- (side < 0 ? strip : 0),
|
|
|
|
|
lid_thickness - strip
|
|
|
|
|
])
|
|
|
|
|
cube([opening_size[0], strip, strip + eps]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module lid_artwork_nominal_2d() {
|
|
|
|
|
translate(lid_art_center)
|
|
|
|
|
rotate(lid_art_rotation)
|
|
|
|
@@ -854,7 +1060,7 @@ module reference_top_feature() {
|
|
|
|
|
cube([
|
|
|
|
|
oled_visible_size[0],
|
|
|
|
|
oled_visible_size[1],
|
|
|
|
|
oled_component_height
|
|
|
|
|
oled_display_height
|
|
|
|
|
]);
|
|
|
|
|
color([0.6, 0.6, 0.6, 0.9])
|
|
|
|
|
for (xy = [
|
|
|
|
@@ -891,15 +1097,15 @@ module reference_battery_and_switch() {
|
|
|
|
|
cylinder(h = battery_length, d = battery_diameter);
|
|
|
|
|
color([0.08, 0.08, 0.08, 0.85])
|
|
|
|
|
translate([
|
|
|
|
|
rocker_center_x - rocker_body_size[0] / 2,
|
|
|
|
|
rocker_center_y - rocker_body_size[1] / 2,
|
|
|
|
|
rocker_installed_center_x - rocker_body_size[0] / 2,
|
|
|
|
|
rocker_installed_center_y - rocker_body_size[1] / 2,
|
|
|
|
|
body_height + exploded_view - rocker_body_size[2]
|
|
|
|
|
])
|
|
|
|
|
cube(rocker_body_size);
|
|
|
|
|
color([0.12, 0.12, 0.12, 0.95])
|
|
|
|
|
translate([
|
|
|
|
|
rocker_center_x - rocker_cutout_size[0] / 2,
|
|
|
|
|
rocker_center_y - rocker_cutout_size[1] / 2,
|
|
|
|
|
rocker_installed_center_x - rocker_cutout_size[0] / 2,
|
|
|
|
|
rocker_installed_center_y - rocker_cutout_size[1] / 2,
|
|
|
|
|
body_height + exploded_view + lid_thickness
|
|
|
|
|
])
|
|
|
|
|
cube([rocker_cutout_size[0], rocker_cutout_size[1], 2.5]);
|
|
|
|
@@ -957,7 +1163,7 @@ module switch_coupon() {
|
|
|
|
|
coupon_height = lid_thickness;
|
|
|
|
|
difference() {
|
|
|
|
|
rounded_box([coupon_length, coupon_width, coupon_height], 3);
|
|
|
|
|
for (i = [0 : len(coupon_steps) - 1])
|
|
|
|
|
for (i = [0 : len(coupon_steps) - 1]) {
|
|
|
|
|
translate([6 + i * spacing, coupon_width / 2, -eps])
|
|
|
|
|
centered_rounded_prism(
|
|
|
|
|
rocker_cutout_size
|
|
|
|
@@ -965,6 +1171,11 @@ module switch_coupon() {
|
|
|
|
|
coupon_height + 2 * eps,
|
|
|
|
|
rocker_corner_radius
|
|
|
|
|
);
|
|
|
|
|
rocker_tab_reliefs(
|
|
|
|
|
[6 + i * spacing, coupon_width / 2],
|
|
|
|
|
rocker_cutout_size + [coupon_steps[i], coupon_steps[i]]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1002,6 +1213,8 @@ module fit_coupon() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_cutout_spacing();
|
|
|
|
|
|
|
|
|
|
if (part == "assembly")
|
|
|
|
|
assembly();
|
|
|
|
|
else if (part == "body")
|
|
|
|
|