/* * Trackball * * Copyright (c) 2022 - 2023 Thomas Buck (thomas@xythobuz.de) * Philipp Schönberger (mail@phschoen.de) * * Required parts: * - 1x Raspberry Pi Pico * - 4x Cherry MX compatible switches and keycaps * - 1x Billard ball, diameter 38mm * - 3x Si3N4 static bearing balls, diameter 3mm * - 1x PMW3360 sensor with breakout board * - 8x M2 screw, length 5mm * - 8x M2 heat melt insert, length 4mm * * For the PMW3360 breakout board get this: * https://github.com/jfedor2/pmw3360-breakout * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See . */ include; // need include since the parameters are in there include ; // need include since the parameters are in there use; use; use; use; use; use; use; include; translate([$t*assembly_dist,$t*-assembly_dist,0]) top_hand_wrest(); trackball_top(); translate([0,0,-$t*assembly_dist]) trackball_bot(); // %translate([-13,38,5-trackball_dia/2]) // scale(150) // rotate([0,0,-133]) // import("external/mouse_scan_v1.stl"); module trackball_bot() { difference() { color("green") trans_bot_of_top_part() translate([0,0,-wall]) linear_extrude(wall) { hull() projection() { trackball_top(); } projection() { top_hand_wrest(); } } trackball_bot_screw_cuts(); } } module trackball_bot_screw_cuts(e=2) { pos= [ // offset angle [ trackball_dia/2 + 15, 52 + 15 ], [ trackball_dia/2 + 15, 52 - 40 ], [ trackball_dia/2 + 15, 52 - 15 + 180 ], [ trackball_dia/2 + 15, 52 + 40 + 180 ], [ trackball_dia/2 + 75, 52 - 90 -10], [ trackball_dia/2 + 75, 52 - 90 +10], ]; for(p = pos) rotate([0,0,p[1]]) translate([p[0],0,0]) translate([0,0,7-$e*2]) trans_bot_of_top_part() rotate([0,180,0]) { m3_screw_insert_cut(8); } } module trackball_top() { if (draw_ball_bearing_holder) %ball_and_bearing_holder(); difference() { hull() { intersection_top_part(is_wristrest = false) trackball_top_hull(with_bot_cuts=true); trans_bot_of_top_part() linear_extrude($e) projection() { intersection_top_part(is_wristrest = false) trackball_top_hull(with_bot_cuts=true); } } top_body_cutouts(); } } module intersection_top_part(is_wristrest = false) { intersection() { rotate([0,0,-38]) translate([37.1,-50,-50]) trans_bot_of_top_part() if(is_wristrest == true) cube([100,200,200]); else translate([-100,0,0]) cube([100,200,200]); children(); } } module m3_screw_insert_cut(l=20) { // screw thread cutout color("silver") cylinder(d=3,h=50); // insert cutout itself color("gold") cylinder(d=4.6+$c*2,h=7); // screw head cutout for sliding in translate([0,0,l]) color("silver") cylinder(d=6+$c*2,h=50); // insert iself // % color("gold") // cylinder(d=4.6,h=5.7); } module wrest_screw_cutout() { for(i=[1,-1]) rotate([0,0,-38]) translate([31,i*15,14]) trans_bot_of_top_part() rotate([0,20,0]) rotate([0,90,0]) { m3_screw_insert_cut(20); } } module top_hand_wrestv1() { difference() { intersection_top_part(is_wristrest = true) rotate([0,0,-38]) translate([60,0,0]) trans_bot_of_top_part() intersection() { scale([3.5,1.5,1]) cylinder(r = 20, h = trackball_dia+17); translate([15,0,20-trackball_dia/2]) rotate([0,30,0]) scale([1.3,1.4,1]) sphere(d=70); } // screw cutout for attaching the wrist rest to the top part wrest_screw_cutout(); // screw cutouts for the bottom plate translate([0,0,-$e]) trackball_bot_screw_cuts(e=0); // pi pico cutout rotate([0,0,50]) trans_bot_of_top_part() translate([3,-80,3]) { %if(draw_pico) pico_wrap(); pico_cutout(20); } } } module top_hand_wrest() { difference() { intersection_top_part(is_wristrest = true) rotate([0,0,-38]) translate([60,0,0]) trans_bot_of_top_part() intersection() { translate([-200,-200,0]) cube([400,400,100]); minkowski() { intersection() { scale([3.5,1.5,1]) cylinder(r = 20, h = trackball_dia+17); translate([15,0,20-trackball_dia/2]) rotate([0,30,0]) scale([1.3,1.4,1]) sphere(d=70); } sphere(r = 3); } } // screw cutout for attaching the wrist rest to the top part wrest_screw_cutout(); // screw cutouts for the bottom plate translate([0,0,-$e]) trackball_bot_screw_cuts(e=0); // pi pico cutout rotate([0,0,50]) trans_bot_of_top_part() translate([3,-80,3]) { %if(draw_pico) pico_wrap(); pico_cutout(20); } } } module pico_cutout(extra) { translate([-pico_w / 2, -pico_l / 2, -extra]) { // pcb cutout cube([pico_w, pico_l, 4 + extra]); // screw cutouts for (x = [0, pico_hole_d_x]) for (y = [0, pico_hole_d_y]) translate([pico_hole_x + x, pico_hole_y + y, extra]) cylinder(d = pico_hole_d, h = pico_d + 2+5); // cable channels translate([pico_w/2-9/2,pico_l-10,0]) cube([9, trackball_dia+10, 4 + extra+2]); } } module top_body_cutouts() { // space for roller holder bearing_rot_trans() translate([0,0,-bearing_h]) cylinder(d = bearing_mount_dia + 1, h = trackball_dia / 2 + bearing_h); // room for ball itself sphere($fn = $fn * 2, d = trackball_dia + $c * 2 + 4); // grub screws for the bearing holder bearing_rot_trans() rotate([0,-90,90]) translate([-bearing_h+4, 0, 0]) { cylinder(d = grub_screw_dia, h = trackball_dia); translate([0, 0, bearing_mount_grub_screw_l]) // outside of wall translate([0, 0, bearing_mount_dia/2 +wall/2]) // outside of wall cylinder(d = grub_channel_dia, h = trackball_dia); } // sensor lens sensor_cutout(); %if(draw_sensor) sensor_pos(); // switch cutouts //translate([0, 0, trackball_dia / 2 + ball_h]) switch_rot_trans(0) translate([10,10,0]) { // switch cutout itself mx_switch_cutout(mx_co_depth); %if(draw_switches) translate([0,0,10]) mx_switch(); } switch_rot_trans(0,false) translate([10,10,0]) { // cable channel rotate([-40,0,0]) rotate([0,180,0]) cylinder(d=3,h=trackball_dia); } // led ring translate([0,0,-trackball_dia/2]) { led_ring(cutout=true, extra = trackball_dia/2); %if(draw_led) led_ring(); } // screw cutouts for the wrist rest wrest_screw_cutout(); // screw cutouts for the bottom plate translate([0,0,-$e]) trackball_bot_screw_cuts(e=0); // pico cutout rotate([0,0,50]) trans_bot_of_top_part() translate([3,-80,3]) { pico_cutout(20); pico_cutout(20); } // usb cutout rotate([0,0,180+50]) trans_bot_of_top_part() translate([0,-trackball_dia/2-8,2]) { %if(draw_usb_c)usb_c(); usb_c_cutout(); } } module switch_rot_trans(r,x_rot=true) { for ( i = [0:len(sw)-1] ) { rotate([0,0,sw[i][0][2]]) translate(sw[i][1]) rotate([90+(x_rot ? sw[i][0][0] : 0),0,sw[i][0][1]]) translate([-sw_mount_w/2 - r, -sw_mount_w/2 - r, -r - r/2]) children(); } } module trans_bot_of_top_part() { translate([0, 0, -trackball_dia / 2 -11]) children(); } module trackball_top_hull(with_bot_cuts=true) { difference() { color("orange") union(){ hull() { // bearing holder cylinders bearing_rot_trans() bearing_holder_mount(); // base circle trans_bot_of_top_part() cylinder(d = base_dia, h = $e); // switch switch_rot_trans(sw_mount_r) translate([0,0,sw_mount_r*2]) fully_rounded_cube([sw_mount_w + sw_mount_r*2, sw_mount_w + sw_mount_r*2, mx_co_depth], sw_mount_r); } } } }