diff --git a/content/blog/arduino-nikon-remote/dream1.webp b/content/blog/arduino-nikon-remote/dream1.webp new file mode 100644 index 0000000..bf9b603 Binary files /dev/null and b/content/blog/arduino-nikon-remote/dream1.webp differ diff --git a/content/blog/arduino-nikon-remote/dream2.webp b/content/blog/arduino-nikon-remote/dream2.webp new file mode 100644 index 0000000..6cdf62f Binary files /dev/null and b/content/blog/arduino-nikon-remote/dream2.webp differ diff --git a/content/blog/arduino-nikon-remote/dream3.webp b/content/blog/arduino-nikon-remote/dream3.webp new file mode 100644 index 0000000..74d9900 Binary files /dev/null and b/content/blog/arduino-nikon-remote/dream3.webp differ diff --git a/content/blog/arduino-nikon-remote/index.fr.md b/content/blog/arduino-nikon-remote/index.fr.md new file mode 100644 index 0000000..52e3dd4 --- /dev/null +++ b/content/blog/arduino-nikon-remote/index.fr.md @@ -0,0 +1,64 @@ ++++ +title = "Timelapse et oiseaux de l'espace" +date = 2025-02-06 +description = "Un déclencheur photo avec capteur de présence" +insert_anchor_links = "left" +[taxonomies] +tags = ["Arduino", "électronique", "photo"] ++++ + +## Un rouge-gorge dans les étoiles + +Rouge-gorges, moineaux, merles et mésanges visitent toute la journée le jardin mais s'envolent à la vue d'hominidés. +Comment donc, sans téléobjectif, les prendre en photo ? +En fabriquant un déclencheur doté d'un capteur de distance infrarouge ! + +![Un rouge-gorge, tête vers le haut, posé sur le bord d'une table de jardin en bois, regarde de travers un petit récipient plat et rond devant lui, à côté duquel sont dispersées quelques graines. Dans le récipient on voit des étoiles et des nébuleuses.](dream1.webp) + +![Le rouge-gorge se penche vers le récipient, en restant à distance. On voit un manchot dans les étoiles, une aile dépassant du bord du récipient.](dream2.webp) + +![Le rouge-gorge s'est retourné dos à la caméra. Il est juste devant le récipient. Deux manchots sont sortis du récipient, qui laisse toujours voir un ciel étoilé. L'un a sauté du bord de la table, les ailes et les pattes déployées. L'autre s'apprête à le suivre, posé au bord, regardant vers le sol.](dream3.webp) + +[Davantage de photos en haute résolution sont disponibles ici.](/img/birds/) + +## Humble timelapse + +Il y a aussi un mode périodique, pour faire un timelapse. + +Dans la vidéo la luminosité varie parce que j'avais oublié de désactiver la balance automatique. + + + +## Le déclencheur + +Mon appareil photo (Nikon D3000) possède un récepteur infrarouge, pour lequel je n'ai pas de télécommande. +Heureusement quelqu'un a publié un code Arduino répliquant le signal de la télécommande: [_Nikon Remote Emulator_ by Gough Lui](https://goughlui.com/2013/12/06/teardown-and-project-clone-nikon-ml-l3-ir-remote-and-emulation/) +Il suffit d'un microcontrôleur et d'une LED infrarouge (récupérée d'une vieille télécommande TV). + +Le détecteur d'oiseau est un capteur de distance infrarouge GP2D12, capable d'évaluer (très grossièrement) la distance sur 20 à 80cm. +Il est perché sur un bras articulé à distance de l'appareil photo (pour pouvoir régler la focale et ne pas effrayer les oiseaux). + +Un interrupteur pour choisir le mode (capteur ou intervalle fixe), et un potentiomètre pour choisir la valeur (distance pour le mode capteur, durée pour le mode intervalle). + +Il faut aussi déclencher l'appareil toutes les quelques minutes pour l'empêcher de se mettre en veille (ce qui demanderait un rallumage manuel). + +Le boîtier est conçu pour une Arduino Micro mais un ATtiny402 suffirait largement. +Le bras articulé se fixe sur le trépied de l'appareil photo. + +![Bras articulé fait de doubles tiges de métal. À une extrémité, un serrage pour fixer le tout au trépied. Entre les deux segments, une jointure en genou. Sur le deuxième segment, le boîtier contenant l'Arduino, la LED, l'interrupteur et le potentiomètre. Au bout, le capteur infrarouge relié par trois fils au boîtier.](remote.webp) + +* [nikon_ir_trigger.ino](nikon_ir_trigger.ino) (programme Arduino) +* [timelapser.scad](timelapser.scad) (modèle 3D [OpenSCAD](https://openscad.org/), pour impression 3D) +* [pitch.scad](pitch.scad) (pas de vis et support de boulon) + +## Crédits + +Le montage avec le rouge-gorge utilise deux images dont je ne suis pas l'auteur : + +* _Adelie Penguin (Pygoscelis adeliae) group on iceberg, Antarctic Peninsula, Antarctica_, image partagée partout sur Internet sans attribution, dont je ne trouve pas la publication originale. +* [Westerlund 2, photographed by the Hubble Space Telescope (heic1509a)](https://esahubble.org/images/heic1509a/) + +Images retouchées avec Gimp, optimisées avec YOGA Image Optimizer. + +Aucun animal n'a subi de violence pendant les séances photo. +Les participants n'ont pas signé de droit à l'image mais ont été rémunérés en graines. diff --git a/content/blog/arduino-nikon-remote/nikon_ir_trigger.ino b/content/blog/arduino-nikon-remote/nikon_ir_trigger.ino new file mode 100644 index 0000000..d6ee4e6 --- /dev/null +++ b/content/blog/arduino-nikon-remote/nikon_ir_trigger.ino @@ -0,0 +1,73 @@ +#define PIN_TRIGGER 13 // IR LED +#define PIN_SENSOR A0 // Distance sensor (GP2D12) +#define PIN_POT A1 // Parameter potentiometer +#define PIN_SWITCH 8 // Mode switch + +#define INTERVAL 1000 +#define N_MEASURE 4 +#define PREVENT_SLEEP_INTERVAL 270000 + +char count; +unsigned long prevent_sleep = 0; +unsigned long last_shot = 0; + +void trigger() { + // Nikon Remote Emulator by Gough Lui + // https://goughlui.com/2013/12/06/teardown-and-project-clone-nikon-ml-l3-ir-remote-and-emulation/ + count = 0; + while(count < 3) { + tone(PIN_TRIGGER, 38000); + delay(2); + noTone(PIN_TRIGGER); + delay(28); + tone(PIN_TRIGGER, 38000); + delayMicroseconds(200); + noTone(PIN_TRIGGER); + delayMicroseconds(1500); + tone(PIN_TRIGGER, 38000); + delayMicroseconds(200); + noTone(PIN_TRIGGER); + delayMicroseconds(3300); + tone(PIN_TRIGGER, 38000); + delayMicroseconds(200); + noTone(PIN_TRIGGER); + delayMicroseconds(100); + delay(63); + + count ++; + } + prevent_sleep = millis() + PREVENT_SLEEP_INTERVAL; + delay(INTERVAL); +} + +int measure() { + int result = 0; + for(char i=0; i threshold) { + trigger(); + } + else if(millis() > prevent_sleep) { + trigger(); + } + } else { // Interval mode + unsigned long interval = map(analogRead(PIN_POT), 0, 1023, 2000, 300000); + if(millis() > last_shot + interval) { + last_shot = millis(); + trigger(); + } + } +} diff --git a/content/blog/arduino-nikon-remote/pitch.scad b/content/blog/arduino-nikon-remote/pitch.scad new file mode 100644 index 0000000..9b5cda4 --- /dev/null +++ b/content/blog/arduino-nikon-remote/pitch.scad @@ -0,0 +1,110 @@ +/* + * CopyLeft 2017-2022 Pascal Engélibert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// Ideal values but may increase depending on printer/slicer settings +// Please print tests to fine tune this parameter +M3_RAY = 1.725; // M3 bolt ray (diameter=3mm) Ideal=1.5 + + +// Functions are accessible outside scope, not variables +function M3_RAY() = M3_RAY; + +module hexagone(l,h) { + a = l/tan(60); + union() { + rotate([0,0,0]) cube([l,a,h],center=true); + rotate([0,0,60]) cube([l,a,h],center=true); + rotate([0,0,120]) cube([l,a,h],center=true); + } +} + +// added h:0.6 w:0.25 +// b:add bottom nut support +// s:bottom support height +module pitch_m2_5(b=0,s=0) { + translate([0,0,(b+s)/2]) difference() { + hexagone(7.25,b+s); + cylinder(h=1+b+s,r=1.375,center=true,$fn=20); + if(s>0) { + translate([0,0,-b]) hexagone(5.25,b+s); + translate([-2.625,0,-(b+s)/2-b]) cube([5.25,5.25,b+s]); + } + } + translate([0,0,1.8+b+s]) difference() { + hexagone(7.25,3.6); + cylinder(h=4.6,r=1.375,center=true,$fn=20); + translate([0,0,-1]) hexagone(5.25,3.6); + translate([-2.625,0,-2.8]) cube([5.25,5.25,3.6]); + } +} + +// b:add bottom nut support +// s:bottom support height +// h:head support additionnal thickness +// w:side additionnal thickness +module pitch_m3(b=0,s=0,h=0,w=0) { + translate([0,0,(b+s)/2]) difference() { + hexagone(7.75+w,b+s); + cylinder(h=1+b+s,r=M3_RAY,center=true,$fn=20); + if(s>0) { + translate([0,0,-b]) hexagone(5.75,b+s); + translate([-2.875,0,-(b+s)/2-b]) cube([5.75,5.75,b+s]); + } + } + translate([0,0,2+b+s+h/2]) difference() { + hexagone(7.75+w,4+h); + cylinder(h=5+h,r=M3_RAY,center=true,$fn=20); + translate([0,0,-1]) hexagone(5.75,4); + translate([-2.875,0,-3]) cube([5.75,5.75,4]); + } +} + +module pitch_inv_m3(y=0, z1=0, z2=0, hp=0) { + translate([0,0,2-hp/2]) hexagone(5.75, 4+hp); + translate([-3,0,-hp]) cube([6, y, 4+hp]); + translate([0,0,-z2-hp]) cylinder(r=M3_RAY, h=z1+4+z2+hp, $fn=20); +} + +module simple_pitch_m3(th=1, h1=1, h2=1) { + difference() { + translate([0, 0, 2+h1/2-h2/2]) hexagone(7+th, 4+h1+h2); + pitch_inv_m3(6+th, h1+1, h2+1); + } +} + +// a:rod radius +// t:thickness +module rod_pitch_m3(a, t=2, center=true) { + translate([0,0,center?0:4.375]) difference() { + union() { + cylinder(h=8.75, r=a+t, center=true, $fn=60); + rotate([0,90,0]) pitch_m3(a+t,0,1,1); + } + cylinder(h=9, r=a, center=true, $fn=60); + rotate([0,90,0]) cylinder(h=a+t+1, r=1.625, $fn=20); + } +} + +module side_pitch_m3(th=1.2, h1=1, h2=2) { + difference() { + translate([0,0,2+h1/2-h2/2]) hexagone(7+th, 4+h1+h2); + pitch_inv_m3(6+th, h1+1, h2+1, h1); + } +} + +//rod_pitch_m3(3); +side_pitch_m3(); diff --git a/content/blog/arduino-nikon-remote/remote.webp b/content/blog/arduino-nikon-remote/remote.webp new file mode 100644 index 0000000..d8ca3e2 Binary files /dev/null and b/content/blog/arduino-nikon-remote/remote.webp differ diff --git a/content/blog/arduino-nikon-remote/timelapser.scad b/content/blog/arduino-nikon-remote/timelapser.scad new file mode 100644 index 0000000..8bece77 --- /dev/null +++ b/content/blog/arduino-nikon-remote/timelapser.scad @@ -0,0 +1,333 @@ +use; + +module Box( + in_l = 50, + in_w = 45, + in_h = 40, + wall_th = 2, + pot_d = 7.7, + pot_x = 36, + pot_y = 20, + pot_mark_dist = 8, + pot_mark_d = 3, + sw_w = 6, + sw_l = 8.2, + sw_joint_dist = 15, + sw_joint_d = 3, + sw_joint_depth = 1, + sw_screw_dist = 8, + sw_screw_d = 3.6, + sw_x = 12, + sw_y = 25, + ino_h = 26, + ino_l = 47, // Length of Arduino itself + ino_w = 18, // Width of Arduino itself + ino_hold1_l = 2.25, // USB side + ino_hold1_w = 4, + ino_hold2_l = 2.25, // Other side + ino_hold2_w = 2, + ino_hold3_m = 1, // Inner side + ino_hold3_h = 3, + ino_hold3_l = 4, + ino_hold3_th = 2, + ino_hold3_supp = 5, + ino_x = 0, // From USB side + usb_y = 3, // From ino_h + usb_w = 11, + usb_h = 7, + usb_y = 3, // Distance between bottom of Arduino PCB and center of USB port + sensor_w = 10, + sensor_h = 7, + sensor_x = 18, + sensor_y = 6, + ir_d = 8, + ir_x = 15, + ir_y = 7, + ir_hold_dist = 17, + ir_hold_small_h = 2.2, + ir_hold_small_d = 7, + ir_hold_offset = -6, + lid_screw_d = 4, + lid_screw_dist_side = 16, + lid_screw_dist_top = 5, + arm_joint_d = 6, + arm_joint_th = 5, + arm_joint_margin = 4, + arm_joint_dist = 8 +) { + difference() { + cube([in_l+2*wall_th, in_w+2*wall_th, in_h+wall_th]); + + // Inside + translate([wall_th, wall_th, wall_th]) cube([in_l, in_w, in_h+wall_th]); + + // Potentiometer + translate([wall_th+pot_x, 0, wall_th+pot_y]) rotate([-90,0,0]) + cylinder(d=pot_d, h=wall_th, $fn=60); + translate([wall_th+pot_x+pot_mark_dist, 0, wall_th+pot_y]) rotate([-90,0,0]) + cylinder(d=pot_mark_d, h=wall_th, $fn=60); + translate([wall_th+pot_x+-pot_mark_dist, 0, wall_th+pot_y]) rotate([-90,0,0]) + cylinder(d=pot_mark_d, h=wall_th, $fn=60); + + // Switch + translate([wall_th+sw_x, 0, wall_th+sw_y]) union() { + translate([-sw_l/2, 0, -sw_w/2]) cube([sw_l, wall_th, sw_w]); + translate([-sw_joint_dist/2, wall_th-sw_joint_depth, 0]) rotate([-90,0,0]) + cylinder(d=sw_joint_d, h=wall_th, $fn=60); + translate([sw_joint_dist/2, wall_th-sw_joint_depth, 0]) rotate([-90,0,0]) + cylinder(d=sw_joint_d, h=wall_th, $fn=60); + translate([0, 0, sw_screw_dist]) rotate([-90,0,0]) + cylinder(d=sw_screw_d, h=wall_th, $fn=60); + }; + + // USB + translate([wall_th+in_l, wall_th+in_w-ino_w/2-usb_w/2, wall_th+ino_h+usb_y-usb_h/2]) + cube([wall_th, usb_w, usb_h]); + + // Sensor cable + translate([wall_th+in_l, wall_th+sensor_x-sensor_w/2, wall_th+sensor_y-sensor_h/2]) + cube([wall_th, sensor_w, sensor_h]); + + // IR LED + translate([0, wall_th+ir_x, wall_th+ir_y]) + rotate([0,90,0]) cylinder(d=ir_d, h=wall_th, $fn=60); + translate([wall_th+ir_hold_dist, wall_th+ir_x+ir_hold_offset, 0]) + rotate([0,0,30]) pitch_inv_m3(); + + // Lid screws + translate([0, wall_th+lid_screw_dist_side, wall_th+in_h-lid_screw_dist_top]) + rotate([0,90,0]) cylinder(d=lid_screw_d, h=wall_th, $fn=60); + translate([wall_th+in_l, wall_th+lid_screw_dist_side, wall_th+in_h-lid_screw_dist_top]) + rotate([0,90,0]) cylinder(d=lid_screw_d, h=wall_th, $fn=60); + } + + // Arduino holder + translate([wall_th, wall_th+in_w-ino_hold2_w, wall_th]) + cube([ino_x+ino_hold2_l, ino_hold2_w, ino_h]); + translate([wall_th, wall_th+in_w-ino_w, wall_th]) + cube([ino_x+ino_hold2_l, ino_hold2_w, ino_h]); + translate([wall_th+in_l-ino_hold1_l, wall_th+in_w-ino_hold1_w, wall_th]) + cube([ino_x+ino_hold1_l, ino_hold1_w, ino_h]); + translate([wall_th+in_l-ino_hold1_l, wall_th+in_w-ino_w, wall_th]) + cube([ino_x+ino_hold1_l, ino_hold1_w, ino_h]); + translate([wall_th, wall_th+in_w-ino_w-ino_hold3_m-ino_hold3_th, wall_th+ino_h]) + hull() { + cube([ino_hold3_l, ino_hold3_th, ino_hold3_h]); + translate([-wall_th, 0, -ino_hold3_supp]) + cube([wall_th, ino_hold3_th, ino_hold3_supp]); + }; + translate([wall_th+in_l-ino_hold3_l, wall_th+in_w-ino_w-ino_hold3_m-ino_hold3_th, wall_th+ino_h]) + hull() { + cube([ino_hold3_l, ino_hold3_th, ino_hold3_h]); + translate([ino_hold3_l, 0, -ino_hold3_supp]) + cube([wall_th, ino_hold3_th, ino_hold3_supp]); + }; + + // IR LED holder + translate([wall_th+ir_hold_dist, wall_th+ir_x+ir_hold_offset, wall_th]) rotate([0,0,-90]) + pitch_m3(s=ir_y-4-ir_hold_small_h); + translate([wall_th+ir_hold_dist, wall_th+ir_x+ir_hold_offset, wall_th+ir_y-ir_hold_small_h]) + difference() { + cylinder(d=ir_hold_small_d, h=ir_hold_small_h, $fn=60); + cylinder(r=M3_RAY(), h=ir_hold_small_h, $fn=20); + }; + + // Arm joint + translate([wall_th+in_l/2, 2*wall_th+in_w, 0]) difference() { + union() { + translate([-arm_joint_d/2-arm_joint_margin, 0, 0]) + cube([arm_joint_d+2*arm_joint_margin, arm_joint_dist, arm_joint_th]); + translate([0, arm_joint_dist, 0]) + cylinder(d=arm_joint_d+2*arm_joint_margin, h=arm_joint_th, $fn=60); + } + translate([0, arm_joint_dist, 0]) + cylinder(d=arm_joint_d, h=arm_joint_th, $fn=60); + }; + + //color("#ff000066") translate([wall_th+sw_x, wall_th+0.4, wall_th+sw_y]) rotate([0,0,180]) SwitchAttach(); +} + +// TODO ajouter supports Arduino +module Lid( + in_l = 50, + in_w = 45, + in_h = 40, + wall_th = 2, + ino_h = 26, + ino_l = 47, // Length of Arduino itself + ino_w = 18, // Width of Arduino itself + ino_hold_h = 13, + ino_hold1_x = 9, + ino_hold1_l = 2, + ino_hold1_y = 29.5, + ino_hold1_w = 11.5, + ino_hold2_x = 37.5, + ino_hold2_l = 2, + ino_hold2_y = 37, + ino_hold2_w = 4, + border_h = 1.6, + border_margin = 0.8, + led1_x = 17, + led1_y = 38, + led1_d = 3, + led2_x = 37, + led2_y = 33, + led2_d = 3, + led3_x = 39, + led3_y = 33, + led3_d = 3, + reset_x = 47, + reset_y = 35, + reset_d = 4.5, + lid_screw_d = 4, + lid_screw_dist_side = 16, + lid_screw_dist_top = 5.5, + lid_screw_margin = 2.5, + lid_screw_th = 1.5 +) { + difference() { + union() { + cube([in_l+2*wall_th, in_w+2*wall_th, wall_th]); + translate([wall_th+border_margin, wall_th+border_margin, wall_th]) difference() { + cube([in_l-2*border_margin, in_w-2*border_margin, border_h]); + translate([wall_th, wall_th, 0]) + cube([in_l-2*border_margin-2*wall_th, in_w-2*border_margin-2*wall_th, border_h]); + } + } + translate([led1_x, led1_y, 0]) + cylinder(d=led1_d, h=wall_th+border_h, $fn=40); + translate([led2_x, led2_y, 0]) + cylinder(d=led2_d, h=wall_th+border_h, $fn=40); + translate([led3_x, led3_y, 0]) + cylinder(d=led3_d, h=wall_th+border_h, $fn=40); + translate([reset_x, reset_y, 0]) + cylinder(d=reset_d, h=wall_th+border_h, $fn=40); + } + + translate([wall_th+border_margin, wall_th+lid_screw_dist_side, wall_th]) difference() { + union() { + translate([0, -lid_screw_d/2-lid_screw_margin, 0]) + cube([lid_screw_th, lid_screw_d+2*lid_screw_margin, lid_screw_dist_top]); + translate([0, 0, lid_screw_dist_top]) + rotate([0,90,0]) + cylinder(d=lid_screw_d+2*lid_screw_margin, h=lid_screw_th, $fn=60); + translate([lid_screw_th,0,lid_screw_dist_top]) rotate([90,0,0]) rotate([0,90,0]) + pitch_m3(); + } + translate([0, 0, lid_screw_dist_top]) + rotate([0,90,0]) cylinder(d=lid_screw_d, h=wall_th, $fn=60); + } + translate([in_l-border_margin+wall_th-lid_screw_th, wall_th+lid_screw_dist_side, wall_th]) difference() { + union() { + translate([0, -lid_screw_d/2-lid_screw_margin, 0]) + cube([lid_screw_th, lid_screw_d+2*lid_screw_margin, lid_screw_dist_top]); + translate([0, 0, lid_screw_dist_top]) + rotate([0,90,0]) + cylinder(d=lid_screw_d+2*lid_screw_margin, h=lid_screw_th, $fn=60); + translate([0,0,lid_screw_dist_top]) rotate([90,0,0]) rotate([0,-90,0]) + pitch_m3(); + } + translate([0, 0, lid_screw_dist_top]) + rotate([0,90,0]) cylinder(d=lid_screw_d, h=wall_th, $fn=60); + } + + translate([wall_th+ino_hold1_x, wall_th+ino_hold1_y, wall_th]) + cube([ino_hold1_l, ino_hold1_w, ino_hold_h]); + translate([wall_th+ino_hold2_x, wall_th+ino_hold2_y, wall_th]) + cube([ino_hold2_l, ino_hold2_w, ino_hold_h]); +} + +module SwitchAttach( + joint_d = 1.5, + joint_dist = 15, + joint_h = 2, + screw_dist = 8, + screw_d = 3.6, + arm_joint_in_w = 1, + arm_joint_out_w = 2, + arm_joint_bottom_w = 2.6, + arm_screw_in_w = 3, + arm_screw_out_w = 5, + arm_th = 2, +) { + translate([-joint_dist/2, arm_th, 0]) rotate([-90,0,0]) + cylinder(d=joint_d, h=joint_h, $fn=60); + translate([joint_dist/2, arm_th, 0]) rotate([-90,0,0]) + cylinder(d=joint_d, h=joint_h, $fn=60); + difference() { + translate([-joint_dist/2-arm_joint_out_w, 0, screw_dist-arm_screw_in_w]) + cube([joint_dist+2*arm_joint_out_w, arm_th, arm_screw_in_w+arm_screw_out_w]); + translate([0, 0, screw_dist]) rotate([-90,0,0]) + cylinder(d=screw_d, h=arm_th, $fn=60); + }; + translate([-joint_dist/2-arm_joint_out_w, 0, -arm_joint_bottom_w]) + cube([arm_joint_out_w+arm_joint_in_w, arm_th, arm_joint_bottom_w+screw_dist]); + translate([joint_dist/2-arm_joint_in_w, 0, -arm_joint_bottom_w]) + cube([arm_joint_out_w+arm_joint_in_w, arm_th, arm_joint_bottom_w+screw_dist]); +} + +module ArmJoint( + d = 19, + rod_d = 3.8, + rod_dist = 8.5, + rod_z = 0.3, + th = 4.8, + hole_d = 4, +) { + difference() { + cylinder(d=d, h=th, $fn=80); + + cylinder(d=hole_d, h=th+1, $fn=40); + + translate([-rod_dist/2, -d/2, rod_z+th]) + rotate([-90, 0, 0]) + cylinder(d=rod_d, h=d, $fn=40); + translate([rod_dist/2, -d/2, rod_z+th]) + rotate([-90, 0, 0]) + cylinder(d=rod_d, h=d, $fn=40); + } +} + +module RodSupport( + ear_l = 15, + ear_th = 4, + out_d = 32, + rod_d = 25.5, + rod_offset = 0.5, + screw_d = 4, + screw_dist = 9, + w = 10, +) { + difference() { + union() { + cylinder(d=out_d, h=w, $fn=80); + hull() { + translate([out_d/2+screw_dist, 0, w/2]) + rotate([-90, 0, 0]) + cylinder(d=w, h=ear_th, $fn=60); + translate([-out_d/2-screw_dist, 0, w/2]) + rotate([-90, 0, 0]) + cylinder(d=w, h=ear_th, $fn=60); + } + //translate([-out_d/2-ear_l, 0, 0]) cube([out_d+2*ear_l, ear_th, w]); + } + + // Ears + translate([-out_d/2, -out_d, 0]) cube([out_d, out_d, w]); + // Rod + translate([0, -rod_offset, 0]) cylinder(d=rod_d, h=w, $fn=80); + // Screws + translate([out_d/2+screw_dist, 0, w/2]) + rotate([-90, 0, 0]) + cylinder(d=screw_d, h=ear_th, $fn=40); + translate([-out_d/2-screw_dist, 0, w/2]) + rotate([-90, 0, 0]) + cylinder(d=screw_d, h=ear_th, $fn=40); + } +} + +//Box(); +//rotate([90,0,0]) SwitchAttach(); +//Lid(); +ArmJoint(); +//RodSupport();