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