Created
September 9, 2026 20:34
-
-
Save palaniraja/2ca92bfb7cdba04a91dacdee92f8ac01 to your computer and use it in GitHub Desktop.
Feeder to use GPI 28-400 compatible bottle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| GPI 28-400 inverted bottle hummingbird feeder - revised thread interface | |
| IMPORTANT DIFFERENCE FROM v1: | |
| The female closure bore CLEARS the bottle's thread crest (T dimension), | |
| and an inward helical rib engages between the bottle's external threads. | |
| v1 incorrectly used a near-neck-diameter bore plus a spiral cut. | |
| 28-400 reference values (mm): | |
| bottle T max/min : 27.64 / 27.12 | |
| bottle E max/min : 25.25 / 24.74 | |
| pitch : 25.4 / 6 = 4.2333 | |
| 400 finish : about 1 turn; closure drawings commonly show 1-1/8 turns | |
| S nominal : about 1.17 | |
| H nominal : about 10.1 | |
| This is an FDM-printable approximation of a packaging thread, not a GPI gauge. | |
| Print `part="thread_test"` first and tune diametral_clearance. | |
| */ | |
| $fn = 96; | |
| part = "assembly"; // assembly, bowl, lid, gasket, thread_test, socket_section | |
| show_bottle_reference = true; | |
| // ---------- 28-400 reference ---------- | |
| T_max = 27.64; | |
| T_nom = 27.38; | |
| E_max = 25.25; | |
| E_nom = 24.99; | |
| pitch = 25.4/6; | |
| finish_h = 10.1; | |
| S_nom = 1.17; | |
| thread_turns = 1.125; | |
| // FDM fit tuning (diametral clearance, not radial) | |
| diametral_clearance = 0.60; | |
| axial_clearance = 0.20; | |
| // Female socket: open bore clears bottle thread crest. | |
| female_bore_d = T_max + diametral_clearance; | |
| // Internal helical rib approaches bottle root but leaves clearance. | |
| female_crest_d = E_max + diametral_clearance; | |
| // Rib cross section dimensions | |
| female_root_width = 1.70 + axial_clearance; | |
| female_crest_width = 0.70 + axial_clearance; | |
| socket_outer_d = 35.0; | |
| socket_h = 11.5; | |
| seal_compressed_h = 1.0; | |
| // Female rib centerline is offset about half a pitch from the external ridge. | |
| female_thread_z0 = seal_compressed_h + S_nom + pitch/2; | |
| female_thread_phase = 180; // tune orientation only; does not change fit class | |
| // ---------- feeder ---------- | |
| bowl_od = 118; | |
| bowl_wall = 2.4; | |
| bowl_floor = 2.6; | |
| bowl_total_h = 16.6; | |
| lid_thickness = 2.6; | |
| lid_overlap_h = 4.0; | |
| lid_fit_clearance = 0.35; | |
| outlet_bottom_z = 5.0; | |
| outlet_opening_top_z = 7.0; | |
| nominal_liquid_z = 9.0; | |
| feed_port_count = 6; | |
| feed_port_circle_d = 82; | |
| feed_port_d = 3.2; | |
| gasket_od = 29.0; | |
| gasket_id = 19.0; | |
| gasket_thickness = 1.5; | |
| down_tube_id = 12.0; | |
| down_tube_wall = 2.0; | |
| down_tube_od = down_tube_id + 2*down_tube_wall; | |
| outlet_slot_w = 8.0; | |
| outlet_slot_h = outlet_opening_top_z - outlet_bottom_z; | |
| outlet_slot_count = 4; | |
| // ---------- helical polyhedron ---------- | |
| // Sweeps a trapezoid in the r-z plane around the Z axis. | |
| // r_root is the outer/root radius; r_crest is the inward crest radius. | |
| module female_helical_rib(r_root, r_crest, z0, p, turns, root_w, crest_w, phase=0, steps_per_turn=120) { | |
| n = ceil(turns * steps_per_turn); | |
| pts = [ | |
| for (i=[0:n]) | |
| let( | |
| a = phase + 360*turns*i/n, | |
| zc = z0 + p*turns*i/n, | |
| ca = cos(a), sa = sin(a) | |
| ) | |
| each [ | |
| [r_root*ca, r_root*sa, zc-root_w/2], | |
| [r_crest*ca, r_crest*sa, zc-crest_w/2], | |
| [r_crest*ca, r_crest*sa, zc+crest_w/2], | |
| [r_root*ca, r_root*sa, zc+root_w/2] | |
| ] | |
| ]; | |
| side_faces = [ | |
| for (i=[0:n-1]) each [ | |
| [4*i+0,4*(i+1)+0,4*(i+1)+1,4*i+1], | |
| [4*i+1,4*(i+1)+1,4*(i+1)+2,4*i+2], | |
| [4*i+2,4*(i+1)+2,4*(i+1)+3,4*i+3], | |
| [4*i+3,4*(i+1)+3,4*(i+1)+0,4*i+0] | |
| ] | |
| ]; | |
| cap0 = [[0,1,2,3]]; | |
| j = 4*n; | |
| cap1 = [[j+3,j+2,j+1,j+0]]; | |
| polyhedron(points=pts, faces=concat(side_faces,cap0,cap1), convexity=20); | |
| } | |
| module female_28_400_socket() { | |
| difference() { | |
| union() { | |
| difference() { | |
| cylinder(d=socket_outer_d, h=socket_h); | |
| // Critical: this bore is LARGER than the male thread crest T. | |
| translate([0,0,-0.05]) cylinder(d=female_bore_d, h=socket_h+0.1); | |
| } | |
| // Inward internal thread rib. | |
| female_helical_rib( | |
| r_root=female_bore_d/2 + 0.35, | |
| r_crest=female_crest_d/2, | |
| z0=female_thread_z0, | |
| p=pitch, | |
| turns=thread_turns, | |
| root_w=female_root_width, | |
| crest_w=female_crest_width, | |
| phase=female_thread_phase | |
| ); | |
| } | |
| // Open flow path at the sealing floor. | |
| translate([0,0,-0.05]) cylinder(d=down_tube_id, h=seal_compressed_h+0.5); | |
| } | |
| } | |
| // Reference bottle neck, for visual verification only. | |
| module bottle_28_400_reference() { | |
| male_thread_h = (T_nom-E_nom)/2; | |
| color([0.3,0.65,0.95,0.45]) | |
| union() { | |
| translate([0,0,seal_compressed_h]) | |
| cylinder(d=E_nom, h=finish_h); | |
| // Simplified external ridge. It is intentionally shown only as reference. | |
| female_helical_rib( | |
| r_root=E_nom/2 - 0.20, | |
| r_crest=T_nom/2, | |
| z0=seal_compressed_h + S_nom, | |
| p=pitch, | |
| turns=1.0, | |
| root_w=1.65, | |
| crest_w=0.65, | |
| phase=0 | |
| ); | |
| } | |
| } | |
| module gasket() { | |
| difference() { | |
| cylinder(d=gasket_od, h=gasket_thickness); | |
| translate([0,0,-0.05]) cylinder(d=gasket_id, h=gasket_thickness+0.1); | |
| } | |
| } | |
| module bowl() { | |
| difference() { | |
| cylinder(d=bowl_od, h=bowl_total_h); | |
| translate([0,0,bowl_floor]) | |
| cylinder(d=bowl_od-2*bowl_wall, h=bowl_total_h-bowl_floor+0.1); | |
| } | |
| } | |
| module lid_plate() { | |
| difference() { | |
| cylinder(d=bowl_od-0.8, h=lid_thickness); | |
| for (a=[0:360/feed_port_count:359]) | |
| rotate([0,0,a]) translate([feed_port_circle_d/2,0,-0.05]) | |
| cylinder(d=feed_port_d, h=lid_thickness+0.1); | |
| translate([0,0,-0.05]) cylinder(d=down_tube_id, h=lid_thickness+0.1); | |
| } | |
| difference() { | |
| translate([0,0,-lid_overlap_h]) | |
| cylinder(d=bowl_od-2*bowl_wall-2*lid_fit_clearance, h=lid_overlap_h); | |
| translate([0,0,-lid_overlap_h-0.05]) | |
| cylinder(d=bowl_od-2*bowl_wall-2*lid_fit_clearance-2.0, h=lid_overlap_h+0.1); | |
| } | |
| } | |
| module down_tube() { | |
| tube_len = bowl_total_h - outlet_bottom_z; | |
| difference() { | |
| translate([0,0,-tube_len]) cylinder(d=down_tube_od, h=tube_len+0.2); | |
| translate([0,0,-tube_len-0.05]) cylinder(d=down_tube_id, h=tube_len+0.35); | |
| for (a=[0:360/outlet_slot_count:359]) | |
| rotate([0,0,a]) | |
| translate([down_tube_od/2-down_tube_wall-0.3,-outlet_slot_w/2,-tube_len-0.05]) | |
| cube([down_tube_wall+1.2,outlet_slot_w,outlet_slot_h]); | |
| } | |
| } | |
| module lid() { | |
| union() { | |
| lid_plate(); | |
| down_tube(); | |
| translate([0,0,lid_thickness]) female_28_400_socket(); | |
| for (a=[0:360/feed_port_count:359]) | |
| rotate([0,0,a]) translate([feed_port_circle_d/2,0,lid_thickness]) | |
| difference() { | |
| cylinder(d=12,h=1.2); | |
| translate([0,0,-0.05]) cylinder(d=feed_port_d,h=1.3); | |
| } | |
| } | |
| } | |
| module assembly() { | |
| assert(female_bore_d > T_max, "Female bore must clear bottle thread crest"); | |
| assert(female_crest_d > E_max, "Female rib crest must clear bottle neck root"); | |
| assert(nominal_liquid_z > outlet_opening_top_z, "Outlet opening is not submerged"); | |
| color("firebrick") # bowl(); | |
| color("gold") translate([0,0,bowl_total_h]) lid(); | |
| color("black") translate([0,0,bowl_total_h+lid_thickness]) gasket(); | |
| color([1,0,0,0.22]) | |
| translate([0,0,bowl_floor]) | |
| cylinder(d=bowl_od-2*bowl_wall-0.8, h=nominal_liquid_z-bowl_floor); | |
| if (show_bottle_reference) | |
| translate([0,0,bowl_total_h+lid_thickness]) bottle_28_400_reference(); | |
| } | |
| module thread_test() { | |
| // Short standalone socket for fitting the real bottle before printing the feeder. | |
| female_28_400_socket(); | |
| if (show_bottle_reference) bottle_28_400_reference(); | |
| } | |
| module socket_section() { | |
| intersection() { | |
| union() { | |
| female_28_400_socket(); | |
| if (show_bottle_reference) bottle_28_400_reference(); | |
| } | |
| translate([-25,-0.01,-1]) cube([50,25,20]); | |
| } | |
| } | |
| if (part=="bowl") bowl(); | |
| else if (part=="lid") lid(); | |
| else if (part=="gasket") gasket(); | |
| else if (part=="thread_test") thread_test(); | |
| else if (part=="socket_section") socket_section(); | |
| else assembly(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment