commit f90ec047a6c07f2259b184ef29cde145855b8eac Author: shy Date: Wed Jun 1 08:29:44 2022 +0200 Moved to github.com. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a046280 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.swp + +notes.md + +specs/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc8ebe8 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Hülle für die u23-Spielekonsole. + +## STL-Dateien +*back full.stl* – Rückseite mit genug Raum für das WLAN-Modul. +*back slim.stl* – Schmalere Rückseite. +*buttons.stl* – Buttons. +*front sticks.stl* – Vorderseite mit Durchbrüchen für die Analog-Sticks. +*front.stl* – Vorderseite. + +## Anmerkungen +* Notwendige Stützstrukturen sind bereits in den Dateien Angelegt. +* Für den Zusammenbau der Hülle braucht es 4 M3-Schrauben und Muttern. + * Schraubenlänge für die schmale Variante: 16mm. + * Schraubenlänge für die dickere Variante: 25mm. +* Die Datei "buttons.scad" liest Parameter aus der Datei "front.scad". Wenn Parameter in "front.scad" geändert werden, sollten auch die Buttons neu gerendert werden. + +## Lizenz +[CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/deed.de) diff --git a/back.scad b/back.scad new file mode 100644 index 0000000..25f954f --- /dev/null +++ b/back.scad @@ -0,0 +1,536 @@ +/************************************************************************* + * Hülle für die u23-Spielekonsole. * + * Rückseite. * + * * + * Author: Shy * + * License: CC0 * + *************************************************************************/ + + include ; + +/* Höhe wichtiger Bauteile: + * + * NRF-Modul: + * H: ~ 15 mm + * + * USB Port: + * GCT USB4105-GF-A + * H: 3.31 mm + * B: 8.94 mm / 9.58 mm + * L: 7.53 mm + * + * Klinkennuchse: + * CUI SJ1-3523N + * H: 6 mm + * B: 12 mm + * L: 14 mm + * + * RJ-45 Buchse: + * RJMG1BD3B8K1ANR + * H: 13.35 mm + * B: 15.75 mm + * L: 21.72 mm + * + * Vibrationsmotor: + * Jinlong Z4KC1B1051202 + * H: 4.5 mm + * B: 4.6 mm + * L: 15.6 mm + * + * CF-Kartenadapter: + * Hirose DM3AT-SF-PEJM5 + * H: 1.6 mm + */ + +/* Default slim (Schrauben: M3, 16mm). + * + * space = 8; + * border_height = 4; + * nut_sink = 6; + * side_recess = 0; + * + * + * Default full (Schrauben: M3, 25mm). + * + * space = 15; + * border_height = 8; + * nut_sink = 4; + * side_recess = 4; + */ + +// Stützstrukturen. +support = true; + +// Abgerundete Kanten (aufwendig). +rounded_corners = true; + +// Dicke der Decke. +thickness = 1; + +// Höhe des Innenraumes. +space = 8; + +// Höhe des Randes. (Empfohlen: space/2.) +border_height = 4; + +// Höhe der Verstrebungen. +struts = 2; + +// Dicke der Aufhängungen an der Vorderkante. +loop_thickness = 3.5; + +// Durchmesser der Bohrungen. +drill = 3.4; + +// Durchmesser der Bohrschäfte. +drill_shaft = 7.4; + +// Wird ein Gewindeeinsatz verwendet? +threaded_insert = false; + +// Tiefe der Versenkung für die Mutter. +nut_sink = 6.0; + +// Schlüsselweite der Mutter (M3 = 5.5 + Spiel). +wrench_size = 5.8; + +// Durchmesser der Mutter von Kante zu Kante. +nut_size = wrench_size * 2 / sqrt(3); + +// Position der Bohrungen. +drill_pos = [ + [10, 10], + [10, 90], + [140, 10], + [140, 90], +]; + + +// Platziere Reflektionsflächen unter den LEDs. +led_reflectors = false; + +// Aussparungen über den Befestigungslöchern an den Seiten. +side_recess = 0; + +// Füge Strukturen zur Stützung der Buttons auf der Vorderseite hinzu. +button_support = true; + +// Füge Strukturen zur Stützung der Analog Sticks hinzu. +stick_support = true; + +// Höhe, die der USB-Port benötigt. +limit_usb = 3.4; + +// Höhe, die die Klinkenbuchse benötigt. +limit_audio_jack = 6.5; + +// Soll ein nRF24-Modul verbaut werden? +nrf_module = false; + +// Höhe, die das nRF24-Modul benötigt. +limit_nrf = 15; + +// Höhe, die der Vibrationsmotor benötigt. +limit_vib = 7.5; + + +/************************************************************************* + * Überprüfe Parameter auf Fehler. * + *************************************************************************/ + +// Achte auf die Höhe ausgewählter Bauteile. +if ((space - border_height) < limit_usb) { + if ($preview) { + // Zeige den Platz, den der USB-Port braucht. + color(color_warning) + translate([40, 92.35, space + thickness - limit_usb]) + cube([9, 8, limit_usb]); + } else { + assert((space - border_height) >= limit_usb, + "USB-Port hat zu wenig Platz!"); + } +} + +if (nrf_module && (space < limit_nrf)) { + if ($preview) { + // Zeige den Platz, den das nRF24-Modul braucht. + color(color_warning) + translate([101.25, 81.75, thickness]) + cube([29.5, 15.75, limit_nrf]); + } else { + assert(space >= limit_nrf, + "nRF24-Modul hat zu wenig Platz!"); + } +} + + +/************************************************************************* + * Einzelteile * + *************************************************************************/ + +// Reflektionsflächen für die LEDs. +module led_reflector() { + h = min(border_height + 2.5, space - 2.5); + l = 12; + w = 4; + $fn = 24; + + // Sockel + translate([-w/2, -l/2, 0]) + cube([w, l, struts]); + + hull() { + translate([0, w/2 - l/2, 0]) { + // Basis. + cylinder(r=w/2, h=1); + // Kuppelform. + translate([0, 0, h - 3.5]) + intersection() { + sphere(r=w/2); + translate([-w/2, -w/2, 0]) + cube([w, w, w]); + } + // Spitze. + translate([0.5 + w/4, 0.5 + w/4, h - 0.25]) + sphere(0.5); + } + translate([0, l/2 - w/2, 0]) { + // Basis. + cylinder(r=w/2, h=1); + // Kuppelform. + translate([0, 0, h - 3.5]) + intersection() { + sphere(r=w/2); + translate([-w/2, -w/2, 0]) + cube([w, w, w]); + } + // Spitze. + translate([0.5 + w/4, 0.5 + w/4, h - 0.25]) + sphere(0.5); + } + } +} + +// Schaft für die Schrauben. +module screw_shaft() { + translate([0, 0, thickness]) + cylinder(h = space, r = drill_shaft/2, $fn=32); + // Verdickung, um die Versenkung zu kompensieren. + if(nut_sink > 0 && !threaded_insert) { + translate([0, 0, thickness]) + cylinder(h = nut_sink, r = (nut_size + drill_shaft - drill)/2, $fn=32); + intersection() { + translate([0, 0, thickness + nut_sink]) + cylinder((nut_size + drill_shaft - drill)/2, (nut_size + drill_shaft - drill)/2, 0, $fn=32); + // Begrenzt die Höhe. + cylinder(r = (nut_size + drill_shaft - drill)/2, h = thickness + space, $fn=32); + } + } +} + +// Bohrung für die Schrauben. +module screw_drill() { + cylinder(h=space + thickness, r=drill/2, $fn=24); + // Versenkung für die Mutter. + if(nut_sink > 0 && !threaded_insert) { + cylinder(h=nut_sink, r=nut_size/2, $fn=6); + } +} + +// Stützstrukturen. +module support(strength) { + + translate([-strength/2, -drill/2, 0]) + cube([strength, drill, nut_sink]); + + $fn = 6; + difference() { + cylinder(h=nut_sink, r=drill/2 + strength); + cylinder(h=nut_sink, r=drill/2); + } +} + + +// Ausschnittobjekt mit zweifach gerundeten Ecken. +// depth = Tiefe +// widht = Weite +// h1 = Höhe links oben +// h2 = Höhe rechts oben +// bevel = Größe der Rungungen +module cutout(depth=4, width=8, h1=2, h2=2, bevel=1) { + + // Addiere 0.01 um im Vorschaumodus Artefakte zu reduzieren. + height = $preview ? max(h1, h2) + 0.01 : max(h1, h2); + assert (width >= bevel * 2); + $fn = 24; + + rotate([90, 0, 0]) { + hull() { + translate([0, max(height * 0.75, bevel), 0]) + cube([width, height/2, depth], center=true); + + translate([-width/2 + bevel, bevel, 0]) + cylinder(h=depth, r=bevel, center=true); + + translate([width/2 - bevel, bevel, 0]) + cylinder(h=depth, r=bevel, center=true); + } + + // Zeichne Rundung nach links außen, falls die Höhe ausreicht. + if (h1 >= bevel * 2) { + translate([-width/2 - bevel, h1 - bevel, 0]) + // Linke Kante. + difference() { + translate([0, 0, -depth/2]) + cube([bevel, bevel, depth]); + cylinder(h=depth, r=bevel, center=true); + } + } + + // Zeichne Rundung nach rechts außen, falls die Höhe ausreicht. + if (h2 >= bevel * 2) { + translate([width/2 + bevel, h2 - bevel, 0]) + difference() { + translate([-bevel, 0, -depth/2]) + cube([bevel, bevel, depth]); + cylinder(h=depth, r=bevel, center=true); + } + } + } +} + +// Aussparungen am Rand der Konsole. +module border_cutouts() { + // Aussparung an der Klinke-Buchse. + if (space - border_height < limit_audio_jack) { + diff = limit_audio_jack - (space - border_height); + + translate([63, 91, border_height - diff]) + cutout(depth=20, width=14, h1=diff, h2=diff); + } + + // Aussparung am Vibrationsmotor. + if (space - border_height < limit_vib) { + diff = limit_vib - (space - border_height); + + translate([118.75, 2 - 0.1 + 15.6/2, border_height - diff]) + cutout(depth=15.6 + 4, width=4.6 + 3.4, h1=diff, h2=diff); + } + + // Aussparungen am Rand neben den seitlichen Befestigungslöchern. + if (side_recess > 0) { + // Die Aussparungen sollten nicht niedriger sein als die Verstrebungen. + recess = min(side_recess, border_height - struts); + + // Aussparung auf der linken Seite. + translate([5, 67, border_height - recess]) + rotate([0, 0, 90]) + cutout( + depth=4, + width=20, + bevel=recess / 2, + h1=recess, + h2=recess); + + // Aussparung auf der rechten Seite. + translate([150 - 5, 67, border_height - recess]) + rotate([0, 0, 90]) + cutout( + depth=4, + width=20, + bevel=recess / 2, + h1=recess, + h2=recess); + + // Bei einer effektiven Höhe der Aussparungen > 2 schrägen wir eine + // Seite ab. + if (recess > 2) { + + multmatrix([ + [1, 0, 0, 0], + [0, 1, -1, border_height - recess ], + [0, 0, 1, 0] + ]) { + + // Linke Seite. + translate([5, 67, border_height - recess]) + rotate([0, 0, 90]) + cutout( + depth=4, + width=20, + bevel=recess / 2, + h1=recess, + h2=recess); + + // Rechte Seite. + translate([150 - 5, 67, border_height - recess]) + rotate([0, 0, 90]) + cutout( + depth=4, + width=20, + bevel=recess / 2, + h1=recess, + h2=recess); + } + } + } +} + +// Die äußeren Teile: Front und Ränder. +module outer() { + // Decke. + color(color_top) + linear_extrude(height=thickness) { + import("./svg/back top.svg"); + } + + // Rand. + translate([0, 0, thickness]) { + // Schneide Aussparungen aus dem Rand. + difference() { + union() { + // Oberer Rand. + color(color_border1) + linear_extrude(height = border_height) { + import("./svg/back borders.svg"); + } + // Unterer Rand. + color(color_border2) + translate([0, 0, border_height]) { + linear_extrude(height=space-border_height) { + import("./svg/back borders lower.svg"); + } + } + } + + render() border_cutouts(); + } + } + + // Verstrebungen. + color(color_struts) + translate([0, 0, thickness]) { + linear_extrude(height=struts) { + import("./svg/back struts.svg"); + } + } + + // Aufhängungen. + color(color_special) + translate([0, 0, 0]) { + linear_extrude(height=max(loop_thickness, thickness)) { + import("./svg/back loops.svg"); + } + } +} + + +/************************************************************************* + * Die Hülle. * + *************************************************************************/ + +module body_back() { + if (rounded_corners) { + intersection() { + union () { + outer(); + } + + color(color_top) + minkowski() { + linear_extrude(height=1) { + import("./svg/back hull.svg"); + } + hull() { + $fn = $preview ? 4 : 16; + for (i = [1:5]) { + translate([0, 0, i - 1]) + cylinder(r=sin(i * (90/5)) * 2, h=thickness + space); + } + } + } + } + } else { + outer(); + } + + // Stützen für die Buttons. + if (button_support) { + color(color_struts) + translate([0, 0, thickness]) { + linear_extrude(height=struts) { + import("./svg/back pillars struts.svg"); + } + } + + color(color_special) + translate([0, 0, thickness]) { + linear_extrude(height=space) { + import("./svg/back pillars.svg"); + } + } + } + + // Stützen für die Analog sticks. + if (stick_support) { + color(color_struts) + translate([0, 0, thickness]) { + linear_extrude(height=struts) { + import("./svg/back sticks struts.svg"); + } + } + + color(color_special) + translate([0, 0, thickness]) { + linear_extrude(height=space - 2) { + import("./svg/back sticks pillars.svg"); + } + } + + color(color_special) + translate([0, 0, thickness + space - 2]) { + linear_extrude(height=2) { + import("./svg/back sticks pillars top.svg"); + } + } + } + + // Schäfte für die Bohrungen. + color(color_drills) + for (i = [0:3]) { + translate([drill_pos[i][0], drill_pos[i][1], 0]) + screw_shaft(); + } + + // LED-Reflektoren. + color(color_special) + if (led_reflectors) { + translate([9, 84, thickness]) + led_reflector(); + translate([141, 84, thickness]) + mirror([1, 0, 0]) + led_reflector(); + } +} + +// Platzierung der Bohrungen. +difference() { + body_back(); + union() { + // Bei einem Gewindeeinsatz wird die Decke nicht durchbohrt. + floor = threaded_insert ? thickness : 0; + for (i = [0:3]) { + translate([drill_pos[i][0], drill_pos[i][1], floor]) + screw_drill(); + } + } +} + +if (support && !threaded_insert) { + color(color_special) + for (i = [0:3]) { + translate([drill_pos[i][0], drill_pos[i][1], 0]) + support(0.5); + } +} + diff --git a/buttons.scad b/buttons.scad new file mode 100644 index 0000000..55c1ce2 --- /dev/null +++ b/buttons.scad @@ -0,0 +1,243 @@ +/************************************************************************* + * Hülle für die u23-Spielekonsole. * + * Buttons. * + * * + * Autor: Shy * + * License: CC0 * + *************************************************************************/ + +/* Maße relevanter Bauteile: + * + * Kurzhubtaster Pads: + * PTS645SL50-2 LFS + * H Taster: 5 mm + * H Gehäuse: 3.45 mm + * D Taster: 3.5 mm + * + * Kurzhubtaster unten: + * PTS645SK43SMTR92 LFS + * H Taster: 4.3 mm + * H Gehäuse: 4.3 + * D Taster: 3.5 mm + * + * Schalter an der Schulter: + * D2FS-FL-N-A + * H: 5.8 mm + * B: 12.8 mm + */ + +include ; +include ; + +// Überstand der Buttons über die Hülle. +protrusion = 2; + +// Tiefe des Zeichnungen auf den Buttons. +carving = 0.5; + +// Buttons haben runde Kanten. (Rechenaufwendiger.) +beveled = true; + +// Größe der Rundung. +bevel_size = 1; + +// Spiel zwischen den Schulterbuttons und der Hülle. +button_trigger_clearance = 0.5; + +// Dicke der Schiene und Widerhaken der Schulterbuttons. +// (5.8 mm - 3 mm) / 2 + 3 mm nach Datenblatt. +button_trigger_rail = space - 4.4 - button_trigger_clearance; + +// Gesamthöhe der Systembuttons. +system_height = space - button_system_size - button_clearance + thickness + protrusion; + +// Gesamthöhe der Aktionsbuttons. +action_height = space - button_action_size - button_clearance + thickness + protrusion; + + +/************************************************************************** + * Einzelteile. * + *************************************************************************/ + +// Aktions-Buttons. +module action_buttons() { + // Basis. + linear_extrude(height = button_action_base) { + import("./svg/buttons action base.svg"); + } + + difference() { + // Hauptteil. + linear_extrude(height = action_height) { + import("./svg/buttons action main.svg"); + } + // Zeichnung. + translate([0, 0, action_height - carving]) + linear_extrude(height = carving) { + import("./svg/buttons action carvings.svg"); + } + + } +} + +// Schulter-Buttons. +module trigger_button() { + // Basis. + linear_extrude(height = button_trigger_rail) { + import("./svg/buttons trigger lower.svg"); + } + + linear_extrude(height = space - button_trigger_clearance) { + import("./svg/buttons trigger upper.svg"); + } +} + +// System-Buttons. +module system_button() { + // Höhe, Basis, Breite. + h = system_height; + b = button_system_base; + w = 3; + + polyhedron(points = [ + // Unterseite + [1, 0, 0], + [9, 0, 0], + [10, b, 0], + [8, b, 0], + [8, h, 0], + [2, h, 0], + [2, b, 0], + [0, b, 0], + // Oberseite + [1, 0, w], + [9, 0, w], + [10, b, w], + [8, b, w], + [8, h, w], + [2, h, w], + [2, b, w], + [0, b, w] + ], faces = [ + [0, 1, 2, 3, 4, 5, 6, 7], + [0, 8, 9, 1], + [1, 9, 10, 2], + [2, 10, 11, 3], + [3, 11, 12, 4], + [4, 12, 13, 5], + [5, 13, 14, 6], + [6, 14, 15, 7], + [7, 15, 8, 0], + [15, 14, 13, 12, 11, 10, 9, 8] + ]); +} + +// Abgerundeter Button. Nur der Hauptteil. +module beveled_button (r=1, h=1, b=1) { + union() { + cylinder(h=h - b, r=r, $fn=24); + + translate([0, 0, h-b*2]) + minkowski() { + cylinder(h=b, r=r-b, $fn=24); + sphere(r=b, $fn=24); + } + } +} + +// Kreuz-Button. +module cross (l=12, w=4, h=2) { + translate([0, 0, h/2]) + union() { + cube([l, w, h], center=true); + rotate([0, 0, 90]) + cube([l, w, h], center=true); + } +} + +// Kreuz-Button mit abgerundeten Kanten. +module beveled_cross (l=12, w=4, h=2, b=1) { + union() { + minkowski() { + cross(l-b*2, w-b*2, h-b*2); + cylinder(r=b, h=b, $fn=12); + } + + translate([0, 0, h-b*2]) + minkowski() { + cross(l-b*2, w-b*2, b); + sphere(r=b, $fn=12); + } + } +} + +// Aktionsbuttons mit abgerundeten Kanten. +module beveled_action(bevel=1) { + // Basis. + linear_extrude(height = button_action_base) { + import("./svg/buttons action base.svg"); + } + + difference() { + union() { + // Plaztiere das Kreuz auf der Basis. + translate([12, 12, 0]) + beveled_cross(l=24, w=8, h=action_height, b=bevel); + + // Platziere vier Buttons auf der Basis. + for (pos = [[31, 7], [41, 17], [53, 7], [63, 17]]) { + translate([pos.x, pos.y, 0]) + beveled_button(r=5, h=action_height, b=bevel); + } + } + // Die Zeichnungen auf der Oberseite. + translate([0, 0, action_height - carving]) + linear_extrude(height = carving) { + import("./svg/buttons action carvings.svg"); + } + } + +} + + +/************************************************************************** + * Anordnung. * + *************************************************************************/ + +// Render nur die Buttons, nicht die eingebundene Datei . +!if(true) { + // Aktions-Buttons. + translate([0, 23, 0]) + if(beveled) { + color(color_buttons) + beveled_action(bevel_size); + } else { + color(color_buttons) + action_buttons(); + } + + // Schulterbuttons. + color(color_buttons) + translate([56, 21, 0]) + rotate([0, 0, 180]) + trigger_button(); + + color(color_buttons) + translate([0, 21, 0]) + rotate([0, 0, 180]) + mirror([1, 0, 0]) + trigger_button(); + + // Drucke drei Systembuttons nebeneinander. + color(color_buttons) + for (i = [0, 11, 22]) { + translate([12 + i, 8, 0]) + system_button(); + } + + // Verbinde die dreit Buttons mit einem dünnen Steg. + color(color_buttons) + translate([12 + 1, 8 + button_system_base / 2, 0]) + cube([30, 1, 3]); +}; + diff --git a/colors.scad b/colors.scad new file mode 100644 index 0000000..06c8297 --- /dev/null +++ b/colors.scad @@ -0,0 +1,16 @@ +/************************************************************************* + * Hülle für die u23-Spielekonsole. * + * Farbdefinitionen. * + * * + * Author: Shy * + * License: CC0 * + *************************************************************************/ + +color_top = "#3399ff"; +color_border1 = "#33cccc"; +color_border2 = "#33ff66"; +color_struts = "#ff9900"; +color_drills = "#ffff33"; +color_buttons = "#ff3399"; +color_special = "#cc33ff"; +color_warning = "#ff000080"; diff --git a/front.scad b/front.scad new file mode 100644 index 0000000..1fe8af5 --- /dev/null +++ b/front.scad @@ -0,0 +1,401 @@ +/************************************************************************* + * Hülle für die u23-Spielekonsole. * + * Vorderseite. * + * * + * Author: Shy * + * License: CC0 * + *************************************************************************/ + +include ; + +/* Höhe wichtiger Bauteile: + * + * Display: + * Z320IT010 + * L: 54 mm + * B: 77.4 mm + * H: 2.4 mm + * Davon LCD: + * L: 48.6 mm + * B: 64.8 mm, mit 3.35 mm Abstand zum rechten Rand + * + * Kurzhubtaster Pads: + * PTS645SL50-2 LFS + * H Taster: 5 mm + * H Gehäuse: 3.45 mm + * D Taster: 3.5 mm + * + * Kurzhubtaster unten: + * PTS645SK43SMTR92 LFS + * H Taster: 4.3 mm + * H Gehäuse: 4.3 + * D Taster: 3.5 mm + * + * Schalter an der Schulter: + * D2FS-FL-N-A + * H: 5.8 mm + * B: 12.8 mm + * + * Analog Stick: + * Alps RKJXV1224005 + * H Gehäuse: 10.8 mm + * H bis Gelenk: 12.46 mm + * L: 21.3 mm + * B: 17.8 mm + * Der Kern ist ein Quadrat mit 13.15 mm Kantenlänge. + * Anschlag 7 mm von der Platine. + */ + +// Abgerundete Kanten (aufwendig). +rounded_corners = true; + +// Dicke der Decke. +thickness = 1.0; + +// Höhe des Innenraumes. +space = 7; + +// Höhe des oberen Randes. +border_height = 4; + +// Höhe der Verstrebungen. +struts = 2; + +// Durchmesser der Bohrungen. +drill = 3.4; + +// Durchmesser der Bohrschäfte. +drill_shaft = 7.4; + +// Position der Bohrungen. +drill_pos = [ + [10, 20], + [10, 100], + [140, 20], + [140, 100], +]; + +// Dicke der Basis der Aktionsbuttons. +button_action_base = 1; + +// Dicke der Basis der Systembuttons. +button_system_base = 2; + +// Tatsächliche Höhe der Taster unter den Aktionsbuttons. +button_action_size = 5; + +// Tatsächliche Höhe der Taster unter den Systembuttons. +button_system_size = 4.3; + +// Spiel der Buttons zwischen Taster und Decke. +button_clearance = 0.2; + +// LEDs am unteren Rand. +led_bottom = true; + +// Y-LED. +led_y = true; + +// LED-Pegel. +led_gauge = true; + +// Aussparungen für die Analog-Sticks. +analog_sticks = false; + + +/************************************************************************* + * Überprüfe Parameter auf Fehler. * + *************************************************************************/ + +//assert(space >= trigger_switch_height + button_trigger_base, +// "Die Höhe reicht nicht für die Schultertasten!"); + + +/************************************************************************* + * Einzelteile * + *************************************************************************/ + +// Durchbrüche in der Front. +module top_cutouts() { + // LEDs am unteren Rand. + if (led_bottom) { + linear_extrude(height=thickness) { + import("./svg/front led bottom.svg"); + } + } + + // LED-Pegel. + if (led_gauge) { + linear_extrude(height=thickness) { + import("./svg/front led gauge.svg"); + } + } + + // Y-LED. + if (led_y) { + linear_extrude(height=thickness) { + import("./svg/front led y.svg"); + } + } +} + +// Schaft für die Schrauben. +module screw_shaft() { + translate([0, 0, thickness]) + cylinder(h = space, r = drill_shaft/2, $fn=32); +} + +// Platzsparendere Stützen für die Bohrungen am oberen Rand. +module screw_support(width, length) { + translate([0, 0, thickness + (space / 2)]) + cube([width, length, space], center=true); +} + +// Bohrung für die Schrauben. +module screw_drill() { + cylinder(h=space + thickness, r=drill/2, $fn=24); +} + +// Maske für Stellen, an denen der Rand nicht ganz auf der Platine aufliegen +// kann. +module border_pcb_cutouts() { + // Vibrator. + translate([28, 10 + 1, -1.5]) + cube([6, 1, 1.5]); + + // Klinkenbuchse. + translate([77.5, 110 - 2, -1]) + cube([13.5, 1, 1]); +} + +// Die äußeren Teile: Front und Ränder. +module outer() { + // Decke. + color(color_top) + linear_extrude(height=thickness) { + import("./svg/front top.svg"); + } + + // Oberer Rand. + color(color_border1) + translate([0, 0, thickness]) { + linear_extrude(height = border_height) { + import("./svg/front borders.svg"); + } + } + + // Unterer Rand. + color(color_border2) + translate([0, 0, thickness + border_height]) { + difference() { + linear_extrude(height=space - border_height) { + import("./svg/front borders lower.svg"); + } + // Stellen, an denen der Rand nicht ganz auf der Platine aufliegen + // kann. + translate([0, 0, space - border_height]) + border_pcb_cutouts(); + } + } + + // Verstrebungen. + color(color_struts) + translate([0, 0, thickness]) { + linear_extrude(height=struts) { + import("./svg/front struts.svg"); + } + } + +} + +// Kleine Streben zwischen Decke und Rand. +module border_strut(width, length, height) { + x = width/2; + y = length/2; + + polyhedron(points = [ + [-x, -y, 0], + [x, -y, 0], + [x, y, 0], + [-x, y, 0], + [-x, y, height], + [x, y, height] + ], faces = [ + [0, 1, 2, 3], + [1, 5, 2], + [2, 5, 4, 3], + [3, 4, 0], + [0, 4, 5, 1] + ]); + +} + + +/************************************************************************* + * Die Hülle. * + *************************************************************************/ + +module casing_front() { + difference() { + if (rounded_corners) { + intersection() { + union () { + outer(); + } + + color(color_top) + minkowski() { + linear_extrude(height=1) { + import("./svg/front hull.svg"); + } + hull() { + $fn = $preview ? 4 : 16; + for (i = [1:5]) { + translate([0, 0, i - 1]) + cylinder(r=sin(i * (90/5)) * 2, h=thickness + space); + } + } + } + } + } else { + outer(); + } + + if ($preview) { + // Vergrößere das zu substrahierende Objekt, um Darstellungsfehler + // in der Voransicht zu vermeiden. + translate([0, 0, -0.1]) + resize([0, 0, thickness + 0.2]) + top_cutouts(); + } else { + top_cutouts(); + } + } + + // Zusätzliche Verstrebungen zwischen Decke und Rand. + if (rounded_corners) { + color(color_struts) + for (pos = [ + // [x, y, rotation, länge] + [49, 107.5, 0, 3], + [57, 107.5, 0, 3], + [65, 107.5, 0, 3], + [73, 107.5, 0, 3], + [80, 107.5, 0, 3], + [87, 107.5, 0, 3], + + [147.5, 85, 270, 3], + [148, 78.5, 270, 2], + [148, 71.5, 270, 2], + [147.5, 65, 270, 3], + [147.5, 57, 270, 3], + [148, 49, 270, 2], + [148, 41, 270, 2], + + [109, 12.5, 180, 3], + [94, 12.5, 180, 3], + [56, 12.5, 180, 3], + [41, 12.5, 180, 3], + [26, 12.5, 180, 3], + + //[5, 40, 90, 2], + [5, 44, 90, 2], + [5, 50, 90, 2], + [2.5, 66, 90, 3], + [2.5, 84, 90, 3], + ]) { + translate([pos[0], pos[1], thickness]) + rotate([0, 0, pos[2]]) + border_strut(1.6, pos[3], pos[3]); + } + } + + // Halterungen für die Aktionsbuttons. + // Begrenzung nach oben. + color(color_buttons) + translate([0, 0, thickness]) { + linear_extrude(height = space - button_action_size - button_action_base - button_clearance) { + import("./svg/front button action upper.svg"); + } + } + + // Führung für die Aktionsbuttons. + color(color_buttons) + translate([0, 0, thickness]) { + // Wir ziehen die Führungen zwei Millimeter tiefer, um auf der sicheren + // Seite zu sein. + linear_extrude(height = space - button_action_size + 2) { + import("./svg/front button action lower.svg"); + } + } + + // Halterungen für die Systembuttons. + // Begrenzung nach oben. + color(color_buttons) + translate([0, 0, thickness]) { + linear_extrude(height = space - button_system_size - button_system_base - button_clearance) { + import("./svg/front button system upper.svg"); + } + } + + // Führung für die Systembuttons. + color(color_buttons) + translate([0, 0, thickness]) { + linear_extrude(height = space - button_system_size) { + import("./svg/front button system lower.svg"); + } + } + + // Schäfte für die Bohrungen. + color(color_drills) + for (i = [0:3]) { + if (drill_pos[i].y > 50) { + // Am oberen Rand brauchen wir andere Stützen, um Platz für die + // Schultertasten zu machen. + if (drill_pos[i].x > 75) { + // Rechts oben. + translate([drill_pos[i].x + (150 - drill_pos[i].x) / 2, drill_pos[i].y, 0]) + // 2 mm kürzer, damit das Bauteil nicht am Rand übersteht. + screw_support(150 - drill_pos[i].x - 2, drill); + } else { + // Links oben. + translate([drill_pos[i].x / 2, drill_pos[i].y, 0]) + screw_support(drill_pos[i].x - 2, drill); + } + + // Die normalen Bohrungen am unteren Rand. + } else { + translate([drill_pos[i].x, drill_pos[i].y, 0]) + screw_shaft(); + } + } + + // Verstärkungen für die Analog-Sticks. + if (analog_sticks) { + color(color_struts) + translate([0, 0, thickness]) { + linear_extrude(height = struts) { + import("./svg/front analog sticks struts.svg"); + } + } + } +} + +// Plazierung der Bohrungen. +difference() { + casing_front(); + union() { + for (i = [0:3]) { + translate([drill_pos[i][0], drill_pos[i][1], 0]) + screw_drill(); + } + + // Die Löcher für die Analog-Sticks. + if (analog_sticks) { + linear_extrude(height = space) { + import("./svg/front analog sticks.svg"); + } + } + } +} + diff --git a/svg/back borders lower.svg b/svg/back borders lower.svg new file mode 100644 index 0000000..cf6ef0d --- /dev/null +++ b/svg/back borders lower.svg @@ -0,0 +1,87 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/svg/back borders.svg b/svg/back borders.svg new file mode 100644 index 0000000..2e3301b --- /dev/null +++ b/svg/back borders.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back construction.svg b/svg/back construction.svg new file mode 100644 index 0000000..4e642ed --- /dev/null +++ b/svg/back construction.svg @@ -0,0 +1,858 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + Shy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/back hull.svg b/svg/back hull.svg new file mode 100644 index 0000000..a7a2daf --- /dev/null +++ b/svg/back hull.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back loops.svg b/svg/back loops.svg new file mode 100644 index 0000000..3576d85 --- /dev/null +++ b/svg/back loops.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back pillars struts.svg b/svg/back pillars struts.svg new file mode 100644 index 0000000..6a791b4 --- /dev/null +++ b/svg/back pillars struts.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back pillars.svg b/svg/back pillars.svg new file mode 100644 index 0000000..14e0f9f --- /dev/null +++ b/svg/back pillars.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/svg/back sticks pillars top.svg b/svg/back sticks pillars top.svg new file mode 100644 index 0000000..f2e56d7 --- /dev/null +++ b/svg/back sticks pillars top.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back sticks pillars.svg b/svg/back sticks pillars.svg new file mode 100644 index 0000000..1941aca --- /dev/null +++ b/svg/back sticks pillars.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back sticks struts.svg b/svg/back sticks struts.svg new file mode 100644 index 0000000..384b691 --- /dev/null +++ b/svg/back sticks struts.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back struts.svg b/svg/back struts.svg new file mode 100644 index 0000000..6234bdc --- /dev/null +++ b/svg/back struts.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/back top.svg b/svg/back top.svg new file mode 100644 index 0000000..22051a5 --- /dev/null +++ b/svg/back top.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/svg/buttons action base.svg b/svg/buttons action base.svg new file mode 100644 index 0000000..dcf5d3f --- /dev/null +++ b/svg/buttons action base.svg @@ -0,0 +1,69 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/svg/buttons action carvings.svg b/svg/buttons action carvings.svg new file mode 100644 index 0000000..31cce47 --- /dev/null +++ b/svg/buttons action carvings.svg @@ -0,0 +1,104 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/svg/buttons action main.svg b/svg/buttons action main.svg new file mode 100644 index 0000000..2e21a13 --- /dev/null +++ b/svg/buttons action main.svg @@ -0,0 +1,66 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/svg/buttons trigger lower.svg b/svg/buttons trigger lower.svg new file mode 100644 index 0000000..668138c --- /dev/null +++ b/svg/buttons trigger lower.svg @@ -0,0 +1,59 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/buttons trigger upper.svg b/svg/buttons trigger upper.svg new file mode 100644 index 0000000..4836d87 --- /dev/null +++ b/svg/buttons trigger upper.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front analog sticks struts.svg b/svg/front analog sticks struts.svg new file mode 100644 index 0000000..c224bf3 --- /dev/null +++ b/svg/front analog sticks struts.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/svg/front analog sticks.svg b/svg/front analog sticks.svg new file mode 100644 index 0000000..54cc222 --- /dev/null +++ b/svg/front analog sticks.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front borders lower.svg b/svg/front borders lower.svg new file mode 100644 index 0000000..bff7d78 --- /dev/null +++ b/svg/front borders lower.svg @@ -0,0 +1,68 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front borders.svg b/svg/front borders.svg new file mode 100644 index 0000000..41dfd9a --- /dev/null +++ b/svg/front borders.svg @@ -0,0 +1,68 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front button action lower.svg b/svg/front button action lower.svg new file mode 100644 index 0000000..20cfbf2 --- /dev/null +++ b/svg/front button action lower.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front button action upper.svg b/svg/front button action upper.svg new file mode 100644 index 0000000..8e6fd26 --- /dev/null +++ b/svg/front button action upper.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front button system lower.svg b/svg/front button system lower.svg new file mode 100644 index 0000000..abe9cfe --- /dev/null +++ b/svg/front button system lower.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front button system upper.svg b/svg/front button system upper.svg new file mode 100644 index 0000000..0bea193 --- /dev/null +++ b/svg/front button system upper.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front construction.svg b/svg/front construction.svg new file mode 100644 index 0000000..2f8d1f8 --- /dev/null +++ b/svg/front construction.svg @@ -0,0 +1,1181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + Shy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/front hull.svg b/svg/front hull.svg new file mode 100644 index 0000000..28100c9 --- /dev/null +++ b/svg/front hull.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front led bottom.svg b/svg/front led bottom.svg new file mode 100644 index 0000000..b1eeee2 --- /dev/null +++ b/svg/front led bottom.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/svg/front led gauge.svg b/svg/front led gauge.svg new file mode 100644 index 0000000..6706ce6 --- /dev/null +++ b/svg/front led gauge.svg @@ -0,0 +1,65 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front led y.svg b/svg/front led y.svg new file mode 100644 index 0000000..b3b7795 --- /dev/null +++ b/svg/front led y.svg @@ -0,0 +1,65 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front struts.svg b/svg/front struts.svg new file mode 100644 index 0000000..14bf55e --- /dev/null +++ b/svg/front struts.svg @@ -0,0 +1,68 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/svg/front top.svg b/svg/front top.svg new file mode 100644 index 0000000..28e0624 --- /dev/null +++ b/svg/front top.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + +