diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec06738 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/gen +*.dsn.autosave diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..950aa44 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Tero Loimuneva + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d834db7 --- /dev/null +++ b/README.md @@ -0,0 +1,115 @@ +# My HOTAS Throttle + +This is mostly 3D printable HOTAS throttle for USB. + +![Full Preview](img/full-preview.png) + +**The photo (don't mind the colors - this is first fully working build)** + +![Full Photo](img/full-photo.png) + +# Features + +This HOTAS throttle has: + + - 3 axis (throttle, vertical, horizontal) with center detents an all + - 5 buttons + - hat switch with 8 directions and press button + - lock/program button + - RGB LED indicating state: throttle center (green), locked (red), program (blue) + - Connect as USB joystick to Windows without needing to install any software + - USB serial connectivity for debugging and programming + - high quality mechanical and electro-mechanical components + - adjustable throttle handle angle (twist) for better ergonomics at different table positioning + +# Design + +The design is based on these guidelines: + + - all buttons and knobs must be reachable without moving the palm of the hand + - smooth and precise throttle operation + - angled design for better ergonomics to miminize stress of the wrist and arm + - parameterized design for tweaking ergonomics and control placement + - allow some key build-time mechanical tuning options in case printing precision is not perfect + +The design is mostly parametrisized so that you can customize it to fit e.g. a bit larger hands better or different angles. This one is optimized for my smaller hands (e.g. Saitek X45 throttle was too big for my hands) so that I can reach all knobs and switches without moving my palm. + +The microcontroller was placed in the handle section to minimize the amount of wires needed to be moved as the throttle is used. This and the cable management was design to allow completely smooth operation of the throttle so that the cables won't hinder or move the throttle at any position. + +# Build + +All 3D printable parts are designed using OpenSCAD and I printed them using my Prusa MK3S running the default .40mm nozzle. Most parts can be printed with 0.30mm layer height but some require 0.20mm layers to get proper results. + +Some parts require or benefit from post-print processing: + + - drill the five button holes with e.g. 8.5mm drill to make the hole smooth and buttons work smoother + - some screw holes may benefit from drilling for easier fit + - throttle center detent hole could be drilled bigger for stronger detent or cone shaped with a lerger drill head for smoother detent exit/entry + +Connect the wires like this to match the code: + +![Circuit Diagram](img/throttle-circuit-tinycad.png) + +## Firmware + +Directory **firmware** contains the code for Arduino. I has been tested with Arduino 1.8.9 on Teensy LC with USB Type **Serial + Keyboard + Mouse + Joystick**. You also need to have/install Bounce2 and FastLED libraries. There is also prebuilt hex-file for Teensy LC there. + +## Other Build Notes + +Lighly lube the two metal rails, just like you do your 3D printer rails. + +Ligtly lube the ball spring plunger with silicone based (or other plastic friently lube) for smoother throttle movement and less noise. + +The right side of the case top that has the center detent - to get smoother and less noisy throttle movement: + + - 3D print it so that the last layer is printed in the same direction as the ball moves! E.g. in Prusa slicer, I rotated this part 45 degrees before printing + - apply little silicone lube spray to where the ball spring plunger meets the plastic + +The X/Y potentiometers' metal shafts I got required good lubing (e.g. CRC Elektro or teflon spray). They were quite stiff to turn, but lubing them made a big difference. I didn't have this sort of issue with other pots I had, but since I wanted ones with a center detent built-in, had to go with these. + +# Parts List + +On top of the 3D printable components: + + - 1 x Teensy LC + - 2 x 10kohm LIN pot (Bourns BI P160 with center detent e.g. KNOC-model) + - 1 x 100mm travel 10kohm lin slide pot (Bourns PS100 B 10K 0E) + - 1 x multidirectional switch 8 dir + press (Alps 688RKJXL 100401V) + - 5 x switches (a tact switch with pins fitting the circuit board listed) + - 1 x slide switch (any generic model you can fit the case) + - 2 x linear bearings 8x15x45 (LM8LUU) + - 1 x linear motion rod 8mm (cut in two 168mm pieces) + - 1 x joystick case (Saitek X45 stick part - or print it yourself) + - 1 x RGB LED (WS2812 compatible) + - some generic circuit board (0.1" / 2.54mm pin pitch) + - wires + - screws (2.5mm and 3mm) + +The Teensy LC can quite easily be replaced with any Arduino compatible device with USB connectivity and enough pins. With more coding effort any USB capable micro controller that fits the case could also do. + +The RGB LED and slide switch (lock/program) are fully optional. The multi-directional hat switch can also easily be left out, replaced with a simple switch or with a similar hat switch. + +## Lock/Program + +I had two options of the Lock-button. One is a slider switch (currently implemented) and another is to use a simple button switch. Either way, once Lock-mode is on, the throttle axis value is locked to its current value. This is to prevent in-game accidents while you're e.g. AFK and your cat jumps on the throttle. + +For simple buttons, the following logic could be made. Press and hold Lock-button for a while and all axis will lock onto their current position. Unlock by pressing Lock button once. Double-click Lock-button to switch between secondary mode that redefines all buttons, hat and axis. + +## TO-DO + +**Fix:** + + - Make printable bottom case (now reusing Saitek X45 throttle case) + - Make case and rod supports to both ends with exact correct rod separation + - Add Lock-switch place to handle print + - Cable insert cut into the handle shaft + - Cleanup 3D code + +**New features:** + + - Easily interchangeable detent disks! Some games can use more or less detents at diffent positions (e.g. no detents at all, 50% for Elite, at 75% for jet plane afterburner, 30% for propeller plane approach and 60% for transition flight, etc.) + - Emboss button and other control names to thumb plate or make way to attach printable labels/sheet + - Add two buttons to front and lower thumb + - Add a place for the lights so they can be seen + - Turn LEDs off when computer is sleeping + - Curvier case cover near to player for better ergonomics when the throttle is placed nearer to the table edge (in certain positions mid-arm may rub against the case cover) diff --git a/commercial-parts.scad b/commercial-parts.scad new file mode 100644 index 0000000..0f1750c --- /dev/null +++ b/commercial-parts.scad @@ -0,0 +1,256 @@ +include ; + +module TeensyPlusPlus() +{ + color ("Green") cube ([51,18,2]); + + color ("Snow") + translate ([0,5,2]) + cube ([9,8,4]); +} + +module SlidePotentiometer100() +{ + lever_range = 100; + lever_width = 8.0; + lever_thickness = 1.0; + lever_height = 20; + slider_length = 128; + slider_width = 8.3; + slider_height = 6.4; + lever_offset = (slider_length - lever_range + lever_width) / 2; + // Slider base + difference () + { + cube ([slider_length,slider_width,slider_height]); + translate ([lever_offset, (slider_width-lever_thickness)/2, 1]) + cube ([lever_range,lever_thickness,slider_height]); + // Screw places + translate ([slider_length - 4, slider_width/2, slider_height - 3]) cylinder (r1=1.5, r2=1.5, h=4); + translate ([4, slider_width/2, slider_height - 3]) cylinder (r1=1.5, r2=1.5, h=3); + } + // Lever + translate ([lever_offset + lever_range/2, (slider_width-lever_thickness)/2, 0]) + cube ([lever_width,lever_thickness,lever_height]); +} + +module PotentiometerBourns95A1() +{ + shaft_diameter = 6.3; + color("blue") cylinder(d1=shaft_diameter, d2=shaft_diameter, h=22.5); + color("white") cylinder(d1=9.4, d2=9.4, h=9.5); + + base_height = 12.5; + color ("blue") + translate ([-17.5/2,-16/2,-base_height]) + cube ([17.5, 16, base_height]); + + // pins + translate ([-17.5/2-5.5,-7.5/2,-4.5-5.5]) + color("white") cube([5.5, 7.5, 4.5]); +} + +module PotentiometerBourns95A1Down() +{ + rotate ([180,0,0]) + PotentiometerBourns95A1(); +} + +// Bourns PCW1J-B24-CCB103L +module PotentiometerBournsPCW1J() +{ + shaft_diameter = 6.3; + color("black") cylinder(d1=shaft_diameter, d2=shaft_diameter, h=19); + color("white") cylinder(d1=8.8, d2=8.8, h=6.5); + + base_height = 7.2; + color ("black") + translate ([-22.5/2,-22.5/2,-base_height]) + cube ([22.5, 22.5, base_height]); + + // notches + translate ([0,22.5/2-1.3,1]) + color("grey") cube([2.7, 1.3, 2], center=true); + + // pins + translate ([-22.5/2-7,-12/2,2-base_height]) + color("white") cube([7, 12, 0.7]); +} + +module PotentiometerBournsPCW1JDown() +{ + rotate ([180,0,0]) + PotentiometerBournsPCW1J(); +} + +// LIN 10kohm pot with center detent +module PotentiometerBournsBIP160(flip = false, cavityOnly = false) +{ + base_diameter = 16.5; + base_height = 9.4; + shaft_neck_height = 6.4; + shaft_neck_diameter = 6.9; + shaft_diameter = 6.0; + shaft_length = 24.4 - base_height - shaft_neck_height; + notch_width = 1.2; + notch_length = 2.7; + notch_height = 11.4 - base_height; + pin_distance = 5.0; + pin_level = 0.0; + pin_length = 8.0; + + rotate([0,flip ? 180 : 0,0]) + { + if (cavityOnly == false) + { + translate([0,0,shaft_neck_height]) + color("silver") cylinder(d=shaft_diameter, h=shaft_length); + color("silver") cylinder(d=shaft_neck_diameter, h=shaft_neck_height); + + color ("silver") + translate ([0,0,-base_height]) + cylinder (d=base_diameter, h=base_height); + + // pins + translate ([0,-pin_distance,pin_level - 2.0]) + color("silver") cube([base_diameter/2 + pin_length, 2*pin_distance, 2.0]); + + // Notch + rotate([0,0,-90]) + translate ([base_diameter / 2 - notch_width, -notch_length/2, 0]) + color("silver") cube([notch_width, notch_length, notch_height]); + } + else + { + tolerance = 0.4; + // Shaft hole + color("silver") cylinder(d=shaft_neck_diameter + tolerance, h=shaft_length + shaft_neck_height); + // Notch cavity + rotate([0,0,-90]) + translate ([base_diameter / 2 - notch_width - tolerance/2, -notch_length/2 - tolerance/2, 0]) + color("silver") cube([notch_width + tolerance, notch_length + tolerance, notch_height + tolerance]); + } + } +} + +// 8-direction + push button switch +module Alps688RKJXL100401V() +{ + color("brown") + { + // Case + rotate([0,0,45]) + difference() + { + rotate([0,0,22.5]) + cylinder(d=14.4, h=6.4, $fn=8); + + // Cover markings + translate([0,4,0]) cube([0.5,2,1], center = true); + translate([0,-4,0]) cube([0.5,2,1], center = true); + translate([4,0,0]) cube([2,0.5,1], center = true); + translate([-4,0,0]) cube([2,0.5,1], center = true); + + rotate([0,0,-45]) + translate([7,0,-0.5]) rotate([0,0,60]) cylinder(d=4, h=1, $fn=3); + } + + // Shaft + difference() + { + translate([0,0,-7]) + cylinder(d=2.2, h=7); + + translate([0.3,-1.1,-7.01]) + cube([0.81, 2.20, 3.51]); + } + + // Installation guides at the bottom + rotate([0,0,-12.5]) + { + translate([-6.4+0.8,0,6]) cylinder(d=1.6, h=2.5); + translate([6.1-0.5,0,6]) cylinder(d=1, h=2); + } + } + // Pins + translate([0,0,6.4]) + color("white") + { + translate([0,7.5,0]) PinFlat(text="C1"); // C1 + translate([0,-7.5,0]) PinFlat(text="C2", d=180); // C2 + + translate([7.5,2,0]) PinFlat(text="1", d=-90); // 1 + translate([-7.5,-2,0]) PinFlat(text="2", d=90); // 2 + + rotate([0,0,45]) + { + translate([7.5,1.2,0]) PinFlat(text="G", d=-90); // A + translate([7.5,-1.2,0]) PinFlat(text="H", d=-90); // B + translate([-7.5,1.2,0]) PinFlat(text="D", d=90); // A + translate([-7.5,-1.2,0]) PinFlat(text="C", d=90); // B + + translate([1.2,7.5,0]) PinFlat(text="F", d=0); // A + translate([-1.2,7.5,0]) PinFlat(text="E", d=0); // B + translate([1.2,-7.5,0]) PinFlat(text="A", d=180); // A + translate([-1.2,-7.5,0]) PinFlat(text="B", d=180); // B + } + } + + // Instruction texts + module t(text, d=0) + { + rotate([0,0,d]) translate([0,-5,0]) rotate([180,0,0]) text(text,size=1,halign="center"); + } + + rotate([0,0,90]) + { + translate([0,7.5,0]) t("1-2",0); + t("A",0); + t("E",180); + t("H",45); + t("G",90); + t("F",135); + t("D",-135); + t("C",-90); + t("B",-45); + } +} + +module Gt2Pulley(d=12.5,dShaft=5, l=14, w=6) +{ + color("silver") + difference() + { + cylinder(d=d,h=l); + + translate([0,0,-0.01]) + cylinder(d=dShaft,h=l+0.02); + + translate([0,0,1]) + difference() + { + cylinder(d=d+0.01,h=w); + cylinder(d=d-2,h=w); + } + } +} + +module DcMotor() +{ + color("silver") + cylinder(d=15,h=30); +} + +module Gt2BeltCircular(l,d=12.5,width=6) +{ + color("brown") + { + cube([l, d, 2]); + translate([0,0,0]) cylinder(d=d,h=width); + translate([l,0,0]) cylinder(d=d,h=width); + for (a = [360/16 : 360/16 : 360]) + { + rotate([0, a, 0]) translate([d/2-2,0,0]) cylinder(d=2,h=width); + } + } +} diff --git a/common.scad b/common.scad new file mode 100644 index 0000000..0f4471a --- /dev/null +++ b/common.scad @@ -0,0 +1,766 @@ +// Common utilities for building stuff + +module flipX() { rotate([180,0,0]) children(); } +module flipY() { rotate([0,180,0]) children(); } +module flipZ() { rotate([0,0,180]) children(); } +module turnX(a) { rotate([a,0,0]) children(); } +module turnY(a) { rotate([0,a,0]) children(); } +module turnZ(a) { rotate([0,0,a]) children(); } +module moveX(l) { translate([l,0,0]) children(); } +module moveY(l) { translate([0,l,0]) children(); } +module moveZ(l) { translate([0,0,l]) children(); } + +module sheet_cone(x, y1, y2, h) +{ + points = [[0,0,0], [0,y1,0], [0,y1/2+y2/2,h], [0,y1/2-y2/2,h], + [x,0,0], [x,y1,0], [x,y1/2+y2/2,h], [x,y1/2-y2/2,h]]; + faces = [[0,1,2,3], // bottom + [4,5,1,0], // front + [7,6,5,4], // top + [5,6,2,1], // right + [6,7,3,2], // back + [7,4,0,3]]; + polyhedron(points=points, faces=faces, convexity=10); +} + +module tube(radius, length, thickness) +{ + difference () + { + cylinder (r=radius,h=length); + translate ([0,0,-1]) + cylinder (r=radius-thickness,h=length + 2); + } +} + +module tubeD(dout, din, h) +{ + difference () + { + cylinder (d=dout,h=h); + translate ([0,0,-1]) + cylinder (d=din,h=h + 2); + } +} + +module tubeD2(dout1, din1, dout2, din2, h) +{ + difference () + { + cylinder (d1=dout1, d2=dout2, h=h); + translate ([0,0,-1]) + cylinder (d1=din1, d2=din2, h=h + 2); + } +} + + +module washer(out, in, h) +{ + difference () + { + cylinder (d=out,h=h); + translate ([0,0,-1]) + cylinder (d=in-h,h=h + 2); + } +} + +module hole(diameter, length) +{ + translate ([0,0,-1]) + cylinder (r1=diameter/2, r2=diameter/2, h=length + 2); +} + +module cube_inside(size, thickness) +{ + translate ([thickness, thickness, thickness]) + cube ([size[0] - thickness*2, size[1] - thickness*2, size[2] - thickness*2]); +} + +module DetentPotSpring(dShaft= 9.5, dBolt = 14.5, length = 6, contactLength = 2) +{ + dOut = dBolt + 2.2; + hull() + { + translate([0,dBolt/2+0.4,-contactLength]) + { + translate([0,0.58,0]) + cylinder(d=1.2, h=contactLength); + } + translate([0,dBolt/2+0.6,-contactLength - 1]) + cylinder(d=1.2, h = contactLength + 1); + } + // Spring shaft + difference() + { + translate([0,dBolt/2+0.6,-length - contactLength - 0.5]) + cylinder(d=1.2, h = length + contactLength - 0.5); + + translate([0,0,-length - contactLength + 0.2]) + cylinder(d=dBolt, h=4); // bolt + } + + washerThickness = 0.7; + + // Mounting washer with surface ripples for better grip + translate([0,0,-length - contactLength - 0.5]) + difference() + { + tubeD(dOut, dShaft, washerThickness); + + for (a = [100:15:440]) + { + translate([0,0,-1.2]) + rotate([45,0,a+7]) + cube([dBolt/2+2,1,1]); + translate([0,0,0.5]) + rotate([45,0,a]) + cube([dBolt/2+2,1,1]); + } + } +} + +// Kobs and knob holes (D/T) +module Knob(d1, d2, length, skirt=5, detent = false) +{ + aargh = true; + difference() + { + union() + { + // Pot shaft holder + translate([0,0,0]) + cylinder (d1=d2, d2=d2, h=length); + + // Outer shell + difference () + { + cylinder (d1=d1, d2=d2, h=length); + translate([0,0,-1]) + cylinder (d1=d1-1.5, d2=d2-1.5, h=length-2); + } + + // Skirt and detent + translate([0,0,-skirt]) + { + tube (d1/2, skirt, 1); + + if (detent == true) + { + translate([-1,d1/2-1,0]) + cylinder(d=1.5, h=skirt); + translate([1,d1/2-1,0]) + cylinder(d=1.5, h=skirt); + } + } + + for (a = [40:40:360]) + { + if (aargh == true) + { + rotate([0,0,a]) + rotate([90,2,0]) + translate([-5.7,-2.4,0]) + rotate_extrude(angle=60,convexity = 10) + translate([15.7, 0, 0]) + circle(r = 0.55); + } + else + { + rotate([0,0,a]) + rotate([90,-9,0]) + translate([-16.9+6.2,-1.2,0]) + rotate_extrude(angle=33,convexity = 10) + translate([20, 0, 0]) + circle(r = 0.5); + } + } + + for (a = [20:40:360]) + { + if (aargh == true) + { + rotate([0,0,a]) + rotate([90,-2,0]) + translate([-13.8,-3,0]) + rotate_extrude(angle=41,convexity = 10) + translate([23.5, 0, 0]) + circle(r = 0.55); + } + else + { + rotate([0,0,a]) + rotate([90,-9.1,0]) + translate([-16.75,-1.27,0]) + rotate_extrude(angle=30.5,convexity = 10) + translate([26, 0, 0]) + circle(r = 0.5); + } + } + } + translate([-15,-15,-20-skirt]) + cube([30,30,20]); + } +} + +module KnobHoleD(diameter = 6, length = 12) +{ + actualD = diameter + 0.2; + difference () + { + cylinder (d1=actualD, d2=actualD, h=length); + translate ([actualD / 2 - 1.4,-5,-1]) + cube ([10,10,length + 2]); + + // Tightening dips + for (a = [-1.8:3.6:1.8]) + { + rotate([0,0,180]) + translate([-7.6,a,5]) + rotate([90,45,0]) + rotate_extrude(angle=90,convexity = 10) + translate([diameter-0.1, 0, 0]) + circle(r = 0.4); + } + } +} + +module KnobHoleT(diameter = 6.3, lengthNoSkirt = 12, skirt = 1) +{ + actualD = diameter + 0.2; + length = lengthNoSkirt + skirt; + difference () + { + cylinder (d1=actualD, d2=actualD, h=length); + translate ([-0.6,-actualD,length - 1.8]) + cube ([1.2,2*actualD,2.5]); + + // Tightening dips + translate ([0,0,skirt]) + for (a = [45:90:360]) + { + rotate([0,0,90 + a]) + translate([-9.4,0,4]) + rotate([90,45,0]) + rotate_extrude(angle=90,convexity = 10) + translate([diameter-0.1, 0, 0]) + circle(r = 0.4); + } + } +} + +module KnobT(d1, d2, length, skirt=1, detent=false) +{ + difference () + { + Knob(d1, d2, length, skirt, detent); + translate ([0,0,-0.2-skirt]) + KnobHoleT(6.4, 12.2, skirt); + } +} + +module KnobD(d1, d2, length) +{ + difference () + { + Knob(d1, d2, length); + translate ([0,0,-0.2]) + KnobHoleD(6, length=12.2); + } +} + +module ScrewCavity3m(thruDepth, capDepth = 20, capDiameter = 7, thruDiameter = 3.2, thickness=2) +{ + translate([0,0,thruDepth]) + cylinder (d1=capDiameter, d2=capDiameter, h=capDepth); + translate([0,0,-thickness]) + cylinder (d1=thruDiameter, d2=thruDiameter, h=thruDepth+thickness*2); +} + +module ScrewThreadSupport3m(length, angle = [0,0,0]) +{ + difference() + { + translate([0,0,-length]) + tube (3, length, 1.8); + + rotate(angle) + translate([-5, -5, 0]) + cube ([10,10,10]); + } +} + +module ScrewEntrySupport3m(thruDepth, capDepth = 20, capDiameter = 7, thickness=2, thruDiameter = 3.2) +{ + translate([0,0,thruDepth-thickness]) + cylinder (d1=capDiameter+thickness, d2=capDiameter+thickness, h=capDepth-thickness); + + translate([0,0,-10]) + cylinder (d1=thruDiameter+thickness, d2=thruDiameter+thickness, h=capDepth-thickness); +} + +module Switch() +{ + color("black") + translate ([-3,-3,-4]) + cube([6,6,3]); + + color("yellow") + translate ([0,0,-1]) + cylinder(d=3.2,h=1); + + r = 2.54; + w = 0.8; + wire_height = 4; + d = 3 * r / 2; + color("white") + translate([-w/2,-w/2,-4-wire_height]) + { + translate ([-d,-r,wire_height+1]) cube([2*w,w,1]); + translate ([-d,-r,0]) cube([w,w,wire_height + 1]); + + translate ([d-w,-r,wire_height+1]) cube([2*w,w,1]); + translate ([d,-r,0]) cube([w,w,wire_height + 1]); + + translate ([-d,r,wire_height+1]) cube([2*w,w,1]); + translate ([-d,r,0]) cube([w,w,wire_height + 1]); + + translate ([d-w,r,wire_height+1]) cube([2*w,w,1]); + translate ([d,r,0]) cube([w,w,wire_height + 1]); + } +} + +module SwitchButton(d=8, h=7, cavityOnly=false) +{ + if (cavityOnly == false) + { + difference() + { + union () + { + intersection() + { + translate([0,0,-0.8]) + cylinder(d=d, h=h+0.8); + translate([0,0,1.6]) + sphere(r=h-0.7); + } + translate([0,0,-1]) + cylinder(d=d+1.6, h=0.8); + } + translate([0,0,-2]) + cylinder(d=d-1, h=h); + } + + translate([0,0,-1.4]) + tube(radius=2, length=h, thickness=1); + } + else + { + translate([0,0,-0.5]) + { + cylinder(d=d, h=10); + } + } +} + +module SwitchButtonBall(d=8, h=7) +{ + difference() + { + union () + { + intersection() + { + translate([0,0,-1.2]) + cylinder(d=d, h=h+0.8); + union() + { + translate([0,0,h - 4]) + sphere(d=d); + translate([0,0,6-d]) + cylinder(d=d, h=h-2); + } + } + translate([0,0,-1.2]) + cylinder(d=d+1.6, h=1.2); + } + translate([0,0,-d/2]) + cylinder(d=d-1, h=h); + translate([0,0,-1.25]) + cylinder(d1=d+0.8, d2=d-1, h=1); + + // Top inner cavity + translate([0,0,h - 4]) + sphere(d=d-1); + } + + // Center shaft + translate([0,0,-1.2]) + cylinder(d=2.4,h=h+0.6); +} + + +module SwitchButtonSpring() +{ + +} + +// Hat Switch with 4 directions and center push with 2x2x2mm shaft +// and a case and a knob for it. + +module Hat5Switch() +{ + { + // Slot for the hat switch + color("grey") + intersection() + { + translate([-4.3,-4.3,0]) + cube ([8.6, 8.6, 3]); + + rotate([0,0,45]) + translate([-4,-4,0]) + cube ([8, 8, 1.8]); + } + color("grey") + translate([0,0,1.8]) cylinder (d1=4.3, d2=3.3, h=0.2); + + color("black") + { + // Shaft and base + translate([-1,-1,2]) + cube ([2, 2, 3]); + translate([0,0,1.8]) cylinder (d1=3, d2=3, h=0.3); + + // Dimples + translate([-2.5,-2.5,1.8]) cylinder (d=0.7, h=0.2); + translate([2.5,2.5,1.8]) cylinder (d=0.7, h=0.2); + translate([2.5,-2.5,1.8]) cylinder (d=0.7, h=0.2); + translate([-2.5,2.5,1.8]) cylinder (d=0.7, h=0.2); + } + } + // Soldering pads + color("white") + rotate([0,0,45]) + { + translate([-2,-4.2,-0.2]) cube ([4, 1, 1]); + translate([-2,4.2-1,-0.2]) cube ([4, 1, 1]); + } +} + +// Printing: Use layer height 0.15 + +module Hat5Case() +{ + switch_height = 2.0; + difference() + { + // Outer casing + case_height = 1.5 + switch_height; + union () + { + rotate([0,0,45]) + translate([-5.5, -6.5, 0]) + cube ([11, 13, case_height]); + } + + // Slot for the hat switch + intersection() + { + translate([-4.3,-4.3,-0.1]) + cube ([8.6, 8.6, 3]); + + rotate([0,0,45]) + translate([-4,-4,-0.1]) + cube ([8, 8, switch_height+0.1]); + } + + Hat5CaseWireHoles(); + + // Dimple groove + rotate([0,0,-45]) + translate([-0.55,-3,1]) + cube ([1.1, 6, 5]); + } +} + +module Hat5CaseTop(height = 0.6) +{ + { + rotate([0,0,45]) + difference() + { + union () + { + translate([-5.5, -6.5, 0]) + cube ([11, 13, height]); + + rotate([0,0,-45]) + { + translate([-4, -1.3, height]) + cube ([8, 2.6, 0.4]); + + translate([-1.3, -4, height]) + cube ([2.6, 8, 0.4]); + } + } + + translate([0, 0, height-0.7]) + cylinder (d1=7, d2=5.5, h=1.11); + translate([0, 0, -0.69]) + cylinder (d1=9.5, d2=7, h=height); + } + } +} + + +module Hat5CaseWireHoles() +{ + rotate([0,0,45]) + translate([-2.5,2,-3]) + cube ([5, 3, 13]); + + rotate([0,0,45]) + translate([-2.5,-5,-3]) + cube ([5, 3, 13]); +} + +module Hat5Shaft(height = 2) +{ + top_h = height + 2; + h = height + 2.5 + 1.5 + 2 + 2.5; + cavity_tolerance = 0.4; // depends on how well the printer does the cavity + + translate([0,0,h]) + { + difference () + { + union() + { + translate([-2,-2,-4]) + cube ([4, 4, 1]); + rotate([0,0,45]) + translate([0,0,-3]) + cylinder (d1=4*1.42, d2=5, h=0.4,$fn=4); + + translate([0,0,-h]) + difference() + { + cylinder (d=5,h=h-5); + + translate([-0.95,-0.95,-0.1]) + cube ([1.9, 1.9, 2.2 + cavity_tolerance]); + } + translate([0,0,-5]) + cylinder (d1=5,d2=5.7,h=1); + } + translate([0,0,-5.6]) + cylinder (d=1.8,h=3.2); + } + } +} + +module Hat5Shaft1(height = 2) +{ + top_h = 5; + h = height + 2.5 + 1.5 + 2 + 2.5; + cavity_tolerance = 0.5; // depends on how well the printer does the cavity + + translate([0,0,h]) + { + difference () + { + union() + { + translate([0,0,-(2.5 + 1.5 + 2)]) + cylinder (d1=5, d2=6.5,h=2); + + translate([-2,-2,-4]) + cube ([4, 4, 1.5]); + + translate([0,0,-h]) + difference() + { + union() + { + cylinder (d=5,h=h-top_h); + + translate([0,0,0.3]) + cylinder (d1=9, d2=5, h=0.7); + cylinder (d1=9, d2=9, h=0.3); + } + + translate([-0.95,-0.95,-0.1]) + cube ([1.9, 1.9, 2.3 + cavity_tolerance]); + } + } + translate([0,0,-4.5-height]) + cylinder (d=2,h=8+height); + } + } +} + + +module Hat5Knob(width = 9, step_multiplier=3.5) +{ + difference() + { + translate([0,0,1]) + { + for (z = [0.4:0.4:1.6]) + { + s = width - z*step_multiplier; + minkowski() + { + translate([-s/2, -s/2,z-0.4]) + cube ([s, s, z + 1]); + cylinder(d=6-z*2,h=0.01); + } + } + } + + translate([0,0,0]) + cylinder (d=2.1,h=8); + translate([0,0,2.29]) + rotate([0,0,0]) + cylinder (d1=3.7,d2=2.1,h=0.8); + translate([0,0,3.8]) + cylinder (d=3.7,h=2); + + rotate([0,0,45]) + translate([0,0,1.989]) + cylinder (d1=4.2*1.42, d2=5, h=0.4,$fn=4); + translate([-2.1,-2.1,0.99]) + cube ([4.2, 4.2, 1]); + } +} + + +module Hat5KnobComplete(shaft_length = 3, width = 9, step_multiplier=3.5) +{ + translate([0,0,1]) + { + for (z = [0.4:0.4:1.6]) + { + s = width - z*step_multiplier; + minkowski() + { + translate([-s/2, -s/2,z]) + cube ([s, s, z + 1]); + cylinder(d=6-z*2,h=0.01); + } + } + } + + translate([0,0,-0.5]) + cylinder (d1=5, d2=12,h=2); + + translate([0,0,-shaft_length]) + difference() + { + cylinder (d=5,h=shaft_length); + + translate([-0.95,-0.95,-0.1]) + cube ([1.9, 1.9, 2.2]); + } +} + +// Circuit Board with inch (2.54mm) raster. +// Define either polygon points or a square in millimeter (x,y) or full pins (px, py). +// Use of square holes is faster to render. +module CircuitBoard( + points = [], + x = 0, y = 0, + h = 1.6, + px = 0, py = 0, + raster = 2.54, rasterOffset = [0, 0]) +{ + if (len(points) > 0) + { + CircuitBoardPoly(points, h, raster, rasterOffset); + } + else if (px > 0 && py > 0) + { + x = px * 2.54; + y = py * 2.54; + poly = [ [0,0], [x,0], [x,y], [0,y] ]; + CircuitBoardPoly(poly, h, raster, rasterOffset); + } + else if (x > 0 && y > 0) + { + poly = [ [0,0], [x,0], [x,y], [0,y] ]; + CircuitBoardPoly(poly, h, raster, rasterOffset); + } +} + +function minAt(m, i) = min([ for (c = m) c[i] ]); +function maxAt(m, i) = max([ for (c = m) c[i] ]); + +module CircuitBoardPoly(poly, h, raster = 2.54, , rasterOffset = [0, 0], use_square_holes = true) +{ + min_x = minAt(poly, 0) + rasterOffset[0]; + max_x = maxAt(poly, 0) + rasterOffset[0]; + min_y = minAt(poly, 1) + rasterOffset[1]; + max_y = maxAt(poly, 1) + rasterOffset[1]; + color("orange") + difference() + { + union() + { + linear_extrude(height=h) + polygon(poly); + } + for (pin_y = [min_y:raster:max_y]) + { + for (pin_x = [min_x:raster:max_x]) + { + if (use_square_holes == true) + { + translate([pin_x-0.5,pin_y-0.5,-0.1]) + cube([1, 1, h+0.2]); + } + else + { + translate([pin_x,pin_y,-0.1]) cylinder(d=1, h=h+0.2); + } + } + } + } +} + +// A flat pin (1.0 x 0.2) with inwards moving connecting hook. Turn around with +module PinFlat(h=3.5,d=0,overshoot=0.4,text="") +{ + thickness = 0.2; + rotate([0,0,d]) + { + translate([-0.5,-thickness,-overshoot]) cube([1,thickness,h + overshoot]); + translate([-0.5,-1,-overshoot]) cube([1,1,thickness]); + if (text != "") + translate([0,-0.1,-overshoot]) + rotate([180,0,0]) + linear_extrude(height = 0.2) + text(text,size=0.5,halign="center"); + } +} + + +module KnurledShaftCavity(d, h, c = 18, includeT=true, tolerance=0.25) +{ + difference() + { + rotate([0,0,180/c]) + cylinder(d=d+tolerance, h=h, $fn=c); + if (includeT == true) + { + tWidth = 1; + translate([0,0,h-0.3]) + cube([d*1.5,tWidth,tWidth], center=true); + } + } +} + +module spring(d,l) +{ + %cylinder(d=d, h=l); +} diff --git a/firmware/throttle-teensy-lc-r1.hex b/firmware/throttle-teensy-lc-r1.hex new file mode 100644 index 0000000..6d64d49 --- /dev/null +++ b/firmware/throttle-teensy-lc-r1.hex @@ -0,0 +1,803 @@ +:1000000000180020C100000065150000291500003F +:1000100029150000291500002915000029150000E8 +:10002000291500002915000029150000651500009C +:10003000651500002915000015250000E5250000C4 +:1000400065150000651500006515000065150000C8 +:1000500065150000651500006515000065150000B8 +:1000600065150000651500006515000065150000A8 +:1000700021260000F9260000D127000065150000A8 +:100080006515000065150000651500006515000088 +:100090006515000065150000651500006515000078 +:1000A0003D1900006515000065150000651500008C +:1000B0006515000065150000651500006515000058 +:1000C000F8B501F053FA0821474A484B1A60484AEC +:1000D000484B1A60484A494B1A60494B1A780A4201 +:1000E00002D01A780A431A702A22464B46481A70E0 +:1000F00000234649464C1A188A4203D2E558043375 +:100100001560F8E70021434B434A934201D202C3F2 +:10011000FBE70023414A424958589446985004331B +:10012000C02BF9D100210326FF2580248A083D4BEE +:100130009200D2180B002F003340DB009F40106864 +:100140000131B84307002000984003003B4313608F +:100150002029EBD16246344B1A608A22334B1A7045 +:10016000334B663A5A707C321A709E3A9979114232 +:10017000FCD0102299791142FCD10C209A792C499B +:100180000240082AFAD1053A0A713D324A71203AF2 +:1001900099791142FCD0402299791142FCD025492D +:1001A000254A11602021224A117014399A790A4097 +:1001B0000C2AFBD1214A224B1A60224A224B1A6098 +:1001C0000022224B1A60224B07321A60214A224B2E +:1001D0001A6062B601F086FA02F050FD01F0CCF927 +:1001E00001F09AF9FEE7C046300004F03480044084 +:1001F000823F0000388004400100000F3C80044032 +:1002000002D0074000E00740300200208C040020AC +:10021000A82F00008C040020AC08002000F9FF1F6C +:100220000000000000E400E008ED00E0005006409F +:10023000004006400000011044800440C000050555 +:10024000048004407FBB000014E000E018E000E000 +:1002500010E000E00000202020ED00E0FFFFFFFFA5 +:10026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E +:10027000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E +:10028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E +:10029000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E +:1002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E +:1002B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E +:1002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E +:1002D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E +:1002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E +:1002F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E +:10030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD +:10031000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED +:10032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD +:10033000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD +:10034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD +:10035000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD +:10036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D +:10037000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D +:10038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D +:10039000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D +:1003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D +:1003B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D +:1003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D +:1003D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D +:1003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D +:1003F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D +:10040000FFFFFFFFFFFFFFFFFFFFFFFFDEF9FFFF23 +:1004100010B5064C2378002B07D1054B002B02D0DA +:10042000044800E000BF0123237010BD8C040020AD +:1004300000000000A82F0000084B10B5002B03D0CF +:100440000749084800E000BF07480368002B00D1B7 +:1004500010BD064B002BFBD09847F9E700000000C9 +:1004600090040020A82F00008C0400200000000051 +:100470004069704737B500246B461C705C709C70F7 +:1004800003680A001D6801AB1C705C709C706946B3 +:10049000A84737BDC82040007047FFFF10B50121B5 +:1004A0000400182001F0AAF880235B03A361014B2C +:1004B000E36110BD000100F80138C0B210B51F287B +:1004C00011D80123084A8340002902D011680B4348 +:1004D00002E0116899430B001360044B1B78002B5A +:1004E00001D101F0B9FD10BD94080020D50600200F +:1004F00010B50F2300281CDB0023162819DD01335B +:10050000432816DD0133702813DD01339D2810DDEB +:100510000133CA280DDD0133F4280ADD92220133AC +:100520005200904205DD5222FF320023904200DC4F +:1005300007330F2205494868904303434B60044B3F +:100540001B78002B01D101F087FD10BD940800201D +:10055000D506002070B5040008000D0002F0BDFBB8 +:100560002368020029005B682000984770BDFFFFE8 +:10057000F7B501220024264B1A706F46254B022145 +:10058000E65C300001F03AF8684600F029FE224DA2 +:10059000E300EA18ED1803CF03C23100280000F091 +:1005A00027FE01340521280000F036FE112CE4D18D +:1005B00001271A4E1A4C35683D4021D1300000F019 +:1005C000DCFF00281CD0FF2323736373A373E37342 +:1005D00023746374134B65601A68A7746561A56022 +:1005E000002A00D11C60104B1A68002A00D09460C9 +:1005F0001C60002323840D4B3000236000F0C1FFFA +:10060000002301220A49200000F050FDF7BDC0463A +:10061000D50600200C2D000008050020AC040020A9 +:10062000B0040020A4050020B4050020F02C000038 +:10063000D8040020F8B5174B17481E78174B184FF1 +:100640001A781D00B24203D102783B789A4220D03A +:10065000144B0022022E03D1FF215A70197010E0B2 +:10066000042E03D11A705A70FF220AE00478FF2189 +:10067000012C01D11A7003E01970022C00D0662100 +:1006800059709A70037808482E7001783B7000F01A +:1006900055FDF8BD30020020DC040020DD04002000 +:1006A000DB040020D8040020940500201D4A70B50A +:1006B00013681600002B32DC1B4C1C492000FFF78E +:1006C00049FF1B4B2000196800F02CFE194920003F +:1006D000FFF740FF184B2000196800F023FE174970 +:1006E0002000FFF737FF164B2000196800F01AFEB4 +:1006F0000025144BEB5C002B07D013492000FFF7BB +:1007000029FF2900200000F00DFE0135112DF0D148 +:10071000200000F0C7FDFA239B00336001E0013B9D +:10072000136070BD00050020340200201D2D000064 +:10073000D4040020222D0000040500202A2D0000F2 +:1007400090050020DE040020322D0000F7B5864C15 +:100750002378022B4CD0022000F096FE834F844E6B +:100760003860012000F090FE824B1860002000F0FD +:100770008BFE814B1860012000F086FE7F4B804A83 +:1007800059681D000A40B04200D9300000011043F2 +:1007900058607C4B1A780193002A01D101F05CFC6F +:1007A000002000F071FE784B6A681340B04200D917 +:1007B0006F4880031843019B68601B78002B01D1B0 +:1007C00001F04AFC3B68B34200D9694B6A681906DC +:1007D0001202120A0A436A600322A9681B0A9143A3 +:1007E0000B43AB60019B1B78002B01D101F034FC63 +:1007F0000025664B664EEF5CEB00F618300000F00B +:100800000DFD300000F046FD002802D00122614BB2 +:10081000DA55300000F036FD002802D000225D4B92 +:10082000DA550135112DE4D15A4D00202B7A002BD9 +:1008300021D1EB79002B02D03C20FF301BE0AB79BB +:10084000002B02D08720400015E06B79E120002BBF +:1008500011D12B7BB420002B0DD1EB7A8720002BFC +:1008600009D1AB7A5A20002B05D16B7A2D20002BB1 +:1008700001D101204042FFF73BFE697801204B1E69 +:100880009941C9B2FFF718FEE97802204B1E994141 +:10089000C9B2FFF711FE297803204B1E9941C9B256 +:1008A000FFF70AFEA97804204B1E9941C9B2FFF751 +:1008B00003FE297905204B1E9941C9B2FFF7FCFDC3 +:1008C000E97C0A204B1E9941C9B2FFF7F5FD297D4D +:1008D0000B204B1E9941C9B2FFF7EEFD697D0C203C +:1008E0004B1E9941C9B2FFF7E7FDA97C14204B1EAE +:1008F0009941C9B2FFF7E0FD01F0AEFBE97C0029A8 +:1009000010D0254B1B78002B2BD0244B1B78DB07FA +:1009100027D5234B234A1B6812689B1A0E2B20D91C +:1009200004231FE0114B01201B6820701D00FC3DBB +:10093000FF3D1D4A082D01D8107007E0D93BFF3B51 +:100940004E2B02D80223137000E01170FFF772FEE5 +:100950002378042B01D1FFF7A9FE012000F0A8FEA7 +:10096000F7BD02232370F1E730020020D4040020F9 +:10097000FF030000040500209005002094080020DB +:100980000FC0FFFFD5060020FF3F00FF0C2D000029 +:1009900008050020DE040020D3060020E806002021 +:1009A000BC050020A8080020DC0400200B7B0A0006 +:1009B00003734B7B10B543738B7B12328373CB7BFA +:1009C000C3730B7C03744B7C43740B680360152367 +:1009D0008C7C8474CC7CC47492780275CB5643753D +:1009E0004B68436083608B69836110BD0023836122 +:1009F0007047F7B5089D0193AC680134AC60EC68B2 +:100A00000134EC602C6901342C61002494462600EA +:100A10001A0027002300644667786E7836063F0682 +:100A200001D17640C046BF1900D3FF4300D33F0633 +:100A3000EC683F0E67436E782C79A61B6E703B0402 +:100A40004160C046C0460324013CFDD15B0000D29A +:100A50008160C046C046C0460824013CFDD181608B +:100A6000C046C0460524013CFDD14160C046C04699 +:100A70000324013CFDD15B0000D28160C046644686 +:100A80002778C046C0460724013CFDD18160C0469E +:100A9000C0460524013CFDD14160C046C046032448 +:100AA000013CFDD15B0000D28160C0462E78360645 +:100AB0003F0601D17640C046C0460624013CFDD128 +:100AC0008160C046C0460524013CFDD14160C0465E +:100AD000C0460324013CFDD15B0000D28160C046CA +:100AE000BF1900D3FF4300D33F060724013CFDD1CB +:100AF0008160C046C0460524013CFDD14160C0462E +:100B0000C0460324013CFDD15B0000D28160C04699 +:100B1000AC683F0E6743C0460724013CFDD18160AD +:100B2000C046C0460524013CFDD14160C046C046D8 +:100B30000324013CFDD15B0000D28160C0462E78C9 +:100B4000EC78A61B2E70C0460624013CFDD18160C6 +:100B5000C046C0460524013CFDD14160C046C046A8 +:100B60000324013CFDD15B0000D28160C046C04639 +:100B7000C0460824013CFDD18160C046C046052422 +:100B8000013CFDD14160C046C0460324013CFDD17B +:100B90005B0000D28160C0463B04C0460824013C93 +:100BA000FDD18160C046C0460524013CFDD14160B5 +:100BB000C046C0460324013CFDD15B0000D28160E9 +:100BC000C046C046C0460824013CFDD18160C046F5 +:100BD000C0460524013CFDD14160C046C046032407 +:100BE000013CFDD15B0000D28160C0466446A7781D +:100BF000C046C0460724013CFDD18160C046C046C6 +:100C00000524013CFDD14160C046C0460324013C9F +:100C1000FDD15B0000D28160C046AE7836063F064B +:100C200001D17640C046C0460624013CFDD181601A +:100C3000C046C0460524013CFDD14160C046C046C7 +:100C40000324013CFDD15B0000D28160C046BF1986 +:100C500000D3FF4300D33F060724013CFDD1816050 +:100C6000C046C0460524013CFDD14160C046C04697 +:100C70000324013CFDD15B0000D28160C0462C6999 +:100C80003F0E6743C0460724013CFDD18160C0464A +:100C9000C0460524013CFDD14160C046C046032446 +:100CA000013CFDD15B0000D28160C046AE786C791A +:100CB000A61BAE70C0460624013CFDD18160C04633 +:100CC000C0460524013CFDD14160C046C046032416 +:100CD000013CFDD15B0000D28160C046AC79A444E8 +:100CE000C046C0460724013CFDD18160C046C046D5 +:100CF0000524013CFDD14160C046C0460324013CAF +:100D0000FDD15B0000D28160C0463B04C046082490 +:100D1000013CFDD18160C046C0460524013CFDD1A7 +:100D20004160C046C0460324013CFDD15B0000D2B7 +:100D30008160C046C046C0460824013CFDD18160A8 +:100D4000C046C0460524013CFDD14160C046C046B6 +:100D50000324013CFDD15B0000D28160C0466446A3 +:100D60006778C046C0460724013CFDD18160C0467B +:100D7000C0460524013CFDD14160C046C046032465 +:100D8000013CFDD15B0000D28160C0466E78360622 +:100D90003F0601D17640C046C0460624013CFDD145 +:100DA0008160C046C0460524013CFDD14160C0467B +:100DB000C0460324013CFDD15B0000D28160C046E7 +:100DC000BF1900D3FF4300D33F060724013CFDD1E8 +:100DD0008160C046C0460524013CFDD14160C0464B +:100DE000C0460324013CFDD15B0000D28160C046B7 +:100DF000EC683F0E6743C0460724013CFDD181608B +:100E0000C046C0460524013CFDD14160C046C046F5 +:100E10000324013CFDD15B0000D28160C0466E78A6 +:100E20002C79A61B6E70C0460624013CFDD1816062 +:100E3000C046C0460524013CFDD14160C046C046C5 +:100E40000324013CFDD15B0000D28160C046C04656 +:100E5000C0460824013CFDD18160C046C04605243F +:100E6000013CFDD14160C046C0460324013CFDD198 +:100E70005B0000D28160C0463B04C0460824013CB0 +:100E8000FDD18160C046C046C0460324013CFDD16F +:100E9000013A01D0C046D3E50198FEBD00B58021DE +:100EA000027B89B003AB1A70427B49035A70827B84 +:100EB0009A70827C0592C27C0692027D0792C27B68 +:100EC000DA70027C1A71427C5A71427D9A710093E9 +:100ED0000CC80248FFF78DFD09B000BD000100F805 +:100EE00070B504000D0088B000F0BEFB238CC01A62 +:100EF00080B23128F8D972B6290001A8FFF756FD53 +:100F000001A8FFF7CBFF00280CD162B6C8239B00D5 +:100F1000013BFDD172B6290001A8FFF747FD01A8EA +:100F2000FFF7BCFFB423DB005843064901F09CFDEA +:100F3000054E35684519356062B600F095FB208492 +:100F400008B070BD80BB0000BC050020072370B551 +:100F500018490A7801321A40063B13400A70DB0137 +:100F6000910701D540210B4304210A4201D02022E0 +:100F7000134305000F3006001033DCB20C3500239C +:100F8000A979994205D08020400001F06DFD013023 +:100F9000C3B25A1C62431212EB70D1B2002A00D0C5 +:100FA00001392970002B01D0013BEB700135AE42B5 +:100FB000E5D170BDA804002070B5160088B001917D +:100FC000197801AC6660A660A1745978827CE174DE +:100FD0009B7805002375012A03D12000FFF7B6FF97 +:100FE00006E0002363742374E373A37363732373B2 +:100FF0000323310020006375FFF7F8FC2B68210004 +:101000009B692800984708B070BD70B5160088B07D +:101010000191197801AC6660A660A1745978827C50 +:10102000E1749B7805002375012A03D12000FFF7A6 +:101030008DFF06E0002363742374E373A37363736B +:1010400023730023310020006375FFF7CFFC2B686A +:1010500021009B692800984708B070BD064B70B509 +:101060001D0090331C00083528000835083800F0B2 +:10107000B7F8A542F8D170BD0805002070B500246E +:10108000047044708470A1420ED0165D002E08D00A +:101090001D5D002D05D00136013575434D432D0CE6 +:1010A00005550134032CF0D170BDFFFFF8B50400E5 +:1010B0000E0017001D1E01DC15000027236820000C +:1010C0009B689847032179432368711861606561C3 +:1010D0005B6920009847011E08D0054801F0C4FC58 +:1010E000044D6B68834200D203006B602000F8BDA2 +:1010F00040420F0094050020F7B50D4C070023680F +:10110000581C20608B4212DB0A4E0B4D3368019352 +:10111000FA239B00019958432B68C91A01F01AFC65 +:1011200000232360336878800093009B2B60F7BD19 +:10113000A8050020BC050020B0050020F0B5050082 +:101140000E0087B06B681E4C002B06D000F08CFAA6 +:101150002368C01A6B689842F4D300F085FAEB68F4 +:101160002060002B03D03000A96898470600154B7B +:101170001C68002C1ED0A37C01936B88632B01D8C4 +:101180000023A374236822005F6863680C32029313 +:10119000636931000393230005A80F33FFF76EFF47 +:1011A00005AB2000039A0299B8476B461B79A374DC +:1011B000A468DEE719212800FFF79EFF07B0F0BD05 +:1011C000AC050020A4050020FF22044B1A70002269 +:1011D0005A80DA60013A9A607047C0469405002050 +:1011E00000230A22036082808371C3717047FFFF6E +:1011F00013B500230400C8B2E071A37100F0ECF94C +:10120000002801D00323A371024B1B680193019BAB +:10121000236013BDBC05002081807047F0B5040039 +:1012200085B0C07900F0D8F9A579431E98412A000D +:1012300008239A431300A27101226E08C0B216401F +:101240000F49B04205D00A680192019A2260022239 +:101250000FE00E6827680296029EF61BA788BE4222 +:1012600009D32A40904206D00A680392039A22606A +:1012700009225340A371A0790007C00F05B0F0BD4B +:10128000BC050020092380791840093843425841A1 +:10129000C0B270470923807918400838434258414A +:1012A000C0B2704737B5022504002A00054901A8DD +:1012B00001F00AFD23682A005B6801A92000984715 +:1012C0003EBDC0463A2D0000F0B50D000F2185B09F +:1012D0009446134A6944ACB22F0C5743013454432B +:1012E000BBB2260C1C19A4193B0C240CDB19E31807 +:1012F0000A245B085C432D1B303501390D701D1E1F +:10130000E9D16346002B02D02D2301390B70CA436B +:1013100004ABD21803685B68984705B0F0BDC046BF +:1013200033330000002210B5914201DA49422D32D8 +:10133000FFF7CAFF10BDFFFF8023F0B51D4A1068FC +:101340001840FCD172B61C4C2378002B30D01B4BBC +:101350001F681B4B1A681B4BBA181E681A4B921950 +:101360001D681A4B52191968194B52181B68194DF0 +:10137000D21892B25208184B2A4392B21A60174BF5 +:1013800017491B68174A9C46174B1B6812680F6861 +:10139000164963440E6816499B180968DB199B19A6 +:1013A0005A1893B25B082B43124A9BB21360207009 +:1013B00062B6F0BD24B00340B905002038B0034048 +:1013C0003CB0034040B0034044B0034048B0034049 +:1013D0004CB003400080FFFF2CB0034058B00340E6 +:1013E00064B0034060B003405CB0034068B00340A9 +:1013F0006CB0034030B003401B4B1C4A19781C4BA7 +:10140000082901D1002102E00A2903D12E311160FF +:10141000132206E00C2901D1283100E03C211160A3 +:1014200012221A60134B1A78134B002A01D00022A3 +:1014300000E001221A60114B1A78114B012A01D8E1 +:1014400080220CE0042A01D8842208E0082A01D86E +:10145000852204E0102A01D8862200E087221A6043 +:101460000122084B1A7070473102002008B0034077 +:101470000CB00340BB05002020B003403202002026 +:1014800024B00340B9050020F8B503000020272B45 +:1014900033D81A4AD45CFF2C2FD0194B1B788342C7 +:1014A00001D0FFF749FF72B610274022154B1442B6 +:1014B00005D01A68BA431A603F231C4002E01A683C +:1014C0003A431A600123104E104D34602B7062B6FF +:1014D00072B680223368134208D00D4B186800237F +:1014E0002B7062B60B4B1B78184106E02B78002B53 +:1014F000DBD062B600F092FFEAE7F8BD3C2D0000B9 +:10150000B90500200CB0034000B00340BA0500202C +:1015100010B00340B805002010B5FFF729F8FFF719 +:1015200015F900F07BFFFAE770B580258026ED0203 +:10153000F6000B4C23682B4201D000F0FFF9236822 +:10154000334201D001F06CF823681B0501D501F08E +:10155000D3F82368DB04ECD501F03AF9E9E7C0469B +:101560003480044010B5FFF7DFFFFFFF0022014B7E +:101570001A60704700810440704700784342584128 +:101580007047012303707047F8B506000D0017007F +:1015900000243900300001F063FA0B003733DBB26E +:1015A000092901D83031CBB230002B55390001F078 +:1015B000D1F9061E01D00134EBE72B1958702B002E +:1015C0005A1B944206DD1A78295D19702A55013399 +:1015D000013CF5E72800F8BD00231A280AD80C338F +:1015E0005843054A83581018007A1B7C0340581E44 +:1015F0008341DBB218007047642D0000F0B51A2853 +:101600002FD80C2217004743164CE5196B682600AB +:10161000012901D0042911D1504334583018257DB7 +:10162000027A20202A432275A22252001A601A68E8 +:10163000042901D1024313E0824311E038592C7A86 +:10164000027DA24302758022002908D0022902D11E +:101650000422FF3204E00022032901D1812252003A +:101660001A60F0BD642D000072B60C4B1A680C4B6A +:1016700018680C4B1B6862B65B0104D53223934299 +:101680009B415B42C018084B9A1A084B5A43FA23F5 +:101690009B005843920D10187047C04618E000E0B8 +:1016A000BC05002004ED00E07FBB000055550100A3 +:1016B00070B50400FFF7D8FF0500002C0FD0FFF72E +:1016C000D3FF074B401B984206D9013C002C06D0A3 +:1016D000FA239B00ED18F2E700F0A0FEEFE770BDE3 +:1016E000E703000080221E4BD20510B51A6080224D +:1016F000002012061A601B4B1B4918601B4B1C4A2A +:101700001960282313601B4A1B4C13601B4A13608B +:101710001B4A13601B4A13601B4A13600922226094 +:101720001A4C20601A4C21601A4C23601A4C23601A +:101730001A4C22601A4C20601A4801601A490B604A +:101740001A490B601A4B1A60FFF756FE1920FFF773 +:10175000AFFF00F005FC1420FF30FFF7A9FF10BD1C +:1017600000E100E004800340FFBF00000880034068 +:101770000C80034014800340008003401C80034021 +:10178000248003402C8003403480034004900340B5 +:10179000089003400C9003401490034000900340D5 +:1017A00004A0034008A003400CA0034014A0034081 +:1017B00000A003400223F0B50A4A0B4E1478C82754 +:1017C0002343DB00F518686008480578002D00D138 +:1017D000403F09040F43F75001235D405C40057012 +:1017E0001470F0BD4406002000F8FF1FD40600204E +:1017F000431E10B50020062B0ED872B6074A99007A +:101800008858002807D044685B008C50044904883D +:101810005A5A121B5A5262B610BDC0462806002002 +:1018200086080020431E0020062B09D872B6054A00 +:101830009B009B58002B02D001305B68FAE762B630 +:101840007047C04688060020F8B572B60123174AD3 +:10185000082594461E00164964465A1EA25C2A4278 +:101860001AD05F017C589A00002C05D1CB190830A2 +:101870005860104BCB5109E03243D2005458002C31 +:101880000AD18B18083058600B4B8B500B4A1378D9 +:10189000013B137062B608E00133082BDCD162B65D +:1018A0000022064B1A7000F02DFCF8BDD02E00006F +:1018B00000F8FF1F88004000C8004000CA06002052 +:1018C000421E10B5062A30D8022380001843174B59 +:1018D000C0001B1872B6164CA05C03280BD801F090 +:1018E00025F816040207052012E0083302200FE055 +:1018F000083304200CE00F48920014580E4B002CC3 +:1019000001D1115001E09858416099500CE003203A +:10191000A0540A0008325A60C822180700D4403A7E +:10192000098809040A431A6062B610BD00F8FF1F57 +:10193000E005002088060020A4060020F7B5BB4B78 +:101940001C781D000423E4B21C421DD0B84B1B7848 +:10195000002B16D0B74A1378DBB2002B05D0013B21 +:10196000DBB21370002B00D100BEB34A1378DBB298 +:10197000002B06D0013BDBB21370002B01D100F02D +:1019800029FD0422A94B1A700822224000D177E2D7 +:10199000AA4BAB4F1B78DBB29C08E4001D093C1935 +:1019A000002D00D0FDE1236865689806000F01381E +:1019B0000C2800D9F1E100F0C3FFA401A401F0015B +:1019C000F001F001F001F001F001CD01F001F001B2 +:1019D000F0010D006B689B4E9B49736000230B6008 +:1019E00001219A482A682060994C32602170994CF4 +:1019F00092B23B61BB610190A2421BD8D020C000D3 +:101A0000824200D3F0E081204000824200D1CDE04C +:101A100006D8802A00D1B2E0822A00D1B4E000E1E9 +:101A20008D4B9A4200D1D0E0FF33FF339A4200D170 +:101A30003EE1F6E08948824200D139E111D8902395 +:101A40001B019A421AD0864B9A4200D156E1A13B23 +:101A50009A4200D0E5E0764B824C1B7823700B0055 +:101A600028E181498A4200D1D3E0804B9A4200D1DB +:101A70001EE17F4B9A4200D0D3E03FE13C00B278B8 +:101A80006B4B7D1C1A702034FF3580222368134273 +:101A900003D06068083800F035FB0834AC42F4D15C +:101AA00000242500734BE058002805D043680093BC +:101AB00000F028FB0098F7E76E4B18516E4BE05092 +:101AC0006E4BE058002805D04368009300F01AFBE5 +:101AD0000098F7E7694B6A4A18516A4BE0506B006F +:101AE000D052694BE85C0238032807D800F01EFF8B +:101AF00002040204002200E001225A550135043498 +:101B0000072DCFD100230124604D2B70604B61491C +:101B1000621EE3188A5C9B001A7008231A4220D0C8 +:101B200000F0D2FA6301002805D0FA18083050609E +:101B3000594AFA5003E0F8502B7801332B7000F02B +:101B4000C3FA0122A3001343DB00002805D0FA18D2 +:101B500008305060019AFA5003E0F8502B789B1837 +:101B60002B7002210023A2001143C9007B500321E6 +:101B70000A438A400134BB50082CC7D199E0394C44 +:101B800023706370022395E07F22B0880240072A09 +:101B900047D8344C920023706370404BD218022314 +:101BA00012781A4200D185E0217083E07F23B28849 +:101BB0001A40072A35D87388002B32D102203749C2 +:101BC000920052181178814329E07F23B2881A408D +:101BD000072A26D87388002B23D102202F49920090 +:101BE0005218117801431AE07288B1882C4B5C6856 +:101BF000002C16D01888904209D15888884206D106 +:101C0000120A032A01D1237854E01B8952E00C33D5 +:101C1000EDE7244A1168244A1160B178234A117013 +:101C200047E00F221D4B1A7068E0C0468020074035 +:101C3000D306002045060020D8060020902007404B +:101C400000F8FF1FC0060020C0050020C8004000AB +:101C5000D40600208106000002030000210A0000D3 +:101C600021090000CB0600202122000021230000D2 +:101C70002120000028060020C4050020880600203E +:101C800086080020A4060020E0050020CA060020E7 +:101C900030C80110D02E000088004000C02007404E +:101CA000D82E0000BC050020A8080020E80600206F +:101CB00000231C00F5889D4200D91D002E00402DF8 +:101CC00000D9402620003100FFF774FDA419AD1B98 +:101CD00003D12F00402E04D010E04027AF4200D99E +:101CE0002F0020003900FFF765FDE419ED1B01D13D +:101CF000402F03D17E4B1C607E4B1D8001227E4B0A +:101D00004AE07E4E7E4B32889A4211D100237D4AB2 +:101D1000E95CD1540133072BFAD113680735862BC0 +:101D200002D10F22784B1A7000210800FFF742FD04 +:101D3000764B32689A420AD1754B72689A4206D144 +:101D400000212A78734B08001A70FFF733FD724B9D +:101D5000236022E0664F3E68002E13D0654B1C883E +:101D60002500402C00D9402529003000FFF722FD36 +:101D7000641B604BA4B21C80002C01D1402D00D10B +:101D800074193C60A0225D4BD2001988914204D1A5 +:101D900000225A709A78614B1A700122564B1A70C1 +:101DA0006AE008226068013D0838EDB2134037D080 +:101DB00000F0A8F95A49A80042585A4B002A1CD0F2 +:101DC000566846501100585D0831616003280BD8F1 +:101DD00000F0ACFD02040608032104E0022102E049 +:101DE000052100E0042159550821C8230C4200D1E7 +:101DF000403B1288120413433DE0585D032808D885 +:101E000000F094FD3A3A0204002200E001225A5503 +:101E100032E008221440621E944102345C552BE0EB +:101E20002268110C8AB2028000291ED03E494380EC +:101E30004360AB005F583D4E002F01D1585001E088 +:101E4000F15848603A496D00F0506B5AD2186A5206 +:101E500000F03AF9002802D00830606005E0354A09 +:101E6000206013780133137006E008231C4201D16F +:101E7000314B00E0284B23600822304B1A705EE59E +:101E8000012004421ED002231B492B4C0B702C4B0B +:101E90002C4E1A702C4B1C609C602C4C1A61DC6020 +:101EA0000D249A615E602A4B1C70FF23294C23701D +:101EB0001A4C2B702270284A13709F22274B1A70DD +:101EC000087013E063B2002B04DA0D22204B1A7065 +:101ED00080232B700222144204D01E490B78DBB2FF +:101EE0000B702A7010231C4200D02B70F7BDC04627 +:101EF000C0050020C806002094200740C00600202E +:101F000021200000A0080020450600202109000231 +:101F100002000100D7060020C800400098200740BA +:101F200088060020E005002028060020C4050020C7 +:101F300086080020CA060020880040008020074054 +:101F400044060020E805002000F8FF1F4806002096 +:101F5000C0200740882007408C200740842007408D +:101F600010B500F0A7FC002211001B4BD1509818AF +:101F700008324160E82AF9D180201849C0020A6875 +:101F800002430A601A0A1649D2B20A701A0C15499D +:101F9000D2B20A70144A1B0E1370FF23134A00209A +:101FA0001370134A13491370134A137001220A70F5 +:101FB0001249087012490A70124A116899430B00BD +:101FC00070210B43136080220F4B52041A601022C1 +:101FD0000E4B1A7010BDC04600F8FF1F348004403D +:101FE0009C200740B0200740B420074080200740D5 +:101FF0008820074094200740102007400021074018 +:102000008420074018E400E000E100E008210740D8 +:1020100010B500F0F7F810BD10B500F0C3F810BD12 +:1020200010B500F097F810BD10B500F0DBF810BD4A +:1020300010B500F0A1F910BD10B500F077F910BD92 +:1020400010B50800110000F0F7F810BD10B5080039 +:1020500000F062F910BD704770B5174C174B1B7834 +:10206000002B25D00620FFF7DDFB154E022803D9F3 +:10207000013C002C13D115E000F026F8051EF7D026 +:10208000002434700C260F493200083000F01CFE8A +:1020900006202E802900FFF713FC20000AE0337889 +:1020A000002B02D00123337002E000F0B7F9D5E72E +:1020B0000120404270BDC04629320000D3060020F6 +:1020C000D60600209408002070B572B60B4D2C681F +:1020D000200000F0B5FD1D2802DD62B600200CE0F6 +:1020E00080231B06C3409C432C6062B648235843A0 +:1020F000034B181800230360436070BD44020020A6 +:10210000C0F9FF1F10B504000E484821201A00F046 +:1021100021FC1D2814D80C4B1B78002B07D00B4B2F +:102120001B78002B03D02000FFF78EFB08E072B66F +:1021300080231B06C340064A11680B43136062B636 +:1021400010BDC046C0F9FF1FCA060020D3060020FC +:102150004402002070B5114D2B68002B0CD028686C +:1021600043885A1CC3181C7A03889A4212D3FFF77B +:10217000C9FF00232B600EE0094B1B78002B02D116 +:102180000120404208E00320FFF732FB28600028CE +:10219000E5D1F5E74280200070BDC046E40600208E +:1021A000D306002010B50D4C2368002B03D0226805 +:1021B000002A0ED103E00A4B1B78002B02D101202C +:1021C000404209E00320FFF713FB20600028EED116 +:1021D000F5E75388D318187A10BDC046E4060020EE +:1021E000D3060020054B9888054B1A68002A03D0B7 +:1021F000138852889B1AC0187047C0468608002072 +:10220000E406002010B50A4B1B78002B0FD0094CB8 +:102210002068002803D0FFF775FF00232360032008 +:10222000FFF7E6FA002802D0FFF76CFFF7E710BDD2 +:10223000D3060020E4060020314A0123F0B50C004B +:10224000170085B00190029113700393002C53D0B6 +:102250002C4D2B68002B25D12B4E2C4B1B78DBB241 +:10226000002B01D13B7017E00420FFF7DBFA0728B1 +:1022700009D86B461B7B3B70FFF726FF214B18608C +:1022800000280FD13870013E214B002E02D01A7861 +:10229000002A04D001221A70012040422EE000F0F2 +:1022A000BDF8DAE700221A4B29681A7040234A88E1 +:1022B000981A2300844200D90300080008308018CF +:1022C000D2184A800022E41A934204D0019EB65CE0 +:1022D00086540132F8E7019AD31801934B883F2BBB +:1022E00006D9402304200B80FFF7EAFA00232B6075 +:1022F0000522084B1A70A9E702983C7005B0F0BDA2 +:10230000D9060020DC06002009750000D306002055 +:10231000E0060020D806002007B56B46D8710733C9 +:1023200001211800FFF788FF0EBDFFFF012370B5E4 +:102330000E4C0F4D23702B68002B10D10D4B1B78CA +:10234000002B02D1002020700FE00420FFF76AFA72 +:102350000728F7D8FFF7B8FE28600028F2D02B68CE +:1023600058884023181A0023237070BDD906002016 +:10237000DC060020D3060020F7B5124B1B78002B9B +:102380001FD00126104F114B3C681E700193104D59 +:10239000002C09D000262E706388210023800420A1 +:1023A000FFF78EFA3E6009E0FFF78EFE011E04D0B3 +:1023B00004202C70FFF784FA00E02E700023019AAD +:1023C0001370F7BDD3060020DC060020D9060020DC +:1023D000D806002070B50E4B1C78E4B2002C15D145 +:1023E0000C4D2968002906D04B8804200B80FFF78C +:1023F00067FA2C600AE0FFF767FE011E03D0042095 +:10240000FFF75EFA02E00122034B1A7070BDC0466E +:10241000D9060020DC060020D806002070B5234C29 +:102420002378002B40D101332370FFF7DBFE002817 +:1024300001D0FFF710FE00F0E3F8002801D000F013 +:10244000DBF800F049F9002801D000F0D6F800F0E0 +:10245000AFF9002801D000F0D1F800232370EFF38A +:102460000583002B20D1EFF3108272B6104D28683F +:10247000002803D00F4C2178002903D0002A13D163 +:1024800062B611E00123237043692B60002B01D059 +:10249000996101E008490B60002A00D162B600256D +:1024A000836845779847257070BDC046E9060020CF +:1024B000FC06002000070020F8060020EFF3108043 +:1024C00072B643425841C0B27047002800D062B68D +:1024D0007047FFFF10B5FFF7F1FF0C4A1468002C9E +:1024E00010D063691360002B02D000229A6101E0D2 +:1024F000074A1360FFF7E9FF002320006377A36812 +:102500009847E8E7FFF7E1FF10BDC046EC06002062 +:10251000F006002010B5FFF7DDFF10BD164930B5FD +:102520000B68002B02D18360C36009E004681A685D +:10253000944207D20025121B8360C560D8601A60E0 +:10254000086016E002681C689968121B0260002986 +:102550000CD00C680B00A242F4D28160C968A21AA8 +:10256000C160D860C16888601A6002E08160C360A1 +:1025700098600223037530BDF406002070B5174D36 +:102580002C68002C19D02368002B02D0013B23605B +:1025900013E0A268002A00D0D360002120692A60DD +:1025A000036821751B68220098476368002BE7D0F9 +:1025B00023602000FFF7B2FFE2E7FFF77FFF002272 +:1025C000074B1C681A60FFF780FF002C05D0A56838 +:1025D0002000FFF7A3FF2C00F7E770BDF4060020F2 +:1025E0000407002010B5034A136801331360FFF796 +:1025F000C5FF10BDBC050020704770477047FFFF46 +:10260000054B1878054BC0B21A78D3B2984200D265 +:102610004030C01A7047C0468B07002009070020D1 +:1026200030B5234A13799B0611D5224BD4791878FB +:1026300019000130E4B200233F2800DC03001E48EB +:102640000078834203D01D48C454DBB20B70D178AC +:102650001748C9B24BB2002B17DA03795BB2002BD3 +:1026600013DA174B174C1D782378DBB29D4202D149 +:102670006C23C37009E001333F2B00D900231248BB +:10268000C05CDBB2C0B2D0712370402319420ED0BF +:10269000117919420BD000210C4B19700C4B1B689F +:1026A0008B4202D00B49097819722C23D37030BDAC +:1026B00000A006408B070020090700204A070020E1 +:1026C000940700208C0700200A070020080700203C +:1026D000900700208A070020054B1878054BC0B2F0 +:1026E0001A78D3B2984200D24030C01A7047C04620 +:1026F000000800209607002030B5234A13799B0676 +:1027000011D5224BD479187819000130E4B2002396 +:102710003F2800DC03001E480078834203D01D4898 +:10272000C454DBB20B70D1781748C9B24BB2002B3E +:1027300017DA03795BB2002B13DA174B174C1D78AD +:102740002378DBB29D4202D16C23C37009E00133D0 +:10275000272B00D900231248C05CDBB2C0B2D07175 +:102760002370402319420ED0117919420BD0002159 +:102770000C4B19700C4B1B688B4202D00B4909782B +:1027800019722C23D37030BD00B006400008002021 +:1027900096070020BF07002008080020010800203D +:1027A000970700209507002004080020FF0700205D +:1027B000054B1878054BC0B21A78D3B2984200D2B4 +:1027C0004030C01A7047C0467C0800201108002025 +:1027D00030B52C4A13799B0623D52B4BD479187826 +:1027E00019000130E4B200233F2800DC0300274831 +:1027F0000078834203D02648C454D8B208702549D3 +:10280000086800280DD024490978C9B28B4201D349 +:102810005B1A01E05B1A4033272B02DD1F4B1B784C +:102820000371D1781748C9B24BB2002B17DA03797C +:102830005BB2002B13DA1A4B174C1D782378DBB2EE +:102840009D4202D16C23C37009E00133272B00D9CC +:1028500000231448C05CDBB2C0B2D07123704023A7 +:1028600019420ED0117919420BD000210E4B19706C +:102870000E4B1B688B4202D00D49097819722C232C +:10288000D37030BD00C006407C0800201108002035 +:102890003B0800200C0800207D0800203A0800209A +:1028A0008408002012080020100800208008002062 +:1028B0007B08002030B585B072B670224121154BDF +:1028C0001A70154A1170154A3239117080221A7027 +:1028D0001A7852B2002AFBDA114B186862B6114B13 +:1028E000984201D80A23584301AC0A222100FEF77E +:1028F0004BFE00230C48E15C0500002905D05A007E +:102900008218013351800A2BF5D101335B002B7003 +:1029100005B030BD00000240070002400600024042 +:10292000080002407F9698007404002002B47146AB +:1029300049084900095C49008E4402BC7047C04602 +:1029400003B47146490840004900095A49008E44C1 +:1029500003BC7047002243088B4274D303098B42A7 +:102960005FD3030A8B4244D3030B8B4228D3030C5F +:102970008B420DD3FF22090212BA030C8B4202D301 +:102980001212090265D0030B8B4219D300E0090A29 +:10299000C30B8B4201D3CB03C01A5241830B8B4232 +:1029A00001D38B03C01A5241430B8B4201D34B031B +:1029B000C01A5241030B8B4201D30B03C01A524180 +:1029C000C30A8B4201D3CB02C01A5241830A8B4205 +:1029D00001D38B02C01A5241430A8B4201D34B02EE +:1029E000C01A5241030A8B4201D30B02C01A524152 +:1029F000CDD2C3098B4201D3CB01C01A5241830906 +:102A00008B4201D38B01C01A524143098B4201D33F +:102A10004B01C01A524103098B4201D30B01C01A6A +:102A20005241C3088B4201D3CB00C01A52418308E4 +:102A30008B4201D38B00C01A524143088B4201D311 +:102A40004B00C01A5241411A00D201465241104671 +:102A50007047FFE701B5002000F0F0F802BDC04666 +:102A60000029F7D076E7704703460B437FD4002256 +:102A700043088B4274D303098B425FD3030A8B4212 +:102A800044D3030B8B4228D3030C8B420DD3FF227C +:102A9000090212BA030C8B4202D31212090265D04A +:102AA000030B8B4219D300E0090AC30B8B4201D3FD +:102AB000CB03C01A5241830B8B4201D38B03C01A44 +:102AC0005241430B8B4201D34B03C01A5241030BBB +:102AD0008B4201D30B03C01A5241C30A8B4201D36C +:102AE000CB02C01A5241830A8B4201D38B02C01A17 +:102AF0005241430A8B4201D34B02C01A5241030A8E +:102B00008B4201D30B02C01A5241CDD2C3098B4272 +:102B100001D3CB01C01A524183098B4201D38B01EF +:102B2000C01A524143098B4201D34B01C01A524192 +:102B300003098B4201D30B01C01A5241C3088B42D7 +:102B400001D3CB00C01A524183088B4201D38B00C2 +:102B5000C01A524143088B4201D34B00C01A524164 +:102B6000411A00D201465241104670475DE0CA0F3B +:102B700000D04942031000D34042534000229C46FB +:102B800003098B422DD3030A8B4212D3FC22890105 +:102B900012BA030A8B420CD3890192118B4208D3DB +:102BA000890192118B4204D389013AD0921100E03D +:102BB0008909C3098B4201D3CB01C01A5241830951 +:102BC0008B4201D38B01C01A524143098B4201D37E +:102BD0004B01C01A524103098B4201D30B01C01AA9 +:102BE0005241C3088B4201D3CB00C01A5241830823 +:102BF0008B4201D38B00C01A5241D9D243088B4279 +:102C000001D34B00C01A5241411A00D2014663461B +:102C100052415B10104601D34042002B00D549427F +:102C2000704763465B1000D3404201B5002000F0BE +:102C300005F802BD0029F8D016E770477047C04676 +:102C40001C2101231B04984201D3000C10391B0ADC +:102C5000984201D3000A08391B09984201D30009A0 +:102C6000043902A2105C40187047C04604030202F7 +:102C70000101010100000000000000000E4B70B5D2 +:102C800000251E000D4CE41AA410A54204D0AB0090 +:102C9000F35898470135F8E700F078F9084B00251C +:102CA0001E00084CE41AA410A54204D0AB00F3584F +:102CB00098470135F8E770BD982F0000982F000065 +:102CC000982F0000A42F0000002310B59A4203D0D3 +:102CD000CC5CC4540133F9E710BD0023C25C01335E +:102CE000002AFBD1581E70470000000000000000C1 +:102CF0000B100000B90F00009D04000075040000D7 +:102D00007104000095040000E10E000001030002C0 +:102D10000413141512080706050C0B0A0954203D6C +:102D20002000207C2048203D2000207C2056203D93 +:102D30002000207C2050494E20000D0A050E080975 +:102D40000D0C06070F0B004417FF050E08090D0CAC +:102D500006070F0B004417FFFFFFFFFFFFFFFFFFFA +:102D6000FFFF1A1B420000F840A0044001000000D1 +:102D7000420000F844A0044002000000C00000F837 +:102D800000C0044001000000000000F8049004406E +:102D900002000000000000F8089004400400000059 +:102DA000C00000F81CC0044080000000C00000F813 +:102DB00010C0044010000000C00000F808C004402B +:102DC00004000000C00000F80CC00440080000002F +:102DD000800000F80CB0044008000000800000F8FB +:102DE00010B0044010000000800000F818B004404B +:102DF00040000000800000F81CB00440800000008B +:102E0000800000F814B0044020000000C00000F86A +:102E100004C0044002000000800000F800B004403C +:102E200001000000400000F800A004400100000084 +:102E3000400000F804A0044002000000400000F838 +:102E40000CA0044008000000400000F808A0044066 +:102E500004000000C00000F814C00440200000007E +:102E6000C00000F818C0044040000000800000F8D6 +:102E700004B0044002000000800000F808B00440E4 +:102E800004000000020100F850D0044010000000CF +:102E9000020100F854D0044020000000030100F8B3 +:102EA00078D0044040000000000000000000000056 +:102EB0004D2000004120000039200000312000009A +:102EC000292000002120000019200000112000000E +:102ED00015151915151515FF000100009C0200209D +:102EE0001200000000020000C6020020AF00000037 +:102EF00000220200750300203F00000000210200B4 +:102F00001A030020090000000022030048020020EC +:102F100054000000002103003303002009000000DA +:102F200000220400B403002055000000002104002A +:102F30004C030020090000000022050009040020C5 +:102F400028000000002105006503002009000000A2 +:102F500000030000700400200000000001030904C9 +:102F6000AE02002000000000020309043204002029 +:102F700000000000030309047404002000000000A6 +:102F8000000000000000000000000000F8B5C0468E +:102F9000F8BC08BC9E467047390400005D10000074 +:042FA000C911000053 +:042FA400F8B5C04676 +:102FA800010A0400B02E000000000000E803000041 +:102FB80000000000FFFFFFFF05010902A1018501D4 +:102FC80005091901290815002501950875018102CF +:102FD80005010930093109381581257F75089503E0 +:102FE8008106050C0A38021581257F75089501812F +:102FF80006C005010902A101850205010930093150 +:10300800150026FF7F751095028102C0120110017C +:10301800EF020140C016870473020102030118037E +:103028005400650065006E00730079006400750047 +:1030380069006E006F000902AF00060100C0320887 +:103048000B0002020201040904000001020201004F +:1030580005240010010524010101042402060524A9 +:103068000600010705820310004009040100020A56 +:103078000000000705030240000007058402400025 +:1030880000090402000103010100092111010001E6 +:10309800223F00070581030800010904030001031A +:1030A80000000009211101000122540007058503D1 +:1030B80008000209040400010300000009211101AD +:1030C8000001225500070586030C000109040500CC +:1030D8000103000000092111010001222800070551 +:1030E800870308000405010906A101750195080573 +:1030F8000719E029E7150025018102950175088166 +:1031080003950575010508190129059102950175B1 +:10311800039103950675081500257F0507190029F1 +:103128007F8100C005010904A10115002501750171 +:103138009520050919012920810215002507350068 +:10314800463B01750495016514050109398142055D +:10315800010901A100150026FF03750A950409302D +:103168000931093209358102C0150026FF03750AA5 +:103178009502093609368102C0050C0901A10175BD +:103188000A950419002A9C021500269C0281000554 +:103198000175089503190029B7150026B7008100A5 +:1031A800C0003E03530065007200690061006C00B6 +:1031B8002F004B006500790062006F00610072000B +:1031C80064002F004D006F007500730065002F002C +:1031D8004A006F00790073007400690063006B0097 +:1031E800040309040C0300000000000000000000B4 +:0C31F800000000000000000000000000CB +:00000001FF diff --git a/firmware/throttle-teensy/throttle-teensy.ino b/firmware/throttle-teensy/throttle-teensy.ino new file mode 100644 index 0000000..54dace1 --- /dev/null +++ b/firmware/throttle-teensy/throttle-teensy.ino @@ -0,0 +1,213 @@ +/* Throttle unit USB adapter for Teensy LC + + The Throttle unit has keyboard diode matrix for buttons, hats and mode switches. +*/ +#define BOUNCE_WITH_PROMPT_DETECTION +#include +#include + +// Modes +const byte MODE_STARTUP = 0; +const byte MODE_NORMAL = 1; +const byte MODE_LOCKED = 2; +const byte MODE_ALT = 4; + +byte mode = MODE_NORMAL; +byte throttleCentered = 0; +int axisThrottle = 0; +int axisHor = 0; +int axisVer = 0; + +// Led drives + +#define NUM_LEDS 1 +CRGB leds[NUM_LEDS]; +const byte pinLed = 24; + +// Mapping pins to buttons and axis + +const byte pinHatButton = 18; +const byte pinHatA = 8; +const byte pinHatB = 7; +const byte pinHatC = 6; +const byte pinHatD = 5; +const byte pinHatE = 12; +const byte pinHatF = 11; +const byte pinHatG = 10; +const byte pinHatH = 9; + +const byte pinUp = 1; +const byte pinDown = 3; +const byte pinForward = 0; +const byte pinBack = 2; +const byte pinExtra = 4; +const byte pinLock = 19; +const byte pinB1 = 20; +const byte pinB2 = 21; + +const byte analogPinThrottle = 2; +const byte analogPinHor = 1; +const byte analogPinVer = 0; +const byte analogPinSlider = 3; + +const byte numButtons = 17; +const byte pins[numButtons] = { pinUp, pinDown, pinForward, pinBack, pinExtra, pinLock, pinB1, pinB2, pinHatButton, pinHatA, pinHatB, pinHatC, pinHatD, pinHatE, pinHatF, pinHatG, pinHatH }; + +byte buttonPressed[32]; +Bounce buttons[numButtons]; + +const byte debounce_interval_ms = 5; + +void setup() +{ + Serial.begin(9600); + Joystick.useManualSend(true); + + for (int i=0; i(leds, NUM_LEDS); +} + +byte previousMode = MODE_STARTUP; +byte previousThrottleCentered = 0; + +void setLeds() +{ + if (mode != previousMode || throttleCentered != previousThrottleCentered) + { + if (mode == MODE_LOCKED) + leds[0] = CRGB (255, 0, 0); // red + else if (mode == MODE_ALT) + leds[0] = CRGB (0, 0, 255); // blue + else + { + if (throttleCentered == 1) + leds[0] = CRGB (0, 255, 0); // green + else if (throttleCentered == 2) + leds[0] = CRGB (255, 255, 0); // yellow + else + leds[0] = CRGB (255, 102, 0); // orange + } + + previousMode = mode; + previousThrottleCentered = throttleCentered; + FastLED.show(); + } +} + +const long printCountMax = 1000; +long printCount = 0; + +void doModeAlt() +{ + if (printCount <= 0) + { + Serial.print("T = "); + Serial.print(axisThrottle); + Serial.print(" | H = "); + Serial.print(axisHor); + Serial.print(" | V = "); + Serial.print(axisVer); + for (int i=0; i 506 and axisThrottle < 516) + throttleCentered = 1; + else if (axisThrottle > 471 and axisThrottle < 551) + throttleCentered = 2; + else + throttleCentered = 0; + } + + setLeds(); + + if (mode == MODE_ALT) + doModeAlt(); + + delay(1); +} diff --git a/img/full-photo.png b/img/full-photo.png new file mode 100644 index 0000000..7f12c1c Binary files /dev/null and b/img/full-photo.png differ diff --git a/img/full-preview.png b/img/full-preview.png new file mode 100644 index 0000000..573642c Binary files /dev/null and b/img/full-preview.png differ diff --git a/img/throttle-circuit-tinycad.png b/img/throttle-circuit-tinycad.png new file mode 100644 index 0000000..fd94a9e Binary files /dev/null and b/img/throttle-circuit-tinycad.png differ diff --git a/knobs.scad b/knobs.scad new file mode 100644 index 0000000..2693a92 --- /dev/null +++ b/knobs.scad @@ -0,0 +1,181 @@ +include ; + +$fn = $preview ? 40 : 100; + +// Recommend 0.20mm layer on 0.4mm nozzle +module hatKnob() +{ + width = 9; + step_multiplier = 3.5; + difference() + { + union() + { + for (z = [0.6:0.2:1.6]) + { + s = width - z*step_multiplier; + minkowski() + { + translate([-s/2, -s/2,z-0.6]) + cube ([s, s, z]); + cylinder(d=6-z*2,h=0.01); + } + } + + translate([0,0,-4.8]) + cylinder (d1=3.7, d2=5,h=4.8); + } + + difference() + { + translate([0,0,-4.8 - 0.01]) + cylinder (d=2.5,h=4.8); + + rotate([0,0,45]) + translate([1.1-0.6,-1.5,-3.6]) + cube([3,3,3.6]); + } + } + +} + +// Kobs and knob holes (D/T) +module PotKnobD(d1, d2, length, skirt=5, detent = false, testDetent = false) +{ + shaftD = 6.4; + difference() + { + union() + { + // Pot shaft holder + difference() + { + translate([0,0,0]) + cylinder (d1=d2, d2=d2, h=length); + + translate([0,0,-0.01]) + cylinder (d=shaftD, h=length+0.01); + } + + // Outer shell + difference () + { + cylinder (d1=d1, d2=d2, h=length); + translate([0,0,-1]) + cylinder (d1=d1-1.5, d2=d2-1.5, h=length-2); + } + + // Skirt and detent + translate([0,0,-skirt]) + { + tube (d1/2, skirt, 1); + + if (detent == true) + { + translate([0,d1/2-1,0]) + cylinder(d=1.5, h=skirt); + } + } + } + + if (testDetent == true) + { + translate([0,0,1]) + cylinder (d=d1, h=length); + } + } + if (testDetent == true) + { + translate([0,0,0]) + tubeD(d1, shaftD, 1); + } +} + + +module PotDetentSpring(dShaft= 9.5, dBolt = 14.5, length = 6, contactLength = 2, rim=20) +{ + dOut = rim - 2; + washerThickness = 0.4; + + // Spring shaft + translate([0,0,-length - contactLength - 0.2]) + difference() + { + intersection() + { + translate([0,dOut/3,0]) + tubeD(dOut/3-0.5, dOut/3 - 3, contactLength); + rotate([0,0,45]) + translate([4,4,0]) + cube([rim,rim,contactLength]); + } + translate([0, rim/2 - 1.0, -0.01]) + cylinder(d=1.7, h=contactLength+0.02); + } + + // Mounting washer with surface ripples for better grip + translate([0,0,-length - contactLength - 0.5]) + difference() + { + tubeD(dOut-0.6, dShaft, washerThickness); + + *for (a = [100:15:440]) + { + translate([0,0,-1.2]) + rotate([45,0,a+7]) + cube([dBolt/2+2,1,1]); + translate([0,0,0.5]) + rotate([45,0,a]) + cube([dBolt/2+2,1,1]); + } + } +} + +// Kobs and knob holes (D/T) +module PotKnobBristles(d1, d2, length, skirt=5) +{ + shaftD = 6; + difference() + { + union() + { + // Pot shaft holder + difference() + { + translate([0,0,0]) + cylinder (d=shaftD + 4, h=length); + + translate([0,0,-0.01]) + cylinder (d=shaftD, h=length+0.01); + } + + // Outer shell + difference () + { + cylinder (d1=d1, d2=d2, h=length); + translate([0,0,-1]) + cylinder (d1=d1-1.5, d2=d2-1.5, h=length-2); + } + + // Skirt + translate([0,0,-skirt]) + { + tube (d1/2, skirt, 1); + } + + // T-slot + translate([0,0,length-4]) + cube([shaftD, 0.8, 2], center=true); + + for (a = [20:20:360]) + { + rotate([0,-3,a]) + rotate([90,16,0]) + translate([-5.3,1.3,0]) + rotate_extrude(angle=46,convexity = 10) + translate([15.9, 0, 0]) + circle(r = 0.6); + } + } + } +} diff --git a/printed-parts.scad b/printed-parts.scad new file mode 100644 index 0000000..a9f625a --- /dev/null +++ b/printed-parts.scad @@ -0,0 +1,68 @@ +include ; +include ; +include ; +include ; + +drawOtherParts = false; +separateHandle = true; + +handleSeparation = separateHandle ? 15 : 0; + +handle_radius = 30; +shaft_intrusion = caseThickness + 1; +shaft_length = 35; + +// Lay out all printable parts on the same level + +// Parts inside +moveZ(base_height) flipY() BasePrinted(); +moveY(50) +{ + moveZ(main_throttle_lever_height) flipY() MainThrottleLever(); + moveX(150) + { + moveZ(backstop_offset_z) turnY(90) Backstop(); + moveX(30) + { + turnY(-90) ThrottleAxisHolder(); + moveX(20) + { + turnY(-90) ThrottleAxisHolder(); + moveX(20) + { + ShaftAndBase(shaft_length, shaft_intrusion); + moveX(20) + BallSpringPlunger(); // center detent actuator + } + } + } + } +} + +// Case and handle +moveY(100) +{ + moveX(60) + { + moveZ(handle_case_print_offset_z) handleCase(); + moveY(100) + { + moveZ(thumb_print_offset_z) + turnX(thumbPlateAngle.x) turnY(180 - thumbPlateAngle.y) + handleThumbPart(); + moveX(100) + { + flipY() + moveY(200) + moveZ(-2*case_cover_height) + { + turnZ(-45) CaseCover(left=false, right=true); + moveX(220) + { + CaseCover(left=true, right=false); + } + } + } + } + } +} diff --git a/special-parts.scad b/special-parts.scad new file mode 100644 index 0000000..66a020d --- /dev/null +++ b/special-parts.scad @@ -0,0 +1,175 @@ +include ; + +module PsButton() +{ + color("black") + { + translate ([0,0,-1.5]) + cylinder (d=10.4, h=10.5); + translate ([-7,-1.5,-1.5]) + cube([14,3,1.5]); + translate ([0,0,-6.5]) + cylinder (d=5, h=5); + } +} + +module PsButtonHole() +{ + translate ([0,0,-1]) + cylinder (d=10.9, h=12); +} + + +module HatSwitchX45() +{ + color("lightgrey") + translate([0,0,-9.5]) + { + difference() + { + translate([-17.5/2,-17.5/2,0]) + cube([17.5, 17.5, 9.5]); + translate([-7/2,-7/2,0.1]) + cube([7, 7, 9.5]); + translate([-5.5,-5.5,6]) + cylinder(d=1.4, h=4); + translate([-5.5,5.5,6]) + cylinder(d=1.4, h=4); + translate([5.5,-5.5,6]) + cylinder(d=1.4, h=4); + translate([5.5,5.5,6]) + cylinder(d=1.4, h=4); + translate([0,0,-0.5]) + tube(25/2, 11, 3); + } + translate([0,0,-8]) + cylinder(d=9, h=8); + } +} + +module HatSwitchX45Holes(screws=true) +{ + // Main shaft hole + cylinder (d=6.5, h=handle_thickness*6); + + if (screws == true) + { + // Screw hole 1 + translate ([-5.5,-5.5,-handle_thickness*3]) + cylinder (d=2, h=handle_thickness*6); + translate ([-5.5,-5.5,1.4]) + cylinder (d=4, h=1); + + // Screw hole 2 + translate ([5.5,5.5,-handle_thickness*3]) + cylinder (d=2, h=handle_thickness*6); + translate ([5.5,5.5,1.4]) + cylinder (d=4, h=1); + + // Screw hole 3 + translate ([5.5,-5.5,-handle_thickness*3]) + cylinder (d=2, h=handle_thickness*6); + translate ([5.5,-5.5,1.4]) + cylinder (d=4, h=1); + } +} + + +module BallSpringPlunger(ball_diameter = 9.5, spring_diameter = 4.5, spring_length = 19, ball_extrusion_ratio = 0.20, springPart=true, ballPart=true) +{ + thickness = 1; + gap = 0.5; + ball_out = ball_extrusion_ratio * ball_diameter; + ball_high = ball_diameter*(1-ball_extrusion_ratio) + gap; + ball_case_inner_top = ball_diameter - thickness + gap; + ball_case_inner_d = ball_diameter + gap; + ball_case_outer_d = ball_case_inner_d + thickness; + spring_case_inner_d = spring_diameter + gap; + spring_case_outer_d = spring_case_inner_d + thickness; + screw_d = 2; + + %translate([0, 0, ball_out]) + { + // spring + translate([0, 0, ball_diameter/2]) + spring(d=spring_diameter, l=spring_length); + // ball + color("silver") sphere(d=ball_diameter); + } + + if (springPart == true) + { + spring_tube_length = spring_length - ball_out - gap; + translate([0, 0, ball_case_inner_top]) + { + // spring tube + tubeD(spring_case_outer_d, spring_case_inner_d, spring_tube_length); + + // top ring + translate([0, 0, spring_tube_length]) tubeD(spring_case_outer_d, 2*gap, thickness); + // ring + tubeD(ball_case_outer_d, spring_case_outer_d, thickness); + + // connector plate + difference() + { + translate([0, 0, thickness-thickness/2]) + cube([ball_case_outer_d, 2*ball_case_outer_d, thickness], center = true); + translate([0,0,-0.1]) + { + cylinder(d=spring_case_inner_d,h=thickness+0.2); + + translate([0,ball_case_outer_d- screw_d,0]) + cylinder(d=screw_d,h=thickness+0.2); + translate([0,-(ball_case_outer_d- screw_d),0]) + cylinder(d=screw_d,h=thickness+0.2); + } + } + + // fixture plate + fixture_plate_height = ball_case_outer_d; + translate([ball_case_outer_d/2, 0, fixture_plate_height/2]) + { + difference() + { + cube([thickness, 2*ball_case_outer_d, ball_case_outer_d], center = true); + + translate([-thickness/2-0.1,ball_case_outer_d- screw_d,0]) + rotate([0,90,0]) + cylinder(d=screw_d,h=thickness+0.2); + translate([-thickness/2-0.1,-(ball_case_outer_d- screw_d),0]) + rotate([0,90,0]) + cylinder(d=screw_d,h=thickness+0.2); + } + } + } + } + + if (ballPart == true) + { + // ball tube + tubeD(ball_case_outer_d, ball_case_inner_d, ball_case_inner_top); + difference() + { + tubeD(ball_case_outer_d, 0, thickness); + translate([0, 0, ball_out]) sphere(d=ball_diameter + gap); + } + + // connector plate + translate([0,0,ball_case_inner_top]) + difference() + { + translate([0,0,-thickness/2]) + cube([ball_case_outer_d, 2*ball_case_outer_d, thickness], center = true); + translate([0,0,-thickness-0.1]) + { + cylinder(d=ball_case_inner_d,h=thickness+0.2); + + translate([0,ball_case_outer_d- screw_d,0]) + cylinder(d=screw_d,h=thickness+0.2); + translate([0,-(ball_case_outer_d- screw_d),0]) + cylinder(d=screw_d,h=thickness+0.2); + } + } + } +} diff --git a/throttle-base.scad b/throttle-base.scad new file mode 100644 index 0000000..86d06f3 --- /dev/null +++ b/throttle-base.scad @@ -0,0 +1,215 @@ +// Throttle handle base that connect the handle to the rails and case bottom + +// Parameters + +bearing_margin_side = 3; +bearing_margin_frontback = 2; +bearing_offset = 60.5 / 2 - 7.5; +bearing_height = 15; +bearing_length = 45; +bearing_radius = 7.5; +bearing_case_margin = 0.17; +bearing_case_radius = bearing_radius + bearing_case_margin; +bearing_case_length = 45 + 2 * bearing_case_margin; + +base_top_height = 5; +base_width = 60.5 + 2 * bearing_margin_side; +base_length = 45 + 2 * bearing_margin_frontback; +base_height = bearing_height + base_top_height; + +shaft_outer_radius = 10; + +// Bearing casing for housing standard linear bearings like LM8LUU + +module BearingCase() +{ + // Bearing case + translate ([-bearing_case_margin,0,bearing_case_radius]) + rotate (a=90, v=[0,1,0]) + cylinder (r1=bearing_case_radius, r2=bearing_case_radius, + h=bearing_case_length); + + // Bearing insert box + translate ([-bearing_case_margin,-bearing_case_radius,-2]) + cube ([bearing_case_length, 2 * bearing_case_radius, bearing_case_radius + 2]); + + // Rod cavity + translate ([-1 - bearing_margin_frontback, 0, bearing_radius]) + rotate (a=90, v=[0,1,0]) + cylinder (r1=5, r2=5, h=base_length + 2); + + // Rod insert box + translate ([-1 - bearing_margin_frontback,-5 , 0]) + cube ([base_length + 2, 10, bearing_radius]); +} + +// The printed part of the base + +module BasePrinted() +{ + // Base + difference () + { + // Main base box + translate ([-base_length/2, -base_width/2,0]) + cube([base_length, + base_width, + base_height]); + + // Left bearing case + translate ([-base_length/2 + bearing_margin_frontback, bearing_offset,0]) BearingCase(); + + // Right bearing case + translate ([-base_length/2 + bearing_margin_frontback, -bearing_offset,0]) BearingCase(); + + // Center cavity + translate ([-1-base_length/2, -12.5,-1]) + cube([base_length + 2, + 25, + 16]); + + // Cable hole and its edge softening + cylinder (r1=7.7, r2=7.7, h=26); + translate ([0,0,base_top_height + 3]) + cylinder(r1=10,r2=7, h=13); + translate ([0,0,base_top_height + 6]) + cylinder(r1=12,r2=7, h=7); + + // Handle mounting holes (4 x 4mm bolts with washers) + translate ([18,8,0]) cylinder (r1=2, r2=2, h=bearing_height + base_top_height + 1); + translate ([18,-8,0]) cylinder (r1=2, r2=2, h=bearing_height + base_top_height + 1); + translate ([-18,8,0]) cylinder (r1=2, r2=2, h=bearing_height + base_top_height + 1); + translate ([-18,-8,0]) cylinder (r1=2, r2=2, h=bearing_height + base_top_height + 1); + + // Lever nut and bolt grooves + translate ([-20,27,15]) cube ([15,7,3.2]); + translate ([0,27,15]) cube ([15,7,3.2]); + translate ([-20,29,13.8]) cube ([15,2.5,10]); + translate ([0,29,13.8]) cube ([15,2.5,10]); + + // Zip tie holes + ziptie_thickness = 1.7; + ziptie_width = 3.2; + translate ([18.5,12,2.5]) cube ([ziptie_width, 22, ziptie_thickness]); + translate ([-21.5,12,2.5]) cube ([ziptie_width, 22, ziptie_thickness]); + translate ([18.5,-34,2.5]) cube ([ziptie_width, 22, ziptie_thickness]); + translate ([-21.5,-34,2.5]) cube ([ziptie_width, 22, ziptie_thickness]); + } +} + +main_throttle_lever_height = 8; +module MainThrottleLever() +{ + // Lever base + difference () + { + translate ([-2,0,-2]) cube ([32, 2, 2 + main_throttle_lever_height]); + + // Screw holes + translate ([3,3,4.5]) + rotate (a=90, v=[1,0,0]) + cylinder (r1=1.6, r2=1.6, h=5); + + translate ([23,3,4.5]) + rotate (a=90, v=[1,0,0]) + cylinder (r1=1.6, r2=1.6, h=5); + } + // Lever + lever_slot_distance = 18; + translate([-1.5,0,0]) + { + translate ([8,0,5]) cube ([6, lever_slot_distance - 3, 3]); + difference () + { + translate ([5,lever_slot_distance-3,5]) cube ([12, 6, 3]); + translate ([7.1,lever_slot_distance-1.4,3]) cube ([7.8, 2.5, 6]); + } + } +} + +// -------------------------- +// Handle shaft and its base +// -------------------------- + +module ShaftAndBase(length, intrusion, handle_base_height = 5, handle_base_width = 40) +{ + difference () + { + union () + { + // Base box + translate ([-base_length/2, -handle_base_width/2,0]) + cube([base_length, + handle_base_width, + handle_base_height]); + + // ???? TODO: Round the cable exit side of the base box + + // Shaft tubes + tube (shaft_outer_radius-2, length, 1); + tube (shaft_outer_radius, length, 1); + + // Shaft supports + translate ([-1.5,9,0]) + rotate ([90,0,0]) + cube ([3,length, 18]); + translate ([-9,-1.5,0]) + rotate ([90,0,90]) + cube ([3,length, 18]); + + // Shaft base support cone + translate([0,0,handle_base_height]) + difference () + { + cylinder (d1=30, d2=20, h=8); + } + + // Handle connector cone + translate ([0,0,length-intrusion-7-7.3]) + cylinder (d1=20, d2=30, h=7); + } + + // Handle mounting holes (4 x 4mm bolts with washers) + translate ([18,8,0]) hole (4, handle_base_height); + translate ([18,-8,0]) hole (4, handle_base_height); + translate ([-18,8,0]) hole (4, handle_base_height); + translate ([-18,-8,0]) hole (4, handle_base_height); + + // Central cable hole + hole(14, length); + + // Side cable exit hole + translate ([0,0,16]) rotate([115,0,0]) hole(6, 18); + } +} + +// -------------------------- +// Throttle axis holders +// -------------------------- + +module ThrottleAxisHolder() +{ + difference() + { + translate ([-3,3.5,12]) + cube ([18,8,2]); + translate([15-2-2.5,3.5+4,1]) + cylinder(d=3.5,h=20); + } + difference(convexity=10) + { + translate([-3,-1.5,0]) + sheet_cone(x=7, y1=18, y2=8, h=12); + + translate([0.5,14,3]) + cylinder(d=5,h=20); + translate([0.5,14,-3]) + cylinder(d=2.5,h=20); + + translate([0.5,1,3]) + cylinder(d=5,h=20); + translate([0.5,1,-3]) + cylinder(d=2.5,h=20); + } +} + diff --git a/throttle-case.scad b/throttle-case.scad new file mode 100644 index 0000000..4fff781 --- /dev/null +++ b/throttle-case.scad @@ -0,0 +1,309 @@ +// Throttle case + +include ; + +// Case parameters + +case_bottom = 9.5; +case_thickness = 3; +case_depth = 24; // outer measurement +case_outside = 175.5; +case_inside = case_outside - 2 * case_thickness; + +case_cover_support_offset = [(case_outside/2 - 10.7 + 3),(case_outside/2 - 13.2 + 3)]; +case_cover_supports = [ + [-case_cover_support_offset.x, -case_cover_support_offset.y], + [-case_cover_support_offset.x, case_cover_support_offset.y], + [case_cover_support_offset.x, -case_cover_support_offset.y], + [case_cover_support_offset.x, case_cover_support_offset.y] + ]; +case_cover_support_height = 31.5; + +// Case cover parameters + +case_cover_height = 23; +grooveClearance = 2; +grooveDiameter = 20 + grooveClearance; +grooveLength = 100 + grooveClearance; +grooveOffset = -(36-14)/2; + + +module CaseBottom() +{ + module CaseNut() + { + difference () + { + cylinder (r1=2.5, r2=2.5, h=6); + cylinder (r1=1.5, r2=1.5, h=7); + } + } + + translate ([-case_outside/2,-case_outside/2,0]) difference () + { + cube ([case_outside, case_outside, case_depth]); + translate ([case_thickness, case_thickness, case_thickness]) cube ([case_outside-2*case_thickness, case_outside-2*case_thickness, case_depth]); + } + + // Main slider nuts + translate ([65.5/2,case_inside/2 - 15,case_thickness]) CaseNut(); + translate ([-65.5/2,case_inside/2 - 15,case_thickness]) CaseNut(); + + // Backstop nuts + translate ([case_inside/2 - 15,32,case_thickness]) CaseNut(); + translate ([case_inside/2 - 15,-32,case_thickness]) CaseNut(); + + // Legs + translate ([case_inside/2 - 30 + 11.5,case_inside/2-44+11.5,case_thickness]) cylinder (r1=11.5, r2=11.5, h=9); + translate ([-case_inside/2 + 30 - 11.5,case_inside/2-44+11.5,case_thickness]) cylinder (r1=11.5, r2=11.5, h=9); + translate ([case_inside/2 - 30 + 11.5,-case_inside/2+44-11.5,case_thickness]) cylinder (r1=11.5, r2=11.5, h=9); + translate ([-case_inside/2 + 30 - 11.5,-case_inside/2+44-11.5,case_thickness]) cylinder (r1=11.5, r2=11.5, h=9); + + // Cover supports + for (s = case_cover_supports) + translate ([s.x, s.y, case_thickness]) cylinder (d=6, h=case_cover_support_height); +} + +backstop_offset_z = 12 + 2; +module Backstop() +{ + difference () + { + union () + { + // Back stopper + translate ([12,0,-7.5]) cube ([2,80,23]); + translate ([3,0,0]) cube ([11,80,2]); + translate ([3,51.5,2]) cube ([4,6,2]); + translate ([3,22,2]) cube ([4,6,2]); + } + + // Nut holes + translate ([8,40 + 32,-0.5]) cylinder(r1=1.7,r2=1.7,h=3); + translate ([8,40 - 32,-0.5]) cylinder(r1=1.7,r2=1.7,h=3); + + // Rod holes + translate ([11.5,40 + bearing_offset,12-3.5]) + rotate (a=90, v=[0,1,0]) + cylinder(r1=4.5,r2=4.0,h=1); + translate ([11.5,40 + bearing_offset,12-3.5]) + rotate (a=90, v=[0,1,0]) + cylinder(r1=4.0,r2=4.0,h=3); + + translate ([11.5,40 - bearing_offset,12-3.5]) + rotate (a=90, v=[0,1,0]) + cylinder(r1=4.5,r2=4.0,h=1); + translate ([11.5,40 - bearing_offset,12-3.5]) + rotate (a=90, v=[0,1,0]) + cylinder(r1=4.0,r2=4.0,h=3); + } +} + +module Tracks() +{ + module Bearing() + { + color("Snow") difference() + { + translate ([0,0,7.5]) rotate (a=90, v=[0,1,0]) + cylinder (r1=7.5, r2=7.5, h=45); + translate ([-1,0,7.5]) rotate (a=90, v=[0,1,0]) + cylinder (r1=4.2, r2=4.2, h=47); + } + } + + module Rod() + { + color("Snow") translate ([0,0,4]) rotate (a=90, v=[0,1,0]) + cylinder (r1=4, r2=4, h=168); + } + + // Bearings and rods + translate ([-base_length/2 + bearing_margin_frontback, bearing_offset,0]) Bearing(); + + translate ([-base_length/2 + bearing_margin_frontback, -bearing_offset,0]) Bearing(); + + translate ([-168/2, bearing_offset,3.5]) Rod(); + translate ([-168/2, -bearing_offset,3.5]) Rod(); +} + +module CaseCoverFull() +{ + difference() + { + translate ([-case_outside/2,-case_outside/2,case_depth]) + difference () + { + union() + { + difference() + { + cube ([case_outside, case_outside, case_cover_height]); + translate ([case_thickness, case_thickness, -case_thickness]) + cube ([case_inside, case_inside, case_cover_height]); + } + + for (s = case_cover_supports) + { + h = case_cover_support_height-case_cover_height; + translate([case_outside/2 + s.x, case_outside/2 + s.y, 0]) + { + translate ([0, 0, h+case_thickness]) cylinder (d=8.5+case_thickness, h=h); + } + } + } + + // Shaft groove + translate ([case_outside/2 + grooveOffset, case_outside/2, case_cover_height - case_thickness - 1]) + { + cube ([grooveLength, grooveDiameter, case_thickness + 6], center = true); + translate ([grooveLength/2,0,0]) + cylinder(d=grooveDiameter, h=case_thickness + 2); + translate ([-grooveLength/2,0,0]) + cylinder(d=grooveDiameter, h=case_thickness + 2); + + translate ([-(60),-28,1]) + cube([120,28, 1]); + } + + // Fitting cuts + case_fitting_cut_thickness = 1; + case_fitting_cut_height = 1.5; + translate ([case_thickness - case_fitting_cut_thickness, case_thickness - case_fitting_cut_thickness, - 0.01]) + cube ([case_inside + case_fitting_cut_thickness*2, case_inside + 2*case_fitting_cut_thickness, case_fitting_cut_height]); + } + for (s = case_cover_supports) + { + translate([s.x, s.y, case_cover_support_height + case_thickness]) + { + translate ([0, 0, 0]) cylinder (d=3.8, h=case_cover_height+case_thickness); + translate ([0, 0, case_thickness]) cylinder (d=8.5, h=case_cover_height); + } + } + } +} + +module CaseCover(left = true, right = true) +{ + // Left half + translate([0,-2,0]) + if (left == true) + { + difference() + { + intersection() + { + CaseCoverFull(); + scale([0.965, 0.965, 0.96]) + minkowski() + { + translate ([-case_outside/2,-case_outside/2,case_depth]) + cube ([case_outside, case_outside, case_cover_height]); + sphere(2.8, $fn=50); + } + } + translate([-100,0,0]) + cube([200, 200, 200]); + } + // Connector supports + translate ([-case_inside/2,-3, case_depth + case_cover_height - 2*case_thickness]) + { + translate ([0, 0, 1.5]) + cube ([10, 5, 1.5]); + translate ([case_inside-10, 0, 1.5]) + cube ([10, 5, 1.5]); + } + translate ([case_inside/2-1.5, -10, case_cover_height-1]) + cube ([1.5, 10, 7]); + translate ([-case_inside/2, -5-35, case_cover_height-3]) + cube ([1.5, 5, 9]); + } + + // Right half + translate([0,2,0]) + if (right == true) + { + difference() + { + intersection() + { + CaseCoverFull(); + scale([0.965, 0.965, 0.965]) + minkowski() + { + translate ([-case_outside/2,-case_outside/2,case_depth]) + cube ([case_outside, case_outside, case_cover_height]); + sphere(3, $fn=50); + } + } + translate([-100,-200,0]) + cube([200, 200, 200]); + + // Center detent + translate([grooveOffset,grooveDiameter/2 + 28,case_depth + case_cover_height - case_thickness -0.1]) + cylinder(d=4.5, h=case_thickness + 0.2); + } + + // Connector supports + translate ([-case_inside/2,0, case_depth + case_cover_height - 2*case_thickness]) + { + translate ([case_inside-30, -2, 1.5]) + cube ([10, 5, 1.5]); + } + translate ([case_inside/2-1.5, 0, case_cover_height-1]) + cube ([1.5, 10, 7]); + translate ([-case_inside/2, 35, case_cover_height-3]) + cube ([1.5, 5, 9]); + } +} + +module CaseCircuitSupport() +{ + difference() + { + translate ([0, 0, 0]) + cube ([15-4+5.3*2, 80, 3]); + + translate ([3 + 16 - 4, 3 + 4, -0.5]) + cylinder(d=6, h=4); + + translate ([3 + 16 - 4, 3 + 4 + 65.5, -0.5]) + cylinder(d=6, h=4); + + translate ([-0.1, 31, -0.1]) + cube ([9, 80, 4]); + } + + ziptie_thickness = 2; + ziptie_width = 3.5; + difference() + { + translate ([0, 0, 3]) + cube ([2, 31, 13]); + + translate ([4, 26, 9]) + rotate([0,-10,90]) + { + cube ([ziptie_width, 22, ziptie_thickness]); + translate ([0,0,5]) + cube ([ziptie_width, 22, ziptie_thickness]); + + translate([-8,0,0]) + { + cube ([ziptie_width, 22, ziptie_thickness]); + translate ([0,0,5]) + cube ([ziptie_width, 22, ziptie_thickness]); + } + } + } + + translate([10,15,3]) + tube(3, 8, 1.5); + + translate([-3.5,11,5]) + cube([3.5, 20, 2]); + + translate([-2,11,5]) + rotate([0,45,0]) + cube([3.5, 20, 2]); +} diff --git a/throttle-circuit-tinycad.dsn b/throttle-circuit-tinycad.dsn new file mode 100644 index 0000000..dce2669 --- /dev/null +++ b/throttle-circuit-tinycad.dsn @@ -0,0 +1,565 @@ + + + + + + Sheet 1 +
+ + + + + 1.0 + + + 1 of 1 + 1 + + +
+ + -10 + 0 + 400 + 0 + 0 + 0 + 0 + Arial + + + -11 + 5 + 400 + 0 + 0 + 0 + 0 + Arial + + + 000000 + 1 + + + 000000 + 1 + + + -1 + 000000 + + + 0 + 000000 + + + New symbol + U? + Teensy LC + 1 + + + $$SPICE_PROLOG_PRIORITY + 5 + 000002 + + 0.00000,0.00000 + + + $$SPICE_EPILOG_PRIORITY + 5 + 000002 + + 0.00000,0.00000 + + + Package + package name for PCB layout + 000001 + + 0.00000,0.00000 + + + + + TX1 + + + + MISO1 + CS1 + RX3 + TX3 + RX2 + TX2/CS0 + MOSI0 + MISO0 + RX1 + SCK0(led) + A0 + A1 + A2 + A3 + SDA0/A4 + SCL0/A5 + SCK1/A6 + A7 + SCL1/A8 + SDA1/A9 + GND + Vin + GND + 3.3V + Vin + 3.3V + GND + Program + DAC/A12 + + + + SWITCH1 + S? + Single Pole Push Switch + 1 + + + + + + + + + + + + + + + + + + + + + + + POT + R? + Variable Resistor + 1 + + + + + + + + + + + + + + + + + + + + + adj + a + b + + + + POT + R? + Variable Resistor + 1 + + + + + + + + + + + + + + + + + + + + + adj + a + b + + + + Alps688RKJXL100401V + S? + New symbol + 1 + + + $$SPICE_PROLOG_PRIORITY + 5 + 000002 + + 0.00000,0.00000 + + + $$SPICE_EPILOG_PRIORITY + 5 + 000002 + + 0.00000,0.00000 + + + Package + Alps688RKJXL100401V + 000001 + + 0.00000,0.00000 + + + + + + + + + + + + + + + + + + + + + + + + + + C1 + C2 + 2 + 1 + H + G + F + E + A + B + D + C + A + B + C + D + E + F + G + H + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + FF0000 + 0000FF + 000000 + 000000 + 208000 + 000000 + 4040C0 + 208020 + FFFFFF + AFFFFF + FF0000 + FF0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/throttle-handle.scad b/throttle-handle.scad new file mode 100644 index 0000000..0897c22 --- /dev/null +++ b/throttle-handle.scad @@ -0,0 +1,569 @@ +include ; +include ; +include ; +include ; + +// -------------------------------------------------- +// Some part definitions for easier switch'n'replace +// -------------------------------------------------- + +module HatSwitch() { Alps688RKJXL100401V(); } +module YAxisPot(cavityOnly=false) { PotentiometerBournsBIP160(cavityOnly=cavityOnly); } +module XAxisPot(cavityOnly=false) { rotate([0,0,180]) PotentiometerBournsBIP160(flip=true,cavityOnly=cavityOnly); } +module XPotKnob() { PotKnobBristles(22.5, 17.5, 12, 3); } +module YPotKnob() { PotKnobBristles(22.5, 17.5, 12, 3); } + +// ---------------------------------- +// Global settings and configuration +// ---------------------------------- + +solidOnly = false; // set true for faster preview rendering +showHandle = true; +showThumb = true; +drawOtherParts = true; // draw also non-printable parts like pots, switches etc. +showKnobs = true; +showHandleInsides = true; + +// Effective values based on the above settings and rendering mode + +thumbSeparation = showHandleInsides ? 10 : -4; +$fn = $preview ? 30 : 100; +fnForMinkowskiHull = $preview ? 10 : 15; +draw_other_parts = $preview ? drawOtherParts : false; + +// ---------------------------------- +// Main handle case parameters +// ---------------------------------- + +caseThickness = 2; +primaryHandleCylinder = [65, 0]; // diameter, offset + +lowerBoxSize = [53,26,85]; +lowerBoxPos = [-25,16,-25]; +handleAngle = 30; +handleLength = 60; // Main handle length +handleBaseHeight = 0; // level of the base connector plate + +// Screw locations and depths for connecting main handle and the thumb side piece +thumb_screws = [ + [[21.7,-20.5, -caseThickness], 2*caseThickness, 20], + [[-25,18,-caseThickness], 2*caseThickness, 20], + [[25.8,37,-caseThickness], 2*caseThickness, 20] + ]; + +thumbLength = 100; +thumbDepth = 40; +thumbAngle = 20; +thumbPlateDepth = 77; +thumbPlateAngle = [10, 40, 0]; +thumbTipDepth = 57; + +thumb_buttons = [ + // x,y,d,z + [-10.7,-0.5,8,0], // forward + [9.7,-0.5,8,0], // back + [-0.5,7,8,0], // down + [-0.5,-8.2,8,0], // up + [9.7,14.7,8,0] // low extra + ]; + +thumbHat = [-2.5, -21, 0, 20-45, 8]; // x,y,z,angle,hole diameter + +button_module_supports = [ + [-12.5,-23],// [-13,-24.5], // left top (hat left) + [7.5,-18.5],// [7,-17], // right top (hat right) + [7.6,-8.7], // upper right + [-3.8,0.7], // center + [14.9,5.7], // right + [-3.8,18.5] // center low + ]; + +back_plate_height = 6.3 + 0.4; // ???? Added a bit (0.4) of extra to make sure buttons get to move +backPlatePoints = [ + [4,-14], + [18,-10], // back button + [18,20], [-7,20], // down button + [-17,20], [-17,-3], // forward button + [-8,-13] + ]; +backPlateOffset = [-2.5, 1.5, -8]; // x, y, angle + +backPlateHatPoints = [ + [-23,-14], + [-23,-18], + [-18,-25], + [6,-25], + [6,-14] + ]; +backPlateHatOffset = [-1.8, -0.8, 20]; // x, y, angle + +// Axis pots +axisHoleDiameter = 9.6 + 0.2; + +axisYPos = [-3, 31, 0]; +axisYRaiserHeight = 1; +axisXPos = [27, 30]; // offset from thumb side bottom, angle +axisXRaiserHeight = 4.5; +axisXRaiserOffset = 0.0; + +module xyPots() +{ + if (draw_other_parts == true) + { + // X-axis pot + rotate([0,thumbAngle,0]) + rotate ([0,0,axisXPos.y]) + translate ([-primaryHandleCylinder[0]/2 - caseThickness + axisXRaiserHeight - axisXRaiserOffset, 0, axisXPos.x-9]) + rotate ([0,90,0]) + union () + { + XAxisPot(); + if (showKnobs) + translate ([0,0,-8]) + rotate ([180,0,0]) + XPotKnob(); + } + + // Y-axis pot + rotate (thumbPlateAngle) + translate ([axisYPos.x,axisYPos.y,thumbLength-thumbPlateDepth - axisYRaiserHeight]) + rotate([0,0,axisYPos.z]) + union () + { + YAxisPot(); + if (showKnobs) + translate ([0,0,8]) + YPotKnob(); + } + + rotate (thumbPlateAngle) + translate ([thumbHat.x, thumbHat.y, thumbHat.z + thumbLength-thumbPlateDepth]) + rotate([0,180,thumbHat[3]]) + HatSwitch(); + } +} + + +module buttonModule() +{ + plateThickness = 1.6; + translate([backPlateOffset.x, backPlateOffset.y, 0]) + rotate([0, 0, backPlateOffset.z]) + { + color("orange") + { + translate ([0, 0, -plateThickness]) + CircuitBoard(backPlatePoints); + } + + translate ([0,0,plateThickness + 3]) + for (b = thumb_buttons) + { + translate ([b[0],b[1],b[3]-0.5]) + { + Switch(); + %translate ([0,0,1.5]) + SwitchButtonBall(); + } + } + } + + translate([backPlateHatOffset.x, backPlateHatOffset.y, 0]) + rotate([0, 0, backPlateHatOffset.z]) + { + color("orange") + { + translate ([0, 0, -plateThickness]) + CircuitBoard(backPlateHatPoints); + } + } +} + +module buttonHoles() +{ + translate([backPlateOffset.x, backPlateOffset.y, 0]) + rotate([0, 0, backPlateOffset.z]) + for (b = thumb_buttons) + { + translate ([b[0],b[1],-0.5]) + SwitchButton(d=b[2],cavityOnly=true); + } +} + +// ------------------------------------------- +// Main handle +// ------------------------------------------- + +module handleLowerBox() +{ + difference() + { + translate(lowerBoxPos) + cube(lowerBoxSize); + } +} + + +module handleSection(l, h1, h2) +{ + intersection() + { + union() + { + cylinder(d=primaryHandleCylinder[0],h=l); + rotate([0,20,20]) scale([1.064,1,1]) translate([5,0,-35]) cylinder(d1=90,d2=60,h=35); + } + // Cone cut straight + translate([-100, -100, h1-0.01]) + cube([200, 200, h2-h1]); + + // Cone cut angled + rotate([0,45,20]) + translate([-100, -100, -40]) + cube([200, 200, 200]); + } +} + + +module handleLimitCuts(extra = 0) +{ + // Cut the extra plate off + translate([-50,-50, handleLength - caseThickness]) + cube([100, 150, 100]); + + // Base connector plane + rotate([-handleAngle,0,0]) + translate([-30, handleBaseHeight + extra,0]) + translate(lowerBoxPos) + cube([100, 100, 100]); +} + +module handleSolidBody() +{ + difference() + { + union() + { + hull() handleSection(90 + caseThickness, -25, 1); + hull() handleSection(90 + caseThickness, 0, handleLength + caseThickness); + + handleLowerBox(); + } + + handleLimitCuts(); + } +} + +shaftOuterDiameter = 20; +shaftConnectorPos = [0,50,0]; +shaftConnectorAngle = [90 - handleAngle,0,0]; + +handle_case_print_offset_z = 25 + caseThickness; + +module handleCase() +{ + difference() + { + union() + { + difference() + { + if (solidOnly == false) + { + minkowski() + { + handleSolidBody(); + sphere(caseThickness, $fn=fnForMinkowskiHull); + //cube([caseThickness * 2, caseThickness * 2, caseThickness * 2], center = true); + } + } + handleSolidBody(); + + translate([0,0,-0.01]) + handleLimitCuts(caseThickness); + } + + // Screw thred supports on the Main Handle + rotate ([0,0,0]) + translate([0,0,handleLength]) + union() + { + for (item = thumb_screws) + translate(item[0]) ScrewThreadSupport3m(12); + } + + // Tilted shaft connector + translate (shaftConnectorPos) + rotate (shaftConnectorAngle) + translate ([0,0,20 - caseThickness]) + cylinder (d1=30,d2=48,h=8); + } + + // Screw locking holes + rotate ([0,0,0]) + translate([0,0,handleLength-1.3]) + for (item = thumb_screws) + translate(item[0]) cylinder(d=4.5, h=2); + + // Tilted shaft hole + translate (shaftConnectorPos) + rotate (shaftConnectorAngle) + cylinder (d=shaftOuterDiameter,h=30); + + // Shaft fixing screw hole + translate (shaftConnectorPos) + rotate (shaftConnectorAngle) + translate ([-9,0,23.5 - caseThickness]) + rotate([-90,0,90]) + { + cylinder (d=3,h=30); + translate ([0,0,5]) + cylinder (d=5,h=30); + } + } +} + +// ------------------------------------------- +// Thumb side of the handle +// ------------------------------------------- + +module thumbLimitCuts(extra = 0) +{ + // Cut off the tip + rotate([0,thumbAngle-40,0]) + translate([-100,-100, thumbLength - thumbTipDepth + extra]) + cube([200,200,100]); + + // Cut off the box + rotate(thumbPlateAngle) + translate([-100,-100, thumbLength - thumbPlateDepth + extra]) + cube([200,200,100]); + + // Cut off to fit the main handle + translate([-100,-100,-thumbLength]) + cube([200,200,thumbLength + extra]); +} + +module thumbSolidBody() +{ + difference() + { + scale([cos(thumbAngle), 1, 1]) + union() + { + rotate([0,thumbAngle,0]) + translate([0,0,-thumbDepth]) + hull() + { + cylinder(d=primaryHandleCylinder[0],h=thumbLength); + //translate([secondaryHandleCylinder[2],0,0]) cylinder(d1=secondaryHandleCylinder[0], d2=secondaryHandleCylinder[1] ,h=thumbLength); + } + + rotate([0,thumbAngle,0]) + translate(lowerBoxPos) + cube(lowerBoxSize); + } + + thumbLimitCuts(); + } +} + +module thumbScrewHoles() +{ + translate([0,0,caseThickness]) + union () + { + for (item = thumb_screws) + translate(item[0]) ScrewCavity3m(item[1], capDepth=thumbLength); + } +} + +module thumbHoles() +{ + // Thumb button holes + rotate(thumbPlateAngle) + translate ([0,0,thumbLength-thumbPlateDepth]) + buttonHoles(); + + // Y-axis pot hole + rotate (thumbPlateAngle) + translate ([axisYPos.x,axisYPos.y,thumbLength-thumbPlateDepth - axisYRaiserHeight-0.01]) + YAxisPot(cavityOnly=true); + + // X-axis pot hole + rotate([0,thumbAngle,0]) + rotate ([0,0,axisXPos.y]) + translate ([-primaryHandleCylinder[0]/2 - caseThickness + axisXRaiserHeight - axisXRaiserOffset, 0, axisXPos.x-9]) + rotate ([0,90,0]) + XAxisPot(cavityOnly=true); + + // Hat shaft hole + rotate (thumbPlateAngle) + translate ([thumbHat.x, thumbHat.y, thumbLength-thumbPlateDepth + 2]) + rotate([0,180,thumbHat[3]]) + cylinder (d2=thumbHat[4]*0.7, d1=thumbHat[4], h=4); +} + +module thumbSupports() +{ + // Thumb button hole rim risers + raiser_height = 0.6; + raiser_width = 3; + rotate(thumbPlateAngle) + translate ([0,0,thumbLength-thumbPlateDepth-raiser_height]) + translate([backPlateOffset.x, backPlateOffset.y, 0]) + rotate([0, 0, backPlateOffset.z]) + for (b = thumb_buttons) + { + translate ([b[0],b[1],0]) + difference() + { + cylinder(h=raiser_height,d=b[2] + raiser_width); + translate ([0,0,-0.1]) + cylinder(h=raiser_height+0.2,d=b[2]); + } + } + + // Hat switch raiser + rotate (thumbPlateAngle) + translate ([thumbHat.x, thumbHat.y, thumbLength-thumbPlateDepth]) + rotate([0,180,thumbHat[3]]) + difference() + { + cylinder(h=raiser_height,d=thumbHat[4] + 4); + translate ([0,0,-0.1]) + cylinder(h=raiser_height+0.2,d=thumbHat[4]-1); + } + + // Backplate supports + rotate(thumbPlateAngle) + translate ([0,0,thumbLength-thumbPlateDepth-2*back_plate_height]) + { + for (b = button_module_supports) + { + translate ([b[0],b[1],back_plate_height]) + difference() + { + union() + { + translate([0,0,back_plate_height-1]) + cylinder(d1=5.2, d2=6.5, h=1); + cylinder(d=5.2, h=back_plate_height); + } + translate([0,0,-0.9]) + cylinder(d=2.5, h=back_plate_height+1); + } + } + + if (draw_other_parts) + { + translate ([0,0,back_plate_height]) + buttonModule(); + } + } + // X-axis pot support + rotate([0,thumbAngle,0]) + rotate ([0,0,axisXPos.y]) + translate ([-primaryHandleCylinder[0]/2-1, 0, axisXPos.x-9]) + rotate ([0,90,0]) + cylinder (d1=axisHoleDiameter+10, d2=axisHoleDiameter+10, h=caseThickness*2-1); + + // Y-axis pot support + rotate (thumbPlateAngle) + translate ([axisYPos.x,axisYPos.y,thumbLength - thumbPlateDepth - axisYRaiserHeight]) + tubeD(dout=axisHoleDiameter+10, din=axisHoleDiameter, h=axisYRaiserHeight); +} + +module thumbScrewSupports() +{ + translate([0,0, 5-caseThickness]) + union () + { + for (item = thumb_screws) + translate(item[0]) ScrewEntrySupport3m(4, item[2]); + } +} + +module thumbConnectors() +{ + l = lowerBoxSize.x * cos(thumbAngle) + caseThickness + 1; + translate([-l/2 + caseThickness, 43 - caseThickness, -3]) + cube([l, 1, 6]); + + translate([0,-primaryHandleCylinder[0]/2 + 0.1,-3]) + cube([4, 1, 6]); +} + +thumb_print_offset_z = 24.5; + +module thumbCase() +{ + difference() + { + union() + { + translate([-2*sin(thumbAngle),0,0]) + difference() + { + union() + { + difference() + { + if (solidOnly == false) + { + minkowski() + { + thumbSolidBody(); + sphere(caseThickness, $fn=fnForMinkowskiHull); + } + } + thumbSolidBody(); + } + translate([2*sin(thumbAngle),0,0]) + thumbScrewSupports(); + } + thumbLimitCuts(caseThickness-0.1); + } + + // Screw locking extrudes + translate([0,0, 5-caseThickness]) + for (item = thumb_screws) + translate(item[0]) tubeD(din=3, dout=4.2, h=1); + + translate([-2*sin(thumbAngle),0,0]) + { + thumbSupports(); + thumbConnectors(); + + xyPots(); + } + } + translate([-2*sin(thumbAngle),0,0]) + thumbHoles(); + } +} + +module handleThumbPart() +{ + difference() + { + thumbCase(); + thumbScrewHoles(); + } +} + +// ------------------------------------------- +// All handle parts together +// ------------------------------------------- + +module handle() +{ + if (showHandle) + handleCase(); + + if (showThumb) + { + translate([0,0, handleLength + thumbSeparation]) + handleThumbPart(); + } +} diff --git a/throttle.scad b/throttle.scad new file mode 100644 index 0000000..21db13b --- /dev/null +++ b/throttle.scad @@ -0,0 +1,48 @@ +include ; +include ; +include ; +include ; + +separateHandle = true; + +handleSeparation = separateHandle ? 15 : 0; + +handle_radius = 30; +shaft_intrusion = caseThickness + 1; +shaft_length = 35; + +// Put it all together for a grand view of it all + +module All() +{ + rotate([0,0,180]) + translate ([0,0,case_bottom]) BasePrinted(); + rotate([0,0,180]) + translate ([-17.5,base_width/2,12+case_bottom]) MainThrottleLever(); + translate ([0,0,case_bottom]) Tracks(); + + color("DimGray") CaseBottom(); + %CaseCover(); + translate ([-(case_inside/2)+case_bottom+12, -case_inside/2 + 29, 9.5 + case_thickness]) + { + translate([0,0,5.6]) + SlidePotentiometer100(); + rotate([0,0,180]) + translate([-134.5,-11.6,0]) + ThrottleAxisHolder(); + translate([-6.5,-3.4,0]) + ThrottleAxisHolder(); + } + + translate ([0,0,base_height + case_bottom]) ShaftAndBase(shaft_length, shaft_intrusion); + + translate ([0,-25,base_height + case_bottom + shaft_length + 18 - shaft_intrusion + handleSeparation]) + rotate([handleAngle-90,0,0]) + handle(); + + translate ([case_inside/2 - 23,-40,6 + case_thickness]) Backstop(); + + moveY(base_width/2 + 6) moveZ(35) flipY() turnZ(-90) BallSpringPlunger(); // center detent actuator +} + +All();