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 = 2; 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 = 90.0; 73 height = 68.3; 74 depth = 16.5; 75 front_depth = 6.5; 76 back_depth = 11.0; 77 78 /* Side thicknesses. */ 79 80 front = 2; 81 back = 3.5; 82 top = 3; 83 front_left = 1; 84 front_right = front_left; 85 back_left = 1.5; 86 back_right = back_left; 87 bottom = 2; 88 89 /* Label details. */ 90 91 front_label_width = 83.0; 92 front_label_height = 46.0; 93 front_label_depth = 1.0; 94 front_label_offset_from_bottom = 19.5; 95 96 top_label_width = front_label_width; 97 top_label_height = 11.5; /* the height as seen from above */ 98 top_label_depth = front_label_depth; 99 top_label_offset_from_front = 2.5; 100 101 /* The groove around the sides and top. */ 102 103 groove_width = 2.5; /* how much the groove cuts out of the back */ 104 groove_depth = 1.0; /* how deep the groove goes into each side */ 105 106 /* Additional cutting to mate the back and front. */ 107 108 top_groove_width = 1.0; 109 top_groove_depth = 2.0; 110 111 /* Space for the back inside the front. */ 112 113 inner_top_front_cutout_width = 87.0; 114 inner_top_front_cutout_depth = 1.0; 115 inner_top_front_cutout_offset = 2.0; 116 117 back_cavity_width = 68.0; 118 back_cavity_inner_width = 65.0; 119 back_cavity_offset_from_left = 10.5; 120 back_cavity_inner_offset_from_left = 12.0; 121 back_cavity_height = 13.5; 122 back_cavity_inner_height = 12.0; 123 back_cavity_depth = 1.5; 124 125 inner_back_cavity_offset = 1.0; 126 inner_back_cavity_offset_from_left = back_left; 127 inner_back_slope_offset_from_left = 10.0; 128 inner_back_slope_width = 2.5; 129 inner_back_slope_depth = 2.5; 130 131 inner_back_edge_offset = 2.0; 132 inner_back_edge_width = 69.0; 133 inner_back_edge_height = 3.0; 134 inner_back_edge_depth = 1.5; 135 136 inner_front_edge_offset = 0.5; 137 inner_front_edge_width = 87.0; 138 inner_front_edge_height = 3.0; 139 inner_front_edge_depth = 1.5; 140 141 bottom_from_base = 14.0; 142 143 edge_connector_cutout_front_offset = 1.0; 144 edge_connector_cutout_front_depth = 1.0; 145 edge_connector_cutout_front_width = 15.0; 146 edge_connector_cutout_back_depth = 3.0; 147 edge_connector_cutout_back_width = 57.5; 148 149 /* 150 Edge connectors are themselves 0.05" or approximately 1.27mm in 151 thickness according to the Acorn Electron Cartridge Interface Specification 152 (Acorn Support Application Group Note 014). 153 */ 154 155 /* Extra internal features. */ 156 157 pcb_back_support_width = 1.2; 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.2; 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 = 13.2; 174 pcb_back_support_right_bump_height = 10.7; 175 pcb_back_support_left_bump_offset_from_bottom = 15.1; 176 pcb_back_support_right_bump_offset_from_bottom = 17.6; 177 pcb_back_support_top_bump_height = 3.8; 178 179 /* Move the PCB support towards the centre. */ 180 pcb_support_margin = 0.55; 181 182 pcb_lug_depth = pcb_back_support_depth + 183 pcb_back_support_bump_depth; 184 pcb_lug_inner_radius = 1.0; 185 pcb_lug_outer_radius = 5.5 / 2; 186 pcb_lug_offset_from_bottom = 14.35; 187 pcb_lug_offset_from_outside = 7.55; 188 189 pcb_lug_cross_width = 6.7; 190 pcb_lug_cross_depth = pcb_lug_depth; 191 pcb_lug_cross_height = 1.4; 192 193 /* Repeated constructs. */ 194 195 module pcb_support(xdir, bump_height, bump_offset) { 196 translate([xdir * 197 (edge_connector_cutout_back_width / 2 - pcb_support_margin), 198 edge_connector_cutout_back_depth, 199 -height / 2 + bottom + bottom_from_base]) 200 justify(pcb_back_support_width, 201 pcb_back_support_depth, 202 pcb_back_support_height, 203 xdir, 1, 1) 204 union() { 205 206 /* Underlying support strut. */ 207 208 cube([pcb_back_support_width, 209 pcb_back_support_depth, 210 pcb_back_support_height], center = true); 211 212 /* Middle bump. */ 213 214 cube_at(pcb_back_support_bump_width, 215 pcb_back_support_bump_depth, 216 bump_height, 217 0, -1, 1, 218 0, 219 -pcb_back_support_depth / 2, 220 -pcb_back_support_height / 2 + bump_offset); 221 222 /* Top bump. */ 223 224 cube_at(pcb_back_support_bump_width, 225 pcb_back_support_bump_depth, 226 pcb_back_support_top_bump_height, 227 0, -1, 1, 228 0, 229 -pcb_back_support_depth / 2, 230 pcb_back_support_height / 2 - 231 pcb_back_support_top_bump_height); 232 } 233 } 234 235 module pcb_lug(xdir) { 236 translate([xdir * 237 (width/2 - pcb_lug_offset_from_outside), 238 back_depth, 239 -height / 2 + bottom + bottom_from_base + 240 pcb_lug_offset_from_bottom 241 ]) 242 rotate([90, 0, 0]) 243 difference() { 244 union() { 245 cylinder(h=pcb_lug_depth, r=pcb_lug_outer_radius); 246 cube_at(pcb_lug_cross_width, 247 pcb_lug_cross_height, pcb_lug_cross_depth, 248 0, 0, 1, 249 0, 0, 0); 250 cube_at(pcb_lug_cross_height, 251 pcb_lug_cross_width, pcb_lug_cross_depth, 252 0, 0, 1, 253 0, 0, 0); 254 } 255 cylinder(h=pcb_lug_depth, r=pcb_lug_inner_radius); 256 } 257 } 258 259 /* The actual shapes. */ 260 261 translate([-width * 0.6, 0, 0]) 262 difference() { 263 264 /* The cartridge surfaces. */ 265 266 union() { 267 268 /* Front portion. */ 269 270 translate([0, -front_depth + front / 2, 0]) 271 cube([width, front, height], center = true); 272 translate([-width / 2 + front_left / 2, -front_depth / 2, 0]) 273 cube([front_left, front_depth, height], center = true); 274 translate([width / 2 - front_right / 2, -front_depth / 2, 0]) 275 cube([front_right, front_depth, height], center = true); 276 translate([0, -front_depth / 2, height / 2 - top / 2]) 277 cube([width, front_depth, top], center = true); 278 difference() { 279 280 /* Floor of cartridge. */ 281 282 cube_at(width, front_depth, bottom, 283 0, -1, 1, 284 0, 0, -height / 2 + bottom_from_base); 285 286 /* Left cutout. */ 287 288 cube_at(edge_connector_cutout_front_width, 289 edge_connector_cutout_front_depth, 290 bottom, 291 1, -1, 1, 292 -width / 2 + edge_connector_cutout_front_offset, 293 0, 294 -height / 2 + bottom_from_base); 295 296 /* Right cutout. */ 297 298 cube_at(edge_connector_cutout_front_width, 299 edge_connector_cutout_front_depth, 300 bottom, 301 -1, -1, 1, 302 width / 2 - edge_connector_cutout_front_offset, 303 0, 304 -height / 2 + bottom_from_base); 305 } 306 307 /* PCB supports. */ 308 309 cube_at(pcb_front_support_width, 310 pcb_front_support_depth, 311 pcb_front_support_height, 312 1, -1, 1, 313 -edge_connector_cutout_back_width / 2 + 314 pcb_support_margin, 315 0, 316 -height / 2 + bottom + bottom_from_base); 317 318 cube_at(pcb_front_support_width, 319 pcb_front_support_depth, 320 pcb_front_support_height, 321 -1, -1, 1, 322 edge_connector_cutout_back_width / 2 - 323 pcb_support_margin, 324 0, 325 -height / 2 + bottom + bottom_from_base); 326 } 327 328 /* Label insets. */ 329 330 union() { 331 332 /* Front label. */ 333 334 translate([-front_label_width / 2, -front_depth, 335 front_label_offset_from_bottom - height / 2]) 336 cube([front_label_width, front_label_depth, 337 front_label_height]); 338 339 /* Top label. */ 340 341 translate([-top_label_width / 2, 342 -front_depth + top_label_offset_from_front, 343 height / 2 - top_label_depth]) 344 cube([top_label_width, top_label_height, 345 top_label_depth]); 346 } 347 348 /* Inner front edge cavity. */ 349 350 translate([inner_front_edge_width / 2, 351 -front_depth + inner_front_edge_offset, -height / 2]) 352 rotate([0, -90, 0]) 353 linear_extrude(height = inner_front_edge_width) 354 polygon([ 355 [-extra, 0], 356 [0, 0], 357 [inner_front_edge_height, inner_front_edge_depth], 358 [-extra, inner_front_edge_depth], 359 ]); 360 361 /* Inner top cutout for the top of the back portion. */ 362 363 translate([0, -inner_top_front_cutout_depth / 2, height / 2 - 364 inner_top_front_cutout_offset - 365 inner_top_front_cutout_depth / 2]) 366 cube([inner_top_front_cutout_width, 367 inner_top_front_cutout_depth, 368 inner_top_front_cutout_depth], center = true); 369 370 /* Fillets to round off the edges. */ 371 372 union() { 373 374 /* Top left and right rounding. */ 375 376 translate([-width / 2 + ro, -front_depth / 2, height / 2 - ro]) 377 rotate([0, 0, 180]) 378 rotate([90, 0, 0]) 379 fillet(rr, front_depth); 380 translate([width / 2 - ro, -front_depth / 2, height / 2 - ro]) 381 rotate([90, 0, 0]) 382 fillet(rr, front_depth); 383 384 /* Top front rounding. */ 385 386 translate([0, -front_depth + ro, height / 2 - ro]) 387 rotate([0, 0, 180]) 388 rotate([0, -90, 0]) 389 fillet(rr, width); 390 391 /* Edge rounding. */ 392 393 translate([-width / 2 + ro, -front_depth + ro, 0]) 394 rotate([0, 0, 180]) 395 fillet(rr, height); 396 translate([width / 2 - ro, -front_depth + ro, 0]) 397 rotate([0, 0, 270]) 398 fillet(rr, height); 399 } 400 } 401 402 translate([width * 0.6, 0, 0]) 403 rotate([0, 0, 180]) 404 difference() { 405 406 /* The cartridge surfaces. */ 407 408 union() { 409 410 /* Back portion. */ 411 412 translate([0, back_depth - back / 2, 0]) 413 cube([width, back, height], center = true); 414 translate([-width / 2 + back_left / 2, back_depth / 2, 0]) 415 cube([back_left, back_depth, height], center = true); 416 translate([width / 2 - back_right / 2, back_depth / 2, 0]) 417 cube([back_right, back_depth, height], center = true); 418 translate([0, back_depth / 2, height / 2 - top / 2]) 419 cube([width, back_depth, top], center = true); 420 difference() { 421 422 /* Floor of cartridge. */ 423 424 cube_at(width, back_depth, bottom, 425 0, 1, 1, 426 0, 0, -height / 2 + bottom_from_base); 427 428 /* Edge connector cutout. */ 429 430 cube_at(edge_connector_cutout_back_width, 431 edge_connector_cutout_back_depth, 432 bottom, 433 0, 1, 1, 434 0, 0, -height / 2 + bottom_from_base); 435 } 436 437 /* PCB supports. */ 438 439 pcb_support(-1, pcb_back_support_left_bump_height, 440 pcb_back_support_left_bump_offset_from_bottom); 441 pcb_support(1, pcb_back_support_right_bump_height, 442 pcb_back_support_right_bump_offset_from_bottom); 443 444 /* Circular "lugs" to hold PCBs in place. */ 445 446 pcb_lug(-1); 447 pcb_lug(1); 448 } 449 450 /* Label insets. */ 451 452 union() { 453 454 /* Top label. */ 455 456 translate([-top_label_width / 2, 457 -front_depth + top_label_offset_from_front, 458 height / 2 - top_label_depth]) 459 cube([top_label_width, top_label_height, 460 top_label_depth]); 461 } 462 463 /* Top and side grooves, positioned in the back portion. */ 464 465 union() { 466 467 /* Left groove. */ 468 469 translate([-width / 2 + groove_depth / 2, groove_width / 2, 0]) 470 cube([groove_depth, groove_width, height], 471 center = true); 472 473 /* Right groove. */ 474 475 translate([width / 2 - groove_depth / 2, groove_width / 2, 0]) 476 cube([groove_depth, groove_width, height], 477 center = true); 478 479 /* Top grooves. */ 480 481 translate([0, groove_width / 2, height / 2 - groove_depth / 2]) 482 cube([width, groove_width, groove_depth], 483 center = true); 484 485 translate([0, top_groove_width / 2, 486 height / 2 - top_groove_depth / 2]) 487 cube([width, top_groove_width, top_groove_depth], 488 center = true); 489 } 490 491 /* Back cavity. */ 492 493 intersection() { 494 495 /* From the bottom upwards. */ 496 497 translate([0, back_depth, -height / 2]) 498 linear_extrude(height = back_cavity_height) 499 translate([-width / 2, 0, 0]) 500 polygon([ 501 [back_cavity_offset_from_left, 0], 502 [back_cavity_inner_offset_from_left, 503 -back_cavity_depth], 504 [back_cavity_inner_offset_from_left + 505 back_cavity_inner_width, 506 -back_cavity_depth], 507 [back_cavity_offset_from_left + 508 back_cavity_width, 0] 509 ]); 510 511 /* From left to right. */ 512 513 translate([back_cavity_width / 2, back_depth, -height / 2]) 514 rotate([0, -90, 0]) 515 linear_extrude(height = back_cavity_width) 516 polygon([ 517 [-extra, -back_cavity_depth], 518 [back_cavity_inner_height, 519 -back_cavity_depth], 520 [back_cavity_height, 0], 521 [-extra, 0] 522 ]); 523 } 524 525 /* Inner back cavities. */ 526 527 translate([0, back_depth - inner_back_cavity_offset, -height / 2]) 528 linear_extrude(height = bottom_from_base) 529 translate([-width / 2, 0, 0]) 530 polygon([ 531 [inner_back_cavity_offset_from_left, 0], 532 [inner_back_slope_offset_from_left, 0], 533 [inner_back_slope_offset_from_left + 534 inner_back_slope_width, 535 -inner_back_slope_depth], 536 [inner_back_cavity_offset_from_left, 537 -inner_back_slope_depth] 538 ]); 539 540 translate([0, back_depth - inner_back_cavity_offset, -height / 2]) 541 linear_extrude(height = bottom_from_base) 542 translate([-width / 2, 0, 0]) 543 polygon([ 544 [width - inner_back_slope_offset_from_left, 0], 545 [width - inner_back_cavity_offset_from_left, 0], 546 [width - inner_back_cavity_offset_from_left, 547 -inner_back_slope_depth], 548 [width - inner_back_slope_offset_from_left - 549 inner_back_slope_width, 550 -inner_back_slope_depth] 551 ]); 552 553 /* Inner back edge cavity. */ 554 555 translate([inner_back_edge_width / 2, 556 back_depth - inner_back_edge_offset, -height / 2]) 557 rotate([0, -90, 0]) 558 linear_extrude(height = inner_back_edge_width) 559 polygon([ 560 [-extra, -inner_back_edge_depth], 561 [inner_back_edge_height, -inner_back_edge_depth], 562 [0, 0], 563 [-extra, 0] 564 ]); 565 566 /* Fillets to round off the edges. */ 567 568 union() { 569 570 /* Top left and right rounding. */ 571 572 translate([-width / 2 + ro, back_depth / 2, height / 2 - ro]) 573 rotate([0, 0, 180]) 574 rotate([90, 0, 0]) 575 fillet(rr, back_depth); 576 translate([width / 2 - ro, back_depth / 2, height / 2 - ro]) 577 rotate([90, 0, 0]) 578 fillet(rr, back_depth); 579 580 /* Top back rounding. */ 581 582 translate([0, back_depth - ro, height / 2 - ro]) 583 rotate([0, -90, 0]) 584 fillet(rr, width); 585 586 /* Edge rounding. */ 587 588 translate([width / 2 - ro, back_depth - ro, 0]) 589 fillet(rr, height); 590 translate([-width / 2 + ro, back_depth - ro, 0]) 591 rotate([0, 0, 90]) 592 fillet(rr, height); 593 } 594 } 595 } 596 597 cartridge(); 598