sirens/designs/openscad/siren_redesign.scad
Michael Winter 0fbf33b756 Restructure project with OpenSCAD redesign and v1 legacy code
- Move legacy FreeCAD files to v1/
- Add OpenSCAD programmatic CAD designs
- Add README and AGENTS.md documentation
- Add .gitignore
- Update firmware to v2 architecture
2026-03-29 14:24:42 +02:00

492 lines
15 KiB
OpenSCAD

/* [Siren General Parameters] */
siren_diameter = 125; // Base diameter - 121 for rotor to fit original siren
siren_height = 45; // Height of the rotor - 60 for rotor to fit original siren
number_of_ports = 3; // Number of ports
vertical_tolerance = 2; // Tolerance between stator and rotor
radial_tolerance = 1.5;
fit_reduction = 0.2;
pressure_zone = 0.5; // Percentage of diameter
round_radius = 4; // Rounding of top/bottom
$fn = $preview ? 32 : 128; // Number of fragments
/* [Stator Parameters] */
stator_wall_thickness = 8; // Stator wall thickness
number_of_mounting_holes = 4; // Number of mounting holes
screw_diameter = 3.2; // Diameter of screws - M3 clearance hole (3.2mm diameter)
screw_bore_diameter = 6.5;
screw_insert_diameter = 4.5;
screw_bore_depth = 2.5;
screw_length = 10 - (stator_wall_thickness - screw_bore_depth);
stator_screw_offset = siren_diameter/2 - stator_wall_thickness/2; //distance to screws
/* [Rotor Parameters] */
rotor_wall_thickness = 3; // Rotor wall thickness
hub_height = 3;
hub_diameter = 12;
blade_angle = 8;
rotor_height = siren_height - (vertical_tolerance * 2);
port_height = siren_height - (rotor_wall_thickness * 2) - (vertical_tolerance * 2); // Height of the port
/* [Motor Parameters] */
motor_shaft_diameter = 8; // Diameter of the motor body
motor_frame_diameter = 60;
motor_diameter = 28; // Diameter of the motor body
motor_height = 24;
magnet_diameter = 7;
motor_screw_x_offset = 9.5;
motor_screw_y_offset = 8;
motor_screw_offset = 8;
motor_frame_screw_offset = motor_frame_diameter/2 - stator_wall_thickness/2; //distance to screws
/* [Base Parameters] */
leg_screw_offset = motor_frame_diameter / 2 + 5;
leg_height = motor_height + 10;
// helper functions
module ports(diameter, height, ports, r_offset = 0) {
radius = diameter / 2;
angle = (180 / ports) - (r_offset * 6);
rotate([0, 0, 180 / ports])
for (i = [0:360/ports:360]) {
rotate([0, 0, i + (r_offset * 6 / 2)])
linear_extrude(height)
polygon(concat(
[[0, 0]], // Center point
[for (j = [0 : $fn])
[radius * cos(angle * j / $fn),
radius * sin(angle * j / $fn)]]
));
}
}
module column(outer_diameter, inner_diameter, height, z_offset = 0) {
difference(){
// Outer cylinder
cylinder(d = outer_diameter, h = height);
// Subtract inner cylinder
translate([0, 0, z_offset])
cylinder(d = inner_diameter, h = height - z_offset);
}
}
module rounded_column(outer_diameter, inner_diameter, height, z_offset = 0){
difference() {
// Rounded top
intersection() {
// Unrounded full cylinder
cylinder(d = outer_diameter, h = height);
// Add Minkowski rounded version
minkowski() {
cylinder(h = height - round_radius, d = outer_diameter - round_radius * 2);
sphere(r = round_radius);
}
}
// Subtract inner cylinder
translate([0, 0, z_offset])
cylinder(d = inner_diameter, h = height);
}
}
module counter_bored_holes(screw_offset){
// Subtract mounting holes for stator
for (i = [0:360/number_of_mounting_holes:360]) {
// Subtract screw hole
rotate([0, 0, i])
translate([screw_offset, 0, -1]) // Adjusted position
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
// Subtract counterbore
rotate([0, 0, i])
translate([screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
}
}
module mounting_holes(screw_offset){
for (i = [0:360/number_of_mounting_holes:360]) {
rotate([0, 0, i])
translate([screw_offset, 0, 0])
cylinder(d = screw_insert_diameter, h = screw_length);
}
}
// Linear Interpolation
function lerp(a, b, t) = a * (1 - t) + b * t;
// Arc between points function
function arc_between_points(p1, p2, height, steps=20) =
[for(t = [0:1/steps:1])
let(
x = lerp(p1.x, p2.x, t),
y = lerp(p1.y, p2.y, t),
// Parabolic arc height calculation
arc_height = height * (1 - pow(2*t-1, 2))
)
[x, y + arc_height]
];
function bezier_curve(t, p0, p1, p2, p3, p4) =
pow(1-t, 4) * p0 +
4 * pow(1-t, 3) * t * p1 +
6 * pow(1-t, 2) * pow(t, 2) * p2 +
4 * (1-t) * pow(t, 3) * p3 +
pow(t, 4) * p4;
function curved_polygon(points, steps, x_shift, y_shift) =
let(
left_curve = [for (t = [0 : 1/steps : 1])
bezier_curve(t, points[0], points[1], points[2], points[3], points[4])
],
right_curve = [for (pt = left_curve)
[pt[0] + x_shift, pt[1] + y_shift]
]
)
concat(left_curve, [for (i = [len(right_curve)-1 : -1 : 0]) right_curve[i]]);
module stator() {
difference() {
// Create column
column(siren_diameter, siren_diameter - (stator_wall_thickness * 2), siren_height);
// Subtract ports
translate([0, 0, (siren_height - port_height) / 2])
ports(siren_diameter, port_height, number_of_ports);
mounting_holes(stator_screw_offset);
translate([0, 0, siren_height - screw_length])
mounting_holes(stator_screw_offset);
}
}
module impeller_blade(radius, rotor_height){
// Generate points
points1 = arc_between_points([0, -hub_diameter / 2], [radius, 0], -blade_angle);
points2 = [for(p = [len(points1)-1:-1:0]) points1[p] - [0, -rotor_wall_thickness]];
// Create the blade by combining top and bottom points
linear_extrude(height = rotor_height)
polygon(concat(points1, points2));
}
module impeller_blades_curved(radius, rotor_height){
difference() {
union() {
// Rotate and create blades
for (i = [0:360/number_of_ports:360]) {
rotate([0, 0, i])
impeller_blade(radius, rotor_height);
}
}
top_rounding_radius = 5;
bottom_rounding_radius = rotor_height / 2 - 1;
//bottom_rounding_radius = rotor_height / 2 - 10; // this needs to be adjusted based on the rotor diameter and height
top_diameter = (siren_diameter * pressure_zone) + (top_rounding_radius * 2);
bottom_diameter = (siren_diameter * pressure_zone) - (2 * bottom_rounding_radius);
top_height = top_rounding_radius + 10;
bottom_height = rotor_height - bottom_rounding_radius - (top_rounding_radius * 2) + 2;
translate([0, 0, rotor_height - top_rounding_radius])
union(){
difference() {
// Column
cylinder(d = top_diameter, h = top_height);
// Top torus
rotate_extrude()
translate([top_diameter / 2, 0, 0])
circle(r = top_rounding_radius);
}
rotate([180, 0, 0])
minkowski() {
// Original cylinder, reduced by twice the rounding radius
cylinder(d = bottom_diameter, h = bottom_height);
// Sphere for rounding
sphere(r = bottom_rounding_radius);
}
}
}
}
module rotor() {
rotor_diameter = siren_diameter - (stator_wall_thickness * 2) - (radial_tolerance * 2);
rotor_inner_diameter = rotor_diameter - (rotor_wall_thickness * 2);
difference() {
union(){
difference() {
column(rotor_diameter, rotor_inner_diameter, rotor_height, rotor_wall_thickness);
// Subtract ports
translate([0, 0, (rotor_height - port_height) / 2])
ports(rotor_diameter, port_height, number_of_ports);
}
// Add blades
impeller_blades_curved(rotor_inner_diameter / 2, rotor_height);
}
translate([0, 0, 0])
hub_attachment(fit_reduction);
}
}
module hub(aug = 0) {
height = (rotor_wall_thickness * 2) + hub_height;
difference(){
union(){
hub_attachment(aug);
cylinder(d = motor_shaft_diameter + 2, h = height);
}
intersection(){
cylinder(d = motor_shaft_diameter, h = height);
cube([motor_shaft_diameter, motor_shaft_diameter-1, height * 2], center = true);
};
cylinder(d = motor_shaft_diameter, h = 2);
}
}
module hub_attachment(aug = 0) {
insert_width = 2;
outer_diameter = hub_diameter + (insert_width * 6) + (aug * 2);
mid_diameter = hub_diameter + (insert_width * 2) + (aug * 2);
inner_diameter = outer_diameter - (insert_width * 2) - (aug * 4);
difference(){
column(outer_diameter, inner_diameter, rotor_wall_thickness);
//translate([0, 0, -10])
ports(outer_diameter, rotor_wall_thickness, number_of_ports, aug * 2);
}
translate([0, 0, rotor_wall_thickness])
cylinder(d = outer_diameter, rotor_wall_thickness);
cylinder(d = mid_diameter, rotor_wall_thickness * 2);
}
module stator_top() {
difference(){
rounded_column(
siren_diameter,
siren_diameter * pressure_zone,
stator_wall_thickness
);
counter_bored_holes(stator_screw_offset);
}
}
module stator_bottom() {
//rotate([180, 0, 0])
difference() {
rounded_column(
siren_diameter,
motor_diameter + (radial_tolerance * 2),
stator_wall_thickness
);
counter_bored_holes(stator_screw_offset);
// Subtract mounting holes for motor frame
rotate([180, 0, 0])
translate([0, 0, -stator_wall_thickness])
counter_bored_holes(motor_frame_screw_offset);
rotate([180, 0, 0])
translate([0, 0, -stator_wall_thickness])
counter_bored_holes(leg_screw_offset);
translate([0, 0, stator_wall_thickness])
rotate([0, 0, -45/2 - 0.6])
for (i = [0:360/number_of_mounting_holes:360]) {
rotate([0, 0, i]) {
leg(fit_reduction);
}
}
}
}
module motor_frame() {
//rotate([180, 0, 0])
//translate([0, 0, -stator_wall_thickness])
motor_frame_height = motor_height - stator_wall_thickness - vertical_tolerance;
difference(){
union(){
inner_diameter = motor_frame_diameter - (stator_wall_thickness * 2);
difference(){
rounded_column(
motor_frame_diameter,
inner_diameter,
motor_frame_height + stator_wall_thickness,
-stator_wall_thickness
);
translate([0, 0, stator_wall_thickness])
ports(inner_diameter, motor_frame_height, number_of_mounting_holes);
//translate([0, 0, -stator_wall_thickness])
//rotate([0, 0, -45/2])
ports(motor_frame_diameter, motor_frame_height, number_of_mounting_holes);
rotate([0, 0, 45/2])
mounting_holes(motor_frame_screw_offset);
}
translate([0, 0, motor_frame_height])
column(motor_screw_x_offset * 2 + (radial_tolerance * 2) , magnet_diameter + (radial_tolerance * 2), stator_wall_thickness);
}
// Subtract mounting holes for motor
translate([0, 0, motor_frame_height])
rotate([0, 0, 45/2])
for (i = [0:360/number_of_mounting_holes:360]) {
rotate([0, 0, i]) {
// Use modulo to alternate between x and y offsets
motor_screw_offset = (floor(i / (360/number_of_mounting_holes)) % 2 == 0) ?
motor_screw_x_offset : motor_screw_y_offset;
translate([motor_screw_offset, 0, 0])
cylinder(d = screw_diameter, h = stator_wall_thickness);
}
}
translate([0, 0, motor_frame_height])
cylinder(d = magnet_diameter + (radial_tolerance * 2), h = stator_wall_thickness);
}
}
module leg(aug = 0) {
motor_spacing = 15;
points = [
[leg_screw_offset + 10, leg_height],
[leg_screw_offset + 5, leg_height-5],
[leg_screw_offset + 0, leg_height / 2],
[leg_screw_offset + 5, 5],
[leg_screw_offset + 10, 0]
];
rotate_extrude(angle = 360/8) {
polygon(curved_polygon(
points,
steps = 100,
x_shift = -stator_wall_thickness / 2,
y_shift = 0
));
}
rotate([0, 0, 12 - (aug / 2)])
translate([0, 0, -1])
difference(){
rotate_extrude(angle = 360/16 + aug) {
translate([leg_screw_offset - ((screw_insert_diameter + radial_tolerance) / 2) - aug, 0, 0])
square([screw_insert_diameter + radial_tolerance + aug * 2, leg_height + 2]);
}
if(aug == 0){
rotate([0, 0, 45/4 - aug])
//translate([0, 0, -2])
mounting_holes(leg_screw_offset);
rotate([0, 0, 45/4 - aug])
translate([0, 0, leg_height - screw_length + 2])
mounting_holes(leg_screw_offset);
}
}
}
module base() {
difference(){
rounded_column(
siren_diameter,
(leg_screw_offset - ((screw_diameter + radial_tolerance) / 2)) * 2 - 5,
stator_wall_thickness
);
counter_bored_holes(stator_screw_offset);
translate([0, 0, stator_wall_thickness])
rotate([180, 0, 0])
//counter_bored_holes(leg_screw_offset - ((screw_diameter + tolerance) / 2));
//rotate([0, 0, -45/2])
counter_bored_holes(leg_screw_offset);
translate([0, 0, stator_wall_thickness])
rotate([0, 0, -45/2 - 0.6])
for (i = [0:360/number_of_mounting_holes:360]) {
rotate([0, 0, i]) {
leg(fit_reduction);
}
}
}
}
//rotate([0, 0, 0])
//translate([0, 0, 0])
//rotor();
//motor_shaft_diameter = 8.2;
hub(0.1);
//stator();
//base();
//stator_bottom();
//rotate([0, 0, 0])
//translate([siren_diameter, 0, vertical_tolerance])
//rotor();
//stator_bottom();
//rotate([0, 0, 67.5])
//motor_frame();
//rotate([0, 0, 0])
//translate([0, 0, siren_height + 2])
//stator_top();
//rotate([180, 0, 0])
//translate([0, 0, 2])
//stator_bottom();
//rotate([180, 0, 45])
//translate([0, 0, stator_wall_thickness + 5])
//motor_frame();
//leg();
//base();
/*
rotate([0, 0, 0])
translate([0, 0, -leg_height - 15 - stator_wall_thickness - 2])
base();
rotate([0, 0, -45/2])
translate([0, 0, -leg_height - 15])
for (i = [0:360/number_of_mounting_holes:360]) {
rotate([0, 0, i]) {
leg();
}
}
*/
/*
//translate([0, 0, 2])
intersection(){
//color("red")
rotor();
cylinder(d = 30, h = 10);
}
translate([040, 0, 0])
hub();
*/