1 /* 2 Copyright (C) 2014 Paul Boddie 3 4 This program is free software; you can redistribute it and/or modify it under 5 the terms of the GNU General Public License as published by the Free Software 6 Foundation; either version 3 of the License, or (at your option) any later 7 version. 8 9 This program is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 more details. 13 14 You should have received a copy of the GNU General Public License along with 15 this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 module cartridge() 19 { 20 $fn = 50; 21 22 /* 23 Rounding/fillet radius and additional margin of subtracted 24 material. The additional margin helps avoid geometry problems. 25 */ 26 27 rr = 1; 28 ro = rr; 29 extra = 0.1; 30 31 module fillet(r, h) { 32 translate([0, 0, -h/2]) 33 difference() { 34 cube([r + extra, r + extra, h + extra]); 35 cylinder(r = r, h = h); 36 } 37 } 38 39 /* 40 Justify an object of the given dimensions, according to the given 41 factors (where 1 indicates moving the object to the positive side of an 42 axis, and -1 indicates moving it to the negative side of an axis). 43 44 NOTE: child should eventually be replaced by children. 45 */ 46 module justify(width, depth, height, wdir, ddir, hdir) { 47 translate([ 48 wdir * width / 2, 49 ddir * depth / 2, 50 hdir * height / 2]) 51 child(); 52 } 53 54 /* 55 Make a cuboid of the given dimensions, justifying it according to the given 56 factors, and moving it to the specified coordinates. 57 58 NOTE: Usage of justify within this module will not work due to recursion 59 NOTE: limitations in openscad, potentially removed in more recent 60 NOTE: releases. Thus, the justify transform is merged in here. 61 */ 62 module cube_at(width, depth, height, wdir, ddir, hdir, x, y, z) { 63 translate([ 64 x + wdir * width / 2, 65 y + ddir * depth / 2, 66 z + hdir * height / 2]) 67 cube([width, depth, height], center = true); 68 } 69 70 /* Cartridge dimensions. */ 71 72 width = 89.0; 73 height = 68.5; 74 depth = 16.5; 75 front_depth = 6.5; 76 back_depth = 11.0; 77 78 /* Label details. */ 79 80 front_label_width = 83.0; 81 front_label_height = 46.0; 82 front_label_depth = 1.0; 83 front_label_offset_from_bottom = 19.5; 84 85 top_label_width = front_label_width; 86 top_label_height = 11.5; /* the height as seen from above */ 87 top_label_depth = front_label_depth; 88 top_label_offset_from_front = 2.5; 89 90 /* The groove around the sides and top. */ 91 92 groove_width = 2.5; /* how much the groove cuts out of the back */ 93 groove_depth = 1.0; /* how deep the groove goes into each side */ 94 95 /* Additional cutting to mate the back and front. */ 96 97 top_groove_width = 1.0; 98 top_groove_depth = 2.0; 99 100 /* Space for the back inside the front. */ 101 102 inner_top_front_cutout_width = 87.0; 103 inner_top_front_cutout_depth = 1.0; 104 inner_top_front_cutout_offset = 2.0; 105 106 back_cavity_width = 68.0; 107 back_cavity_inner_width = 65.0; 108 back_cavity_offset_from_left = 10.5; 109 back_cavity_inner_offset_from_left = 12.0; 110 back_cavity_height = 13.5; 111 back_cavity_inner_height = 12.0; 112 back_cavity_depth = 1.5; 113 114 inner_back_cavity_offset = 1.0; 115 inner_back_cavity_offset_from_left = 2.0; 116 inner_back_slope_offset_from_left = 10.0; 117 inner_back_slope_width = 2.5; 118 inner_back_slope_depth = 2.5; 119 120 inner_back_edge_offset = 2.0; 121 inner_back_edge_width = 69.0; 122 inner_back_edge_height = 3.0; 123 inner_back_edge_depth = 1.5; 124 125 inner_front_edge_offset = 0.5; 126 inner_front_edge_width = 87.0; 127 inner_front_edge_height = 3.0; 128 inner_front_edge_depth = 1.5; 129 130 bottom_from_base = 13.5; 131 132 edge_connector_cutout_front_offset = 1.0; 133 edge_connector_cutout_front_depth = 1.0; 134 edge_connector_cutout_front_width = 15.0; 135 edge_connector_cutout_back_depth = 3.0; 136 edge_connector_cutout_back_width = 57.0; 137 138 /* 139 Edge connectors are themselves 0.05" or approximately 1.27mm in 140 thickness according to the Acorn Electron Cartridge Interface Specification 141 (Acorn Support Application Group Note 014). 142 */ 143 144 /* Side thicknesses. */ 145 146 front = 2; 147 back = 3.5; 148 top = 3; 149 front_left = 1; 150 front_right = 1; 151 back_left = 2; 152 back_right = 2; 153 bottom = 2; 154 155 /* Extra internal features. */ 156 157 pcb_back_support_width = 1.0; 158 pcb_back_support_depth = back_depth - 159 edge_connector_cutout_back_depth; 160 pcb_back_support_height = height - bottom_from_base - top - bottom; 161 162 pcb_front_support_width = 1.0; 163 pcb_front_support_depth = front_depth; 164 pcb_front_support_height = pcb_back_support_height; 165 166 /* 167 Features measured from the Stardot Dual ROM Adaptor cartridge board 168 dimensions diagram. 169 */ 170 171 pcb_back_support_bump_width = pcb_front_support_width; 172 pcb_back_support_bump_depth = 1.5; 173 pcb_back_support_left_bump_height = 14.3; 174 pcb_back_support_right_bump_height = 11.7; 175 pcb_back_support_left_bump_offset_from_bottom = 26.0 - 11.8; 176 pcb_back_support_right_bump_offset_from_bottom = 28.6 - 11.8; 177 178 translate([-width * 0.6, 0, 0]) 179 difference() { 180 181 /* The cartridge surfaces. */ 182 183 union() { 184 185 /* Front portion. */ 186 187 translate([0, -front_depth + front / 2, 0]) 188 cube([width, front, height], center = true); 189 translate([-width / 2 + front_left / 2, -front_depth / 2, 0]) 190 cube([front_left, front_depth, height], center = true); 191 translate([width / 2 - front_right / 2, -front_depth / 2, 0]) 192 cube([front_right, front_depth, height], center = true); 193 translate([0, -front_depth / 2, height / 2 - top / 2]) 194 cube([width, front_depth, top], center = true); 195 difference() { 196 197 /* Floor of cartridge. */ 198 199 cube_at(width, front_depth, bottom, 200 0, -1, 1, 201 0, 0, -height / 2 + bottom_from_base); 202 203 /* Left cutout. */ 204 205 cube_at(edge_connector_cutout_front_width, 206 edge_connector_cutout_front_depth, 207 bottom, 208 1, -1, 1, 209 -width / 2 + edge_connector_cutout_front_offset, 210 0, 211 -height / 2 + bottom_from_base); 212 213 /* Right cutout. */ 214 215 cube_at(edge_connector_cutout_front_width, 216 edge_connector_cutout_front_depth, 217 bottom, 218 -1, -1, 1, 219 width / 2 - edge_connector_cutout_front_offset, 220 0, 221 -height / 2 + bottom_from_base); 222 } 223 224 /* PCB supports. */ 225 226 cube_at(pcb_front_support_width, 227 pcb_front_support_depth, 228 pcb_front_support_height, 229 1, -1, 1, 230 -edge_connector_cutout_back_width / 2, 231 0, 232 -height / 2 + bottom + bottom_from_base); 233 234 cube_at(pcb_front_support_width, 235 pcb_front_support_depth, 236 pcb_front_support_height, 237 -1, -1, 1, 238 edge_connector_cutout_back_width / 2, 239 0, 240 -height / 2 + bottom + bottom_from_base); 241 } 242 243 /* Label insets. */ 244 245 union() { 246 247 /* Front label. */ 248 249 translate([-front_label_width / 2, -front_depth, 250 front_label_offset_from_bottom - height / 2]) 251 cube([front_label_width, front_label_depth, 252 front_label_height]); 253 254 /* Top label. */ 255 256 translate([-top_label_width / 2, 257 -front_depth + top_label_offset_from_front, 258 height / 2 - top_label_depth]) 259 cube([top_label_width, top_label_height, 260 top_label_depth]); 261 } 262 263 /* Inner front edge cavity. */ 264 265 translate([inner_front_edge_width / 2, 266 -front_depth + inner_front_edge_offset, -height / 2]) 267 rotate([0, -90, 0]) 268 linear_extrude(height = inner_front_edge_width) 269 polygon([ 270 [-extra, 0], 271 [0, 0], 272 [inner_front_edge_height, inner_front_edge_depth], 273 [-extra, inner_front_edge_depth], 274 ]); 275 276 /* Inner top cutout for the top of the back portion. */ 277 278 translate([0, -inner_top_front_cutout_depth / 2, height / 2 - 279 inner_top_front_cutout_offset - 280 inner_top_front_cutout_depth / 2]) 281 cube([inner_top_front_cutout_width, 282 inner_top_front_cutout_depth, 283 inner_top_front_cutout_depth], center = true); 284 285 /* Fillets to round off the edges. */ 286 287 union() { 288 289 /* Top left and right rounding. */ 290 291 translate([-width / 2 + ro, -front_depth / 2, height / 2 - ro]) 292 rotate([0, 0, 180]) 293 rotate([90, 0, 0]) 294 fillet(rr, front_depth); 295 translate([width / 2 - ro, -front_depth / 2, height / 2 - ro]) 296 rotate([90, 0, 0]) 297 fillet(rr, front_depth); 298 299 /* Top front rounding. */ 300 301 translate([0, -front_depth + ro, height / 2 - ro]) 302 rotate([0, 0, 180]) 303 rotate([0, -90, 0]) 304 fillet(rr, width); 305 306 /* Edge rounding. */ 307 308 translate([-width / 2 + ro, -front_depth + ro, 0]) 309 rotate([0, 0, 180]) 310 fillet(rr, height); 311 translate([width / 2 - ro, -front_depth + ro, 0]) 312 rotate([0, 0, 270]) 313 fillet(rr, height); 314 } 315 } 316 317 translate([width * 0.6, 0, 0]) 318 rotate([0, 0, 180]) 319 difference() { 320 321 /* The cartridge surfaces. */ 322 323 union() { 324 325 /* Back portion. */ 326 327 translate([0, back_depth - back / 2, 0]) 328 cube([width, back, height], center = true); 329 translate([-width / 2 + back_left / 2, back_depth / 2, 0]) 330 cube([back_left, back_depth, height], center = true); 331 translate([width / 2 - back_right / 2, back_depth / 2, 0]) 332 cube([back_right, back_depth, height], center = true); 333 translate([0, back_depth / 2, height / 2 - top / 2]) 334 cube([width, back_depth, top], center = true); 335 difference() { 336 337 /* Floor of cartridge. */ 338 339 cube_at(width, back_depth, bottom, 340 0, 1, 1, 341 0, 0, -height / 2 + bottom_from_base); 342 343 /* Edge connector cutout. */ 344 345 cube_at(edge_connector_cutout_back_width, 346 edge_connector_cutout_back_depth, 347 bottom, 348 0, 1, 1, 349 0, 0, -height / 2 + bottom_from_base); 350 } 351 352 /* PCB supports. */ 353 354 translate([-edge_connector_cutout_back_width / 2, 355 edge_connector_cutout_back_depth, 356 -height / 2 + bottom + bottom_from_base]) 357 justify(pcb_back_support_width, 358 pcb_back_support_depth, 359 pcb_back_support_height, 360 1, 1, 1) 361 union() { 362 cube([pcb_back_support_width, 363 pcb_back_support_depth, 364 pcb_back_support_height], center = true); 365 cube_at(pcb_back_support_bump_width, 366 pcb_back_support_bump_depth, 367 pcb_back_support_left_bump_height, 368 0, -1, 1, 369 0, 370 -pcb_back_support_depth / 2, 371 -pcb_back_support_height / 2 + 372 pcb_back_support_left_bump_offset_from_bottom); 373 } 374 375 translate([edge_connector_cutout_back_width / 2, 376 edge_connector_cutout_back_depth, 377 -height / 2 + bottom + bottom_from_base]) 378 justify(pcb_back_support_width, 379 pcb_back_support_depth, 380 pcb_back_support_height, 381 -1, 1, 1) 382 union() { 383 cube([pcb_back_support_width, 384 pcb_back_support_depth, 385 pcb_back_support_height], center = true); 386 cube_at(pcb_back_support_bump_width, 387 pcb_back_support_bump_depth, 388 pcb_back_support_right_bump_height, 389 0, -1, 1, 390 0, 391 -pcb_back_support_depth / 2, 392 -pcb_back_support_height / 2 + 393 pcb_back_support_right_bump_offset_from_bottom); 394 } 395 } 396 397 /* Label insets. */ 398 399 union() { 400 401 /* Top label. */ 402 403 translate([-top_label_width / 2, 404 -front_depth + top_label_offset_from_front, 405 height / 2 - top_label_depth]) 406 cube([top_label_width, top_label_height, 407 top_label_depth]); 408 } 409 410 /* Top and side grooves, positioned in the back portion. */ 411 412 union() { 413 414 /* Left groove. */ 415 416 translate([-width / 2 + groove_depth / 2, groove_width / 2, 0]) 417 cube([groove_depth, groove_width, height], 418 center = true); 419 420 /* Right groove. */ 421 422 translate([width / 2 - groove_depth / 2, groove_width / 2, 0]) 423 cube([groove_depth, groove_width, height], 424 center = true); 425 426 /* Top grooves. */ 427 428 translate([0, groove_width / 2, height / 2 - groove_depth / 2]) 429 cube([width, groove_width, groove_depth], 430 center = true); 431 432 translate([0, top_groove_width / 2, 433 height / 2 - top_groove_depth / 2]) 434 cube([width, top_groove_width, top_groove_depth], 435 center = true); 436 } 437 438 /* Back cavity. */ 439 440 intersection() { 441 442 /* From the bottom upwards. */ 443 444 translate([0, back_depth, -height / 2]) 445 linear_extrude(height = back_cavity_height) 446 translate([-width / 2, 0, 0]) 447 polygon([ 448 [back_cavity_offset_from_left, 0], 449 [back_cavity_inner_offset_from_left, 450 -back_cavity_depth], 451 [back_cavity_inner_offset_from_left + 452 back_cavity_inner_width, 453 -back_cavity_depth], 454 [back_cavity_offset_from_left + 455 back_cavity_width, 0] 456 ]); 457 458 /* From left to right. */ 459 460 translate([back_cavity_width / 2, back_depth, -height / 2]) 461 rotate([0, -90, 0]) 462 linear_extrude(height = back_cavity_width) 463 polygon([ 464 [-extra, -back_cavity_depth], 465 [back_cavity_inner_height, 466 -back_cavity_depth], 467 [back_cavity_height, 0], 468 [-extra, 0] 469 ]); 470 } 471 472 /* Inner back cavities. */ 473 474 translate([0, back_depth - inner_back_cavity_offset, -height / 2]) 475 linear_extrude(height = bottom_from_base) 476 translate([-width / 2, 0, 0]) 477 polygon([ 478 [inner_back_cavity_offset_from_left, 0], 479 [inner_back_slope_offset_from_left, 0], 480 [inner_back_slope_offset_from_left + 481 inner_back_slope_width, 482 -inner_back_slope_depth], 483 [inner_back_cavity_offset_from_left, 484 -inner_back_slope_depth] 485 ]); 486 487 translate([0, back_depth - inner_back_cavity_offset, -height / 2]) 488 linear_extrude(height = bottom_from_base) 489 translate([-width / 2, 0, 0]) 490 polygon([ 491 [width - inner_back_slope_offset_from_left, 0], 492 [width - inner_back_cavity_offset_from_left, 0], 493 [width - inner_back_cavity_offset_from_left, 494 -inner_back_slope_depth], 495 [width - inner_back_slope_offset_from_left - 496 inner_back_slope_width, 497 -inner_back_slope_depth] 498 ]); 499 500 /* Inner back edge cavity. */ 501 502 translate([inner_back_edge_width / 2, 503 back_depth - inner_back_edge_offset, -height / 2]) 504 rotate([0, -90, 0]) 505 linear_extrude(height = inner_back_edge_width) 506 polygon([ 507 [-extra, -inner_back_edge_depth], 508 [inner_back_edge_height, -inner_back_edge_depth], 509 [0, 0], 510 [-extra, 0] 511 ]); 512 513 /* Fillets to round off the edges. */ 514 515 union() { 516 517 /* Top left and right rounding. */ 518 519 translate([-width / 2 + ro, back_depth / 2, height / 2 - ro]) 520 rotate([0, 0, 180]) 521 rotate([90, 0, 0]) 522 fillet(rr, back_depth); 523 translate([width / 2 - ro, back_depth / 2, height / 2 - ro]) 524 rotate([90, 0, 0]) 525 fillet(rr, back_depth); 526 527 /* Top back rounding. */ 528 529 translate([0, back_depth - ro, height / 2 - ro]) 530 rotate([0, -90, 0]) 531 fillet(rr, width); 532 533 /* Edge rounding. */ 534 535 translate([width / 2 - ro, back_depth - ro, 0]) 536 fillet(rr, height); 537 translate([-width / 2 + ro, back_depth - ro, 0]) 538 rotate([0, 0, 90]) 539 fillet(rr, height); 540 } 541 } 542 } 543 544 cartridge(); 545